/// <summary> /// Construct a processed callstack. /// </summary> /// <param name="CurrentCrash"></param> public CallStackContainer( Crash CurrentCrash ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(CrashId=" + CurrentCrash.Id + ")" ) ) { ParseCallStack( CurrentCrash ); } }
/// <summary> /// Add a crash passed in the payload as Xml to the database. /// </summary> /// <param name="id">Unused.</param> /// <returns>The row id of the newly added crash.</returns> public ActionResult AddCrash( int id ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(NewCrashId=" + id + ")" ) ) { CrashRepository Crashes = new CrashRepository(); CrashReporterResult NewCrashResult = new CrashReporterResult(); NewCrashResult.ID = -1; try { using( StreamReader Reader = new StreamReader( Request.InputStream, Request.ContentEncoding ) ) { string Result = Reader.ReadToEnd(); CrashDescription NewCrash = XmlHandler.FromXmlString<CrashDescription>( Result ); NewCrashResult.ID = Crashes.AddNewCrash( NewCrash ); NewCrashResult.bSuccess = true; } } catch( Exception Ex ) { NewCrashResult.Message = Ex.ToString(); NewCrashResult.bSuccess = false; } string ReturnResult = XmlHandler.ToXmlString<CrashReporterResult>( NewCrashResult ); return Content( ReturnResult, "text/xml" ); } }
/// <summary> /// Return a dictionary of crashes per group grouped by week. /// </summary> /// <param name="Crashes">A set of crashes to interrogate.</param> /// <param name="UserGroupId">The id of the user group to interrogate.</param> /// <returns>A dictionary of week vs. crash count.</returns> public Dictionary<DateTime, int> GetWeeklyCountsByGroup( List<FCrashMinimal> Crashes, int UserGroupId ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(UserGroupId=" + UserGroupId + ")" ) ) { Dictionary<DateTime, int> Results = new Dictionary<DateTime, int>(); var UsersIds = new HashSet<int>(_entities.Users.Distinct().Select(data => data.Id)); // Trim crashes to user group. if( UserGroupId != DashboardController.AllUserGroupId ) { Crashes = Crashes.Where( Crash => UsersIds.Contains( Crash.UserId ) ).ToList(); } try { Results = ( from CrashDetail in Crashes group CrashDetail by CrashDetail.TimeOfCrash.AddDays( -(int)CrashDetail.TimeOfCrash.DayOfWeek ).Date into GroupCount orderby GroupCount.Key select new { Count = GroupCount.Count(), Date = GroupCount.Key } ).ToDictionary( x => x.Date, y => y.Count ); } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetWeeklyCountsByGroup: " + Ex.ToString() ); } return Results; } }
/// <summary> /// /// </summary> /// <returns></returns> public EntitySet<Crash> GetCrashes() { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { int CrashCount = Crashes.Count; if( NumberOfCrashes != CrashCount ) { NumberOfCrashes = CrashCount; if( NumberOfCrashes > 0 ) { BuggRepository LocalBuggRepository = new BuggRepository(); LocalBuggRepository.UpdateBuggData( this, Crashes ); } } // Just fill the CallStackContainers foreach( Crash CurrentCrash in Crashes ) { if( CurrentCrash.CallStackContainer == null ) { CurrentCrash.CallStackContainer = CurrentCrash.GetCallStack(); } if( SourceContext == null ) { SourceContext = CurrentCrash.SourceContext; } } return Crashes; } }
/// <summary> /// The Index action. /// </summary> /// <param name="BuggsForm">The form of user data passed up from the client.</param> /// <returns>The view to display a list of Buggs on the client.</returns> public ActionResult Index( FormCollection BuggsForm ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { FormHelper FormData = new FormHelper( Request, BuggsForm, "CrashesInTimeFrameGroup" ); BuggsViewModel Results = LocalBuggRepository.GetResults( FormData ); return View( "Index", Results ); } }
/// <summary> /// The main view of the home page. /// </summary> public ActionResult Index() { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true )) { CrashesViewModel Result = new CrashesViewModel(); Result.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", Result ); } }
/// <summary> /// Create html with links to represent last page, next page, current page etc. /// </summary> /// <param name="Html">The html document writer for the current page.</param> /// <param name="WebPagingInfo">Information about the paging of the current page.</param> /// <param name="PageUrl">An Url to link to the correct page.</param> /// <returns>A string suitable for MVC to render.</returns> public static MvcHtmlString PageLinks( this HtmlHelper Html, PagingInfo WebPagingInfo, Func<int, string> PageUrl ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( "PagingHelper" ) ) { StringBuilder ResultString = new StringBuilder(); // Go to first page TagBuilder FirstTag = new TagBuilder( "a" ); // Construct an <a> Tag FirstTag.MergeAttribute( "href", PageUrl( WebPagingInfo.FirstPage ) ); FirstTag.InnerHtml = "<<"; ResultString.AppendLine( FirstTag.ToString() ); // Go to previous page TagBuilder PreviousTag = new TagBuilder( "a" ); // Construct an <a> Tag PreviousTag.MergeAttribute( "href", PageUrl( WebPagingInfo.PreviousPageIndex ) ); PreviousTag.InnerHtml = "<"; ResultString.AppendLine( PreviousTag.ToString() ); for( int PageIndex = WebPagingInfo.FirstPageIndex; PageIndex <= WebPagingInfo.LastPageIndex; PageIndex++ ) { TagBuilder Tag = new TagBuilder( "a" ); // Construct an <a> Tag Tag.MergeAttribute( "href", PageUrl( PageIndex ) ); Tag.InnerHtml = PageIndex.ToString(); if( PageIndex == WebPagingInfo.CurrentPage ) { Tag.AddCssClass( "selectedPage" ); } ResultString.AppendLine( Tag.ToString() ); } // Go to next page TagBuilder NextTag = new TagBuilder( "a" ); // Construct an <a> Tag NextTag.MergeAttribute( "href", PageUrl( WebPagingInfo.NextPageIndex ) ); NextTag.InnerHtml = ">"; ResultString.AppendLine( NextTag.ToString() ); // Go to last page TagBuilder LastTag = new TagBuilder( "a" ); // Construct an <a> Tag LastTag.MergeAttribute( "href", PageUrl( WebPagingInfo.LastPage ) ); LastTag.InnerHtml = ">>"; ResultString.AppendLine( LastTag.ToString() ); return MvcHtmlString.Create( ResultString.ToString() ); } }
/// <summary> /// Display a summary list of crashes based on the search criteria. /// </summary> /// <param name="crashesForm">A form of user data passed up from the client.</param> /// <returns>A view to display a list of crash reports.</returns> public ActionResult Index( FormCollection crashesForm ) { using( var logTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true ) ) { // Handle any edits made in the Set form fields foreach( var Entry in crashesForm ) { int Id = 0; if( int.TryParse( Entry.ToString(), out Id ) ) { Crash currentCrash = _unitOfWork.CrashRepository.GetById(Id); if( currentCrash != null ) { if( !string.IsNullOrEmpty( crashesForm["SetStatus"] ) ) { currentCrash.Status = crashesForm["SetStatus"]; } if( !string.IsNullOrEmpty( crashesForm["SetFixedIn"] ) ) { currentCrash.FixedChangeList = crashesForm["SetFixedIn"]; } if( !string.IsNullOrEmpty( crashesForm["SetTTP"] ) ) { currentCrash.Jira = crashesForm["SetTTP"]; } } } _unitOfWork.Save(); } // <STATUS> // Parse the contents of the query string, and populate the form var formData = new FormHelper( Request, crashesForm, "TimeOfCrash" ); var result = GetResults( formData ); result.BranchNames = _unitOfWork.CrashRepository.GetBranchesAsListItems(); result.VersionNames = _unitOfWork.CrashRepository.GetVersionsAsListItems(); result.PlatformNames = _unitOfWork.CrashRepository.GetPlatformsAsListItems(); result.EngineModes = _unitOfWork.CrashRepository.GetEngineModesAsListItems(); result.EngineVersions = _unitOfWork.CrashRepository.GetEngineVersionsAsListItems(); // Add the FromCollection to the CrashesViewModel since we don't need it for the get results function but we do want to post it back to the page. result.FormCollection = crashesForm; result.GenerationTime = logTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", result ); } }
/// <summary> /// Retrieve a cached (and pre-parsed) callstack container from the cache, or parse the raw callstack, and add to the cache. /// </summary> /// <param name="CurrentCrash">The crash to retrieve the parsed callstack for.</param> /// <returns>A parsed callstack.</returns> public DataModels.CallStackContainer GetCallStackFast( DataModels.Crash CurrentCrash ) { using( var logTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(CrashId=" + CurrentCrash.Id + ")" ) ) { var key = CacheKeyPrefix + CallstackKeyPrefix + CurrentCrash.Id; var callStack = (CallStackContainer)CacheInstance[key]; if (callStack != null) return callStack; callStack = new CallStackContainer( CurrentCrash ); callStack.bDisplayFunctionNames = true; CacheInstance.Insert( key, callStack ); return callStack; } }
/// <summary> /// The main view of the home page. /// </summary> public ActionResult Index() { using (var logTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true )) { var result = new CrashesViewModel(); result.BranchNames = _unitOfWork.CrashRepository.GetBranchesAsListItems(); result.VersionNames = _unitOfWork.CrashRepository.GetVersionsAsListItems(); result.PlatformNames = _unitOfWork.CrashRepository.GetPlatformsAsListItems(); result.EngineModes = _unitOfWork.CrashRepository.GetEngineModesAsListItems(); result.EngineVersions = _unitOfWork.CrashRepository.GetEngineVersionsAsListItems(); result.GenerationTime = logTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", result ); } }
/// <summary> /// Retrieve a cached (and pre-parsed) callstack container from the cache, or parse the raw callstack, and add to the cache. /// </summary> /// <param name="CurrentCrash">The crash to retrieve the parsed callstack for.</param> /// <returns>A parsed callstack.</returns> public CallStackContainer GetCallStackFast( Crash CurrentCrash ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(CrashId=" + CurrentCrash.Id + ")" ) ) { string Key = CacheKeyPrefix + CallstackKeyPrefix + CurrentCrash.Id; CallStackContainer CallStack = (CallStackContainer)CacheInstance[Key]; if( CallStack == null ) { CallStack = new CallStackContainer( CurrentCrash ); CallStack.bDisplayFunctionNames = true; CacheInstance.Insert( Key, CallStack ); } return CallStack; } }
/// <summary> /// Display a summary list of crashes based on the search criteria. /// </summary> /// <param name="CrashesForm">A form of user data passed up from the client.</param> /// <returns>A view to display a list of crash reports.</returns> public ActionResult Index( FormCollection CrashesForm ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true ) ) { CrashRepository Crashes = new CrashRepository(); // Handle any edits made in the Set form fields foreach( var Entry in CrashesForm ) { int Id = 0; if( int.TryParse( Entry.ToString(), out Id ) ) { Crash CurrentCrash = Crashes.GetCrash( Id ); if( CurrentCrash != null ) { if( !string.IsNullOrEmpty( CrashesForm["SetStatus"] ) ) { CurrentCrash.Status = CrashesForm["SetStatus"]; } if( !string.IsNullOrEmpty( CrashesForm["SetFixedIn"] ) ) { CurrentCrash.FixedChangeList = CrashesForm["SetFixedIn"]; } if( !string.IsNullOrEmpty( CrashesForm["SetTTP"] ) ) { CurrentCrash.Jira = CrashesForm["SetTTP"]; } } } Crashes.SubmitChanges(); } // <STATUS> // Parse the contents of the query string, and populate the form FormHelper FormData = new FormHelper( Request, CrashesForm, "TimeOfCrash" ); CrashesViewModel Result = Crashes.GetResults( FormData ); // Add the FromCollection to the CrashesViewModel since we don't need it for the get results function but we do want to post it back to the page. Result.FormCollection = CrashesForm; Result.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", Result ); } }
/* /// <summary> /// The empty view of the buggs page. /// </summary> /// public ActionResult Index() { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true )) { BuggsViewModel Results = new BuggsViewModel(); Results.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", Results ); } }*/ /// <summary> /// The Index action. /// </summary> /// <param name="BuggsForm">The form of user data passed up from the client.</param> /// <returns>The view to display a list of Buggs on the client.</returns> public ActionResult Index( FormCollection BuggsForm ) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true )) { BuggRepository Buggs = new BuggRepository(); FormHelper FormData = new FormHelper( Request, BuggsForm, "CrashesInTimeFrameGroup" ); BuggsViewModel Results = Buggs.GetResults( FormData ); foreach (var Bugg in Results.Results) { // Populate function calls. Bugg.GetFunctionCalls(); } Results.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", Results ); } }
/// <summary> /// Sets the JIRA for all crashes in a Bugg. /// </summary> /// <param name="JIRA">A string representing a TTP.</param> /// <param name="BuggId">The id of the Bugg to update the crashes for.</param> public void SetJIRAForBuggAndCrashes(string JIRA, int BuggId) { try { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer("SetJIRAForBuggAndCrashes (" + BuggId + ")")) { string Query = "UPDATE Crashes SET TTPID = {0} WHERE Id IN ( SELECT CrashId FROM Buggs_Crashes WHERE BuggId = {1} )"; Context.ExecuteCommand(Query, JIRA, BuggId); Query = "UPDATE Buggs SET TTPID = {0} WHERE id = {1}"; Context.ExecuteCommand(Query, JIRA, BuggId); } } catch (Exception Ex) { FLogger.Global.WriteException("SetBuggTTPID: " + Ex.ToString()); } }
/// <summary> /// Filter a set of Buggs to a date range. /// </summary> /// <param name="Results">The unfiltered set of Buggs.</param> /// <param name="DateFrom">The earliest date to filter by.</param> /// <param name="DateTo">The latest date to filter by.</param> /// <returns>The set of Buggs between the earliest and latest date.</returns> public IEnumerable <Bugg> FilterByDate(IQueryable <Bugg> Results, DateTime DateFrom, DateTime DateTo) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + " SQL")) { var DateTo1 = DateTo.AddDays(1); IQueryable <Bugg> BuggsInTimeFrame = Results .Where(Bugg => (Bugg.TimeOfFirstCrash <= DateFrom && Bugg.TimeOfLastCrash >= DateTo1) || (Bugg.TimeOfFirstCrash >= DateFrom && Bugg.TimeOfLastCrash <= DateTo1) || (Bugg.TimeOfFirstCrash >= DateFrom && Bugg.TimeOfFirstCrash <= DateTo1 && Bugg.TimeOfLastCrash >= DateTo1) || (Bugg.TimeOfFirstCrash <= DateFrom && Bugg.TimeOfLastCrash <= DateTo1 && Bugg.TimeOfLastCrash >= DateFrom) ); IEnumerable <Bugg> BuggsInTimeFrameEnumerable = BuggsInTimeFrame.ToList(); return(BuggsInTimeFrameEnumerable); } }
/// <summary> /// Sets the JIRA for all crashes in a Bugg. /// </summary> /// <param name="JIRA">A string representing a TTP.</param> /// <param name="BuggId">The id of the Bugg to update the crashes for.</param> public static void SetJIRAForBuggAndCrashes( string JIRA, int BuggId ) { try { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( "SetJIRAForBuggAndCrashes (" + BuggId + ")" ) ) { string Query = "UPDATE Crashes SET TTPID = {0} WHERE Id IN ( SELECT CrashId FROM Buggs_Crashes WHERE BuggId = {1} )"; Context.ExecuteCommand( Query, JIRA, BuggId ); Query = "UPDATE Buggs SET TTPID = {0} WHERE id = {1}"; Context.ExecuteCommand( Query, JIRA, BuggId ); } } catch( Exception Ex ) { FLogger.WriteException( "SetBuggTTPID: " + Ex.ToString() ); } }
/// <summary> /// Gets a container of crash counts per user group for the set of crashes passed in. /// </summary> /// <param name="EnumerableCrashes">The set of crashes to tabulate by user group.</param> /// <returns>A dictionary of user group names, and the count of Buggs for each group.</returns> public Dictionary <string, int> GetCountsByGroupFromCrashes(IEnumerable <Crash> EnumerableCrashes) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { Dictionary <string, int> Results = new Dictionary <string, int>(); try { var UsersIDsAndGroupIDs = Context.Users.Select(User => new { UserId = User.Id, UserGroupId = User.UserGroupId }).ToList(); var UserGroupArray = Context.UserGroups.ToList(); UserGroupArray.Sort((UG1, UG2) => UG1.Name.CompareTo(UG2.Name)); // Initialize all groups to 0. foreach (var UserGroup in UserGroupArray) { Results.Add(UserGroup.Name, 0); } Dictionary <int, string> UserIdToGroupName = new Dictionary <int, string>(); foreach (var UserIds in UsersIDsAndGroupIDs) { // Find group name for the user id. string UserGroupName = UserGroupArray.Where(UG => UG.Id == UserIds.UserGroupId).First().Name; UserIdToGroupName.Add(UserIds.UserId, UserGroupName); } //Get list of user Ids foreach crash range List <int> UserIdCrashes = EnumerableCrashes.Select(Crash => Crash.UserNameId.Value).ToList(); //count the number of crashes in each group using the mapping of user id to crash. foreach (int UserId in UserIdCrashes) { string UserGroupName = UserIdToGroupName[UserId]; Results[UserGroupName]++; } } catch (Exception Ex) { Debug.WriteLine("Exception in GetCountsByGroupFromCrashes: " + Ex.ToString()); } return(Results); } }
/// <summary> /// Retrieves a list of distinct UE4 Versions from the CrashRepository /// </summary> public static List <SelectListItem> GetVersionsAsListItems() { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer("CrashRepository.GetVersions")) { CrashReportDataContext Context = new CrashReportDataContext(); DateTime Now = DateTime.UtcNow; if (Now - LastVersionDate > TimeSpan.FromHours(1)) { var BuildVersions = Context.Crashes .Where(c => c.TimeOfCrash > DateTime.Now.AddDays(-14)) .Where(c => c.CrashType != 3) // Ignore ensures .Select(c => c.BuildVersion) .Distinct() .ToList(); DistinctBuildVersions = new HashSet <string>(); foreach (var BuildVersion in BuildVersions) { var BVParts = BuildVersion.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if (BVParts.Length > 2 && BVParts[0] != "0") { string CleanBV = string.Format("{0}.{1}.{2}", BVParts[0], BVParts[1], BVParts[2]); DistinctBuildVersions.Add(CleanBV); } } VersionsAsSelectList = DistinctBuildVersions .Select(listitem => new SelectListItem { Selected = false, Text = listitem, Value = listitem }) .ToList(); VersionsAsSelectList.Insert(0, new SelectListItem { Selected = true, Text = "", Value = "" }); LastVersionDate = Now; } return(VersionsAsSelectList); } }
/// <summary> /// Apply a dynamic query to a set of results. /// </summary> /// <param name="Results">The initial set of results.</param> /// <param name="Query">The query to apply.</param> /// <returns>A set of results filtered by the query.</returns> public IEnumerable <Crash> Search(IEnumerable <Crash> Results, string Query) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(Query=" + Query + ")")) { IEnumerable <Crash> Crashes = null; var IntermediateQueryable = Results.AsQueryable(); try { string[] Terms = Query.Split("-, ;+".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); // Iterate over all crashes. // Brute force search. foreach (string Term in Terms) { Results = Results.Where(X => (!string.IsNullOrEmpty(X.BuiltFromCL) ? X.BuiltFromCL.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.PlatformName) ? X.PlatformName.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.Summary) ? X.Summary.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.Description) ? X.Description.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.RawCallStack) ? X.RawCallStack.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.CommandLine) ? X.CommandLine.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.MachineId) ? X.MachineId.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.Module) ? X.Module.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.BaseDir) ? X.BaseDir.ToLower().Contains(Term) : false) || (!string.IsNullOrEmpty(X.Jira) ? X.Jira.ToLower().Contains(Term) : false) ); } Crashes = Results; } catch (Exception Ex) { Debug.WriteLine("Exception in Search: " + Ex.ToString()); Crashes = Results; } return(Crashes); } }
/// <summary> /// Add a crash passed in the payload as Xml to the database. /// </summary> /// <param name="id">Unused.</param> /// <returns>The row id of the newly added crash.</returns> public ActionResult AddCrash(int id) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(NewCrashId=" + id + ")")) { CrashRepository Crashes = new CrashRepository(); CrashReporterResult NewCrashResult = new CrashReporterResult(); NewCrashResult.ID = -1; for (int Index = 0; Index < 3; Index++) { try { UnsafeAddCrash(NewCrashResult, Crashes); break; } catch (SqlException SqlExc) { if (SqlExc.Number == -2) { FLogger.Global.WriteEvent(string.Format("AddCrash:Timeout, retrying {0} of 3", Index + 1)); } else { NewCrashResult.Message = SqlExc.ToString(); NewCrashResult.bSuccess = false; break; } } catch (Exception Ex) { NewCrashResult.Message = Ex.ToString(); NewCrashResult.bSuccess = false; break; } System.Threading.Thread.Sleep(5000 * (Index + 1)); } string ReturnResult = XmlHandler.ToXmlString <CrashReporterResult>(NewCrashResult); return(Content(ReturnResult, "text/xml")); } }
/// <summary> /// /// </summary> /// <returns></returns> public List <SelectListItem> GetBranchesAsListItems() { var branchesAsSelectList = cache["branches"] as List <SelectListItem>; if (branchesAsSelectList == null) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer("CrashRepository.GetBranches")) { var date = DateTime.Now.AddDays(-14); var BranchList = _entityContext.Crashes .Where(n => n.TimeOfCrash > date) .Where(c => c.CrashType != 3) // Ignore ensures // Depot - //depot/UE4* || Stream //UE4, //Something etc. .Where(n => n.Branch.StartsWith("UE4") || n.Branch.StartsWith("//")) .Select(n => n.Branch) .Distinct() .ToList(); branchesAsSelectList = BranchList .Select(listItem => new SelectListItem { Selected = false, Text = listItem, Value = listItem }) .OrderBy(listItem => listItem.Text) .ToList(); branchesAsSelectList.Insert(0, new SelectListItem { Selected = true, Text = "", Value = "" }); } var searchFilterCachePolicy = new CacheItemPolicy() { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(Settings.Default.SearchFilterCacheInMinutes) }; cache.Set("branches", branchesAsSelectList, searchFilterCachePolicy); } return(branchesAsSelectList); }
/// <summary> /// /// </summary> /// <param name="ReportsForm"></param> /// <returns></returns> public ActionResult Index(FormCollection ReportsForm) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { FormHelper FormData = new FormHelper(Request, ReportsForm, "JustReport"); // Handle 'CopyToJira' button int BuggIDToBeAddedToJira = 0; foreach (var Entry in ReportsForm) { if (int.TryParse(Entry.ToString(), out BuggIDToBeAddedToJira)) { break; } } ReportsViewModel Results = GetResults(FormData, BuggIDToBeAddedToJira); Results.GenerationTime = LogTimer.GetElapsedSeconds().ToString("F2"); return(View("Index", Results)); } }
/// <summary> /// Gets a container of crash counts per user group for all crashes. /// </summary> /// <returns>A dictionary of user group names, and the count of crashes for each group.</returns> public Dictionary <string, int> GetCountsByGroup() { // @TODO 2014-11-06 Optimize? using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + " SQL OPT")) { Dictionary <string, int> Results = new Dictionary <string, int>(); try { var GroupCounts = ( from UserDetail in Context.Users join UserGroupDetail in Context.UserGroups on UserDetail.UserGroupId equals UserGroupDetail.Id group UserDetail by UserGroupDetail.Name into GroupCount select new { Key = GroupCount.Key, Count = GroupCount.Count() } ); foreach (var GroupCount in GroupCounts) { Results.Add(GroupCount.Key, GroupCount.Count); } // Add in all groups, even though there are no crashes associated IEnumerable <string> UserGroups = (from UserGroupDetail in Context.UserGroups select UserGroupDetail.Name); foreach (string UserGroupName in UserGroups) { if (!Results.Keys.Contains(UserGroupName)) { Results[UserGroupName] = 0; } } } catch (Exception Ex) { Debug.WriteLine("Exception in GetCountsByGroup: " + Ex.ToString()); } return(Results); } }
/// <summary> /// Get a report with the default form data and return the reports index view /// </summary> /// <param name="ReportsForm"></param> /// <returns></returns> public ActionResult Index( FormCollection ReportsForm ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true ) ) { FormHelper FormData = new FormHelper( Request, ReportsForm, "JustReport" ); // Handle 'CopyToJira' button int BuggIDToBeAddedToJira = 0; foreach( var Entry in ReportsForm ) { if (Entry.ToString().Contains("CopyToJira-")) { int.TryParse(Entry.ToString().Substring("CopyToJira-".Length), out BuggIDToBeAddedToJira); break; } } var results = GetResults( FormData, BuggIDToBeAddedToJira ); results.GenerationTime = LogTimer.GetElapsedSeconds().ToString( "F2" ); return View( "Index", results ); } }
/// <summary> /// Return to the index view with a report for a specific branch /// </summary> /// <param name="ReportsForm"></param> /// <returns></returns> public ActionResult BranchReport(FormCollection ReportsForm) { using (var logTimer = new FAutoScopedLogTimer(this.GetType().ToString(), bCreateNewLog: true)) { var formData = new FormHelper(Request, ReportsForm, "JustReport"); var buggIdToBeAddedToJira = 0; // Handle 'CopyToJira' button foreach (var Entry in ReportsForm) { if (Entry.ToString().Contains("CopyToJira-")) { int.TryParse(Entry.ToString().Substring("CopyToJira-".Length), out buggIdToBeAddedToJira); break; } } var results = GetResults(formData, buggIdToBeAddedToJira); results.GenerationTime = logTimer.GetElapsedSeconds().ToString("F2"); return(View("Index", results)); } }
/// <summary> /// Retrieves a list of function names from a list of function name ids. /// Primarily used to fill GetFunctionCalls in CachedDataService /// </summary> /// <param name="Ids">A list of unique function name ids.</param> /// <returns>A list of strings that make up a callstack.</returns> public List <string> GetFunctionCalls(List <int> Ids) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(Ids.Count=" + Ids.Count + ")")) { List <string> FunctionCalls = new List <string>(); try { List <FunctionCall> Funcs = Context.FunctionCalls.Where(FuncCall => Ids.Contains(FuncCall.Id)).ToList(); // Order by Ids foreach (int Id in Ids) { var Found = Funcs.Find(FC => FC.Id == Id); FunctionCalls.Add(Found.Call); } } catch (Exception Ex) { Debug.WriteLine("Exception in GetFunctionCalls: " + Ex.ToString()); } return(FunctionCalls); } }
/// <summary> /// /// </summary> /// <param name="UserGroupId"></param> /// <returns></returns> public List <string> GetUsersForGroupId(int UserGroupId) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(UserGroupId=" + UserGroupId + ")")) { List <string> Users = new List <string>(); try { Users = ( from UserDetail in Context.Users where UserDetail.UserGroupId == UserGroupId orderby UserDetail.UserName select UserDetail.UserName ).ToList(); } catch (Exception Ex) { Debug.WriteLine("Exception in GetUsersForGroup: " + Ex.ToString()); } return(Users); } }
/// <summary> /// Gets the crash from an id. /// </summary> /// <param name="Id">The id of the crash we wish to examine.</param> /// <returns>The crash with the requested id.</returns> public Crash GetCrash(int Id) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(CrashId=" + Id + ")")) { try { IQueryable <Crash> Crashes = ( from CrashDetail in Context.Crashes where CrashDetail.Id == Id select CrashDetail ); return(Crashes.FirstOrDefault()); } catch (Exception Ex) { Debug.WriteLine("Exception in GetCrash: " + Ex.ToString()); } return(null); } }
/// <summary> /// Get a Bugg from an id /// </summary> /// <param name="Id">The id of a Bugg.</param> /// <returns>A Bugg representing a group of crashes.</returns> public Bugg GetBugg(int Id) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(" + Id + ")")) { Bugg Result = null; try { Result = ( from BuggDetail in Context.Buggs where BuggDetail.Id == Id select BuggDetail ).FirstOrDefault(); } catch (Exception Ex) { Debug.WriteLine("Exception in GetBugg: " + Ex.ToString()); } return(Result); } }
/// <summary> /// Bucket the Buggs by user group. /// </summary> /// <param name="Buggs">A list of Buggs to bucket.</param> /// <returns>A dictionary of user group names, and the count of Buggs for each group.</returns> public SortedDictionary <string, int> GetCountsByGroup(IEnumerable <Bugg> Buggs) { // @TODO yrx 2014-11-06 Optimize using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + " SQL OPT")) { Dictionary <string, int> Results = new Dictionary <string, int>(); try { Results = ( from BuggDetail in Buggs join BuggsUserGroupDetail in Context.Buggs_UserGroups on BuggDetail.Id equals BuggsUserGroupDetail.BuggId join UserGroupDetail in Context.UserGroups on BuggsUserGroupDetail.UserGroupId equals UserGroupDetail.Id group 0 by UserGroupDetail.Name into GroupCount select new { Key = GroupCount.Key, Count = GroupCount.Count() } ).ToDictionary(x => x.Key, y => y.Count); // Add in all groups, even though there are no crashes associated IEnumerable <string> UserGroups = (from UserGroupDetail in Context.UserGroups select UserGroupDetail.Name); foreach (string UserGroupName in UserGroups) { if (!Results.Keys.Contains(UserGroupName)) { Results[UserGroupName] = 0; } } } catch (Exception Ex) { Debug.WriteLine("Exception in GetCountsByGroup: " + Ex.ToString()); } SortedDictionary <string, int> SortedResults = new SortedDictionary <string, int>(Results); return(SortedResults); } }
/// <summary> /// Gets a container of crash counts per user group for the set of crashes passed in. /// </summary> /// <param name="Crashes">The set of crashes to tabulate by user group.</param> /// <returns>A dictionary of user group names, and the count of Buggs for each group.</returns> public Dictionary <string, int> GetCountsByGroupFromCrashes(IQueryable <Crash> Crashes) { // @TODO yrx 2014-11-06 Optimize? using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { Dictionary <string, int> Results = new Dictionary <string, int>(); try { Results = ( from CrashDetail in Crashes from UserDetail in CrashRepositoryDataContext.Users join UserGroupDetail in CrashRepositoryDataContext.UserGroups on UserDetail.UserGroupId equals UserGroupDetail.Id where CrashDetail.UserNameId == UserDetail.Id || CrashDetail.UserName == UserDetail.UserName group CrashDetail by UserGroupDetail.Name into GroupCount select new { Key = GroupCount.Key, Count = GroupCount.Count() } ).ToDictionary(x => x.Key, y => y.Count); // Add in all groups, even though there are no crashes associated IEnumerable <string> UserGroups = (from UserGroupDetail in CrashRepositoryDataContext.UserGroups select UserGroupDetail.Name); foreach (string UserGroupName in UserGroups) { if (!Results.Keys.Contains(UserGroupName)) { Results[UserGroupName] = 0; } } } catch (Exception Ex) { Debug.WriteLine("Exception in GetCountsByGroupFromCrashes: " + Ex.ToString()); } return(Results); } }
/// <summary> /// Retrieve a list of function call names from an encoded pattern. This is either from the cache, or from the database and then added to the cache. /// </summary> /// <param name="Pattern">A callstack pattern in the form '1+3+34+2'.</param> /// <returns>A list of function names.</returns> public List <string> GetFunctionCalls(string Pattern) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(Count=" + CacheInstance.Count + ")")) { string Key = CacheKeyPrefix + FunctionCallKeyPrefix + Pattern; List <string> FunctionCalls = (List <string>)CacheInstance[Key]; if (FunctionCalls == null) { string[] Ids = Pattern.Split("+".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); List <int> IdList = new List <int>(); foreach (string id in Ids) { int i; if (int.TryParse(id, out i)) { IdList.Add(i); } } // These function calls don't add any new information. IdList.Remove(63039); // 63039 KERNELBASE!RaiseException() IdList.Remove(63138); // 63138 UE4Editor_Core!FDebug::EnsureFailed() IdList.Remove(63137); // 63137 UE4Editor_Core!NewReportEnsure() IdList.Remove(63149); // 63149 UE4Editor_Core!FOutputDeviceWindowsError::Serialize() IdList.Remove(63151); // 63151 UE4Editor_Core!FDebug::AssertFailed() IdList.Remove(63445); // 63445 UE4Editor_Core!FDebug::EnsureNotFalseFormatted() IdList.Remove(64334); // 64334 UE4Editor_Core!FOutputDevice::Logf__VA() FunctionCalls = BuggRepositoryInstance.GetFunctionCalls(IdList); CacheInstance.Insert(Key, FunctionCalls); } return(FunctionCalls); } }
/// <summary> /// Filter the list of Buggs by a search query. /// </summary> /// <param name="Results">The unfiltered set of Buggs.</param> /// <param name="Query">The query to use as a filter.</param> /// <returns>A filtered set of Buggs.</returns> public IQueryable <Bugg> Search(IQueryable <Bugg> Results, string Query) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(Query=" + Query + ")")) { // Also may want to revisit how we search since this could get inefficient for a big search set. IQueryable <Bugg> Buggs; try { string QueryString = HttpUtility.HtmlDecode(Query.ToString()); if (QueryString == null) { QueryString = ""; } // Take out terms starting with a - string[] Terms = QueryString.Split("-, ;+".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); string TermsToUse = ""; foreach (string Term in Terms) { string CallId = GetFunctionCallId(Term).ToString(); if (!TermsToUse.Contains(CallId)) { TermsToUse = TermsToUse + "+" + CallId; } } Buggs = (IQueryable <Bugg>)Results.Search(new string[] { "Pattern" }, TermsToUse.Split("+".ToCharArray())); } catch (Exception Ex) { Debug.WriteLine("Exception in Search: " + Ex.ToString()); Buggs = Results; } return(Buggs); } }
/// <summary> /// Get the id of a function from its name. /// </summary> /// <param name="FunctionCallName">The name of the function to look up.</param> /// <returns>The unique id of the function name, or zero if none is found.</returns> public int GetFunctionCallId( string FunctionCallName ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { try { FunctionCall FunctionCall = BuggsDataContext.FunctionCalls.Where( FunctionCallInstance => FunctionCallInstance.Call.Contains( FunctionCallName ) ).First(); return FunctionCall.Id; } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetFunctionCallId: " + Ex.ToString() ); } return 0; } }
/// <summary> /// Bucket the Buggs by user group. /// </summary> /// <param name="Buggs">A list of Buggs to bucket.</param> /// <returns>A dictionary of user group names, and the count of Buggs for each group.</returns> public Dictionary<string, int> GetCountsByGroup( IQueryable<Bugg> Buggs ) { // @TODO yrx 2014-11-06 Optimize using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { Dictionary<string, int> Results = new Dictionary<string, int>(); try { Results = ( from BuggDetail in Buggs join BuggsUserGroupDetail in BuggsDataContext.Buggs_UserGroups on BuggDetail.Id equals BuggsUserGroupDetail.BuggId join UserGroupDetail in BuggsDataContext.UserGroups on BuggsUserGroupDetail.UserGroupId equals UserGroupDetail.Id group 0 by UserGroupDetail.Name into GroupCount select new { Key = GroupCount.Key, Count = GroupCount.Count() } ).ToDictionary( x => x.Key, y => y.Count ); // Add in all groups, even though there are no crashes associated IEnumerable<string> UserGroups = ( from UserGroupDetail in BuggsDataContext.UserGroups select UserGroupDetail.Name ); foreach( string UserGroupName in UserGroups ) { if( !Results.Keys.Contains( UserGroupName ) ) { Results[UserGroupName] = 0; } } } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetCountsByGroup: " + Ex.ToString() ); } return Results; } }
/// <summary> /// Retrieve all Buggs matching the search criteria. /// </summary> /// <param name="FormData">The incoming form of search criteria from the client.</param> /// <param name="BuggIDToBeAddedToJira">ID of the bugg that will be added to JIRA</param> /// <returns>A view to display the filtered Buggs.</returns> public ReportsViewModel GetResults(FormHelper FormData, int BuggIDToBeAddedToJira) { BuggRepository BuggsRepo = new BuggRepository(); CrashRepository CrashRepo = new CrashRepository(); // @TODO yrx 2015-02-17 BuggIDToBeAddedToJira replace with List<int> based on check box and Submit? // Enumerate JIRA projects if needed. // https://jira.ol.epicgames.net//rest/api/2/project var JC = JiraConnection.Get(); var JiraComponents = JC.GetNameToComponents(); var JiraVersions = JC.GetNameToVersions(); using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { string AnonumousGroup = "Anonymous"; //List<String> Users = FRepository.Get().GetUserNamesFromGroupName( AnonumousGroup ); int AnonymousGroupID = FRepository.Get(BuggsRepo).FindOrAddGroup(AnonumousGroup); HashSet <int> AnonumousIDs = FRepository.Get(BuggsRepo).GetUserIdsFromUserGroup(AnonumousGroup); int AnonymousID = AnonumousIDs.First(); HashSet <string> UserNamesForUserGroup = FRepository.Get(BuggsRepo).GetUserNamesFromGroupName(AnonumousGroup); //FormData.DateFrom = DateTime.Now.AddDays( -1 ); var Crashes = CrashRepo .FilterByDate(CrashRepo.ListAll(), FormData.DateFrom, FormData.DateTo.AddDays(1)) // Only crashes and asserts .Where(Crash => Crash.CrashType == 1 || Crash.CrashType == 2) // Only anonymous user .Where(Crash => Crash.UserNameId.Value == AnonymousID) .Select(Crash => new { ID = Crash.Id, TimeOfCrash = Crash.TimeOfCrash.Value, //UserID = Crash.UserNameId.Value, BuildVersion = Crash.BuildVersion, JIRA = Crash.TTPID, Platform = Crash.PlatformName, FixCL = Crash.FixedChangeList, BuiltFromCL = Crash.ChangeListVersion, Pattern = Crash.Pattern, MachineID = Crash.ComputerName, Branch = Crash.Branch, Description = Crash.Description, RawCallStack = Crash.RawCallStack, }) .ToList(); int NumCrashes = Crashes.Count; /* * // Build patterns for crashes where patters is null. * var CrashesWithoutPattern = FRepository.Get().Crashes * .FilterByDate( FRepository.Get().Crashes.ListAll(), FormData.DateFrom, FormData.DateTo.AddDays( 1 ) ) * // Only crashes and asserts * .Where( Crash => Crash.Pattern == null || Crash.Pattern == "" ) * .Select( Crash => Crash ) * .ToList(); * * foreach( var Crash in CrashesWithoutPattern ) * { * Crash.BuildPattern(); * } */ // Total # of ALL (Anonymous) crashes in timeframe int TotalAnonymousCrashes = NumCrashes; // Total # of UNIQUE (Anonymous) crashes in timeframe HashSet <string> UniquePatterns = new HashSet <string>(); HashSet <string> UniqueMachines = new HashSet <string>(); Dictionary <string, int> PatternToCount = new Dictionary <string, int>(); //List<int> CrashesWithoutPattern = new List<int>(); //List<DateTime> CrashesWithoutPatternDT = new List<DateTime>(); foreach (var Crash in Crashes) { if (string.IsNullOrEmpty(Crash.Pattern)) { //CrashesWithoutPattern.Add( Crash.ID ); //CrashesWithoutPatternDT.Add( Crash.TimeOfCrash ); continue; } UniquePatterns.Add(Crash.Pattern); UniqueMachines.Add(Crash.MachineID); bool bAdd = !PatternToCount.ContainsKey(Crash.Pattern); if (bAdd) { PatternToCount.Add(Crash.Pattern, 1); } else { PatternToCount[Crash.Pattern]++; } } var PatternToCountOrdered = PatternToCount.OrderByDescending(X => X.Value).ToList(); // The 100 top. var PatternAndCount = PatternToCountOrdered.Take(100).ToDictionary(X => X.Key, Y => Y.Value); int TotalUniqueAnonymousCrashes = UniquePatterns.Count; // Total # of AFFECTED USERS (Anonymous) in timeframe int TotalAffectedUsers = UniqueMachines.Count; var RealBuggs = BuggsRepo.Context.Buggs.Where(Bugg => PatternAndCount.Keys.Contains(Bugg.Pattern)).ToList(); // Build search string. HashSet <string> FoundJiras = new HashSet <string>(); Dictionary <string, List <Bugg> > JiraIDtoBugg = new Dictionary <string, List <Bugg> >(); List <Bugg> Buggs = new List <Bugg>(100); foreach (var Top in PatternAndCount) { Bugg NewBugg = RealBuggs.Where(X => X.Pattern == Top.Key).FirstOrDefault(); if (NewBugg != null) { using (FAutoScopedLogTimer TopTimer = new FAutoScopedLogTimer(string.Format("{0}:{1}", Buggs.Count + 1, NewBugg.Id))) { var CrashesForBugg = Crashes.Where(Crash => Crash.Pattern == Top.Key).ToList(); // Convert anonymous to full type; var FullCrashesForBugg = new List <Crash>(CrashesForBugg.Count); foreach (var Anon in CrashesForBugg) { FullCrashesForBugg.Add(new Crash() { ID = Anon.ID, TimeOfCrash = Anon.TimeOfCrash, BuildVersion = Anon.BuildVersion, JIRA = Anon.JIRA, Platform = Anon.Platform, FixCL = Anon.FixCL, BuiltFromCL = Anon.BuiltFromCL, Pattern = Anon.Pattern, MachineID = Anon.MachineID, Branch = Anon.Branch, Description = Anon.Description, RawCallStack = Anon.RawCallStack, }); } NewBugg.PrepareBuggForJira(FullCrashesForBugg); // Verify valid JiraID, this may be still a TTP if (!string.IsNullOrEmpty(NewBugg.TTPID)) { int TTPID = 0; int.TryParse(NewBugg.TTPID, out TTPID); if (TTPID == 0) { AddBuggJiraMapping(NewBugg, ref FoundJiras, ref JiraIDtoBugg); } } Buggs.Add(NewBugg); } } else { FLogger.Global.WriteEvent("Bugg for pattern " + Top.Key + " not found"); } } if (BuggIDToBeAddedToJira > 0) { var Bugg = Buggs.Where(X => X.Id == BuggIDToBeAddedToJira).FirstOrDefault(); if (Bugg != null) { Bugg.CopyToJira(); AddBuggJiraMapping(Bugg, ref FoundJiras, ref JiraIDtoBugg); } } if (JC.CanBeUsed()) { var BuggsCopy = new List <Bugg>(Buggs); // Grab the data form JIRA. string JiraSearchQuery = string.Join(" OR ", FoundJiras); using (FAutoScopedLogTimer JiraResultsTimer = new FAutoScopedLogTimer("JiraResults")) { var JiraResults = JC.SearchJiraTickets( JiraSearchQuery, new string[] { "key", // string "summary", // string "components", // System.Collections.ArrayList, Dictionary<string,object>, name "resolution", // System.Collections.Generic.Dictionary`2[System.String,System.Object], name "fixVersions", // System.Collections.ArrayList, Dictionary<string,object>, name "customfield_11200" // string }); // Jira Key, Summary, Components, Resolution, Fix version, Fix changelist foreach (var Jira in JiraResults) { string JiraID = Jira.Key; string Summary = (string)Jira.Value["summary"]; string ComponentsText = ""; System.Collections.ArrayList Components = (System.Collections.ArrayList)Jira.Value["components"]; foreach (Dictionary <string, object> Component in Components) { ComponentsText += (string)Component["name"]; ComponentsText += " "; } Dictionary <string, object> ResolutionFields = (Dictionary <string, object>)Jira.Value["resolution"]; string Resolution = ResolutionFields != null ? (string)ResolutionFields["name"] : ""; string FixVersionsText = ""; System.Collections.ArrayList FixVersions = (System.Collections.ArrayList)Jira.Value["fixVersions"]; foreach (Dictionary <string, object> FixVersion in FixVersions) { FixVersionsText += (string)FixVersion["name"]; FixVersionsText += " "; } int FixCL = Jira.Value["customfield_11200"] != null ? (int)(decimal)Jira.Value["customfield_11200"] : 0; List <Bugg> BuggsForJira; JiraIDtoBugg.TryGetValue(JiraID, out BuggsForJira); //var BuggsForJira = JiraIDtoBugg[JiraID]; if (BuggsForJira != null) { foreach (Bugg Bugg in BuggsForJira) { Bugg.JiraSummary = Summary; Bugg.JiraComponentsText = ComponentsText; Bugg.JiraResolution = Resolution; Bugg.JiraFixVersionsText = FixVersionsText; if (FixCL != 0) { Bugg.JiraFixCL = FixCL.ToString(); } BuggsCopy.Remove(Bugg); } } } } // If there are buggs, we need to update the summary to indicate an error. // Usually caused when bugg's project has changed. foreach (var Bugg in BuggsCopy.Where(b => !string.IsNullOrEmpty(b.TTPID))) { Bugg.JiraSummary = "JIRA MISMATCH"; Bugg.JiraComponentsText = "JIRA MISMATCH"; Bugg.JiraResolution = "JIRA MISMATCH"; Bugg.JiraFixVersionsText = "JIRA MISMATCH"; Bugg.JiraFixCL = "JIRA MISMATCH"; } } Buggs = Buggs.OrderByDescending(b => b.NumberOfCrashes).ToList(); return(new ReportsViewModel { Buggs = Buggs, /*Crashes = Crashes,*/ DateFrom = (long)(FormData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds, DateTo = (long)(FormData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds, TotalAffectedUsers = TotalAffectedUsers, TotalAnonymousCrashes = TotalAnonymousCrashes, TotalUniqueAnonymousCrashes = TotalUniqueAnonymousCrashes }); } }
/// <summary> /// The Show action. /// </summary> /// <param name="BuggsForm">The form of user data passed up from the client.</param> /// <param name="id">The unique id of the Bugg.</param> /// <returns>The view to display a Bugg on the client.</returns> public ActionResult Show(FormCollection BuggsForm, int id) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(BuggId=" + id + ")", bCreateNewLog: true)) { // Handle 'CopyToJira' button int BuggIDToBeAddedToJira = 0; foreach (var Entry in BuggsForm) { if (Entry.ToString().Contains(Bugg.JiraSubmitName)) { int.TryParse(Entry.ToString().Substring(Bugg.JiraSubmitName.Length), out BuggIDToBeAddedToJira); break; } } BuggRepository Buggs = new BuggRepository(); // Set the display properties based on the radio buttons bool DisplayModuleNames = false; if (BuggsForm["DisplayModuleNames"] == "true") { DisplayModuleNames = true; } bool DisplayFunctionNames = false; if (BuggsForm["DisplayFunctionNames"] == "true") { DisplayFunctionNames = true; } bool DisplayFileNames = false; if (BuggsForm["DisplayFileNames"] == "true") { DisplayFileNames = true; } bool DisplayFilePathNames = false; if (BuggsForm["DisplayFilePathNames"] == "true") { DisplayFilePathNames = true; DisplayFileNames = false; } bool DisplayUnformattedCallStack = false; if (BuggsForm["DisplayUnformattedCallStack"] == "true") { DisplayUnformattedCallStack = true; } // Create a new view and populate with crashes List <Crash> Crashes = null; BuggViewModel Model = new BuggViewModel(); Bugg NewBugg = Buggs.GetBugg(id); if (NewBugg == null) { return(RedirectToAction("")); } Crashes = NewBugg.GetCrashes(); using (FAutoScopedLogTimer GetCrashesTimer = new FAutoScopedLogTimer("Bugg.PrepareBuggForJira")) { if (Crashes.Count > 0) { NewBugg.PrepareBuggForJira(Crashes); if (BuggIDToBeAddedToJira != 0) { NewBugg.CopyToJira(); } } } using (FAutoScopedLogTimer JiraResultsTimer = new FAutoScopedLogTimer("Bugg.GrabJira")) { var JC = JiraConnection.Get(); bool bValidJira = false; // Verify valid JiraID, this may be still a TTP if (!string.IsNullOrEmpty(NewBugg.Jira)) { int TTPID = 0; int.TryParse(NewBugg.Jira, out TTPID); if (TTPID == 0) { //AddBuggJiraMapping( NewBugg, ref FoundJiras, ref JiraIDtoBugg ); bValidJira = true; } } if (JC.CanBeUsed() && bValidJira) { // Grab the data form JIRA. string JiraSearchQuery = "key = " + NewBugg.Jira; var JiraResults = JC.SearchJiraTickets( JiraSearchQuery, new string[] { "key", // string "summary", // string "components", // System.Collections.ArrayList, Dictionary<string,object>, name "resolution", // System.Collections.Generic.Dictionary`2[System.String,System.Object], name "fixVersions", // System.Collections.ArrayList, Dictionary<string,object>, name "customfield_11200" // string }); // Jira Key, Summary, Components, Resolution, Fix version, Fix changelist foreach (var Jira in JiraResults) { string JiraID = Jira.Key; string Summary = (string)Jira.Value["summary"]; string ComponentsText = ""; System.Collections.ArrayList Components = (System.Collections.ArrayList)Jira.Value["components"]; foreach (Dictionary <string, object> Component in Components) { ComponentsText += (string)Component["name"]; ComponentsText += " "; } Dictionary <string, object> ResolutionFields = (Dictionary <string, object>)Jira.Value["resolution"]; string Resolution = ResolutionFields != null ? (string)ResolutionFields["name"] : ""; string FixVersionsText = ""; System.Collections.ArrayList FixVersions = (System.Collections.ArrayList)Jira.Value["fixVersions"]; foreach (Dictionary <string, object> FixVersion in FixVersions) { FixVersionsText += (string)FixVersion["name"]; FixVersionsText += " "; } int FixCL = Jira.Value["customfield_11200"] != null ? (int)(decimal)Jira.Value["customfield_11200"] : 0; NewBugg.JiraSummary = Summary; NewBugg.JiraComponentsText = ComponentsText; NewBugg.JiraResolution = Resolution; NewBugg.JiraFixVersionsText = FixVersionsText; if (FixCL != 0) { NewBugg.JiraFixCL = FixCL.ToString(); } break; } } } // Apply any user settings if (BuggsForm.Count > 0) { if (!string.IsNullOrEmpty(BuggsForm["SetStatus"])) { NewBugg.Status = BuggsForm["SetStatus"]; Buggs.SetBuggStatus(NewBugg.Status, id); } if (!string.IsNullOrEmpty(BuggsForm["SetFixedIn"])) { NewBugg.FixedChangeList = BuggsForm["SetFixedIn"]; Buggs.SetBuggFixedChangeList(NewBugg.FixedChangeList, id); } if (!string.IsNullOrEmpty(BuggsForm["SetTTP"])) { NewBugg.Jira = BuggsForm["SetTTP"]; Buggs.SetJIRAForBuggAndCrashes(NewBugg.Jira, id); } // <STATUS> } // Set up the view model with the crash data Model.Bugg = NewBugg; Model.Crashes = Crashes; Crash NewCrash = Model.Crashes.FirstOrDefault(); if (NewCrash != null) { using (FScopedLogTimer LogTimer2 = new FScopedLogTimer("CallstackTrimming")) { CallStackContainer CallStack = new CallStackContainer(NewCrash); // Set callstack properties CallStack.bDisplayModuleNames = DisplayModuleNames; CallStack.bDisplayFunctionNames = DisplayFunctionNames; CallStack.bDisplayFileNames = DisplayFileNames; CallStack.bDisplayFilePathNames = DisplayFilePathNames; CallStack.bDisplayUnformattedCallStack = DisplayUnformattedCallStack; Model.CallStack = CallStack; // Shorten very long function names. foreach (CallStackEntry Entry in Model.CallStack.CallStackEntries) { Entry.FunctionName = Entry.GetTrimmedFunctionName(128); } Model.SourceContext = NewCrash.SourceContext; } Model.Bugg.LatestCrashSummary = NewCrash.Summary; } Model.GenerationTime = LogTimer.GetElapsedSeconds().ToString("F2"); return(View("Show", Model)); } }
/// <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> /// <returns>A sorted container of Buggs.</returns> public IQueryable<Bugg> GetSortedResults( IQueryable<Bugg> Results, string SortTerm, bool bSortDescending, DateTime DateFrom, DateTime DateTo ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { try { var IntermediateResults = ( from BuggCrashDetail in BuggsDataContext.Buggs_Crashes where BuggCrashDetail.Crash.TimeOfCrash >= DateFrom && BuggCrashDetail.Crash.TimeOfCrash <= AddOneDayToDate( DateTo ) group BuggCrashDetail by BuggCrashDetail.BuggId into CrashesGrouped join BuggDetail in Results on CrashesGrouped.Key equals BuggDetail.Id select new { Bugg = BuggDetail, Count = CrashesGrouped.Count() } ); using( FScopedLogTimer LogTimer2 = new FScopedLogTimer( "BuggRepository.GetSortedResults.CrashesInTimeFrame" ) ) { foreach( var Result in IntermediateResults ) { Result.Bugg.CrashesInTimeFrame = Result.Count; } } switch( SortTerm ) { case "CrashesInTimeFrame": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Count, bSortDescending ); break; case "Id": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.Id, bSortDescending ); break; case "BuildVersion": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.BuildVersion, bSortDescending ); break; case "LatestCrash": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.TimeOfLastCrash, bSortDescending ); break; case "FirstCrash": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.TimeOfFirstCrash, bSortDescending ); break; case "NumberOfCrashes": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.NumberOfCrashes, bSortDescending ); break; case "NumberOfUsers": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.NumberOfUsers, bSortDescending ); break; case "Pattern": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.Pattern, bSortDescending ); break; case "Status": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.Status, bSortDescending ); break; case "FixedChangeList": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.FixedChangeList, bSortDescending ); break; case "TTPID": IntermediateResults = CrashRepository.OrderBy( IntermediateResults, BuggCrashInstance => BuggCrashInstance.Bugg.TTPID, bSortDescending ); break; } return IntermediateResults.Select( x => x.Bugg ); } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetSortedResults: " + Ex.ToString() ); } return Results; } }
/// <summary> /// Associate a set of crashes and their owners with a Bugg. /// </summary> /// <param name="Bugg">The Bugg to get the data.</param> /// <param name="Crashes">The set of crashes to add to the Bugg.</param> public void UpdateBuggData( Bugg Bugg, IEnumerable<Crash> Crashes ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString()+ "(" + Bugg.Id + ")" ) ) { FLogger.WriteEvent( "UpdateBuggData Bugg.Id=" + Bugg.Id ); DateTime? TimeOfFirstCrash = null; DateTime? TimeOfLastCrash = null; string BuildVersion = null; List<int> UserNameIds = new List<int>(); int CrashCount = 0; // Set or return min date max date while we're iterating through the crashes bool bHasChanges = false; try { foreach( Crash CurrentCrash in Crashes ) { CrashCount++; if( TimeOfFirstCrash == null || CurrentCrash.TimeOfCrash < TimeOfFirstCrash ) { TimeOfFirstCrash = CurrentCrash.TimeOfCrash; } if( TimeOfLastCrash == null || CurrentCrash.TimeOfCrash > TimeOfLastCrash ) { TimeOfLastCrash = CurrentCrash.TimeOfCrash; } if( BuildVersion == null || CurrentCrash.BuildVersion.CompareTo( BuildVersion ) > 0 ) { BuildVersion = CurrentCrash.BuildVersion; } // Handle user count if( Bugg.Id != 0 ) { if( !UserNameIds.Contains( CurrentCrash.User.Id ) ) { // Add username to local users variable that we will use later to set user count and associate users and user groups to the bugg. UserNameIds.Add( CurrentCrash.User.Id ); } } CurrentCrash.FixedChangeList = Bugg.FixedChangeList; CurrentCrash.TTPID = Bugg.TTPID; CurrentCrash.Status = Bugg.Status; if( Bugg.Id != 0 ) { bHasChanges |= LinkCrashToBugg( Bugg, CurrentCrash ); } } if( CrashCount > 1 ) { Bugg.TimeOfLastCrash = TimeOfLastCrash; Bugg.TimeOfFirstCrash = TimeOfFirstCrash; Bugg.NumberOfUsers = UserNameIds.Count(); Bugg.NumberOfCrashes = CrashCount; Bugg.BuildVersion = BuildVersion; foreach( int UserNameId in UserNameIds ) { // Link user to Bugg bHasChanges |= LinkUserToBugg( Bugg, UserNameId ); } } if( bHasChanges ) { BuggsDataContext.SubmitChanges(); } } catch( Exception Ex ) { Debug.WriteLine( "Exception in UpdateBuggData: " + Ex.ToString() ); } } }
/// <summary> /// The Show action. /// </summary> /// <param name="buggsForm">The form of user data passed up from the client.</param> /// <param name="id">The unique id of the Bugg.</param> /// <returns>The view to display a Bugg on the client.</returns> public ActionResult Show(FormCollection buggsForm, int id, int page = 0) { using (var logTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(BuggId=" + id + ")")) { using (var unitOfWork = new UnitOfWork(new CrashReportEntities())) { // Set the display properties based on the radio buttons var displayModuleNames = buggsForm["DisplayModuleNames"] == "true"; var displayFunctionNames = buggsForm["DisplayFunctionNames"] == "true"; var displayFileNames = buggsForm["DisplayFileNames"] == "true"; var displayFilePathNames = false; // Set up the view model with the Crash data if (buggsForm["Page"] != null) { int.TryParse(buggsForm["Page"], out page); } if (buggsForm["DisplayFilePathNames"] == "true") { displayFilePathNames = true; displayFileNames = false; } var displayUnformattedCallStack = buggsForm["DisplayUnformattedCallStack"] == "true"; var model = GetResult(id, page, pageSize, unitOfWork); model.SourceContext = model.CrashData.First().SourceContext; model.Bugg.PrepareBuggForJira(model.CrashData); // Handle 'CopyToJira' button var buggIdToBeAddedToJira = 0; foreach (var entry in buggsForm.Cast <object>().Where(entry => entry.ToString().Contains("CopyToJira-"))) { int.TryParse(entry.ToString().Substring("CopyToJira-".Length), out buggIdToBeAddedToJira); break; } if (buggIdToBeAddedToJira != 0) { model.Bugg.JiraProject = buggsForm["JiraProject"]; model.Bugg.CopyToJira(); } var jc = JiraConnection.Get(); var bValidJira = false; // Verify valid JiraID, this may be still a TTP if (!string.IsNullOrEmpty(model.Bugg.TTPID)) { var jira = 0; int.TryParse(model.Bugg.TTPID, out jira); if (jira == 0) { bValidJira = true; } } if (jc.CanBeUsed() && bValidJira) { // Grab the data form JIRA. var jiraSearchQuery = "key = " + model.Bugg.TTPID; var jiraResults = new Dictionary <string, Dictionary <string, object> >(); try { jiraResults = jc.SearchJiraTickets( jiraSearchQuery, new string[] { "key", // string "summary", // string "components", // System.Collections.ArrayList, Dictionary<string,object>, name "resolution", // System.Collections.Generic.Dictionary`2[System.String,System.Object], name "fixVersions", // System.Collections.ArrayList, Dictionary<string,object>, name "customfield_11200" // string }); } catch (System.Exception) { model.Bugg.JiraSummary = "JIRA MISMATCH"; model.Bugg.JiraComponentsText = "JIRA MISMATCH"; model.Bugg.JiraResolution = "JIRA MISMATCH"; model.Bugg.JiraFixVersionsText = "JIRA MISMATCH"; model.Bugg.JiraFixCL = "JIRA MISMATCH"; } // Jira Key, Summary, Components, Resolution, Fix version, Fix changelist if (jiraResults.Any()) { var jira = jiraResults.First(); var summary = (string)jira.Value["summary"]; var componentsText = ""; var components = (System.Collections.ArrayList)jira.Value["components"]; foreach (Dictionary <string, object> component in components) { componentsText += (string)component["name"]; componentsText += " "; } var resolutionFields = (Dictionary <string, object>)jira.Value["resolution"]; var resolution = resolutionFields != null ? (string)resolutionFields["name"] : ""; var fixVersionsText = ""; var fixVersions = (System.Collections.ArrayList)jira.Value["fixVersions"]; foreach (Dictionary <string, object> fixVersion in fixVersions) { fixVersionsText += (string)fixVersion["name"]; fixVersionsText += " "; } var fixCl = jira.Value["customfield_11200"] != null ? (int)(decimal)jira.Value["customfield_11200"] : 0; //Conversion to ado.net entity framework model.Bugg.JiraSummary = summary; model.Bugg.JiraComponentsText = componentsText; model.Bugg.JiraResolution = resolution; model.Bugg.JiraFixVersionsText = fixVersionsText; if (fixCl != 0) { model.Bugg.FixedChangeList = fixCl.ToString(); } } } // Apply any user settings if (buggsForm.Count > 0) { if (!string.IsNullOrEmpty(buggsForm["SetStatus"])) { model.Bugg.Status = buggsForm["SetStatus"]; unitOfWork.CrashRepository.SetStatusByBuggId(model.Bugg.Id, buggsForm["SetFixedIn"]); } if (!string.IsNullOrEmpty(buggsForm["SetFixedIn"])) { model.Bugg.FixedChangeList = buggsForm["SetFixedIn"]; unitOfWork.CrashRepository.SetFixedCLByBuggId(model.Bugg.Id, buggsForm["SetFixedIn"]); } if (!string.IsNullOrEmpty(buggsForm["SetTTP"])) { model.Bugg.TTPID = buggsForm["SetTTP"]; unitOfWork.CrashRepository.SetJiraByBuggId(model.Bugg.Id, buggsForm["SetTTP"]); } unitOfWork.BuggRepository.Update(model.Bugg); unitOfWork.Save(); } var newCrash = model.CrashData.FirstOrDefault(); if (newCrash != null) { var callStack = new CallStackContainer(newCrash.CrashType, newCrash.RawCallStack, newCrash.PlatformName); // Set callstack properties callStack.bDisplayModuleNames = displayModuleNames; callStack.bDisplayFunctionNames = displayFunctionNames; callStack.bDisplayFileNames = displayFileNames; callStack.bDisplayFilePathNames = displayFilePathNames; callStack.bDisplayUnformattedCallStack = displayUnformattedCallStack; model.CallStack = callStack; // Shorten very long function names. foreach (var entry in model.CallStack.CallStackEntries) { entry.FunctionName = entry.GetTrimmedFunctionName(128); } model.SourceContext = newCrash.SourceContext; model.LatestCrashSummary = newCrash.Summary; } model.LatestCrashSummary = newCrash.Summary; model.Bugg.LatestCrashSummary = newCrash.Summary; model.GenerationTime = logTimer.GetElapsedSeconds().ToString("F2"); //Populate Jira Projects var jiraConnection = JiraConnection.Get(); var response = jiraConnection.JiraRequest("/issue/createmeta", JiraConnection.JiraMethod.GET, null, HttpStatusCode.OK); using (var responseReader = new StreamReader(response.GetResponseStream())) { var responseText = responseReader.ReadToEnd(); JObject jsonObject = JObject.Parse(responseText); JToken fields = jsonObject["projects"]; foreach (var project in fields) { model.JiraProjects.Add(new SelectListItem() { Text = project["name"].ToObject <string>(), Value = project["key"].ToObject <string>() }); } } model.PagingInfo = new PagingInfo { CurrentPage = page, PageSize = pageSize, TotalResults = model.Bugg.NumberOfCrashes.Value }; return(View("Show", model)); } } }
/// <summary> /// The Show action. /// </summary> /// <param name="buggsForm">The form of user data passed up from the client.</param> /// <param name="id">The unique id of the Bugg.</param> /// <returns>The view to display a Bugg on the client.</returns> public ActionResult Show(FormCollection buggsForm, int id) { using (var logTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(BuggId=" + id + ")", bCreateNewLog: true)) { // Set the display properties based on the radio buttons var displayModuleNames = buggsForm["DisplayModuleNames"] == "true"; var displayFunctionNames = buggsForm["DisplayFunctionNames"] == "true"; var displayFileNames = buggsForm["DisplayFileNames"] == "true"; var displayFilePathNames = false; if (buggsForm["DisplayFilePathNames"] == "true") { displayFilePathNames = true; displayFileNames = false; } var displayUnformattedCallStack = buggsForm["DisplayUnformattedCallStack"] == "true"; // Create a new view and populate with crashes List <Crash> crashes = null; var model = new BuggViewModel(); var newBugg = _unitOfWork.BuggRepository.GetById(id); if (newBugg == null) { return(RedirectToAction("Index")); } crashes = newBugg.Crashes.OrderByDescending(data => data.TimeOfCrash).ToList(); newBugg.CrashesInTimeFrameAll = crashes.Count; newBugg.CrashesInTimeFrameGroup = crashes.Count; newBugg.NumberOfCrashes = crashes.Count; // Handle 'CopyToJira' button var buggIdToBeAddedToJira = 0; foreach (var entry in buggsForm.Cast <object>().Where(entry => entry.ToString().Contains("CopyToJira-"))) { int.TryParse(entry.ToString().Substring("CopyToJira-".Length), out buggIdToBeAddedToJira); break; } newBugg.PrepareBuggForJira(crashes); if (buggIdToBeAddedToJira != 0) { newBugg.JiraProject = buggsForm["JiraProject"]; newBugg.CopyToJira(); } var jc = JiraConnection.Get(); var bValidJira = false; // Verify valid JiraID, this may be still a TTP if (!string.IsNullOrEmpty(newBugg.TTPID)) { var jira = 0; int.TryParse(newBugg.TTPID, out jira); if (jira == 0) { bValidJira = true; } } if (jc.CanBeUsed() && bValidJira) { // Grab the data form JIRA. var jiraSearchQuery = "key = " + newBugg.TTPID; var jiraResults = new Dictionary <string, Dictionary <string, object> >(); try { jiraResults = jc.SearchJiraTickets( jiraSearchQuery, new string[] { "key", // string "summary", // string "components", // System.Collections.ArrayList, Dictionary<string,object>, name "resolution", // System.Collections.Generic.Dictionary`2[System.String,System.Object], name "fixVersions", // System.Collections.ArrayList, Dictionary<string,object>, name "customfield_11200" // string }); } catch (System.Exception) { newBugg.JiraSummary = "JIRA MISMATCH"; newBugg.JiraComponentsText = "JIRA MISMATCH"; newBugg.JiraResolution = "JIRA MISMATCH"; newBugg.JiraFixVersionsText = "JIRA MISMATCH"; newBugg.JiraFixCL = "JIRA MISMATCH"; } // Jira Key, Summary, Components, Resolution, Fix version, Fix changelist if (jiraResults.Any()) { var jira = jiraResults.First(); var summary = (string)jira.Value["summary"]; var componentsText = ""; var components = (System.Collections.ArrayList)jira.Value["components"]; foreach (Dictionary <string, object> component in components) { componentsText += (string)component["name"]; componentsText += " "; } var resolutionFields = (Dictionary <string, object>)jira.Value["resolution"]; var resolution = resolutionFields != null ? (string)resolutionFields["name"] : ""; var fixVersionsText = ""; var fixVersions = (System.Collections.ArrayList)jira.Value["fixVersions"]; foreach (Dictionary <string, object> fixVersion in fixVersions) { fixVersionsText += (string)fixVersion["name"]; fixVersionsText += " "; } var fixCl = jira.Value["customfield_11200"] != null ? (int)(decimal)jira.Value["customfield_11200"] : 0; //Conversion to ado.net entity framework newBugg.JiraSummary = summary; newBugg.JiraComponentsText = componentsText; newBugg.JiraResolution = resolution; newBugg.JiraFixVersionsText = fixVersionsText; if (fixCl != 0) { newBugg.FixedChangeList = fixCl.ToString(); } } } // Apply any user settings if (buggsForm.Count > 0) { if (!string.IsNullOrEmpty(buggsForm["SetStatus"])) { newBugg.Status = buggsForm["SetStatus"]; newBugg.Crashes.ForEach(data => data.Status = buggsForm["SetStatus"]); } if (!string.IsNullOrEmpty(buggsForm["SetFixedIn"])) { newBugg.FixedChangeList = buggsForm["SetFixedIn"]; newBugg.Crashes.ForEach(data => data.FixedChangeList = buggsForm["SetFixedIn"]); } if (!string.IsNullOrEmpty(buggsForm["SetTTP"])) { newBugg.TTPID = buggsForm["SetTTP"]; newBugg.Crashes.ForEach(data => data.Jira = buggsForm["SetTTP"]); } _unitOfWork.BuggRepository.Update(newBugg); _unitOfWork.Save(); } // Set up the view model with the crash data model.Crashes = crashes; model.Bugg = newBugg; var newCrash = model.Crashes.FirstOrDefault(); if (newCrash != null) { var callStack = new CallStackContainer(newCrash); // Set callstack properties callStack.bDisplayModuleNames = displayModuleNames; callStack.bDisplayFunctionNames = displayFunctionNames; callStack.bDisplayFileNames = displayFileNames; callStack.bDisplayFilePathNames = displayFilePathNames; callStack.bDisplayUnformattedCallStack = displayUnformattedCallStack; model.CallStack = callStack; // Shorten very long function names. foreach (var entry in model.CallStack.CallStackEntries) { entry.FunctionName = entry.GetTrimmedFunctionName(128); } model.SourceContext = newCrash.SourceContext; model.LatestCrashSummary = newCrash.Summary; } model.LatestCrashSummary = newCrash.Summary; model.Bugg.LatestCrashSummary = newCrash.Summary; model.GenerationTime = logTimer.GetElapsedSeconds().ToString("F2"); return(View("Show", model)); } }
/// <summary> /// /// </summary> /// <param name="UserGroupId"></param> /// <returns></returns> public List<string> GetUsersForGroupId( int UserGroupId ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(UserGroupId=" + UserGroupId + ")" ) ) { List<string> Users = new List<string>(); try { Users = ( from UserDetail in Context.Users where UserDetail.UserGroupId == UserGroupId orderby UserDetail.UserName select UserDetail.UserName ).ToList(); } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetUsersForGroup: " + Ex.ToString() ); } return Users; } }
/// <summary> /// Retrieve all Buggs matching the search criteria. /// </summary> /// <param name="FormData">The incoming form of search criteria from the client.</param> /// <param name="BuggIDToBeAddedToJira">ID of the bugg that will be added to JIRA</param> /// <returns>A view to display the filtered Buggs.</returns> public ReportsViewModel GetResults(FormHelper FormData, int BuggIDToBeAddedToJira) { // @TODO yrx 2015-02-17 BuggIDToBeAddedToJira replace with List<int> based on check box and Submit? // Enumerate JIRA projects if needed. // https://jira.ol.epicgames.net//rest/api/2/project var JC = JiraConnection.Get(); var JiraComponents = JC.GetNameToComponents(); var JiraVersions = JC.GetNameToVersions(); using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { string AnonumousGroup = "Anonymous"; List <String> Users = CrashRepository.GetUsersForGroup(AnonumousGroup); int AnonymousGroupID = BuggRepository.GetIdFromUserGroup(AnonumousGroup); HashSet <int> AnonumousIDs = BuggRepository.GetUserIdsFromUserGroup(AnonumousGroup); int AnonymousID = AnonumousIDs.First(); HashSet <string> UserNamesForUserGroup = BuggRepository.GetUserNamesFromUserGroups(AnonumousGroup); //FormData.DateFrom = DateTime.Now.AddDays( -1 ); var Crashes = CrashRepository .FilterByDate(CrashRepository.ListAll(), FormData.DateFrom, FormData.DateTo.AddDays(1)) // Only crashes and asserts .Where(Crash => Crash.CrashType == 1 || Crash.CrashType == 2) // Only anonymous user .Where(Crash => Crash.UserNameId.Value == AnonymousID) .Select(Crash => new { ID = Crash.Id, TimeOfCrash = Crash.TimeOfCrash.Value, //UserID = Crash.UserNameId.Value, BuildVersion = Crash.BuildVersion, JIRA = Crash.TTPID, Platform = Crash.PlatformName, FixCL = Crash.FixedChangeList, BuiltFromCL = Crash.ChangeListVersion, Pattern = Crash.Pattern, MachineID = Crash.ComputerName, Branch = Crash.Branch, }) .ToList(); int NumCrashes = Crashes.Count; /* * // Build patterns for crashes where patters is null. * var CrashesWithoutPattern = CrashRepository * .FilterByDate( CrashRepository.ListAll(), FormData.DateFrom, FormData.DateTo.AddDays( 1 ) ) * // Only crashes and asserts * .Where( Crash => Crash.Pattern == null || Crash.Pattern == "" ) * .Select( Crash => Crash ) * .ToList(); * * foreach( var Crash in CrashesWithoutPattern ) * { * ////BuildPattern( Crash ); * } */ // Total # of ALL (Anonymous) crashes in timeframe int TotalAnonymousCrashes = NumCrashes; // Total # of UNIQUE (Anonymous) crashes in timeframe HashSet <string> UniquePatterns = new HashSet <string>(); HashSet <string> UniqueMachines = new HashSet <string>(); Dictionary <string, int> PatternToCount = new Dictionary <string, int>(); //List<int> CrashesWithoutPattern = new List<int>(); //List<DateTime> CrashesWithoutPatternDT = new List<DateTime>(); foreach (var Crash in Crashes) { if (string.IsNullOrEmpty(Crash.Pattern)) { //CrashesWithoutPattern.Add( Crash.ID ); //CrashesWithoutPatternDT.Add( Crash.TimeOfCrash ); continue; } UniquePatterns.Add(Crash.Pattern); UniqueMachines.Add(Crash.MachineID); bool bAdd = !PatternToCount.ContainsKey(Crash.Pattern); if (bAdd) { PatternToCount.Add(Crash.Pattern, 1); } else { PatternToCount[Crash.Pattern]++; } } var PatternToCountOrdered = PatternToCount.OrderByDescending(X => X.Value).ToList(); // The 100 top. var PatternAndCount = PatternToCountOrdered.Take(100).ToDictionary(X => X.Key, Y => Y.Value); int TotalUniqueAnonymousCrashes = UniquePatterns.Count; // Total # of AFFECTED USERS (Anonymous) in timeframe int TotalAffectedUsers = UniqueMachines.Count; var RealBuggs = BuggRepository.GetDataContext().Buggs.Where(Bugg => PatternAndCount.Keys.Contains(Bugg.Pattern)).ToList(); // Build search string. List <string> FoundJiras = new List <string>(); Dictionary <string, List <Bugg> > JiraIDtoBugg = new Dictionary <string, List <Bugg> >(); List <Bugg> Buggs = new List <Bugg>(100); foreach (var Top in PatternAndCount) { Bugg NewBugg = new Bugg(); Bugg RealBugg = RealBuggs.Where(X => X.Pattern == Top.Key).FirstOrDefault(); if (RealBugg != null) { using (FAutoScopedLogTimer TopTimer = new FAutoScopedLogTimer(string.Format("{0}:{1}", Buggs.Count + 1, RealBugg.Id))) { // Index // number NewBugg.Id = RealBugg.Id; // CrashGroup URL (a link to the Bugg) NewBugg.TTPID = RealBugg.TTPID; // JIRA NewBugg.FixedChangeList = RealBugg.FixedChangeList; // FixCL NewBugg.NumberOfCrashes = Top.Value; // # Occurrences NewBugg.Pattern = RealBugg.Pattern; // # Occurrences //NewBugg.BuildVersion = var CrashesForBugg = Crashes.Where(Crash => Crash.Pattern == Top.Key).ToList(); NewBugg.AffectedVersions = new SortedSet <string>(); NewBugg.AffectedMajorVersions = new SortedSet <string>(); // 4.4, 4.5 and so NewBugg.BranchesFoundIn = new SortedSet <string>(); NewBugg.AffectedPlatforms = new SortedSet <string>(); HashSet <string> MachineIds = new HashSet <string>(); int FirstCLAffected = int.MaxValue; foreach (var Crash in CrashesForBugg) { // Only add machine if the number has 32 characters if (Crash.MachineID != null && Crash.MachineID.Length == 32) { MachineIds.Add(Crash.MachineID); } // Ignore bad build versions. // @TODO yrx 2015-02-17 What about launcher? if (Crash.BuildVersion.StartsWith("4.")) { if (!string.IsNullOrEmpty(Crash.BuildVersion)) { NewBugg.AffectedVersions.Add(Crash.BuildVersion); } if (!string.IsNullOrEmpty(Crash.Branch) && Crash.Branch.StartsWith("UE4")) { NewBugg.BranchesFoundIn.Add(Crash.Branch); } int CrashBuiltFromCL = 0; int.TryParse(Crash.BuiltFromCL, out CrashBuiltFromCL); if (CrashBuiltFromCL > 0) { FirstCLAffected = Math.Min(FirstCLAffected, CrashBuiltFromCL); } if (!string.IsNullOrEmpty(Crash.Platform)) { // Platform = "Platform [Desc]"; var PlatSubs = Crash.Platform.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (PlatSubs.Length >= 1) { NewBugg.AffectedPlatforms.Add(PlatSubs[0]); } } } } // CopyToJira NewBugg.ToJiraFirstCLAffected = FirstCLAffected; if (NewBugg.AffectedVersions.Count > 0) { NewBugg.BuildVersion = NewBugg.AffectedVersions.Last(); // Latest Version Affected } foreach (var AffectedBuild in NewBugg.AffectedVersions) { var Subs = AffectedBuild.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (Subs.Length >= 2) { string MajorVersion = Subs[0] + "." + Subs[1]; NewBugg.AffectedMajorVersions.Add(MajorVersion); } } NewBugg.NumberOfUniqueMachines = MachineIds.Count; // # Affected Users string LatestCLAffected = CrashesForBugg. // CL of the latest build Where(Crash => Crash.BuildVersion == NewBugg.BuildVersion). Max(Crash => Crash.BuiltFromCL); int ILatestCLAffected = -1; int.TryParse(LatestCLAffected, out ILatestCLAffected); NewBugg.LatestCLAffected = ILatestCLAffected; // Latest CL Affected string LatestOSAffected = CrashesForBugg.OrderByDescending(Crash => Crash.TimeOfCrash).First().Platform; NewBugg.LatestOSAffected = LatestOSAffected; // Latest Environment Affected NewBugg.TimeOfFirstCrash = RealBugg.TimeOfFirstCrash; // First Crash Timestamp // ToJiraSummary var Callstack = RealBugg.GetFunctionCalls(); NewBugg.ToJiraSummary = Callstack.Count > 1 ? Callstack[0] : "No valid callstack found"; // ToJiraVersions NewBugg.ToJiraVersions = new List <object>(); foreach (var Version in NewBugg.AffectedMajorVersions) { bool bValid = JC.GetNameToVersions().ContainsKey(Version); if (bValid) { NewBugg.ToJiraVersions.Add(JC.GetNameToVersions()[Version]); } } // ToJiraBranches NewBugg.ToJiraBranches = new List <object>(); foreach (var Platform in NewBugg.BranchesFoundIn) { string CleanedBranch = Platform.Contains("UE4-Releases") ? "UE4-Releases" : Platform; Dictionary <string, object> JiraBranch = null; JC.GetNameToBranchFoundIn().TryGetValue(CleanedBranch, out JiraBranch); if (JiraBranch != null && !NewBugg.ToJiraBranches.Contains(JiraBranch)) { NewBugg.ToJiraBranches.Add(JiraBranch); } } // ToJiraPlatforms NewBugg.ToJiraPlatforms = new List <object>(); foreach (var Platform in NewBugg.AffectedPlatforms) { bool bValid = JC.GetNameToPlatform().ContainsKey(Platform); if (bValid) { NewBugg.ToJiraPlatforms.Add(JC.GetNameToPlatform()[Platform]); } } // Verify valid JiraID, this may be still a TTP if (!string.IsNullOrEmpty(NewBugg.TTPID)) { int TTPID = 0; int.TryParse(NewBugg.TTPID, out TTPID); if (TTPID == 0) { AddBuggJiraMapping(NewBugg, ref FoundJiras, ref JiraIDtoBugg); } } Buggs.Add(NewBugg); } } else { FLogger.WriteEvent("Bugg for pattern " + Top.Key + " not found"); } } if (BuggIDToBeAddedToJira > 0) { var Bugg = Buggs.Where(X => X.Id == BuggIDToBeAddedToJira).FirstOrDefault(); if (Bugg != null) { Bugg.CopyToJira(); AddBuggJiraMapping(Bugg, ref FoundJiras, ref JiraIDtoBugg); } } if (JC.CanBeUsed()) { // Grab the data form JIRA. string JiraSearchQuery = string.Join(" OR ", FoundJiras); using (FAutoScopedLogTimer JiraResultsTimer = new FAutoScopedLogTimer("JiraResults")) { var JiraResults = JC.SearchJiraTickets( JiraSearchQuery, new string[] { "key", // string "summary", // string "components", // System.Collections.ArrayList, Dictionary<string,object>, name "resolution", // System.Collections.Generic.Dictionary`2[System.String,System.Object], name "fixVersions", // System.Collections.ArrayList, Dictionary<string,object>, name "customfield_11200" // string }); // Jira Key, Summary, Components, Resolution, Fix version, Fix changelist foreach (var Jira in JiraResults) { string JiraID = Jira.Key; string Summary = (string)Jira.Value["summary"]; string ComponentsText = ""; System.Collections.ArrayList Components = (System.Collections.ArrayList)Jira.Value["components"]; foreach (Dictionary <string, object> Component in Components) { ComponentsText += (string)Component["name"]; ComponentsText += " "; } Dictionary <string, object> ResolutionFields = (Dictionary <string, object>)Jira.Value["resolution"]; string Resolution = ResolutionFields != null ? (string)ResolutionFields["name"] : ""; string FixVersionsText = ""; System.Collections.ArrayList FixVersions = (System.Collections.ArrayList)Jira.Value["fixVersions"]; foreach (Dictionary <string, object> FixVersion in FixVersions) { FixVersionsText += (string)FixVersion["name"]; FixVersionsText += " "; } int FixCL = Jira.Value["customfield_11200"] != null ? (int)(decimal)Jira.Value["customfield_11200"] : 0; var BuggsForJira = JiraIDtoBugg[JiraID]; foreach (Bugg Bugg in BuggsForJira) { Bugg.JiraSummary = Summary; Bugg.JiraComponentsText = ComponentsText; Bugg.JiraResolution = Resolution; Bugg.JiraFixVersionsText = FixVersionsText; if (FixCL != 0) { Bugg.JiraFixCL = FixCL.ToString(); } } } } } return(new ReportsViewModel { Buggs = Buggs, /*Crashes = Crashes,*/ DateFrom = (long)(FormData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds, DateTo = (long)(FormData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds, TotalAffectedUsers = TotalAffectedUsers, TotalAnonymousCrashes = TotalAnonymousCrashes, TotalUniqueAnonymousCrashes = TotalUniqueAnonymousCrashes }); } }
/// <summary>Gets a set of user's names from the group name</summary> public HashSet<string> GetUserNamesFromGroupName( string GroupName ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(UserGroup=" + GroupName + ")" ) ) { int UserGroupId = FindOrAddGroup( GroupName ); var UserNames = Context.Users.Where( X => X.UserGroupId == UserGroupId ).Select( X => X.UserName ); return new HashSet<string>( UserNames ); } }
/// <summary> /// Gets a container of crash counts per user group for all crashes. /// </summary> /// <returns>A dictionary of user group names, and the count of crashes for each group.</returns> public Dictionary<string, int> GetCountsByGroup() { // @TODO yrx 2014-11-06 Optimize? using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + " SQL OPT" ) ) { Dictionary<string, int> Results = new Dictionary<string, int>(); try { var GroupCounts = ( from UserDetail in Context.Users join UserGroupDetail in Context.UserGroups on UserDetail.UserGroupId equals UserGroupDetail.Id group UserDetail by UserGroupDetail.Name into GroupCount select new { Key = GroupCount.Key, Count = GroupCount.Count() } ); foreach( var GroupCount in GroupCounts ) { Results.Add( GroupCount.Key, GroupCount.Count ); } // Add in all groups, even though there are no crashes associated IEnumerable<string> UserGroups = ( from UserGroupDetail in Context.UserGroups select UserGroupDetail.Name ); foreach( string UserGroupName in UserGroups ) { if( !Results.Keys.Contains( UserGroupName ) ) { Results[UserGroupName] = 0; } } } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetCountsByGroup: " + Ex.ToString() ); } return Results; } }
/// <summary> /// Find a user group, or add it if it doesn't exist. Return the id of the user group. /// </summary> /// <param name="UserGroupName">The user group name to find or add.</param> /// <returns>The id of the user group that was found or added.</returns> /// <remarks>All user group interaction is done this way to remove any dependencies on pre-populated tables.</remarks> public int FindOrAddGroup( string UserGroupName ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(GroupName=" + UserGroupName + ")" ) ) { int UserGroupNameId = 0; try { IQueryable<int> UserGroups = ( from UserGroupDetail in Context.UserGroups where UserGroupDetail.Name.ToLower() == UserGroupName.ToLower() select UserGroupDetail.Id ); // If there is no existing group, add a new one if( UserGroups.Count() == 0 ) { UserGroup NewUserGroup = new UserGroup(); NewUserGroup.Name = UserGroupName; Context.UserGroups.InsertOnSubmit( NewUserGroup ); Context.SubmitChanges(); UserGroupNameId = NewUserGroup.Id; } else { UserGroupNameId = UserGroups.First(); } } catch( Exception Ex ) { Debug.WriteLine( "FindOrAddUserGroup: " + Ex.ToString() ); } return UserGroupNameId; } }
/// <summary> /// Retrieves a list of function names from a list of function name ids. /// Primarily used to fill GetFunctionCalls in CachedDataService /// </summary> /// <param name="Ids">A list of unique function name ids.</param> /// <returns>A list of strings that make up a callstack.</returns> public List<string> GetFunctionCalls( List<int> Ids ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(Ids.Count=" + Ids.Count + ")" ) ) { List<string> FunctionCalls = new List<string>(); try { List<FunctionCall> Funcs = BuggsDataContext.FunctionCalls.Where( FuncCall => Ids.Contains( FuncCall.Id ) ).ToList(); // Order by Ids foreach( int Id in Ids ) { var Found = Funcs.Find( FC => FC.Id == Id ); FunctionCalls.Add( Found.Call ); } } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetFunctionCalls: " + Ex.ToString() ); } return FunctionCalls; } }
/// <summary> /// Return the top lines of a callstack. /// </summary> /// <returns>A list of callstack entries.</returns> public List<string> GetFunctionCalls() { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(Id=" + this.Id + ")" ) ) { BuggRepository LocalBuggRepository = new BuggRepository(); List<string> Results = LocalBuggRepository.GetFunctionCalls( Pattern ); return Results; } }
/// <summary> /// Retrieve all Buggs matching the search criteria. /// </summary> /// <param name="FormData">The incoming form of search criteria from the client.</param> /// <returns>A view to display the filtered Buggs.</returns> public BuggsViewModel GetResults( FormHelper FormData ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() ) ) { // Right now we take a Result IQueryable starting with ListAll() Buggs then we widdle away the result set by tacking on // Linq queries. Essentially it's Results.ListAll().Where().Where().Where().Where().Where().Where() // Could possibly create more optimized queries when we know exactly what we're querying // The downside is that if we add more parameters each query may need to be updated.... Or we just add new case statements // The other downside is that there's less code reuse, but that may be worth it. IQueryable<Bugg> Results = null; int Skip = ( FormData.Page - 1 ) * FormData.PageSize; int Take = FormData.PageSize; // Get every Bugg. Results = ListAll(); // Look at all Buggs that are still 'open' i.e. the last crash occurred in our date range. Results = FilterByDate( Results, FormData.DateFrom, FormData.DateTo ); // Filter results by build version. Results = FilterByBuildVersion( Results, FormData.BuildVersion ); // Run at the end if( !string.IsNullOrEmpty( FormData.SearchQuery ) ) { Results = Search( Results, FormData.SearchQuery ); } // Filter by Crash Type if( FormData.CrashType != "All" ) { switch( FormData.CrashType ) { case "Crashes": Results = Results.Where( BuggInstance => BuggInstance.CrashType == 1 ); break; case "Assert": Results = Results.Where( BuggInstance => BuggInstance.CrashType == 2 ); break; case "Ensure": Results = Results.Where( BuggInstance => BuggInstance.CrashType == 3 ); break; case "CrashesAsserts": Results = Results.Where( BuggInstance => BuggInstance.CrashType == 1 || BuggInstance.CrashType == 2 ); break; } } // Get UserGroup ResultCounts Dictionary<string, int> GroupCounts = GetCountsByGroup( Results ); // Filter by user group if present Results = FilterByUserGroup( Results, FormData.UserGroup ); // Pass in the results and return them sorted properly IEnumerable<Bugg> SortedResults = GetSortedResults( Results, FormData.SortTerm, ( FormData.SortOrder == "Descending" ), FormData.DateFrom, FormData.DateTo ); // Grab just the results we want to display on this page SortedResults = SortedResults.Skip( Skip ).Take( Take ).ToList(); return new BuggsViewModel { Results = SortedResults, PagingInfo = new PagingInfo { CurrentPage = FormData.Page, PageSize = FormData.PageSize, TotalResults = Results.Count() }, SortTerm = FormData.SortTerm, SortOrder = FormData.SortOrder, UserGroup = FormData.UserGroup, CrashType = FormData.CrashType, SearchQuery = FormData.SearchQuery, DateFrom = (long)( FormData.DateFrom - CrashesViewModel.Epoch ).TotalMilliseconds, DateTo = (long)( FormData.DateTo - CrashesViewModel.Epoch ).TotalMilliseconds, BuildVersion = FormData.BuildVersion, GroupCounts = GroupCounts, }; } }
/// <summary> /// Return lines of processed callstack entries. /// </summary> /// <param name="StartIndex">The starting entry of the callstack.</param> /// <param name="Count">The number of lines to return.</param> /// <returns>Lines of processed callstack entries.</returns> public List<CallStackEntry> GetCallStackEntries( int StartIndex, int Count ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(Count=" + Count + ")" ) ) { IEnumerable<CallStackEntry> Results = new List<CallStackEntry>() { new CallStackEntry() }; if( CallStackContainer != null && CallStackContainer.CallStackEntries != null ) { int MaxCount = Math.Min( Count, CallStackContainer.CallStackEntries.Count ); Results = CallStackContainer.CallStackEntries.Take( MaxCount ); } return Results.ToList(); } }
/// <summary> /// Filter the list of Buggs by a search query. /// </summary> /// <param name="Results">The unfiltered set of Buggs.</param> /// <param name="Query">The query to use as a filter.</param> /// <returns>A filtered set of Buggs.</returns> public IQueryable<Bugg> Search( IQueryable<Bugg> Results, string Query ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(Query=" + Query + ")" ) ) { // Also may want to revisit how we search since this could get inefficient for a big search set. IQueryable<Bugg> Buggs; try { string QueryString = HttpUtility.HtmlDecode( Query.ToString() ); if( QueryString == null ) { QueryString = ""; } // Take out terms starting with a - string[] Terms = QueryString.Split( "-, ;+".ToCharArray(), StringSplitOptions.RemoveEmptyEntries ); string TermsToUse = ""; foreach( string Term in Terms ) { string CallId = GetFunctionCallId( Term ).ToString(); if( !TermsToUse.Contains( CallId ) ) { TermsToUse = TermsToUse + "+" + CallId; } } Buggs = (IQueryable<Bugg>)Results.Search( new string[] { "Pattern" }, TermsToUse.Split( "+".ToCharArray() ) ); } catch( Exception Ex ) { Debug.WriteLine( "Exception in Search: " + Ex.ToString() ); Buggs = Results; } return Buggs; } }
/// <summary> /// Show detailed information about a crash. /// </summary> /// <param name="CrashesForm">A form of user data passed up from the client.</param> /// <param name="id">The unique id of the crash we wish to show the details of.</param> /// <returns>A view to show crash details.</returns> public ActionResult Show(FormCollection CrashesForm, int id) { using (var logTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(CrashId=" + id + ")", bCreateNewLog: true)) { CallStackContainer currentCallStack = null; // Update the selected crash based on the form contents var currentCrash = _unitOfWork.CrashRepository.GetById(id); if (currentCrash == null) { return(RedirectToAction("Index")); } string FormValue; FormValue = CrashesForm["SetStatus"]; if (!string.IsNullOrEmpty(FormValue)) { currentCrash.Status = FormValue; } FormValue = CrashesForm["SetFixedIn"]; if (!string.IsNullOrEmpty(FormValue)) { currentCrash.FixedChangeList = FormValue; } FormValue = CrashesForm["SetTTP"]; if (!string.IsNullOrEmpty(FormValue)) { currentCrash.Jira = FormValue; } // Valid to set description to an empty string FormValue = CrashesForm["Description"]; if (FormValue != null) { currentCrash.Description = FormValue; } currentCallStack = new CallStackContainer(currentCrash); currentCrash.Module = currentCallStack.GetModuleName(); //Set call stack properties currentCallStack.bDisplayModuleNames = true; currentCallStack.bDisplayFunctionNames = true; currentCallStack.bDisplayFileNames = true; currentCallStack.bDisplayFilePathNames = true; currentCallStack.bDisplayUnformattedCallStack = false; currentCrash.CallStackContainer = new CallStackContainer(currentCrash); // Populate the crash with the correct user data //_crashRepo.PopulateUserInfo( CurrentCrash ); //_crashRepo.SubmitChanges(); var Model = new CrashViewModel { Crash = currentCrash, CallStack = currentCallStack }; Model.GenerationTime = logTimer.GetElapsedSeconds().ToString("F2"); return(View("Show", Model)); } }
/// <summary> /// Filter a set of Buggs by user group name. /// </summary> /// <param name="SetOfBuggs">The unfiltered set of Buggs.</param> /// <param name="UserGroup">The user group name to filter by.</param> /// <returns>The set of Buggs by users in the requested user group.</returns> public IQueryable<Bugg> FilterByUserGroup( IQueryable<Bugg> SetOfBuggs, string UserGroup ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(UserGroup=" + UserGroup + ")" ) ) { IQueryable<Bugg> NewSetOfBuggs = null; try { NewSetOfBuggs = ( from BuggDetail in SetOfBuggs join BuggsUserGroupDetail in BuggsDataContext.Buggs_UserGroups on BuggDetail.Id equals BuggsUserGroupDetail.BuggId join UserGroupDetail in BuggsDataContext.UserGroups on BuggsUserGroupDetail.UserGroupId equals UserGroupDetail.Id where UserGroupDetail.Name.Contains( UserGroup ) select BuggDetail ); } catch( Exception Ex ) { Debug.WriteLine( "Exception in FilterByUserGroup: " + Ex.ToString() ); } return NewSetOfBuggs; } }
//static readonly int MinNumberOfCrashes = 2; /// <summary> /// Retrieve all Buggs matching the search criteria. /// </summary> /// <param name="FormData">The incoming form of search criteria from the client.</param> /// <returns>A view to display the filtered Buggs.</returns> public CSV_ViewModel GetResults(FormHelper FormData) { BuggRepository BuggsRepo = new BuggRepository(); CrashRepository CrashRepo = new CrashRepository(); using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { string AnonymousGroup = "Anonymous"; //List<String> Users = FRepository.Get().GetUserNamesFromGroupName( AnonumousGroup ); int AnonymousGroupID = FRepository.Get(BuggsRepo).FindOrAddGroup(AnonymousGroup); HashSet <int> AnonumousIDs = FRepository.Get(BuggsRepo).GetUserIdsFromUserGroup(AnonymousGroup); int AnonymousID = AnonumousIDs.First(); HashSet <string> UserNamesForUserGroup = FRepository.Get(BuggsRepo).GetUserNamesFromGroupName(AnonymousGroup); // Enable to narrow results and improve debugging performance. //FormData.DateFrom = FormData.DateTo.AddDays( -1 ); FormData.DateTo = FormData.DateTo.AddDays(1); var FilteringQueryJoin = CrashRepo .ListAll() .Where(c => c.EpicAccountId != "") // Only crashes and asserts .Where(c => c.CrashType == 1 || c.CrashType == 2) // Only anonymous user .Where(c => c.UserNameId == AnonymousID) // Filter be date .Where(c => c.TimeOfCrash > FormData.DateFrom && c.TimeOfCrash < FormData.DateTo) .Join ( CrashRepo.Context.Buggs_Crashes, c => c, bc => bc.Crash, (c, bc) => new { GameName = c.GameName, TimeOfCrash = c.TimeOfCrash.Value, BuiltFromCL = c.BuiltFromCL, PlatformName = c.PlatformName, EngineMode = c.EngineMode, MachineId = c.MachineId, Module = c.Module, BuildVersion = c.BuildVersion, Jira = c.Jira, Branch = c.Branch, CrashType = c.CrashType.Value, EpicId = c.EpicAccountId, BuggId = bc.BuggId, } ); var FilteringQueryCrashes = CrashRepo .ListAll() .Where(c => c.EpicAccountId != "") // Only crashes and asserts .Where(c => c.CrashType == 1 || c.CrashType == 2) // Only anonymous user .Where(c => c.UserNameId == AnonymousID); int TotalCrashes = CrashRepo .ListAll() .Count(); int TotalCrashesYearToDate = CrashRepo .ListAll() // Year to date .Where(c => c.TimeOfCrash > new DateTime(DateTime.UtcNow.Year, 1, 1)) .Count(); var CrashesFilteredWithDateQuery = FilteringQueryCrashes // Filter be date .Where(c => c.TimeOfCrash > FormData.DateFrom && c.TimeOfCrash < FormData.DateTo); int CrashesFilteredWithDate = CrashesFilteredWithDateQuery .Count(); int CrashesYearToDateFiltered = FilteringQueryCrashes // Year to date .Where(c => c.TimeOfCrash > new DateTime(DateTime.UtcNow.Year, 1, 1)) .Count(); int AffectedUsersFiltered = FilteringQueryCrashes .Select(c => c.EpicAccountId) .Distinct() .Count(); int UniqueCrashesFiltered = FilteringQueryCrashes .Select(c => c.Pattern) .Distinct() .Count(); int NumCrashes = FilteringQueryJoin.Count(); // Get all users // SLOW //var Users = FRepository.Get( BuggsRepo ).GetAnalyticsUsers().ToDictionary( X => X.EpicAccountId, Y => Y ); // Export data to the file. string CSVPathname = Path.Combine(Settings.Default.CrashReporterCSV, DateTime.UtcNow.ToString("yyyy-MM-dd.HH-mm-ss")); CSVPathname += string .Format("__[{0}---{1}]__{2}", FormData.DateFrom.ToString("yyyy-MM-dd"), FormData.DateTo.ToString("yyyy-MM-dd"), NumCrashes) + ".csv"; string ServerPath = Server.MapPath(CSVPathname); var CSVFile = new StreamWriter(ServerPath, true, Encoding.UTF8); using (FAutoScopedLogTimer ExportToCSVTimer = new FAutoScopedLogTimer("ExportToCSV")) { var RowType = FilteringQueryJoin.FirstOrDefault().GetType(); string AllProperties = ""; foreach (var Property in RowType.GetProperties()) { AllProperties += Property.Name; AllProperties += "; "; } // Write header CSVFile.WriteLine(AllProperties); foreach (var Row in FilteringQueryJoin) { var BVParts = Row.BuildVersion.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if (BVParts.Length > 2 && BVParts[0] != "0") { string CleanEngineVersion = string.Format("{0}.{1}.{2}", BVParts[0], BVParts[1], BVParts[2]); string[] RowProperties = new string[] { Row.GameName, Row.TimeOfCrash.ToString(), Row.BuiltFromCL, Row.PlatformName, Row.EngineMode, Row.MachineId, Row.Module, CleanEngineVersion, Row.Jira, Row.Branch, Row.CrashType == 1 ? "Crash" : "Assert", Row.EpicId, Row.BuggId.ToString() }; string JoinedLine = string.Join("; ", RowProperties); JoinedLine += "; "; CSVFile.WriteLine(JoinedLine); } } CSVFile.Flush(); CSVFile.Close(); CSVFile = null; } List <FCSVRow> CSVRows = FilteringQueryJoin .OrderByDescending(X => X.TimeOfCrash) .Take(32) .Select(c => new FCSVRow { GameName = c.GameName, TimeOfCrash = c.TimeOfCrash, BuiltFromCL = c.BuiltFromCL, PlatformName = c.PlatformName, EngineMode = c.EngineMode, MachineId = c.MachineId, Module = c.Module, BuildVersion = c.BuildVersion, Jira = c.Jira, Branch = c.Branch, CrashType = c.CrashType, EpicId = c.EpicId, BuggId = c.BuggId, }) .ToList(); return(new CSV_ViewModel { CSVRows = CSVRows, CSVPathname = CSVPathname, DateFrom = (long)(FormData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds, DateTo = (long)(FormData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds, DateTimeFrom = FormData.DateFrom, DateTimeTo = FormData.DateTo, AffectedUsersFiltered = AffectedUsersFiltered, UniqueCrashesFiltered = UniqueCrashesFiltered, CrashesFilteredWithDate = CrashesFilteredWithDate, TotalCrashes = TotalCrashes, TotalCrashesYearToDate = TotalCrashesYearToDate, }); } }
/// <summary> /// Retrieve all Buggs matching the search criteria. /// </summary> /// <param name="FormData">The incoming form of search criteria from the client.</param> /// <returns>A view to display the filtered Buggs.</returns> public CSV_ViewModel GetResults(FormHelper FormData) { using (var logTimer = new FAutoScopedLogTimer(this.GetType().ToString())) { var anonymousGroup = _unitOfWork.UserGroupRepository.First(data => data.Name == "Anonymous"); var anonymousGroupId = anonymousGroup.Id; var anonumousIDs = new HashSet <int>(anonymousGroup.Users.Select(data => data.Id)); var anonymousId = anonumousIDs.First(); var userNamesForUserGroup = new HashSet <string>(anonymousGroup.Users.Select(data => data.UserName)); // Enable to narrow results and improve debugging performance. //FormData.DateFrom = FormData.DateTo.AddDays( -1 ); //FormData.DateTo = FormData.DateTo.AddDays( 1 ); var filteringQueryJoin = _unitOfWork.CrashRepository .ListAll() .Where(c => c.EpicAccountId != "") // Only Crashes, GPUCrashes and asserts .Where(c => c.CrashType == 1 || c.CrashType == 2 || c.CrashType == 4) // Only anonymous user .Where(c => c.UserId == anonymousId) // Filter be date .Where(c => c.TimeOfCrash > FormData.DateFrom && c.TimeOfCrash < FormData.DateTo) .Select ( c => new { GameName = c.GameName, TimeOfCrash = c.TimeOfCrash, BuiltFromCL = c.ChangelistVersion, PlatformName = c.PlatformName, EngineMode = c.EngineMode, MachineId = c.ComputerName, Module = c.Module, BuildVersion = c.BuildVersion, Jira = c.Jira, Branch = c.Branch, CrashType = c.CrashType, EpicId = c.EpicAccountId, BuggId = c.Bugg.Id } ); var filteringQueryCrashes = _unitOfWork.CrashRepository .ListAll() .Where(c => c.EpicAccountId != null) // Only Crashes and asserts .Where(c => c.CrashType == 1 || c.CrashType == 2 || c.CrashType == 4) // Only anonymous user .Where(c => c.UserId == anonymousId); //44 //Server timeout var totalCrashes = _unitOfWork.CrashRepository.ListAll().Count(); var startOfYear = new DateTime(DateTime.UtcNow.Year, 1, 1); var totalCrashesYearToDate = _unitOfWork.CrashRepository .ListAll().Count(c => c.TimeOfCrash > startOfYear); var CrashesFilteredWithDateQuery = filteringQueryCrashes // Filter be date .Where(c => c.TimeOfCrash > FormData.DateFrom && c.TimeOfCrash < FormData.DateTo); int CrashesFilteredWithDate = CrashesFilteredWithDateQuery .Count(); int CrashesYearToDateFiltered = filteringQueryCrashes.Count(c => c.TimeOfCrash > startOfYear); //DB server timeout int AffectedUsersFiltered = filteringQueryCrashes.Select(c => c.EpicAccountId).Distinct().Count(); //DB server time out int UniqueCrashesFiltered = filteringQueryCrashes.Select(c => c.Pattern).Count(); int NumCrashes = filteringQueryJoin.Count(); // Export data to the file. string CSVPathname = Path.Combine(Settings.Default.CrashReporterCSV, DateTime.UtcNow.ToString("yyyy-MM-dd.HH-mm-ss")); CSVPathname += string .Format("__[{0}---{1}]__{2}", FormData.DateFrom.ToString("yyyy-MM-dd"), FormData.DateTo.ToString("yyyy-MM-dd"), NumCrashes) + ".csv"; var serverPath = Server.MapPath(CSVPathname); var csvFile = new StreamWriter(serverPath, true, Encoding.UTF8); using (var exportToCsvTimer = new FAutoScopedLogTimer("ExportToCSV")) { var rowType = filteringQueryJoin.FirstOrDefault().GetType(); string AllProperties = ""; foreach (var Property in rowType.GetProperties()) { AllProperties += Property.Name; AllProperties += "; "; } // Write header csvFile.WriteLine(AllProperties); foreach (var Row in filteringQueryJoin) { var BVParts = Row.BuildVersion.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if (BVParts.Length > 2 && BVParts[0] != "0") { var cleanEngineVersion = string.Format("{0}.{1}.{2}", BVParts[0], BVParts[1], BVParts[2]); var rowProperties = new string[] { Row.GameName, Row.TimeOfCrash.ToString(), Row.BuiltFromCL, Row.PlatformName, Row.EngineMode, Row.MachineId, Row.Module, cleanEngineVersion, Row.Jira, Row.Branch, Row.CrashType == 1 ? "Crash" : "Assert", Row.EpicId, Row.BuggId.ToString() }; var joinedLine = string.Join("; ", rowProperties); joinedLine += "; "; csvFile.WriteLine(joinedLine); } } csvFile.Flush(); csvFile.Close(); csvFile = null; } List <FCSVRow> CSVRows = filteringQueryJoin .OrderByDescending(X => X.TimeOfCrash) .Take(32) .Select(c => new FCSVRow { GameName = c.GameName, TimeOfCrash = c.TimeOfCrash, BuiltFromCL = c.BuiltFromCL, PlatformName = c.PlatformName, EngineMode = c.EngineMode, MachineId = c.MachineId, Module = c.Module, BuildVersion = c.BuildVersion, Jira = c.Jira, Branch = c.Branch, CrashType = c.CrashType, EpicId = c.EpicId, BuggId = c.BuggId, }) .ToList(); return(new CSV_ViewModel() { CSVRows = CSVRows, CSVPathname = CSVPathname, DateFrom = (long)(FormData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds, DateTo = (long)(FormData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds, DateTimeFrom = FormData.DateFrom, DateTimeTo = FormData.DateTo, AffectedUsersFiltered = AffectedUsersFiltered, UniqueCrashesFiltered = UniqueCrashesFiltered, CrashesFilteredWithDate = CrashesFilteredWithDate, TotalCrashes = totalCrashes, TotalCrashesYearToDate = totalCrashesYearToDate, }); } }
/// <summary> /// Get a Bugg from an id /// </summary> /// <param name="Id">The id of a Bugg.</param> /// <returns>A Bugg representing a group of crashes.</returns> public Bugg GetBugg( int Id ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(" + Id + ")" ) ) { Bugg Result = null; try { Result = ( from BuggDetail in BuggsDataContext.Buggs where BuggDetail.Id == Id select BuggDetail ).FirstOrDefault(); } catch( Exception Ex ) { Debug.WriteLine( "Exception in GetBugg: " + Ex.ToString() ); } return Result; } }
/// <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> /// The main view of the dash board. /// </summary> /// <returns>A view showing two charts of Crashes over time.</returns> public ActionResult Index() { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString(), bCreateNewLog: true)) { DateTime Today = DateTime.UtcNow; DateTime AfewMonthsAgo = Today.AddMonths(-6); FAutoScopedLogTimer LogTimerSQL = new FAutoScopedLogTimer("CrashesFilterByDate", "", ""); // Get engine versions from the last 6 months. var TempVersions = _Crashes.GetVersions(); List <string> EngineUE4Versions = new List <string>(); foreach (var Version in TempVersions) { if (Version.StartsWith("4.")) { EngineUE4Versions.Add(Version); } } // Only 4 latest version. EngineUE4Versions = EngineUE4Versions.OrderByDescending(item => item).Take(5).ToList(); var endDate = Today.AddDays(1); IQueryable <Crash> CrashesInTimeFrame = _Crashes.ListAll() .Where(MyCrash => MyCrash.TimeOfCrash >= AfewMonthsAgo && MyCrash.TimeOfCrash <= endDate); //IEnumerable<Crash> Crashes = FRepository.Get().Crashes.FilterByDate( FRepository.Get().Crashes.ListAll(), AfewMonthsAgo, Today ); var VMinimalCrashes = CrashesInTimeFrame.Select(Crash => new { TimeOfCrash = Crash.TimeOfCrash, UserID = Crash.UserId, EngineVersion = Crash.BuildVersion, }) .ToList(); List <FCrashMinimal> MinimalCrashes = new List <FCrashMinimal>(VMinimalCrashes.Count); foreach (var Anotype in VMinimalCrashes) { MinimalCrashes.Add(new FCrashMinimal(Anotype.TimeOfCrash, Anotype.UserID, Anotype.EngineVersion)); } LogTimerSQL.Dispose(); HashSet <int> AnonumousIDs = new HashSet <int>(_entities.UserGroups.First(data => data.Name == "Anonymous").Users.Select(data => data.Id)); int AnonymousID = AnonumousIDs.First(); int GeneralUserGroupId = 1; //FRepository.Get( _Crashes ).FindOrAddGroup( "General" ); int CoderUserGroupId = 3; //FRepository.Get( _Crashes ).FindOrAddGroup( "Coder" ); int EngineQAUserGroupId = 22; //FRepository.Get( _Crashes ).FindOrAddGroup( "EngineQA" ); int GameQAUserGroupId = 21; //FRepository.Get( _Crashes ).FindOrAddGroup( "GameQA" ); // Weekly Dictionary <DateTime, int> WeeklyGeneralResults = GetWeeklyCountsByGroup(MinimalCrashes, GeneralUserGroupId); Dictionary <DateTime, int> WeeklyCoderResults = GetWeeklyCountsByGroup(MinimalCrashes, CoderUserGroupId); Dictionary <DateTime, int> WeeklyEngineQAResults = GetWeeklyCountsByGroup(MinimalCrashes, EngineQAUserGroupId); Dictionary <DateTime, int> WeeklyGameQAResults = GetWeeklyCountsByGroup(MinimalCrashes, GameQAUserGroupId); Dictionary <string, Dictionary <DateTime, int> > WeeklyEngineVersionResults = new Dictionary <string, Dictionary <DateTime, int> >(); foreach (string UE4Version in EngineUE4Versions) { Dictionary <DateTime, int> Results = GetWeeklyCountsByVersion(MinimalCrashes, UE4Version, AnonymousID); WeeklyEngineVersionResults.Add(UE4Version, Results); } Dictionary <DateTime, int> WeeklyAllResults = GetWeeklyCountsByGroup(MinimalCrashes, AllUserGroupId); // Daily Dictionary <DateTime, int> DailyGeneralResults = GetDailyCountsByGroup(MinimalCrashes, GeneralUserGroupId); Dictionary <DateTime, int> DailyCoderResults = GetDailyCountsByGroup(MinimalCrashes, CoderUserGroupId); Dictionary <DateTime, int> DailyEngineQAResults = GetDailyCountsByGroup(MinimalCrashes, EngineQAUserGroupId); Dictionary <DateTime, int> DailyGameQAResults = GetDailyCountsByGroup(MinimalCrashes, GameQAUserGroupId); Dictionary <string, Dictionary <DateTime, int> > DailyEngineVersionResults = new Dictionary <string, Dictionary <DateTime, int> >(); foreach (string UE4Version in EngineUE4Versions) { Dictionary <DateTime, int> Results = GetDailyCountsByVersion(MinimalCrashes, UE4Version, AnonymousID); DailyEngineVersionResults.Add(UE4Version, Results); } Dictionary <DateTime, int> DailyAllResults = GetDailyCountsByGroup(MinimalCrashes, AllUserGroupId); // Get daily buggs stats. List <Bugg> Buggs = _Buggs.ListAll().Where(Bugg => Bugg.TimeOfFirstCrash >= AfewMonthsAgo).ToList(); Dictionary <DateTime, int> BuggDailyAllResults = ( from Bugg in Buggs group Bugg by Bugg.TimeOfFirstCrash.Value.Date into GroupCount orderby GroupCount.Key select new { Count = GroupCount.Count(), Date = GroupCount.Key } ).ToDictionary(x => x.Date, y => y.Count); string CrashesByWeek = ""; foreach (KeyValuePair <DateTime, int> WeeklyResult in WeeklyAllResults) { int GeneralCrashes = 0; WeeklyGeneralResults.TryGetValue(WeeklyResult.Key, out GeneralCrashes); int CoderCrashes = 0; WeeklyCoderResults.TryGetValue(WeeklyResult.Key, out CoderCrashes); int EngineQACrashes = 0; WeeklyEngineQAResults.TryGetValue(WeeklyResult.Key, out EngineQACrashes); int GameQACrashes = 0; WeeklyGameQAResults.TryGetValue(WeeklyResult.Key, out GameQACrashes); string AnonymousLine = ""; foreach (var VersionCrashes in WeeklyEngineVersionResults) { int EngineVersionCrashes = 0; VersionCrashes.Value.TryGetValue(WeeklyResult.Key, out EngineVersionCrashes); AnonymousLine += EngineVersionCrashes; AnonymousLine += ", "; } int Year = WeeklyResult.Key.Year; int Month = WeeklyResult.Key.AddMonths(-1).Month; if (WeeklyResult.Key.Month == 13 || WeeklyResult.Key.Month == 1) { Month = 0; } int Day = WeeklyResult.Key.Day + 6; string Line = "[new Date(" + Year + ", " + Month + ", " + Day + "), " + GeneralCrashes + ", " + CoderCrashes + ", " + EngineQACrashes + ", " + GameQACrashes + ", " + AnonymousLine + WeeklyResult.Value + "], "; CrashesByWeek += Line; } CrashesByWeek = CrashesByWeek.TrimEnd(", ".ToCharArray()); string CrashesByDay = ""; foreach (KeyValuePair <DateTime, int> DailyResult in DailyAllResults) { int GeneralCrashes = 0; DailyGeneralResults.TryGetValue(DailyResult.Key, out GeneralCrashes); int CoderCrashes = 0; DailyCoderResults.TryGetValue(DailyResult.Key, out CoderCrashes); int EngineQACrashes = 0; DailyEngineQAResults.TryGetValue(DailyResult.Key, out EngineQACrashes); int GameQACrashes = 0; DailyGameQAResults.TryGetValue(DailyResult.Key, out GameQACrashes); string AnonymousLine = ""; foreach (var VersionCrashes in DailyEngineVersionResults) { int EngineVersionCrashes = 0; VersionCrashes.Value.TryGetValue(DailyResult.Key, out EngineVersionCrashes); AnonymousLine += EngineVersionCrashes; AnonymousLine += ", "; } int Year = DailyResult.Key.Year; int Month = DailyResult.Key.AddMonths(-1).Month; if (DailyResult.Key.Month == 13 || DailyResult.Key.Month == 1) { Month = 0; } int Day = DailyResult.Key.Day; string Line = "[new Date(" + Year + ", " + Month + ", " + Day + "), " + GeneralCrashes + ", " + CoderCrashes + ", " + EngineQACrashes + ", " + GameQACrashes + ", " + AnonymousLine + DailyResult.Value + "], "; CrashesByDay += Line; } CrashesByDay = CrashesByDay.TrimEnd(", ".ToCharArray()); string BuggsByDay = ""; foreach (KeyValuePair <DateTime, int> DailyResult in BuggDailyAllResults) { int Year = DailyResult.Key.Year; int Month = DailyResult.Key.AddMonths(-1).Month; if (DailyResult.Key.Month == 13 || DailyResult.Key.Month == 1) { Month = 0; } int Day = DailyResult.Key.Day; string Line = "[new Date(" + Year + ", " + Month + ", " + Day + "), " + DailyResult.Value + "], "; BuggsByDay += Line; } BuggsByDay = BuggsByDay.TrimEnd(", ".ToCharArray()); var ResultDashboard = new DashboardViewModel { CrashesByWeek = CrashesByWeek, CrashesByDay = CrashesByDay, BuggsByDay = BuggsByDay, EngineVersions = EngineUE4Versions, }; ResultDashboard.GenerationTime = LogTimer.GetElapsedSeconds().ToString("F2"); return(View("Index", ResultDashboard)); } }
/// <summary> /// Apply the sort term to a set of crashes. /// </summary> /// <param name="Results">The unsorted set of crashes.</param> /// <param name="SortTerm">The term to sort by. e.g. UserName</param> /// <param name="bSortByDescending">Whether to sort ascending or descending.</param> /// <returns>A sorted set of crashes.</returns> public IEnumerable <Crash> GetSortedResults(IEnumerable <Crash> Results, string SortTerm, bool bSortByDescending) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(SortTerm=" + SortTerm + ")")) { switch (SortTerm) { case "Id": //Results = bSortByDescending ? Results.OrderByDescending( CrashInstance => CrashInstance.Id ) : Results.OrderBy( CrashInstance => CrashInstance.Id ); Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.Id, bSortByDescending); break; case "TimeOfCrash": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.TimeOfCrash, bSortByDescending); break; case "UserName": // Note: only works where user is stored by Id Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.UserName, bSortByDescending); //OrderBy( IntermediateQueryable.Join( Context.Users, CrashInstance => CrashInstance.UserNameId, UserInstance => UserInstance.Id, // ( CrashInstance, UserInstance ) => new { CrashInstance, UserInstance.UserName } ), // Joined => Joined.UserName, bSortByDescending ).Select( Joined => Joined.CrashInstance ).AsEnumerable(); break; case "RawCallStack": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.RawCallStack, bSortByDescending); break; case "GameName": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.GameName, bSortByDescending); break; case "EngineMode": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.EngineMode, bSortByDescending); break; case "FixedChangeList": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.FixedChangeList, bSortByDescending); break; case "TTPID": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.Jira, bSortByDescending); break; case "Branch": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.Branch, bSortByDescending); break; case "ChangeListVersion": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.BuiltFromCL, bSortByDescending); break; case "ComputerName": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.MachineId, bSortByDescending); break; case "PlatformName": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.PlatformName, bSortByDescending); break; case "Status": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.Status, bSortByDescending); break; case "Module": Results = EnumerableOrderBy(Results, CrashInstance => CrashInstance.Module, bSortByDescending); break; } return(Results); } }
/// <summary> /// Show detailed information about a crash. /// </summary> /// <param name="CrashesForm">A form of user data passed up from the client.</param> /// <param name="id">The unique id of the crash we wish to show the details of.</param> /// <returns>A view to show crash details.</returns> public ActionResult Show(FormCollection CrashesForm, int id) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer(this.GetType().ToString() + "(CrashId=" + id + ")")) { CallStackContainer CurrentCallStack = null; // Update the selected crash based on the form contents Crash CurrentCrash = LocalCrashRepository.GetCrash(id); if (CurrentCrash == null) { return(RedirectToAction("")); } string FormValue; FormValue = CrashesForm["SetStatus"]; if (!string.IsNullOrEmpty(FormValue)) { CurrentCrash.Status = FormValue; } FormValue = CrashesForm["SetFixedIn"]; if (!string.IsNullOrEmpty(FormValue)) { CurrentCrash.FixedChangeList = FormValue; } FormValue = CrashesForm["SetTTP"]; if (!string.IsNullOrEmpty(FormValue)) { CurrentCrash.TTPID = FormValue; } // Valid to set description to an empty string FormValue = CrashesForm["Description"]; if (FormValue != null) { CurrentCrash.Description = FormValue; } CurrentCallStack = new CallStackContainer(CurrentCrash); // Set callstack properties CurrentCallStack.bDisplayModuleNames = true; CurrentCallStack.bDisplayFunctionNames = true; CurrentCallStack.bDisplayFileNames = true; CurrentCallStack.bDisplayFilePathNames = true; CurrentCallStack.bDisplayUnformattedCallStack = false; CurrentCrash.CallStackContainer = LocalCrashRepository.GetCallStack(CurrentCrash); // Populate the crash with the correct user data LocalCrashRepository.PopulateUserInfo(CurrentCrash); LocalCrashRepository.SubmitChanges(); return(View("Show", new CrashViewModel { Crash = CurrentCrash, CallStack = CurrentCallStack })); } }