You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3229011 on 2016/12/09 by Steve.Robb Licensee version updated in FWorldTileInfo::Read(). https://udn.unrealengine.com/questions/325874/fworldtileinfo-not-passing-fileversionlicenseeue4.html Change 3230493 on 2016/12/12 by Robert.Manuszewski Adding a check against assembling the reference token stream while streaming without locking GC. Change 3230515 on 2016/12/12 by Steve.Robb GetStaticEnum and GetStaticStruct removed. Various generated code tidy-ups. Change 3230522 on 2016/12/12 by Steve.Robb UHT no longer complains about bases with different prefixes. References to obsolete DependsOn removed. Change 3230528 on 2016/12/12 by Steve.Robb ReferenceChainSearch tidyups. Change 3234235 on 2016/12/14 by Robert.Manuszewski PR #2695: fix comments (Contributed by wyhily2010) Change 3234237 on 2016/12/14 by Robert.Manuszewski PR #2614: [GenericPlatformFile] New Function, GetTimeStampLocal, returns file time stamp in local time instead of UTC Rama (Contributed by EverNewJoy) Change 3236214 on 2016/12/15 by Robert.Manuszewski PR# 1988 : Allow absolute path in -UserDir=<Path> argument (contributed by bozaro) Change 3236582 on 2016/12/15 by Robert.Manuszewski Allow commandline use in shipping builds #jira UE-24613 Change 3236591 on 2016/12/15 by Robert.Manuszewski Removed unnecessary console variable logspam #jira UE-24614 Change 3236737 on 2016/12/15 by Steve.Robb Fixes to non-contiguous enums in OSS. Change 3239686 on 2016/12/19 by Chris.Wood Fixed CompressionHelper method UE4CompressFileGZIP() that leaked a file handle when a compression error occurred (CRP v1.2.12) [UE-39910] - CrashReportProcess leaks file handles and doesn't cleanup folders after compression fails during output to S3 Change 3240687 on 2016/12/20 by Chris.Wood Improved CrashReportProcess retry logic to avoid stuck threads when CRW fails to add crashes (CRP 1.2.13) [UE-39941] - Improve CrashReportProcess retry logic when CR website returns failed response to AddCrash Request Change 3246347 on 2017/01/04 by Steve.Robb Readability, debuggability and standards improvements. Change 3249122 on 2017/01/06 by Steve.Robb Generic FPaths::Combine, allowing a mix of string argument types and unlimited arity. Change 3249580 on 2017/01/06 by Steve.Robb Fix for TArray::HeapSort when array contains pointers. See: https://answers.unrealengine.com/questions/545533/bug-heapsort-with-tarray-of-pointers-fails-to-comp.html Change 3250593 on 2017/01/09 by Robert.Manuszewski PR #3046: UE-39578: Added none to invalid filenames (Contributed by projectgheist) Change 3250596 on 2017/01/09 by Robert.Manuszewski PR #3094: Fixing typo in comments for LODColoration in BaseEngine.ini - UE-40196 (Contributed by sanjay-nambiar) Change 3250599 on 2017/01/09 by Robert.Manuszewski PR #3096: Fixed Log message in ExclusiveLoadPackageTimeTracker : UE-37583 (Contributed by sanjay-nambiar) Change 3250863 on 2017/01/09 by Steve.Robb Build configuration option to force the use of the Debug version of UnrealHeaderTool. Change 3250994 on 2017/01/09 by Ben.Zeigler Remove bad or redundant ini redirects. These did not work with the old system but were silently ignored, my new system throws warnings about them Change 3251000 on 2017/01/09 by Ben.Zeigler #jira UE-39599 Add FCoreRedirects which replaces and unifies the redirect systems in LinkerLoad, K2Node, Enum, and TaggedProperty. This fixes various bugs and makes things uniform. It will parse the previous ini files, or load out of a [CoreRedirects] section in any loaded ini file The old redirect system can be re-enabled by setting USE_CORE_REDIRECTS to 0 in CoreRedirects.h. This will be removed eventually Some refactors to pass in information needed by the new system that the old system didn't need Add LoadTimeVerbose stats for processing redirects and enable that group during -LoadTimeFile Change 3253580 on 2017/01/11 by Graeme.Thornton Added some validation of the class index in exportmap entries #jira UE-37873 Change 3253777 on 2017/01/11 by Graeme.Thornton Increase SerialSize and SerialOffset in FObjectExport to 64bits, to handle super large files #jira UE-39946 Change 3257750 on 2017/01/13 by Ben.Zeigler Fix issue where incorrectly set up animation node redirects (were ActiveClassRedirects, should have been ActiveStructRedirects) didn't work in the new redirect system because it validated more. Added backward compatibilty code and fixed some conflicts in the ini. Change 3261176 on 2017/01/17 by Ben.Zeigler #jira UE-40746 Fix redundant ini redirect #jira UE-40725 Fix section of Match3 defaultengine.ini that appears to have been accidentally duplicated from baseengine.ini several years ago Change 3261915 on 2017/01/18 by Steve.Robb Fixes to localized printf formats. Change 3262142 on 2017/01/18 by Ben.Zeigler Remove runtime code for old ActiveClassRedirects and related systems. It was already disabled and the old ini format is still parsed and converted to FCoreRedirects at runtime so there should be no functionality change. Merged the deprecated tagged property and enum redirect ini parsing into LinkerLoad, and remove the RemapImports step entirely as it's part of FixupImportMap. Change 3263596 on 2017/01/19 by Gil.Gribb UE4 - Fixed many bugs with the event driven loader and allowed it to work at boot time. Change 3263597 on 2017/01/19 by Gil.Gribb UE4 - Allowed UnrealPak to do a better job with EDL pak files when the order provided is old or from the cooker. Several minor tweaks to low level async IO stuff in support of switch experiments. Change 3263922 on 2017/01/19 by Gil.Gribb UE4 - Fixed a bug with nativized blueprints that was introduced with the boot time EDL changes. Change 3264131 on 2017/01/19 by Robert.Manuszewski Simple app to test hard to repro bugs Change 3264849 on 2017/01/19 by Ben.Zeigler Change FParse::Value to treat ) like , for parsing to handle config parsing struct format. This fixes cases where lines end with bool or FName variables that aren't written out quoted: +ClassRedirects=(OldName="LandscapeProxy",NewName="LandscapeStreamingProxy",InstanceOnly=True) Change 3265232 on 2017/01/19 by Ben.Zeigler #jira UE-39599 Finish class redirect refactor by cleaning up BaseEngine.ini Move plugin-specific redirects to new plugin ini files Move all redirects from BaseEngine.ini prior to 4.11 to native registration in FCoreRedirects. Needed to split up functions to avoid long compile times Move all redirects after 4.11 to new ini format Some related blueprint fixup code changes, these weren't cooperating well with some ini redirects Change 3265490 on 2017/01/20 by Steve.Robb Prevent engine reinstancing on hot reload. #jira UE-40765 Change 3265593 on 2017/01/20 by Gil.Gribb UE4 - Stored a copy of the callback in async read request so that we don't need to worry about lifetime so we can capture variables as needed. Also fixed race in audio streaming. Change 3266003 on 2017/01/20 by Gil.Gribb UE4 - Fixed bug which would cause a fatal error when cooking subobjects that were pending kill. Change 3267433 on 2017/01/22 by Gil.Gribb UE4 - Fixed a bug with EDL at boot time which caused a fatal error with unfired imports. Change 3267677 on 2017/01/23 by Steve.Robb Fix for whitespace before UCLASS() causing compile errors. #jira UE-24110 Change 3267685 on 2017/01/23 by Steve.Robb First pass of fixes to printf-style calls to only use TCHAR[] specifiers. Change 3267746 on 2017/01/23 by Steven.Hutton Resolve offline work Changes to repositories to support better handling of db connections. Change 3267865 on 2017/01/23 by Steve.Robb Clarification of TArray::FindLastByPredicate() and FString::FindLastCharByPredicate(). #fyi nick.darnell Change 3268075 on 2017/01/23 by Gil.Gribb UE4 - Fixed another bug with RF_PendingKill subobjects and the new loader. Change 3268447 on 2017/01/23 by Gil.Gribb Fortnite - Removed calls to ::StaticClass() before main starts; this is not allowed. Change 3269491 on 2017/01/24 by Gil.Gribb UE4 - Cancelling async loading with the EDL loader now prints a warning and does a flush instead. Change 3269492 on 2017/01/24 by Gil.Gribb UE4 - Suppressed a few EDL cook wanrings. Change 3270085 on 2017/01/24 by Gil.Gribb UE4 - Remove pak highwater spam. Change 3270089 on 2017/01/24 by Gil.Gribb UE4 - fix random bug with memory counting and some vertex buffer Change 3271246 on 2017/01/25 by Chris.Wood Fixed CrashReportProcess pipeline for Mac and Linux crashes lacking machine Ids (CRP v1.2.14) [UE-40605] - Machine ID is not being shown on the crashreporter website Change 3271827 on 2017/01/25 by Steve.Robb C4946 warning disabled in third party headers (triggers in Clang/LLVM). Change 3271874 on 2017/01/25 by Steve.Robb Fix for missing error check after header preparsing. Change 3271911 on 2017/01/25 by Steve.Robb ObjectMacros.h now automatically included by generated headers. #fyi jamie.dale Change 3273125 on 2017/01/26 by Steve.Robb Check to ensure that a .generated.h header is included by headers which have exported types, to avoid crazy compiler errors. #fyi james.golding Change 3273209 on 2017/01/26 by Steve.Robb UnrealCodeAnalyzer compilation fixes. Change 3274917 on 2017/01/27 by Steve.Robb GC disabled when recompiling child BPs, as is already the case for the parent (CL# 2731120). Now-unused field removed. Change 3279091 on 2017/01/31 by Ben.Marsh UBT: Remove code paths which assume relative paths based on a particular CWD. Use the absolute paths stored in UnrealBuildTool.RootDirectory/UnrealBuildTool.EngineDirectory instead. Change 3279195 on 2017/01/31 by Gil.Gribb Turned EDL on for orion Change 3279493 on 2017/01/31 by Ben.Zeigler #jira UE-41341 Redo redirector fixups that got undone in merge from Main Change 3280284 on 2017/01/31 by Ben.Zeigler #jira UE-41357 Fix typo in vehicle redirect. Also fix base crash when converting old content with nodes that don't exist. Fix issues with loading plugin ini files. They weren't properly "diffing" against the base/default source file so my redirect typo fix didn't propagate. Some general config system refactors on Josh's advice, and make base.ini optional if reading out of a non-standard engine directory Engine plugin ini are now BasePlugin.ini, game plugins are still DefaultPlugin.ini. Fix crash when loading old content pointing to nonexistent node type. It will still error/ensure but won't crash. Change 3280299 on 2017/01/31 by Gil.Gribb possibly fix edl at boot with orion server....though was no-repro Change 3280386 on 2017/01/31 by Ben.Zeigler Header include fixes for -nopch, fixes incremental build Change 3280557 on 2017/01/31 by Ben.Zeigler Fix Config crash. FConfigFile's copy constructor is apparently not safe and resulted in garbage memory in some cases Change 3280817 on 2017/02/01 by Steve.Robb Unused SmartCastProperty removed. Change 3280897 on 2017/02/01 by Chris.Wood Improved CRP shutdown code to abort AddCrash requests when cancel is requested (CRP v1.2.15) [UE-41338] - Fix CRP shutdown when website isn't accepting new crashes Also, improved shutdown code to try to avoid occassional exception when writing out the report index. Looks like it isn't shutting down worker threads cleanly sometimes. Added more logging to this too. Change 3280989 on 2017/02/01 by Gil.Gribb New unrealpak binaries Change 3281416 on 2017/02/01 by Michael.Trepka Updated UnrealPak binaries for Mac Change 3282457 on 2017/02/01 by Ben.Zeigler #jira UE-41425 Protect against issues with streamable manager requests recursively completing by caching the array locally. This code is safer in general in my local version so just doing a quick fix for now Change 3282619 on 2017/02/01 by Arciel.Rekman Linux: update UnrealPak. [CL 3283649 by Ben Marsh in Main branch]
663 lines
28 KiB
C#
663 lines
28 KiB
C#
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Net;
|
|
using System.Web;
|
|
using System.Web.Mvc;
|
|
using System.Web.UI;
|
|
using Microsoft.Practices.ObjectBuilder2;
|
|
using Newtonsoft.Json.Linq;
|
|
using Tools.CrashReporter.CrashReportWebSite.DataModels;
|
|
using Tools.CrashReporter.CrashReportWebSite.DataModels.Repositories;
|
|
using Tools.CrashReporter.CrashReportWebSite.ViewModels;
|
|
|
|
namespace Tools.CrashReporter.CrashReportWebSite.Controllers
|
|
{
|
|
/// <summary>
|
|
/// A controller to handle the Bugg data.
|
|
/// </summary>
|
|
public class BuggsController : Controller
|
|
{
|
|
private int pageSize = 50;
|
|
|
|
/// <summary>
|
|
/// An empty constructor.
|
|
/// </summary>
|
|
public BuggsController()
|
|
{
|
|
}
|
|
|
|
/// <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 (var logTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true ))
|
|
{
|
|
var formData = new FormHelper( Request, BuggsForm, "CrashesInTimeFrameGroup" );
|
|
var results = GetResults( formData );
|
|
results.GenerationTime = logTimer.GetElapsedSeconds().ToString( "F2" );
|
|
return View( "Index", results );
|
|
}
|
|
}
|
|
|
|
/// <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";
|
|
|
|
var model = GetResult(id);
|
|
model.Bugg.PrepareBuggForJira(model.Crashes);
|
|
// 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"];
|
|
model.Bugg.Crashes.ForEach(data =>data.Status = buggsForm["SetStatus"]);
|
|
}
|
|
|
|
if( !string.IsNullOrEmpty( buggsForm["SetFixedIn"] ) )
|
|
{
|
|
model.Bugg.FixedChangeList = buggsForm["SetFixedIn"];
|
|
model.Bugg.Crashes.ForEach(data => data.FixedChangeList = buggsForm["SetFixedIn"]);
|
|
}
|
|
|
|
if( !string.IsNullOrEmpty( buggsForm["SetTTP"] ) )
|
|
{
|
|
model.Bugg.TTPID = buggsForm["SetTTP"];
|
|
model.Bugg.Crashes.ForEach(data => data.Jira = buggsForm["SetTTP"]);
|
|
}
|
|
using (var unitOfWork = new UnitOfWork(new CrashReportEntities()))
|
|
{
|
|
unitOfWork.BuggRepository.Update(model.Bugg);
|
|
unitOfWork.Save();
|
|
}
|
|
}
|
|
|
|
// Set up the view model with the crash data
|
|
int page;
|
|
int.TryParse(buggsForm["Page"], out page);
|
|
|
|
model.Crashes = model.Crashes.Skip(page * pageSize).Take(pageSize).ToList();
|
|
|
|
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" );
|
|
|
|
//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 );
|
|
}
|
|
}
|
|
|
|
#region Private Methods
|
|
|
|
/// <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>
|
|
private BuggsViewModel GetResults(FormHelper FormData)
|
|
{
|
|
// 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.
|
|
var fromDate = FormData.DateFrom;
|
|
var toDate = FormData.DateTo.AddDays(1);
|
|
|
|
IQueryable<Bugg> results = null;
|
|
var skip = (FormData.Page - 1) * FormData.PageSize;
|
|
var take = FormData.PageSize;
|
|
|
|
var sortedResultsList = new List<Bugg>();
|
|
var groupCounts = new SortedDictionary<string, int>();
|
|
int totalCountedRecords = 0;
|
|
|
|
using (var unitOfWork = new UnitOfWork(new CrashReportEntities()))
|
|
{
|
|
var userGroupId = unitOfWork.UserGroupRepository.First(data => data.Name == FormData.UserGroup).Id;
|
|
|
|
// Get every Bugg.
|
|
var resultsAll = unitOfWork.BuggRepository.ListAll();
|
|
|
|
// Look at all Buggs that are still 'open' i.e. the last crash occurred in our date range.
|
|
results = FilterByDate(resultsAll, FormData.DateFrom, FormData.DateTo);
|
|
|
|
// Filter results by build version.
|
|
if (!string.IsNullOrEmpty(FormData.VersionName))
|
|
{
|
|
results = FilterByBuildVersion(results, FormData.VersionName);
|
|
}
|
|
|
|
// Filter by BranchName
|
|
if (!string.IsNullOrEmpty(FormData.BranchName))
|
|
{
|
|
results =
|
|
results.Where(
|
|
data => data.Crashes.Any(da => da.Branch.Equals(FormData.BranchName)));
|
|
}
|
|
|
|
// Filter by PlatformName
|
|
if (!string.IsNullOrEmpty(FormData.PlatformName))
|
|
{
|
|
results =
|
|
results.Where(
|
|
data => data.Crashes.Any(da => da.PlatformName.Equals(FormData.PlatformName)));
|
|
}
|
|
|
|
// Run at the end
|
|
if (!string.IsNullOrEmpty(FormData.SearchQuery))
|
|
{
|
|
try
|
|
{
|
|
var queryString = HttpUtility.HtmlDecode(FormData.SearchQuery.ToString()) ?? "";
|
|
|
|
// Take out terms starting with a -
|
|
var terms = queryString.Split("-, ;+".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
|
var allFuncionCallIds = new HashSet<int>();
|
|
foreach (var term in terms)
|
|
{
|
|
var functionCallIds = unitOfWork.FunctionRepository.Get(functionCallInstance => functionCallInstance.Call.Contains(term)).Select(x => x.Id).ToList();
|
|
foreach (var id in functionCallIds)
|
|
{
|
|
allFuncionCallIds.Add(id);
|
|
}
|
|
}
|
|
|
|
// Search for all function ids. OR operation, not very efficient, but for searching for one function should be ok.
|
|
results = allFuncionCallIds.Aggregate(results, (current, id) => current.Where(x => x.Pattern.Contains(id + "+") || x.Pattern.Contains("+" + id)));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine("Exception in Search: " + ex.ToString());
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// Filter by user group if present
|
|
if (!string.IsNullOrEmpty(FormData.UserGroup))
|
|
results = FilterByUserGroup(results, FormData.UserGroup);
|
|
|
|
if (!string.IsNullOrEmpty(FormData.JiraId))
|
|
{
|
|
results = FilterByJira(results, FormData.JiraId);
|
|
}
|
|
|
|
// Grab just the results we want to display on this page
|
|
totalCountedRecords = results.Count();
|
|
|
|
var crashesByUserGroupCounts = unitOfWork.CrashRepository.ListAll().Where(data =>
|
|
data.User.UserGroupId == userGroupId &&
|
|
data.TimeOfCrash >= fromDate &&
|
|
data.TimeOfCrash <= toDate &&
|
|
data.PatternId != null)
|
|
.GroupBy(data => data.PatternId)
|
|
.Select(data => new {data.Key, Count = data.Count()})
|
|
.OrderByDescending(data => data.Count)
|
|
.ToDictionary(data => data.Key, data => data.Count);
|
|
|
|
var crashesInTimeFrameCounts = unitOfWork.CrashRepository.ListAll().Where(data =>
|
|
data.TimeOfCrash >= fromDate &&
|
|
data.TimeOfCrash <= toDate
|
|
&& data.PatternId != null)
|
|
.GroupBy(data => data.PatternId)
|
|
.Select(data => new {data.Key, Count = data.Count()})
|
|
.OrderByDescending(data => data.Count)
|
|
.ToDictionary(data => data.Key, data => data.Count);
|
|
|
|
var affectedUsers = unitOfWork.CrashRepository.ListAll().Where(data =>
|
|
data.User.UserGroupId == userGroupId &&
|
|
data.TimeOfCrash >= fromDate &&
|
|
data.TimeOfCrash <= toDate &&
|
|
data.PatternId != null)
|
|
.Select(data => new {PatternId = data.PatternId, ComputerName = data.ComputerName})
|
|
.Distinct()
|
|
.GroupBy(data => data.PatternId)
|
|
.Select(data => new {data.Key, Count = data.Count()})
|
|
.OrderByDescending(data => data.Count)
|
|
.ToDictionary(data => data.Key, data => data.Count);
|
|
|
|
results = GetSortedResults(results, FormData.SortTerm, FormData.SortTerm == "descending",
|
|
FormData.DateFrom, FormData.DateTo, FormData.UserGroup);
|
|
|
|
sortedResultsList =
|
|
results.ToList();
|
|
|
|
// Get UserGroup ResultCounts
|
|
var groups =
|
|
results.SelectMany(data => data.UserGroups)
|
|
.GroupBy(data => data.Name)
|
|
.Select(data => new {Key = data.Key, Count = data.Count()})
|
|
.ToDictionary(x => x.Key, y => y.Count);
|
|
|
|
groupCounts = new SortedDictionary<string, int>(groups);
|
|
|
|
foreach (var bugg in sortedResultsList)
|
|
{
|
|
if (bugg.PatternId.HasValue && crashesByUserGroupCounts.ContainsKey(bugg.PatternId.Value))
|
|
{
|
|
bugg.CrashesInTimeFrameGroup = crashesByUserGroupCounts[bugg.PatternId.Value];
|
|
}
|
|
else
|
|
bugg.CrashesInTimeFrameGroup = 0;
|
|
|
|
if (bugg.PatternId.HasValue && crashesInTimeFrameCounts.ContainsKey(bugg.PatternId.Value))
|
|
{
|
|
bugg.CrashesInTimeFrameAll = crashesInTimeFrameCounts[bugg.PatternId.Value];
|
|
}
|
|
else
|
|
bugg.CrashesInTimeFrameAll = 0;
|
|
|
|
if (bugg.PatternId.HasValue && affectedUsers.ContainsKey(bugg.PatternId.Value))
|
|
{
|
|
bugg.NumberOfUniqueMachines = affectedUsers[bugg.PatternId.Value];
|
|
}
|
|
else
|
|
bugg.NumberOfUniqueMachines = 0;
|
|
}
|
|
|
|
sortedResultsList = sortedResultsList.OrderByDescending(data => data.CrashesInTimeFrameGroup).Skip(skip)
|
|
.Take(totalCountedRecords >= skip + take ? take : totalCountedRecords).ToList();
|
|
|
|
foreach (var bugg in sortedResultsList)
|
|
{
|
|
var crash =
|
|
unitOfWork.CrashRepository.First(data => data.BuggId == bugg.Id);
|
|
bugg.FunctionCalls = crash.GetCallStack().GetFunctionCalls();
|
|
}
|
|
}
|
|
|
|
List<SelectListItem> branchNames;
|
|
List<SelectListItem> versionNames;
|
|
List<SelectListItem> platformNames;
|
|
using (var unitOfWork = new UnitOfWork(new CrashReportEntities()))
|
|
{
|
|
branchNames = unitOfWork.CrashRepository.GetBranchesAsListItems();
|
|
versionNames = unitOfWork.CrashRepository.GetVersionsAsListItems();
|
|
platformNames = unitOfWork.CrashRepository.GetPlatformsAsListItems();
|
|
}
|
|
|
|
return new BuggsViewModel()
|
|
{
|
|
Results = sortedResultsList,
|
|
PagingInfo = new PagingInfo { CurrentPage = FormData.Page, PageSize = FormData.PageSize, TotalResults = totalCountedRecords },
|
|
SortTerm = FormData.SortTerm,
|
|
SortOrder = FormData.SortOrder,
|
|
UserGroup = FormData.UserGroup,
|
|
CrashType = FormData.CrashType,
|
|
SearchQuery = FormData.SearchQuery,
|
|
BranchNames = branchNames,
|
|
VersionNames = versionNames,
|
|
PlatformNames = platformNames,
|
|
DateFrom = (long)(FormData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds,
|
|
DateTo = (long)(FormData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds,
|
|
VersionName = FormData.VersionName,
|
|
PlatformName = FormData.PlatformName,
|
|
BranchName = FormData.BranchName,
|
|
GroupCounts = groupCounts,
|
|
Jira = FormData.JiraId,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve all Data for a single bug given by the id
|
|
/// </summary>
|
|
/// <param name="buggsForm"></param>
|
|
/// <param name="id"></param>
|
|
/// <returns></returns>
|
|
private BuggViewModel GetResult(int id)
|
|
{
|
|
var model = new BuggViewModel();
|
|
using (var unitOfWork = new UnitOfWork(new CrashReportEntities()))
|
|
{
|
|
// Create a new view and populate with crashes
|
|
List<Crash> crashes = null;
|
|
model.Bugg = unitOfWork.BuggRepository.GetById(id);
|
|
|
|
crashes = model.Bugg.Crashes.OrderByDescending(data => data.TimeOfCrash).ToList();
|
|
model.Bugg.CrashesInTimeFrameAll = crashes.Count;
|
|
model.Bugg.CrashesInTimeFrameGroup = crashes.Count;
|
|
model.Bugg.NumberOfCrashes = crashes.Count;
|
|
model.Crashes = crashes;
|
|
}
|
|
|
|
return model;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filter a set of Buggs by a jira ticket.
|
|
/// </summary>
|
|
/// <param name="results">The unfiltered set of Buggs</param>
|
|
/// <param name="jira">The ticket by which to filter the list of buggs</param>
|
|
/// <returns></returns>
|
|
private IQueryable<Bugg> FilterByJira(IQueryable<Bugg> results, string jira)
|
|
{
|
|
return results.Where(bugg => bugg.TTPID == jira);
|
|
}
|
|
|
|
/// <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>
|
|
private IQueryable<Bugg> FilterByDate(IQueryable<Bugg> results, DateTime dateFrom, DateTime dateTo)
|
|
{
|
|
dateTo = dateTo.AddDays(1);
|
|
var buggsInTimeFrame =
|
|
results.Where(bugg => (bugg.TimeOfFirstCrash >= dateFrom && bugg.TimeOfFirstCrash <= dateTo) ||
|
|
(bugg.TimeOfLastCrash >= dateFrom && bugg.TimeOfLastCrash <= dateTo) || (bugg.TimeOfFirstCrash <= dateFrom && bugg.TimeOfLastCrash >= dateTo));
|
|
|
|
return buggsInTimeFrame;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filter a set of Buggs by build version.
|
|
/// </summary>
|
|
/// <param name="results">The unfiltered set of Buggs.</param>
|
|
/// <param name="buildVersion">The build version to filter by.</param>
|
|
/// <returns>The set of Buggs that matches specified build version</returns>
|
|
private IQueryable<Bugg> FilterByBuildVersion(IQueryable<Bugg> results, string buildVersion)
|
|
{
|
|
if (!string.IsNullOrEmpty(buildVersion))
|
|
{
|
|
results = results.Where(data => data.BuildVersion.Contains(buildVersion));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filter a set of Buggs by user group name.
|
|
/// </summary>
|
|
/// <param name="setOfBuggs">The unfiltered set of Buggs.</param>
|
|
/// <param name="groupName">The user group name to filter by.</param>
|
|
/// <returns>The set of Buggs by users in the requested user group.</returns>
|
|
private IQueryable<Bugg> FilterByUserGroup(IQueryable<Bugg> setOfBuggs, string groupName)
|
|
{
|
|
return setOfBuggs.Where(data => data.UserGroups.Any(ug => ug.Name == groupName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sort the container of Buggs by the requested criteria.
|
|
/// </summary>
|
|
/// <param name="results">A container of unsorted Buggs.</param>
|
|
/// <param name="sortTerm">The term to sort by.</param>
|
|
/// <param name="bSortDescending">Whether to sort by descending or ascending.</param>
|
|
/// <param name="dateFrom">The date of the earliest Bugg to examine.</param>
|
|
/// <param name="dateTo">The date of the most recent Bugg to examine.</param>
|
|
/// <param name="groupName">The user group name to filter by.</param>
|
|
/// <returns>A sorted container of Buggs.</returns>
|
|
private IOrderedQueryable<Bugg> GetSortedResults(IQueryable<Bugg> results, string sortTerm, bool bSortDescending, DateTime dateFrom, DateTime dateTo, string groupName)
|
|
{
|
|
IOrderedQueryable<Bugg> orderedResults = null;
|
|
|
|
try
|
|
{
|
|
switch (sortTerm)
|
|
{
|
|
case "Id":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.Id, bSortDescending);
|
|
break;
|
|
case "BuildVersion":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.BuildVersion, bSortDescending);
|
|
break;
|
|
case "LatestCrash":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.TimeOfLastCrash, bSortDescending);
|
|
break;
|
|
case "FirstCrash":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.TimeOfFirstCrash, bSortDescending);
|
|
break;
|
|
case "NumberOfCrashes":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.NumberOfCrashes, bSortDescending);
|
|
break;
|
|
case "NumberOfUsers":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.NumberOfUniqueMachines, bSortDescending);
|
|
break;
|
|
case "Pattern":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.Pattern, bSortDescending);
|
|
break;
|
|
case "CrashType":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.CrashType, bSortDescending);
|
|
break;
|
|
case "Status":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.Status, bSortDescending);
|
|
break;
|
|
case "FixedChangeList":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.FixedChangeList, bSortDescending);
|
|
break;
|
|
case "TTPID":
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.TTPID, bSortDescending);
|
|
break;
|
|
default:
|
|
orderedResults = EnumerableOrderBy(results, buggCrashInstance => buggCrashInstance.Id, bSortDescending);
|
|
break;
|
|
|
|
}
|
|
return orderedResults;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine("Exception in GetSortedResults: " + ex.ToString());
|
|
}
|
|
return orderedResults;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <typeparam name="TKey"></typeparam>
|
|
/// <param name="query"></param>
|
|
/// <param name="predicate"></param>
|
|
/// <param name="bDescending"></param>
|
|
/// <returns></returns>
|
|
private IOrderedQueryable<Bugg> EnumerableOrderBy<TKey>(IQueryable<Bugg> query, Expression<Func<Bugg, TKey>> predicate, bool bDescending)
|
|
{
|
|
return bDescending ? query.OrderByDescending(predicate) : query.OrderBy(predicate);
|
|
}
|
|
|
|
/// <summary>Dispose the controller - safely getting rid of database connections</summary>
|
|
/// <param name="disposing"></param>
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|