/// <summary> /// Add a new crash to the db based on a CrashDescription sent from the client. /// </summary> /// <param name="NewCrashInfo">The crash description sent from the client.</param> /// <returns>The id of the newly added crash.</returns> public int AddNewCrash(CrashDescription NewCrashInfo) { int NewID = -1; Crash NewCrash = new Crash(); NewCrash.Branch = NewCrashInfo.BranchName; NewCrash.BaseDir = NewCrashInfo.BaseDir; NewCrash.BuildVersion = NewCrashInfo.BuildVersion; NewCrash.BuiltFromCL = NewCrashInfo.BuiltFromCL.ToString(); NewCrash.CommandLine = NewCrashInfo.CommandLine; NewCrash.EngineMode = NewCrashInfo.EngineMode; NewCrash.MachineId = NewCrashInfo.MachineGuid; //if there's a valid username assign the associated UserNameId else use "anonymous" NewCrash.UserNameId = FRepository.Get(this).FindOrAddUser(!string.IsNullOrEmpty(NewCrashInfo.UserName) ? NewCrashInfo.UserName : UserNameAnonymous); //If there's a valid EpicAccountId assign that. if (!string.IsNullOrEmpty(NewCrashInfo.EpicAccountId)) { NewCrash.EpicAccountId = NewCrashInfo.EpicAccountId; } NewCrash.Description = ""; if (NewCrashInfo.UserDescription != null) { NewCrash.Description = string.Join(Environment.NewLine, NewCrashInfo.UserDescription); } NewCrash.EngineMode = NewCrashInfo.EngineMode; NewCrash.GameName = NewCrashInfo.GameName; NewCrash.LanguageExt = NewCrashInfo.Language; // Converted by the crash process. NewCrash.PlatformName = NewCrashInfo.Platform; if (NewCrashInfo.ErrorMessage != null) { NewCrash.Summary = string.Join("\n", NewCrashInfo.ErrorMessage); } if (NewCrashInfo.CallStack != null) { NewCrash.RawCallStack = string.Join("\n", NewCrashInfo.CallStack); } if (NewCrashInfo.SourceContext != null) { NewCrash.SourceContext = string.Join("\n", NewCrashInfo.SourceContext); } NewCrash.TimeOfCrash = NewCrashInfo.TimeofCrash; NewCrash.bAllowToBeContacted = NewCrashInfo.bAllowToBeContacted; NewCrash.Jira = ""; NewCrash.FixedChangeList = ""; // Set the crash type NewCrash.CrashType = 1; if (NewCrash.RawCallStack != null) { if (NewCrash.RawCallStack.Contains("FDebug::AssertFailed")) { NewCrash.CrashType = 2; } else if (NewCrash.RawCallStack.Contains("FDebug::Ensure")) { NewCrash.CrashType = 3; } else if (NewCrash.RawCallStack.Contains("FDebug::OptionallyLogFormattedEnsureMessageReturningFalse")) { NewCrash.CrashType = 3; } else if (NewCrash.RawCallStack.Contains("NewReportEnsure")) { NewCrash.CrashType = 3; } } // As we're adding it, the status is always new NewCrash.Status = "New"; /* * Unused Crashes' fields. * * Title nchar(20) * Selected bit * Version int * AutoReporterID int * Processed bit -> renamed to AllowToBeContacted * HasDiagnosticsFile bit always true * HasNewLogFile bit * HasMetaData bit always true */ // Set the unused fields to the default values. //NewCrash.Title = ""; removed from dbml //NewCrash.Selected = false; removed from dbml //NewCrash.Version = 4; removed from dbml //NewCrash.AutoReporterID = 0; removed from dbml //NewCrash.HasNewLogFile = false;removed from dbml // //NewCrash.HasDiagnosticsFile = true; //NewCrash.HasMetaData = true; NewCrash.UserActivityHint = NewCrashInfo.UserActivityHint; // Add the crash to the database Context.Crashes.InsertOnSubmit(NewCrash); SubmitChanges(); NewID = NewCrash.Id; // Build a callstack pattern for crash bucketing NewCrash.BuildPattern(Context); return(NewID); }
/// <summary> /// Sort the container of Buggs by the requested criteria. /// </summary> /// <param name="Results">A container of unsorted Buggs.</param> /// <param name="SortTerm">The term to sort by.</param> /// <param name="bSortDescending">Whether to sort by descending or ascending.</param> /// <param name="DateFrom">The date of the earliest Bugg to examine.</param> /// <param name="DateTo">The date of the most recent Bugg to examine.</param> /// <param name="GroupName">The user group name to filter by.</param> /// <returns>A sorted container of Buggs.</returns> public IEnumerable <Bugg> GetSortedResults(IEnumerable <Bugg> Results, string SortTerm, bool bSortDescending, DateTime DateFrom, DateTime DateTo, string GroupName) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { try { // Get the group id and grab all buggs for the specified group. HashSet <string> UserNamesForUserGroup = FRepository.Get(this).GetUserNamesFromGroupName(GroupName); // Simplified query. var BuggIdToCountMapGroup = new Dictionary <int, int>(); var BuggIdToCountMapRest = new Dictionary <int, int>(); var BuggIdToMachineSet = new Dictionary <int, HashSet <string> >(); Dictionary <string, int> MachineIdToCountMap = new Dictionary <string, int>(); List <Buggs_Crash> BuggsFromDate = null; Dictionary <int, string> CrashToUser = null; Dictionary <int, string> CrashToMachine = null; // Get all buggs from the date range. using (FScopedLogTimer LogTimer1 = new FScopedLogTimer("BuggRepository.GetSortedResults.BuggsFromDate SQL")) { BuggsFromDate = ( from BuggCrash in Context.Buggs_Crashes where BuggCrash.Crash.TimeOfCrash >= DateFrom && BuggCrash.Crash.TimeOfCrash <= DateTo.AddDays(1) select BuggCrash ).AsEnumerable().ToList(); var CrashesWithIdUserMachine = ( from Crash in Context.Crashes where Crash.TimeOfCrash >= DateFrom && Crash.TimeOfCrash <= DateTo.AddDays(1) select new { Id = Crash.Id, UserName = Crash.UserName, MachineId = Crash.MachineId } ); CrashToUser = CrashesWithIdUserMachine.ToDictionary(x => x.Id, y => y.UserName); CrashToMachine = CrashesWithIdUserMachine.ToDictionary(x => x.Id, y => y.MachineId); } using (FScopedLogTimer LogTimer0 = new FScopedLogTimer("BuggRepository.GetSortedResults.Filtering")) { // This calculates total crashes for selected group and all groups. foreach (Buggs_Crash BuggCrash in BuggsFromDate) { string MachineId; CrashToMachine.TryGetValue(BuggCrash.CrashId, out MachineId); string UserName = CrashToUser[BuggCrash.CrashId]; bool bValidForGroup = UserNamesForUserGroup.Contains(UserName); if (!bValidForGroup) { int CountRest = 0; bool bFoundRestKey = BuggIdToCountMapRest.TryGetValue(BuggCrash.BuggId, out CountRest); if (bFoundRestKey) { BuggIdToCountMapRest[BuggCrash.BuggId]++; } else { BuggIdToCountMapRest.Add(BuggCrash.BuggId, 1); } continue; } int Count = 0; bool bFoundKey = BuggIdToCountMapGroup.TryGetValue(BuggCrash.BuggId, out Count); if (bFoundKey) { BuggIdToCountMapGroup[BuggCrash.BuggId]++; } else { BuggIdToCountMapGroup.Add(BuggCrash.BuggId, 1); } if (MachineId != null && MachineId.Length > 0) { HashSet <string> MachineSet = null; bool bFoundMachineKey = BuggIdToMachineSet.TryGetValue(BuggCrash.BuggId, out MachineSet); if (!bFoundMachineKey) { BuggIdToMachineSet.Add(BuggCrash.BuggId, new HashSet <string>()); } BuggIdToMachineSet[BuggCrash.BuggId].Add(MachineId); } } } using (FScopedLogTimer LogTimer2 = new FScopedLogTimer("BuggRepository.GetSortedResults.CrashesInTimeFrame")) { foreach (var Result in Results) { int GroupCount = 0; BuggIdToCountMapGroup.TryGetValue(Result.Id, out GroupCount); Result.CrashesInTimeFrameGroup = GroupCount; int GroupRest = 0; BuggIdToCountMapRest.TryGetValue(Result.Id, out GroupRest); Result.CrashesInTimeFrameAll = GroupCount + GroupRest; } } using (FScopedLogTimer LogTimer2 = new FScopedLogTimer("BuggRepository.GetSortedResults.UniqueMachineCrashesInTimeFrame")) { foreach (var Result in Results) { HashSet <string> MachineSet = null; bool bFoundKey = BuggIdToMachineSet.TryGetValue(Result.Id, out MachineSet); if (bFoundKey) { Result.NumberOfUniqueMachines = MachineSet.Count; } else { Result.NumberOfUniqueMachines = 0; } } } switch (SortTerm) { case "CrashesInTimeFrameGroup": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.CrashesInTimeFrameGroup, bSortDescending); break; case "CrashesInTimeFrameAll": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.CrashesInTimeFrameAll, bSortDescending); break; case "Id": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.Id, bSortDescending); break; case "BuildVersion": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.BuildVersion, bSortDescending); break; case "LatestCrash": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.TimeOfLastCrash, bSortDescending); break; case "FirstCrash": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.TimeOfFirstCrash, bSortDescending); break; case "NumberOfCrashes": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.NumberOfCrashes, bSortDescending); break; case "NumberOfUsers": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.NumberOfUniqueMachines, bSortDescending); break; case "Pattern": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.Pattern, bSortDescending); break; case "CrashType": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.CrashType, bSortDescending); break; case "Status": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.Status, bSortDescending); break; case "FixedChangeList": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.FixedChangeList, bSortDescending); break; case "TTPID": Results = EnumerableOrderBy(Results, BuggCrashInstance => BuggCrashInstance.Jira, bSortDescending); break; } return(Results); } catch (Exception Ex) { Debug.WriteLine("Exception in GetSortedResults: " + Ex.ToString()); } return(Results); } }
/// <summary> /// Return a view model containing a sorted list of crashes based on the user input. /// </summary> /// <param name="FormData">The user input from the client.</param> /// <returns>A view model to use to display a list of crashes.</returns> /// <remarks>The filtering mechanism works by generating a fluent query to describe the set of crashes we wish to view. Basically, 'Select.Where().Where().Where().Where()' etc. /// The filtering is applied in the following order: /// 1. Get all crashes. /// 2. Filter between the start and end dates. /// 3. Apply the search query. /// 4. Filter by the branch. /// 5. Filter by the game name. /// 6. Filter by the type of reports to view. /// 7. Filter by the user group. /// 8. Sort the results by the sort term. /// 9. Take one page of results.</remarks> public CrashesViewModel GetResults(FormHelper FormData) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { UsersMapping UniqueUser = null; IEnumerable <Crash> Results = null; int Skip = (FormData.Page - 1) * FormData.PageSize; int Take = FormData.PageSize; var ResultsAll = ConstructQueryForFiltering(FormData); // Filter by data and get as enumerable. Results = FilterByDate(ResultsAll, FormData.DateFrom, FormData.DateTo); // Filter by BuggId if (!string.IsNullOrEmpty(FormData.BuggId)) { int BuggId = 0; bool bValid = int.TryParse(FormData.BuggId, out BuggId); if (bValid) { BuggRepository Buggs = new BuggRepository(); Bugg NewBugg = Buggs.GetBugg(BuggId); if (NewBugg != null) { List <Crash> Crashes = NewBugg.GetCrashes(); var NewResult = Results.Intersect(Crashes, new CrashComparer()); Results = NewResult; } } } // Get UserGroup ResultCounts Dictionary <string, int> GroupCounts = GetCountsByGroupFromCrashes(Results); // Filter by user group if present int UserGroupId; if (!string.IsNullOrEmpty(FormData.UserGroup)) { UserGroupId = FRepository.Get(this).FindOrAddGroup(FormData.UserGroup); } else { UserGroupId = 1; } HashSet <int> UserIdsForGroup = FRepository.Get(this).GetUserIdsFromUserGroupId(UserGroupId); using (FScopedLogTimer LogTimer3 = new FScopedLogTimer("CrashRepository.Results.Users")) { List <Crash> NewResults = new List <Crash>(); foreach (Crash Crash in Results) { if (UserIdsForGroup.Contains(Crash.UserNameId.Value)) { NewResults.Add(Crash); } } Results = NewResults; } // Pass in the results and return them sorted properly Results = GetSortedResults(Results, FormData.SortTerm, (FormData.SortOrder == "Descending")); // Get the Count for pagination int ResultCount = 0; using (FScopedLogTimer LogTimer3 = new FScopedLogTimer("CrashRepository.Results.Users")) { ResultCount = Results.Count(); } // Grab just the results we want to display on this page Results = Results.Skip(Skip).Take(Take); using (FScopedLogTimer LogTimer3 = new FScopedLogTimer("CrashRepository.GetResults.GetCallstacks")) { // Process call stack for display foreach (Crash CrashInstance in Results) { // Put callstacks into an list so we can access them line by line in the view CrashInstance.CallStackContainer = GetCallStack(CrashInstance); } } return(new CrashesViewModel { Results = Results, PagingInfo = new PagingInfo { CurrentPage = FormData.Page, PageSize = FormData.PageSize, TotalResults = ResultCount }, SortOrder = FormData.SortOrder, SortTerm = FormData.SortTerm, UserGroup = FormData.UserGroup, CrashType = FormData.CrashType, SearchQuery = FormData.SearchQuery, UsernameQuery = FormData.UsernameQuery, EpicIdOrMachineQuery = FormData.EpicIdOrMachineQuery, MessageQuery = FormData.MessageQuery, BuiltFromCL = FormData.BuiltFromCL, BuggId = FormData.BuggId, JiraQuery = FormData.JiraQuery, DateFrom = (long)(FormData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds, DateTo = (long)(FormData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds, BranchName = FormData.BranchName, VersionName = FormData.VersionName, PlatformName = FormData.PlatformName, GameName = FormData.GameName, GroupCounts = GroupCounts, RealUserName = UniqueUser != null?UniqueUser.ToString() : null, }); } }
/// <summary> /// Add a new crash to the db based on a CrashDescription sent from the client. /// </summary> /// <param name="NewCrashInfo">The crash description sent from the client.</param> /// <returns>The id of the newly added crash.</returns> public int AddNewCrash(CrashDescription NewCrashInfo) { int NewID = -1; Crash NewCrash = new Crash(); NewCrash.Branch = NewCrashInfo.BranchName; NewCrash.BaseDir = NewCrashInfo.BaseDir; NewCrash.BuildVersion = NewCrashInfo.BuildVersion; NewCrash.ChangeListVersion = NewCrashInfo.BuiltFromCL.ToString(); NewCrash.CommandLine = NewCrashInfo.CommandLine; NewCrash.EngineMode = NewCrashInfo.EngineMode; NewCrash.ComputerName = NewCrashInfo.MachineGuid; // Valid MachineID and UserName, updated crash from non-UE4 release if (!string.IsNullOrEmpty(NewCrashInfo.UserName)) { NewCrash.UserNameId = FRepository.Get(this).FindOrAddUser(NewCrashInfo.UserName); } // Valid MachineID and EpicAccountId, updated crash from UE4 release else if (!string.IsNullOrEmpty(NewCrashInfo.EpicAccountId)) { NewCrash.EpicAccountId = NewCrashInfo.EpicAccountId; NewCrash.UserNameId = FRepository.Get(this).FindOrAddUser(UserNameAnonymous); } // Crash from an older version. else { // MachineGuid for older crashes is obsolete, so ignore it. //NewCrash.ComputerName = NewCrashInfo.MachineGuid; NewCrash.UserNameId = FRepository.Get(this).FindOrAddUser ( !string.IsNullOrEmpty(NewCrashInfo.UserName) ? NewCrashInfo.UserName : UserNameAnonymous ); } NewCrash.Description = ""; if (NewCrashInfo.UserDescription != null) { NewCrash.Description = string.Join(Environment.NewLine, NewCrashInfo.UserDescription); } NewCrash.EngineMode = NewCrashInfo.EngineMode; NewCrash.GameName = NewCrashInfo.GameName; NewCrash.LanguageExt = NewCrashInfo.Language; // Converted by the crash process. NewCrash.PlatformName = NewCrashInfo.Platform; if (NewCrashInfo.ErrorMessage != null) { NewCrash.Summary = string.Join("\n", NewCrashInfo.ErrorMessage); } if (NewCrashInfo.CallStack != null) { NewCrash.RawCallStack = string.Join("\n", NewCrashInfo.CallStack); } if (NewCrashInfo.SourceContext != null) { NewCrash.SourceContext = string.Join("\n", NewCrashInfo.SourceContext); } NewCrash.TimeOfCrash = NewCrashInfo.TimeofCrash; NewCrash.HasLogFile = NewCrashInfo.bHasLog; NewCrash.HasMiniDumpFile = NewCrashInfo.bHasMiniDump; NewCrash.HasDiagnosticsFile = NewCrashInfo.bHasDiags; NewCrash.HasVideoFile = NewCrashInfo.bHasVideo; NewCrash.HasMetaData = NewCrashInfo.bHasWERData; NewCrash.bAllowToBeContacted = NewCrashInfo.bAllowToBeContacted; NewCrash.TTPID = ""; NewCrash.FixedChangeList = ""; // Set the crash type NewCrash.CrashType = 1; if (NewCrash.RawCallStack != null) { if (NewCrash.RawCallStack.Contains("FDebug::AssertFailed()")) { NewCrash.CrashType = 2; } else if (NewCrash.RawCallStack.Contains("FDebug::EnsureFailed()")) { NewCrash.CrashType = 3; } } // As we're adding it, the status is always new NewCrash.Status = "New"; /* * Unused Crashes' fields. * * Title nchar(20) * Selected bit * Version int * AutoReporterID int * Processed bit -> renamed to AllowToBeContacted * HasDiagnosticsFile bit always true * HasNewLogFile bit * HasMetaData bit always true */ // Set the unused fields to the default values. //NewCrash.Title = ""; removed from dbml //NewCrash.Selected = false; removed from dbml //NewCrash.Version = 4; removed from dbml //NewCrash.AutoReporterID = 0; removed from dbml //NewCrash.HasNewLogFile = false;removed from dbml // //NewCrash.HasDiagnosticsFile = true; //NewCrash.HasMetaData = true; // Add the crash to the database Context.Crashes.InsertOnSubmit(NewCrash); SubmitChanges(); NewID = NewCrash.Id; // Build a callstack pattern for crash bucketing NewCrash.BuildPattern(Context); return(NewID); }