// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Web.Mvc; using Tools.CrashReporter.CrashReportWebSite.DataModels; using Tools.CrashReporter.CrashReportWebSite.DataModels.Repositories; using Tools.CrashReporter.CrashReportWebSite.Models; namespace Tools.CrashReporter.CrashReportWebSite.Controllers { /// /// /// public struct FCrashMinimal { /// /// /// public readonly DateTime TimeOfCrash; /// /// /// public readonly int UserId; /// /// /// public readonly string EngineVersion; /// /// /// /// /// /// public FCrashMinimal( DateTime InTimeOfCrash, int InUserId, string InEngineVersion ) { TimeOfCrash = InTimeOfCrash; UserId = InUserId; EngineVersion = InEngineVersion; } } /// /// The controller to handle graphing of Crashes per user group over time. /// public class DashboardController : Controller { /// Fake id for all user groups public static readonly int AllUserGroupId = -1; // CrashReportEntities _entities = new CrashReportEntities(); private CrashRepository _Crashes; private BuggRepository _Buggs; public DashboardController() { //Hideous temporary code //no! Unity! Dependency injection! Single Context wrapper! _Crashes = new CrashRepository(_entities); _Buggs = new BuggRepository(_entities); } /// /// Return a dictionary of Crashes per group grouped by week. /// /// A set of Crashes to interrogate. /// The id of the user group to interrogate. /// A dictionary of week vs. Crash count. public Dictionary GetWeeklyCountsByGroup( List Crashes, int UserGroupId ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(UserGroupId=" + UserGroupId + ")" ) ) { Dictionary Results = new Dictionary(); var UsersIds = new HashSet(_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; } } /// /// Return a dictionary of Crashes per version grouped by week. /// /// A set of Crashes to interrogate. /// Engine version /// Anonymous id /// A dictionary of week vs. Crash count. public Dictionary GetWeeklyCountsByVersion( List Crashes, string EngineVersion, int AnonymousID ) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(EngineVersion=" + EngineVersion + ")" )) { Dictionary Results = new Dictionary(); Crashes = Crashes .Where( Crash => Crash.UserId == AnonymousID ) .Where( Crash => Crash.EngineVersion.Contains( EngineVersion ) ) .ToList(); try { Results = ( from CrashDetail in Crashes group CrashDetail by CrashDetail.TimeOfCrash.AddDays( -(int)CrashDetail.TimeOfCrash.DayOfWeek ).Date into VersionCount orderby VersionCount.Key select new { Count = VersionCount.Count(), Date = VersionCount.Key } ).ToDictionary( x => x.Date, y => y.Count ); } catch (Exception Ex) { Debug.WriteLine( "Exception in GeWeeklyCountsByVersion: " + Ex.ToString() ); } return Results; } } /// /// Return a dictionary of Crashes per group grouped by day. /// /// A set of Crashes to interrogate. /// The id of the user group to interrogate. /// A dictionary of day vs. Crash count. public Dictionary GetDailyCountsByGroup( List Crashes, int UserGroupId ) { using( FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(UserGroupId=" + UserGroupId + ")" ) ) { Dictionary Results = new Dictionary(); var UsersIds = _entities.UserGroups.First(data => data.Id == UserGroupId ).Users.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.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 GetDailyCountsByGroup: " + Ex.ToString() ); } return Results; } } /// /// Return a dictionary of Crashes per version grouped by day. /// /// A set of Crashes to interrogate. /// Engine version /// Anonymous id /// A dictionary of day vs. Crash count. public Dictionary GetDailyCountsByVersion( List Crashes, string EngineVersion, int AnonymousID ) { using (FAutoScopedLogTimer LogTimer = new FAutoScopedLogTimer( this.GetType().ToString() + "(EngineVersion=" + EngineVersion + ")" )) { Dictionary Results = new Dictionary(); Crashes = Crashes .Where( Crash => Crash.UserId == AnonymousID ) .Where( Crash => Crash.EngineVersion.Contains( EngineVersion ) ) .ToList(); try { Results = ( from CrashDetail in Crashes group CrashDetail by CrashDetail.TimeOfCrash.Date into VersionCount orderby VersionCount.Key select new { Count = VersionCount.Count(), Date = VersionCount.Key } ).ToDictionary( x => x.Date, y => y.Count ); } catch (Exception Ex) { Debug.WriteLine( "Exception in GetDailyCountsByVersion: " + Ex.ToString() ); } return Results; } } /// /// The main view of the dash board. /// /// A view showing two charts of Crashes over time. 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 EngineUE4Versions = new List(); 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 CrashesInTimeFrame = _Crashes.ListAll() .Where(MyCrash => MyCrash.TimeOfCrash >= AfewMonthsAgo && MyCrash.TimeOfCrash <= endDate); //IEnumerable 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 MinimalCrashes = new List( VMinimalCrashes.Count ); foreach( var Anotype in VMinimalCrashes ) { MinimalCrashes.Add( new FCrashMinimal( Anotype.TimeOfCrash, Anotype.UserID, Anotype.EngineVersion ) ); } LogTimerSQL.Dispose(); HashSet AnonumousIDs = new HashSet(_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 WeeklyGeneralResults = GetWeeklyCountsByGroup( MinimalCrashes, GeneralUserGroupId ); Dictionary WeeklyCoderResults = GetWeeklyCountsByGroup( MinimalCrashes, CoderUserGroupId ); Dictionary WeeklyEngineQAResults = GetWeeklyCountsByGroup( MinimalCrashes, EngineQAUserGroupId ); Dictionary WeeklyGameQAResults = GetWeeklyCountsByGroup( MinimalCrashes, GameQAUserGroupId ); Dictionary> WeeklyEngineVersionResults = new Dictionary>(); foreach (string UE4Version in EngineUE4Versions) { Dictionary Results = GetWeeklyCountsByVersion( MinimalCrashes, UE4Version, AnonymousID ); WeeklyEngineVersionResults.Add( UE4Version, Results ); } Dictionary WeeklyAllResults = GetWeeklyCountsByGroup( MinimalCrashes, AllUserGroupId ); // Daily Dictionary DailyGeneralResults = GetDailyCountsByGroup( MinimalCrashes, GeneralUserGroupId ); Dictionary DailyCoderResults = GetDailyCountsByGroup( MinimalCrashes, CoderUserGroupId ); Dictionary DailyEngineQAResults = GetDailyCountsByGroup( MinimalCrashes, EngineQAUserGroupId ); Dictionary DailyGameQAResults = GetDailyCountsByGroup( MinimalCrashes, GameQAUserGroupId ); Dictionary> DailyEngineVersionResults = new Dictionary>(); foreach(string UE4Version in EngineUE4Versions) { Dictionary Results = GetDailyCountsByVersion( MinimalCrashes, UE4Version, AnonymousID ); DailyEngineVersionResults.Add( UE4Version, Results ); } Dictionary DailyAllResults = GetDailyCountsByGroup( MinimalCrashes, AllUserGroupId ); // Get daily buggs stats. List Buggs = _Buggs.ListAll().Where( Bugg => Bugg.TimeOfFirstCrash >= AfewMonthsAgo ).ToList(); Dictionary 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 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 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 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 ); } } } }