<commit_before>﻿using Octokit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGServiceInterface;
using TGServiceInterface.Components;

namespace TGControlPanel
{
	sealed partial class TestMergeManager : ServerOpForm
	{
		/// <summary>
		/// Error message format used when <see cref="ITGRepository.MergedPullRequests(out string)"/> fails
		/// </summary>
		const string MergedPullsError = "Error retrieving currently merged pull requests: {0}";

		/// <summary>
		/// The <see cref="IInterface"/> connected to an <see cref="ITGInstance"/> to handle the pull requests for
		/// </summary>
		readonly IInterface currentInterface;

		/// <summary>
		/// The <see cref="GitHubClient"/> to use to read PR lists
		/// </summary>
		readonly GitHubClient client;

		/// <summary>
		/// The owner of the target <see cref="Repository"/>
		/// </summary>
		string repoOwner;
		/// <summary>
		/// The name of the target <see cref="Repository"/>
		/// </summary>
		string repoName;

		/// <summary>
		/// Construct a <see cref="TestMergeManager"/>
		/// </summary>
		/// <param name="interfaceToUse">The <see cref="IInterface"/> to use for managing the <see cref="ITGInstance"/></param>
		/// <param name="clientToUse">The <see cref="GitHubClient"/> to use for getting pull request information</param>
		public TestMergeManager(IInterface interfaceToUse, GitHubClient clientToUse)
		{
			InitializeComponent();
			DialogResult = DialogResult.Cancel;
			UpdateToRemoteRadioButton.Checked = true;
			currentInterface = interfaceToUse;
			client = clientToUse;
			Load += PullRequestManager_Load;
		}

		/// <summary>
		/// Populate <see cref="PullRequestListBox"/> with <see cref="PullRequestInfo"/> from github and check off ones that are currently merged. Prompts the user to login to GitHub if they hit the rate limit
		/// </summary>
		async void LoadPullRequests()
		{
			try
			{
				Enabled = false;
				UseWaitCursor = true;
				try
				{
					PullRequestListBox.Items.Clear();

					var repo = currentInterface.GetComponent<ITGRepository>();
					string error = null;
					List<PullRequestInfo> pulls = null;
					
					//get started on this while we're processing here
					var pullsRequest = Task.Factory.StartNew(() => pulls = repo.MergedPullRequests(out error));

					//Search for open PRs
					Enabled = false;
					UseWaitCursor = true;
					SearchIssuesResult result;
					try
					{
						result = await client.Search.SearchIssues(new SearchIssuesRequest
						{
							Repos = new RepositoryCollection { { repoOwner, repoName } },
							State = ItemState.Open,
							Type = IssueTypeQualifier.PullRequest
						});
					}
					finally
					{
						Enabled = true;
						UseWaitCursor = false;
					}

					//now we need to know what's merged
					await pullsRequest;
					if (pulls == null)
						MessageBox.Show(String.Format(MergedPullsError, error));

					//insert the open pull requests, checking already merged once
					foreach (var I in result.Items)
						InsertPullRequest(I, pulls.RemoveAll(x => x.Number == I.Number) > 0);

					//insert remaining merged pulls
					foreach (var I in pulls)
						InsertItem(String.Format("#{0} - {1}", I.Number, I.Title), true, true);
				}
				finally
				{
					Enabled = true;
					UseWaitCursor = false;
				}
			}
			catch (ForbiddenException)
			{
				if (client.Credentials.AuthenticationType == AuthenticationType.Anonymous)  //assume request limit hit
				{
					if(Program.RateLimitPrompt(client))
						LoadPullRequests();
				}
				else
					throw;
			}
		}

		/// <summary>
		/// Format an entry for <paramref name="I"/> and insert it into <see cref="PullRequestListBox"/>
		/// </summary>
		/// <param name="I">The <see cref="Issue"/> to format</param>
		/// <param name="isChecked">If the item should be checked</param>
		void InsertPullRequest(Issue I, bool isChecked)
		{
			bool needsTesting = false;
			foreach (var J in I.Labels)
				if (J.Name.ToLower().Contains("test"))
				{
					needsTesting = true;
					break;
				}
			var itemString = String.Format("#{0} - {1}{2}", I.Number, I.Title, I.PullRequest.Merged ? " - MERGED ON REMOTE" : needsTesting ? " - TESTING REQUESTED" : "");
			InsertItem(itemString, needsTesting, isChecked);
		}

