private void FillSubGrid(bool isRefreshNeeded = false, string grouping95 = "") { List <string> listSelectedVersions = listVersionsFilter.SelectedItems.OfType <string>().ToList(); if (listSelectedVersions.Contains("All")) { listSelectedVersions.Clear(); } if (isRefreshNeeded && listSelectedVersions.IsNullOrEmpty()) { if (!MsgBox.Show(MsgBoxButtons.YesNo, "All bug submissions are going to be downloaded...\r\nAre you sure about this?")) { return; } } Action loadingProgress = null; Cursor = Cursors.WaitCursor; #region gridSubs columns gridSubs.BeginUpdate(); gridSubs.ListGridColumns.Clear(); gridSubs.ListGridColumns.Add(new GridColumn("Submitter", 140)); gridSubs.ListGridColumns.Add(new GridColumn("Vers.", 55, GridSortingStrategy.VersionNumber)); if (comboGrouping.SelectedIndex == 0) //Group by 'None' { gridSubs.ListGridColumns.Add(new GridColumn("DateTime", 75, GridSortingStrategy.DateParse)); } else { gridSubs.ListGridColumns.Add(new GridColumn("#", 30, HorizontalAlignment.Right, GridSortingStrategy.AmountParse)); } gridSubs.ListGridColumns.Add(new GridColumn("Flag", 50, HorizontalAlignment.Center)); gridSubs.ListGridColumns.Add(new GridColumn("Msg Text", 0)); gridSubs.AllowSortingByColumn = true; gridSubs.ListGridRows.Clear(); #endregion bugSubmissionControl.ClearCustomerInfo(); bugSubmissionControl.SetTextDevNoteEnabled(false); if (isRefreshNeeded) { loadingProgress = ODProgress.Show(ODEventType.BugSubmission, typeof(BugSubmissionEvent), Lan.g(this, "Refreshing Data") + "..."); #region Refresh Logic if (_viewMode.In(FormBugSubmissionMode.ViewOnly, FormBugSubmissionMode.ValidationMode)) { _listAllSubs = ListViewedSubs; } else { BugSubmissionEvent.Fire(ODEventType.BugSubmission, Lan.g(this, "Refreshing Data: Bugs")); _listAllSubs = BugSubmissions.GetAllInRange(dateRangePicker.GetDateTimeFrom(), dateRangePicker.GetDateTimeTo(), listSelectedVersions); } try { BugSubmissionEvent.Fire(ODEventType.BugSubmission, Lan.g(this, "Refreshing Data: Patients")); _dictPatients = RegistrationKeys.GetPatientsByKeys(_listAllSubs.Select(x => x.RegKey).ToList()); } catch (Exception e) { e.DoNothing(); _dictPatients = new Dictionary <string, Patient>(); } BugSubmissionEvent.Fire(ODEventType.BugSubmission, Lan.g(this, "Refreshing Data: JobLinks")); _listJobLinks = JobLinks.GetManyForType(JobLinkType.Bug, _listAllSubs.Select(x => x.BugId).Where(x => x != 0).Distinct().ToList()); #endregion } #region Filter Logic BugSubmissionEvent.Fire(ODEventType.BugSubmission, "Filtering Data"); List <BugSubmission> listFilteredSubs = null; List <string> listSelectedRegKeys = comboRegKeys.ListSelectedItems.Select(x => (string)x).ToList(); if (listSelectedRegKeys.Contains("All")) { listSelectedRegKeys.Clear(); } List <string> listStackFilters = textStackFilter.Text.Split(',') .Where(x => !string.IsNullOrWhiteSpace(x)) .Select(x => x.ToLower()).ToList(); List <string> listPatNumFilters = textPatNums.Text.Split(',') .Where(x => !string.IsNullOrWhiteSpace(x)) .Select(x => x.ToLower()).ToList(); _listAllSubs.ForEach(x => x.TagCustom = null); List <string> listCategoryFilters = textCategoryFilters.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string msgText = textMsgText.Text; string devNoteFilter = textDevNoteFilter.Text; DateTime dateTimeFrom = dateRangePicker.GetDateTimeFrom(); DateTime dateTimeTo = dateRangePicker.GetDateTimeTo(); //Filter the list of all bug submissions and then order it by program version and submission date time so that the grouping is predictable. listFilteredSubs = _listAllSubs.Where(x => PassesFilterValidation(x, listCategoryFilters, listSelectedRegKeys, listStackFilters, listPatNumFilters, listSelectedVersions, grouping95, msgText, devNoteFilter, dateTimeFrom, dateTimeTo) ) .OrderByDescending(x => new Version(x.ProgramVersion)) .ThenByDescending(x => x.SubmissionDateTime) .ToList(); if (isRefreshNeeded) { FillPatNameFilter(_listAllSubs); } #endregion #region Grouping Logic List <BugSubmission> listGridSubmissions = new List <BugSubmission>(); BugSubmissionEvent.Fire(ODEventType.BugSubmission, "Grouping Data"); switch (comboGrouping.SelectedIndex) { case 0: #region None foreach (BugSubmission bugSubmission in listFilteredSubs) { AddGroupedSubsToGridSubs(listGridSubmissions, new List <BugSubmission>() { bugSubmission }); } listShowHideOptions.SetSelected(3, false); //Deselect 'None' _minGroupingCount = -1; butAddJob.Enabled = true; #endregion break; case 1: #region RegKey/Ver/Stack listFilteredSubs.GroupBy(x => new { x.BugId, x.RegKey, x.ProgramVersion, x.ExceptionMessageText, x.ExceptionStackTrace }) .ToDictionary(x => x.Key, x => x.ToList()) .ForEach(x => AddGroupedSubsToGridSubs(listGridSubmissions, x.Value)); butAddJob.Enabled = true; #endregion break; case 2: #region StackTrace listFilteredSubs.GroupBy(x => new { x.BugId, x.ExceptionMessageText, x.ExceptionStackTrace }) .ToDictionary(x => x.Key, x => x.ToList()) .ForEach(x => AddGroupedSubsToGridSubs(listGridSubmissions, x.Value)); butAddJob.Enabled = true; #endregion break; case 3: #region 95% //At this point all bugSubmissions in listFilteredSubs is at least a 95% match. Group them all together in a single row. AddGroupedSubsToGridSubs(listGridSubmissions, listFilteredSubs); butAddJob.Enabled = true; #endregion break; case 4: #region StackSig listFilteredSubs.GroupBy(x => new { x.BugId, x.ExceptionMessageText, x.OdStackSignature }) .ToDictionary(x => x.Key, x => x.ToList()) .ForEach(x => AddGroupedSubsToGridSubs(listGridSubmissions, x.Value)); butAddJob.Enabled = false; //Can not add jobs in this mode. #endregion break; case 5: #region StackSimple listFilteredSubs.GroupBy(x => new { x.BugId, x.ExceptionMessageText, x.SimplifiedStackTrace }) .ToDictionary(x => x.Key, x => x.ToList()) .ForEach(x => AddGroupedSubsToGridSubs(listGridSubmissions, x.Value)); butAddJob.Enabled = false; //Can not add jobs in this mode. #endregion break; case 6: #region Hash listFilteredSubs.GroupBy(x => new { x.BugId, x.BugSubmissionHashNum }) .ToDictionary(x => x.Key, x => x.ToList()) .ForEach(x => AddGroupedSubsToGridSubs(listGridSubmissions, x.Value)); butAddJob.Enabled = false; //Can not add jobs in this mode. #endregion break; } if (_minGroupingCount > 0) { listGridSubmissions.RemoveAll(x => (x.TagCustom as List <BugSubmission>).Count < _minGroupingCount); } #endregion #region Sorting Logic BugSubmissionEvent.Fire(ODEventType.BugSubmission, "Sorting Data"); switch (comboSortBy.SelectedIndex) { case 0: listGridSubmissions = listGridSubmissions.OrderByDescending(x => new Version(x.ProgramVersion)) .ThenByDescending(x => GetGroupCount(x)) .ThenByDescending(x => x.SubmissionDateTime).ToList(); break; } #endregion #region Fill gridSubs BugSubmissionEvent.Fire(ODEventType.BugSubmission, "Filling Grid"); foreach (BugSubmission sub in listGridSubmissions) { gridSubs.ListGridRows.Add(GetODGridRowForSub(sub)); } gridSubs.EndUpdate(); #endregion loadingProgress?.Invoke(); Cursor = Cursors.Default; }
///<summary></summary> public static bool TryAssociateSimilarBugSubmissions(List <BugSubmission> listAllSubs, Point pointFormLocaiton) { List <BugSubmission> listUnattachedSubs = listAllSubs.Where(x => x.BugId == 0).ToList(); if (listUnattachedSubs.Count == 0) { MsgBox.Show("FormBugSubmissions", "All submissions are associated to bugs already."); return(false); } //Dictionary where key is a BugId and the value is list of submissions associated to that BugID. //StackTraces are unique and if there are duplicate stack trace entries we select the one with the newest version. Dictionary <long, List <BugSubmission> > dictAttachedSubs = listAllSubs.Where(x => x.BugId != 0) .GroupBy(x => x.BugId) .ToDictionary(x => x.Key, x => x.GroupBy(y => y.ExceptionStackTrace) //Sub dictionary of unique ExceptionStackStraces as the key and the value is the submission from the highest version. .ToDictionary(y => y.Key, y => y.OrderByDescending(z => new Version(z.Info.DictPrefValues[PrefName.ProgramVersion])).First()) .Values.ToList() ); Dictionary <long, List <BugSubmission> > dictSimilarBugSubs = new Dictionary <long, List <BugSubmission> >(); List <long> listOrderedKeys = dictAttachedSubs.Keys.OrderByDescending(x => x).ToList(); foreach (long key in listOrderedKeys) //Loop through submissions that are already attached to bugs { dictSimilarBugSubs[key] = new List <BugSubmission>(); foreach (BugSubmission sub in dictAttachedSubs[key]) //Loop through the unique exception text from the submission with thie highest reported version. { List <BugSubmission> listSimilarBugSubs = listUnattachedSubs.Where(x => x.ExceptionStackTrace == sub.ExceptionStackTrace//Find submissions that are not attached to bugs with identical ExceptionStackTrace ).ToList(); if (listSimilarBugSubs.Count == 0) { continue; //All submissions with this stack trace are attached to a bug. } listUnattachedSubs.RemoveAll(x => listSimilarBugSubs.Contains(x)); dictSimilarBugSubs[key].AddRange(listSimilarBugSubs); } } if (dictSimilarBugSubs.All(x => x.Value.Count == 0)) { MsgBox.Show("FormBugSubmissions", "All similar submissions are already attached to bugs. No action needed."); return(false); } dictSimilarBugSubs = dictSimilarBugSubs.Where(x => x.Value.Count != 0).ToDictionary(x => x.Key, x => x.Value); bool isAutoAssign = (MsgBox.Show("FormBugSubmissions", MsgBoxButtons.YesNo, "Click Yes to auto attach duplicate submissions to bugs with identical stack traces?" + "\r\nClick No to manually validate all groupings found.")); List <long> listBugIds = listAllSubs.Where(x => x.BugId != 0).Select(x => x.BugId).ToList(); List <JobLink> listLinks = JobLinks.GetManyForType(JobLinkType.Bug, listBugIds); List <Bug> listBugs = Bugs.GetMany(listBugIds); foreach (KeyValuePair <long, List <BugSubmission> > pair in dictSimilarBugSubs) { Bug bugFixed = listBugs.FirstOrDefault(x => x.BugId == pair.Key && !string.IsNullOrEmpty(x.VersionsFixed)); if (bugFixed != null) { List <BugSubmission> listIssueSubs = pair.Value.Where(x => new Version(x.Info.DictPrefValues[PrefName.ProgramVersion]) >= new Version(bugFixed.VersionsFixed.Split(';').Last())).ToList(); if (listIssueSubs.Count > 0) { MsgBox.Show("FormBugSubmissions", "There appears to be a submission on a version that is newer than the previous fixed bug version (BugId: " + POut.Long(bugFixed.BugId) + "). " + "These will be excluded."); //TODO: Allow user to view these excluded submissions somehow. pair.Value.RemoveAll(x => listIssueSubs.Contains(x)); if (pair.Value.Count == 0) { continue; } } } if (!isAutoAssign) { FormBugSubmissions formGroupBugSubs = new FormBugSubmissions(viewMode: FormBugSubmissionMode.ValidationMode); formGroupBugSubs.ListViewedSubs = pair.Value; //Add unnattached submissions to grid formGroupBugSubs.ListViewedSubs.AddRange(dictAttachedSubs[pair.Key]); //Add already attached submissions to grid formGroupBugSubs.StartPosition = FormStartPosition.Manual; Point newLoc = pointFormLocaiton; newLoc.X += 10; //Offset newLoc.Y += 10; formGroupBugSubs.Location = newLoc; if (formGroupBugSubs.ShowDialog() != DialogResult.OK) { continue; } } BugSubmissions.UpdateBugIds(pair.Key, pair.Value.Select(x => x.BugSubmissionNum).ToList()); } MsgBox.Show("FormBugSubmissions", "Done."); return(true); }
private void FillGrids(bool isRefreshNeeded = true) { if (_patCur == null || gridBugsFixed == null) //Just in case. { return; } gridBugsInProgress.BeginUpdate(); gridBugsFixed.BeginUpdate(); gridBugSubmissions.BeginUpdate(); #region Init Columns if (gridBugsFixed.ListGridColumns.Count == 0) { gridBugsInProgress.ListGridColumns.Add(new GridColumn("Description", -1)); gridBugsInProgress.ListGridColumns.Add(new GridColumn("Expert", 55, HorizontalAlignment.Center)); gridBugsInProgress.ListGridColumns.Add(new GridColumn("Has Job", 55, HorizontalAlignment.Center)); gridBugsFixed.ListGridColumns.Add(new GridColumn("Description", -1)); gridBugsFixed.ListGridColumns.Add(new GridColumn("Vers.", 55, HorizontalAlignment.Center)); gridBugsFixed.ListGridColumns.Add(new GridColumn("Has Job", 55, HorizontalAlignment.Center)); gridBugSubmissions.ListGridColumns.Add(new GridColumn("Description", -1)); gridBugSubmissions.ListGridColumns.Add(new GridColumn("Vers.", 55, HorizontalAlignment.Center)); gridBugSubmissions.ListGridColumns.Add(new GridColumn("Has Job", 55, HorizontalAlignment.Center)); } #endregion if (isRefreshNeeded) { List <string> listKeys = RegistrationKeys.GetForPatient(_patCur.PatNum).Select(x => x.RegKey).ToList(); try { _listBugSubs = BugSubmissions.GetBugSubsForRegKeys(listKeys, datePickerHqBugSub.GetDateTimeFrom(), datePickerHqBugSub.GetDateTimeTo()); } catch (Exception ex) { //Should only occur if running in debug at HQ and no bugs database on local machine. ex.DoNothing(); label1.Text = "NO BUGS DB"; //Inform dev of issue. } _listJobLinks = JobLinks.GetManyForType(JobLinkType.Bug, _listBugSubs.Select(x => x.BugId).ToList()); List <long> listNewBugUserIds = _listBugSubs.Where(x => x.BugObj != null && !_dictHqBugSubNames.ContainsKey(x.BugObj.Submitter)) .Select(x => x.BugObj.Submitter).ToList(); if (listNewBugUserIds.Count > 0) { Bugs.GetDictSubmitterNames(listNewBugUserIds).ToList() .ForEach(x => _dictHqBugSubNames[x.Key] = x.Value); } } gridBugsInProgress.ListGridRows.Clear(); gridBugsFixed.ListGridRows.Clear(); gridBugSubmissions.ListGridRows.Clear(); List <long> listBugNums = new List <long>(); //Keep track of BubIds so that we do not show duplicates. foreach (BugSubmission sub in _listBugSubs) { bool hasJob = (_listJobLinks.Any(x => x.FKey == sub.BugObj?.BugId)); string expertName = (sub.BugObj == null ? "" : _dictHqBugSubNames[sub.BugObj.Submitter].ToUpperFirstOnly()); if (sub.BugObj != null && !listBugNums.Contains(sub.BugObj.BugId)) //BugSubmission has an associated bug and it is not a duplicate. { if (string.IsNullOrEmpty(sub.BugObj.VersionsFixed)) //Bug is not fixed { #region In Progress grid listBugNums.Add(sub.BugObj.BugId); GridRow rowBug = new GridRow(sub.BugObj.Description, expertName, (hasJob?"X":"") ); rowBug.Tag = sub.BugObj; gridBugsInProgress.ListGridRows.Add(rowBug); #endregion } else //Bug is fixed { #region Fixed grid listBugNums.Add(sub.BugObj.BugId); GridRow rowBug = new GridRow(sub.BugObj.Description + "\r\nEXPERT: " + expertName, sub.BugObj.VersionsFixed.Replace(';', '\n'), (hasJob?"X":"") ); rowBug.Tag = sub.BugObj; gridBugsFixed.ListGridRows.Add(rowBug); #endregion } } #region All grid GridRow rowSub = new GridRow(sub.ExceptionMessageText + "\r\n" + sub.SubmissionDateTime.ToString("MM/dd/yyyy HH:mm:ss", CultureInfo.InvariantCulture) + "\r\nEXPERT: " + expertName, sub.TryGetPrefValue(PrefName.ProgramVersion, "0.0.0.0"), (hasJob?"X":"") ); rowSub.Tag = sub; gridBugSubmissions.ListGridRows.Add(rowSub); #endregion } gridBugsInProgress.EndUpdate(); gridBugsFixed.EndUpdate(); gridBugSubmissions.EndUpdate(); }
public static bool TryAssociateSimilarBugSubmissions(Point pointFormLocaiton) { List <BugSubmission> listAllSubs = BugSubmissions.GetAll(); List <BugSubmission> listUnattachedSubs = listAllSubs.Where(x => x.BugId == 0).ToList(); if (listUnattachedSubs.Count == 0) { MsgBox.Show("FormBugSubmissions", "All submissions are associated to bugs already."); return(false); } //Dictionary where key is a BugId and the value is list of submissions associated to that BugID. //StackTraces are unique and if there are duplicate stack trace entries we select the one with the newest version. Dictionary <long, List <BugSubmission> > dictAttachedSubs = listAllSubs.Where(x => x.BugId != 0) .GroupBy(x => x.BugId) .ToDictionary(x => x.Key, x => x.GroupBy(y => y.ExceptionStackTrace) //Sub dictionary of unique ExceptionStackStraces as the key and the value is the submission from the highest version. .ToDictionary(y => y.Key, y => y.OrderByDescending(z => new Version(z.Info.DictPrefValues[PrefName.ProgramVersion])).First()) .Values.ToList() ); Dictionary <long, List <BugSubmission> > dictSimilarBugSubs = new Dictionary <long, List <BugSubmission> >(); List <long> listOrderedKeys = dictAttachedSubs.Keys.OrderByDescending(x => x).ToList(); foreach (long key in listOrderedKeys) //Loop through submissions that are already attached to bugs { dictSimilarBugSubs[key] = new List <BugSubmission>(); foreach (BugSubmission sub in dictAttachedSubs[key]) //Loop through the unique exception text from the submission with thie highest reported version. { List <BugSubmission> listSimilarBugSubs = listUnattachedSubs.Where(x => x.ExceptionStackTrace == sub.ExceptionStackTrace//Find submissions that are not attached to bugs with identical ExceptionStackTrace ).ToList(); if (listSimilarBugSubs.Count == 0) { continue; //All submissions with this stack trace are attached to a bug. } listUnattachedSubs.RemoveAll(x => listSimilarBugSubs.Contains(x)); dictSimilarBugSubs[key].AddRange(listSimilarBugSubs); } } if (dictSimilarBugSubs.All(x => x.Value.Count == 0)) { MsgBox.Show("FormBugSubmissions", "All similar submissions are already attached to bugs. No action needed."); return(false); } dictSimilarBugSubs = dictSimilarBugSubs.Where(x => x.Value.Count != 0).ToDictionary(x => x.Key, x => x.Value); bool isAutoAssign = (MsgBox.Show("FormBugSubmissions", MsgBoxButtons.YesNo, "Click Yes to auto attach duplicate submissions to bugs with identical stack traces?" + "\r\nClick No to manually validate all groupings found.")); List <long> listBugIds = listAllSubs.Where(x => x.BugId != 0).Select(x => x.BugId).ToList(); List <JobLink> listLinks = JobLinks.GetManyForType(JobLinkType.Bug, listBugIds); List <Bug> listBugs = Bugs.GetMany(listBugIds); StringBuilder issueSubmissionPrompt = new StringBuilder(); foreach (KeyValuePair <long, List <BugSubmission> > pair in dictSimilarBugSubs) { Bug bugFixed = listBugs.FirstOrDefault(x => x.BugId == pair.Key && !string.IsNullOrEmpty(x.VersionsFixed)); if (bugFixed != null) { List <BugSubmission> listIssueSubs = pair.Value.Where(x => new Version(x.Info.DictPrefValues[PrefName.ProgramVersion]) >= new Version(bugFixed.VersionsFixed.Split(';').Last())).ToList(); if (listIssueSubs.Count > 0) { List <JobLink> listBugJobLinks = listLinks.FindAll(x => x.FKey == bugFixed.BugId); List <Job> listBugJobs = Jobs.GetMany(listBugJobLinks.Select(x => x.JobNum).ToList()); if (issueSubmissionPrompt.Length == 0) { issueSubmissionPrompt.AppendLine("The following completed jobs have submissions from newer versions then the jobs reported fixed version: "); } listBugJobs.ForEach(x => issueSubmissionPrompt.AppendLine("- " + " (" + x.Category.ToString().Substring(0, 1) + x.JobNum + ")" + x.Title)); pair.Value.RemoveAll(x => listIssueSubs.Contains(x)); if (pair.Value.Count == 0) { continue; } } } if (!isAutoAssign) { FormBugSubmissions formGroupBugSubs = new FormBugSubmissions(viewMode: FormBugSubmissionMode.ValidationMode); formGroupBugSubs.ListViewedSubs = pair.Value; //Add unnattached submissions to grid formGroupBugSubs.ListViewedSubs.AddRange(dictAttachedSubs[pair.Key]); //Add already attached submissions to grid formGroupBugSubs.StartPosition = FormStartPosition.Manual; Point newLoc = pointFormLocaiton; newLoc.X += 10; //Offset newLoc.Y += 10; formGroupBugSubs.Location = newLoc; if (formGroupBugSubs.ShowDialog() != DialogResult.OK) { continue; } } BugSubmissions.UpdateBugIds(pair.Key, pair.Value.Select(x => x.BugSubmissionNum).ToList()); } string msg = ""; dictSimilarBugSubs.Keys.ToList().FindAll(x => dictSimilarBugSubs[x].Count > 0) .ForEach(x => msg += "Bug: " + x + " Found submissions: " + dictSimilarBugSubs[x].Count + "\r\n"); msg += issueSubmissionPrompt.ToString(); new MsgBoxCopyPaste(msg) { Text = "Done" }.ShowDialog(); return(true); }