		/// <summary>
		/// Insert an <paramref name="itemString"/> into <see cref="PullRequestListBox"/>
		/// </summary>
		/// <param name="itemString">The <see cref="string"/> to insert</param>
		/// <param name="prioritize">If this or <paramref name="isChecked"/> is <see langword="true"/>, <paramref name="itemString"/> will be inserted at the top of <see cref="PullRequestListBox"/> as opposed to the bottom</param>
		/// <param name="isChecked">If the item should be checked</param>
		void InsertItem(string itemString, bool prioritize, bool isChecked)
		{
			prioritize = prioritize || isChecked;
			if (prioritize)
			{
				PullRequestListBox.Items.Insert(0, itemString);
				if (isChecked)
					PullRequestListBox.SetItemChecked(0, true);
			}
			else
				PullRequestListBox.Items.Add(itemString, isChecked);
		}

		/// <summary>
		/// Called when the <see cref="TestMergeManager"/> is loaded. Sets <see cref="repoOwner"/> and <see cref="repoName"/>
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="e">The <see cref="EventArgs"/></param>
		void PullRequestManager_Load(object sender, EventArgs e)
		{
			Enabled = false;
			var repo = currentInterface.GetComponent<ITGRepository>();
			if(!Program.GetRepositoryRemote(repo, out repoOwner, out repoName))
			{
				Close();
				return;
			}
			LoadPullRequests();
		}

		/// <summary>
		/// Calls <see cref="ITGRepository.GenerateChangelog(out string)"/> and shows the user an error prompt if it fails
		/// </summary>
		/// <param name="repo">The <see cref="ITGRepository"/> to call <see cref="ITGRepository.GenerateChangelog(out string)"/> on</param>
		async void GenerateChangelog(ITGRepository repo)
		{
			string error = null;
			await WrapServerOp(() => repo.GenerateChangelog(out error));
			if (error != null)
				MessageBox.Show(String.Format("Error generating changelog: {0}", error));
		}

		/// <summary>
		/// Called when the <see cref="ApplyButton"/> is clicked. Calls <see cref="ITGRepository.Update(bool)"/> if necessary, merge pull requests, and call <see cref="ITGCompiler.Compile(bool)"/>. Closes the <see cref="TestMergeManager"/> if appropriate
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="e">The <see cref="EventArgs"/></param>
		async void ApplyButton_Click(object sender, EventArgs e)
		{
			Enabled = false;
			UseWaitCursor = true;
			ApplyingPullRequestsLabel.Visible = true;
			ApplyingPullRequestsProgressBar.Visible = true;
			PullRequestListBox.Visible = false;
			try
			{
				//so first collect a list of pulls that are checked
				var pulls = new List<int>();
				foreach (var I in PullRequestListBox.CheckedItems)
					pulls.Add(Convert.ToInt32(((string)I).Split(' ')[0].Substring(1)));

				var repo = currentInterface.GetComponent<ITGRepository>();

				string error = null;
				//Do standard repo updates
				if (UpdateToRemoteRadioButton.Checked)
					await WrapServerOp(() => error = repo.Update(true));
				else if (UpdateToOriginRadioButton.Checked)
					await WrapServerOp(() => error = repo.Reset(true));

				if (error != null)
				{
					MessageBox.Show(String.Format("Error updating repository: {0}", error));
					return;
				}

				if (UpdateToRemoteRadioButton.Checked)
				{
					GenerateChangelog(repo);
					await WrapServerOp(() => error = repo.SynchronizePush());
					if (error != null)
						MessageBox.Show(String.Format("Error sychronizing repo: {0}", error));
				}

				//Merge the PRs, collect errors
				IList<string> errors = null;
				await WrapServerOp(() =>
				{
					errors = new List<string>();
					foreach (var I in pulls)
					{
						var res = repo.MergePullRequest(I);
						if (res != null)
							errors.Add(String.Format("Error merging PR #{0}: {1}", I, res));
					}
				});

				//Show any errors
				foreach (var I in errors)
					MessageBox.Show(I);
				if (errors.Count != 0)
					return;

				if (pulls.Count > 0)
					//regen the changelog
					GenerateChangelog(repo);

				//Start the compile
				if (!currentInterface.GetComponent<ITGCompiler>().Compile(pulls.Count == 1))
					MessageBox.Show("Could not start compilation!");
				else
					MessageBox.Show("Test merges updated and compilation started!");<commit_msg>Seperate entry in TestMergeManager for outdated PRs<commit_after>﻿using Octokit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGServiceInterface;
using TGServiceInterface.Components;

namespace TGControlPanel
{
	sealed partial class TestMergeManager : ServerOpForm
	{
		/// <summary>
		/// Error message format used when <see cref="ITGRepository.MergedPullRequests(out string)"/> fails
		/// </summary>
		const string MergedPullsError = "Error retrieving currently merged pull requests: {0}";

		/// <summary>
		/// The <see cref="IInterface"/> connected to an <see cref="ITGInstance"/> to handle the pull requests for
		/// </summary>
		readonly IInterface currentInterface;

		/// <summary>
		/// The <see cref="GitHubClient"/> to use to read PR lists
		/// </summary>
		readonly GitHubClient client;

		/// <summary>
		/// The owner of the target <see cref="Repository"/>
		/// </summary>
		string repoOwner;
		/// <summary>
		/// The name of the target <see cref="Repository"/>
		/// </summary>
		string repoName;

		/// <summary>
		/// Construct a <see cref="TestMergeManager"/>
		/// </summary>
		/// <param name="interfaceToUse">The <see cref="IInterface"/> to use for managing the <see cref="ITGInstance"/></param>
		/// <param name="clientToUse">The <see cref="GitHubClient"/> to use for getting pull request information</param>
		public TestMergeManager(IInterface interfaceToUse, GitHubClient clientToUse)
		{
			InitializeComponent();
			DialogResult = DialogResult.Cancel;
			UpdateToRemoteRadioButton.Checked = true;
			currentInterface = interfaceToUse;
			client = clientToUse;
			Load += PullRequestManager_Load;
		}

		/// <summary>
		/// Populate <see cref="PullRequestListBox"/> with <see cref="PullRequestInfo"/> from github and check off ones that are currently merged. Prompts the user to login to GitHub if they hit the rate limit
		/// </summary>
		async void LoadPullRequests()
		{
			try
			{
				Enabled = false;
				UseWaitCursor = true;
				try
				{
					PullRequestListBox.Items.Clear();

					var repo = currentInterface.GetComponent<ITGRepository>();
					string error = null;
					List<PullRequestInfo> pulls = null;
					
					//get started on this while we're processing here
					var pullsRequest = Task.Factory.StartNew(() => pulls = repo.MergedPullRequests(out error));

					//Search for open PRs
					Enabled = false;
					UseWaitCursor = true;
					SearchIssuesResult result;
					try
					{
						result = await client.Search.SearchIssues(new SearchIssuesRequest
						{
							Repos = new RepositoryCollection { { repoOwner, repoName } },
							State = ItemState.Open,
							Type = IssueTypeQualifier.PullRequest
						});
					}
					finally
					{
						Enabled = true;
						UseWaitCursor = false;
					}

					//now we need to know what's merged
					await pullsRequest;
					if (pulls == null)
						MessageBox.Show(String.Format(MergedPullsError, error));

					//insert the open pull requests, checking already merged once
					foreach (var I in result.Items)
					{
						bool alreadyMerged = false;
						var pull = pulls.Where(x => x.Number == I.Number).FirstOrDefault();
						if (pull != null)
						{
							//we need the full info for this PR
							alreadyMerged = (await client.PullRequest.Get(repoOwner, repoName, I.Number)).Head.Sha == pull.Sha;
							if (alreadyMerged)
								pulls.Remove(pull);
						}
						InsertPullRequest(I, alreadyMerged);
					}

					//insert remaining merged pulls
					foreach (var I in pulls)
						InsertItem(String.Format("#{0} - {1} - OUTDATED: {2}", I.Number, I.Title, I.Sha), true, true);
				}
				finally
				{
					Enabled = true;
					UseWaitCursor = false;
				}
			}
			catch (ForbiddenException)
			{
				if (client.Credentials.AuthenticationType == AuthenticationType.Anonymous)  //assume request limit hit
				{
					if(Program.RateLimitPrompt(client))
						LoadPullRequests();
				}
				else
					throw;
			}
		}

		/// <summary>
		/// Format an entry for <paramref name="I"/> and insert it into <see cref="PullRequestListBox"/>
		/// </summary>
		/// <param name="I">The <see cref="Issue"/> to format</param>
		/// <param name="isChecked">If the item should be checked</param>
		void InsertPullRequest(Issue I, bool isChecked)
		{
			bool needsTesting = false;
			foreach (var J in I.Labels)
				if (J.Name.ToLower().Contains("test"))
				{
					needsTesting = true;
					break;
				}
			var itemString = String.Format("#{0} - {1}{2}", I.Number, I.Title, I.PullRequest.Merged ? " - MERGED ON REMOTE" : needsTesting ? " - TESTING REQUESTED" : "");
			InsertItem(itemString, needsTesting, isChecked);
		}

		/// <summary>
		/// Insert an <paramref name="itemString"/> into <see cref="PullRequestListBox"/>
		/// </summary>
		/// <param name="itemString">The <see cref="string"/> to insert</param>
		/// <param name="prioritize">If this or <paramref name="isChecked"/> is <see langword="true"/>, <paramref name="itemString"/> will be inserted at the top of <see cref="PullRequestListBox"/> as opposed to the bottom</param>
		/// <param name="isChecked">If the item should be checked</param>
		void InsertItem(string itemString, bool prioritize, bool isChecked)
		{
			prioritize = prioritize || isChecked;
			if (prioritize)
			{
				PullRequestListBox.Items.Insert(0, itemString);
				if (isChecked)
					PullRequestListBox.SetItemChecked(0, true);
			}
			else
				PullRequestListBox.Items.Add(itemString, isChecked);
		}

		/// <summary>
		/// Called when the <see cref="TestMergeManager"/> is loaded. Sets <see cref="repoOwner"/> and <see cref="repoName"/>
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="e">The <see cref="EventArgs"/></param>
		void PullRequestManager_Load(object sender, EventArgs e)
		{
			Enabled = false;
			var repo = currentInterface.GetComponent<ITGRepository>();
			if(!Program.GetRepositoryRemote(repo, out repoOwner, out repoName))
			{
				Close();
				return;
			}
			LoadPullRequests();
		}

		/// <summary>
		/// Calls <see cref="ITGRepository.GenerateChangelog(out string)"/> and shows the user an error prompt if it fails
		/// </summary>
		/// <param name="repo">The <see cref="ITGRepository"/> to call <see cref="ITGRepository.GenerateChangelog(out string)"/> on</param>
		async void GenerateChangelog(ITGRepository repo)
		{
			string error = null;
			await WrapServerOp(() => repo.GenerateChangelog(out error));
			if (error != null)
				MessageBox.Show(String.Format("Error generating changelog: {0}", error));
		}

		/// <summary>
		/// Called when the <see cref="ApplyButton"/> is clicked. Calls <see cref="ITGRepository.Update(bool)"/> if necessary, merge pull requests, and call <see cref="ITGCompiler.Compile(bool)"/>. Closes the <see cref="TestMergeManager"/> if appropriate
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="e">The <see cref="EventArgs"/></param>
		async void ApplyButton_Click(object sender, EventArgs e)
		{
			Enabled = false;
			UseWaitCursor = true;
			ApplyingPullRequestsLabel.Visible = true;
			ApplyingPullRequestsProgressBar.Visible = true;
			PullRequestListBox.Visible = false;
			try
			{
				//so first collect a list of pulls that are checked
				var pulls = new Dictionary<int, string>();
				foreach (var I in PullRequestListBox.CheckedItems)
				{
					var S = (string)I;
					string mergedSha = null;
					var splits = S.Split(' ');
					if(S.Contains(" - OUTDATED: "))
						mergedSha = splits[splits.Length - 1];
					var key = Convert.ToInt32((splits[0].Substring(1)));
					try
					{
						pulls.Add(key, mergedSha);
					}
					catch
					{
						MessageBox.Show(String.Format("Checked both keep and update option for #{0}", key), "Error");
						return;
					}
				}

				var repo = currentInterface.GetComponent<ITGRepository>();

				string error = null;
				//Do standard repo updates
				if (UpdateToRemoteRadioButton.Checked)
					await WrapServerOp(() => error = repo.Update(true));
				else if (UpdateToOriginRadioButton.Checked)
					await WrapServerOp(() => error = repo.Reset(true));

				if (error != null)
				{
					MessageBox.Show(String.Format("Error updating repository: {0}", error));
					return;
				}

				if (UpdateToRemoteRadioButton.Checked)
				{
					GenerateChangelog(repo);
					await WrapServerOp(() => error = repo.SynchronizePush());
					if (error != null)
						MessageBox.Show(String.Format("Error sychronizing repo: {0}", error));
				}

				//Merge the PRs, collect errors
				IList<string> errors = null;
				await WrapServerOp(() =>
				{
					errors = new List<string>();
					foreach (var I in pulls)
					{
						var res = repo.MergePullRequest(I.Key, I.Value);
						if (res != null)
							errors.Add(String.Format("Error merging PR #{0}: {1}", I, res));
					}
				});

				//Show any errors
				foreach (var I in errors)
					MessageBox.Show(I);
				if (errors.Count != 0)
					return;

				if (pulls.Count > 0)
					//regen the changelog
					GenerateChangelog(repo);

				//Start the compile
				if (!currentInterface.GetComponent<ITGCompiler>().Compile(pulls.Count == 1))
					MessageBox.Show("Could not start compilation!");
				else
					MessageBox.Show("Test merges updated and compilation started!");
{'fraction_non_alphanumeric': 0.13558848196812973, 'fraction_numerical': 0.002460162147050601, 'mean_word_length': 3.1576011157601114, 'pattern_counts': {'":': 0, '<': 183, '<?xml version=': 0, '>': 200, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4534027', 'n_tokens_mistral': 6671, 'n_tokens_neox': 5311, 'n_words': 1483}