diff --git a/Backup/BuildMonitor.sln b/Backup/BuildMonitor.sln
new file mode 100644
index 0000000..18202dc
--- /dev/null
+++ b/Backup/BuildMonitor.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{4FDB4354-8389-4675-ACED-6CA6DF570561}"
+ ProjectSection(SolutionItems) = preProject
+ .nuget\NuGet.exe = .nuget\NuGet.exe
+ .nuget\NuGet.targets = .nuget\NuGet.targets
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildMonitor", "BuildMonitor\BuildMonitor.csproj", "{700A9D28-E9DE-428B-A8F2-B40BAF4A3E87}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildMonitor.UnitTests", "BuildMonitor.UnitTests\BuildMonitor.UnitTests.csproj", "{4107248F-F2FC-4152-816B-E8633C4913F5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{68E63FC4-249A-4EFE-A8D8-5154DA8E3415}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildMonitorPackage", "BuildMonitorPackage\BuildMonitorPackage.csproj", "{F1623C5E-614B-435B-B625-AC534EF3920F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {700A9D28-E9DE-428B-A8F2-B40BAF4A3E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {700A9D28-E9DE-428B-A8F2-B40BAF4A3E87}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {700A9D28-E9DE-428B-A8F2-B40BAF4A3E87}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {700A9D28-E9DE-428B-A8F2-B40BAF4A3E87}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4107248F-F2FC-4152-816B-E8633C4913F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4107248F-F2FC-4152-816B-E8633C4913F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4107248F-F2FC-4152-816B-E8633C4913F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4107248F-F2FC-4152-816B-E8633C4913F5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F1623C5E-614B-435B-B625-AC534EF3920F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F1623C5E-614B-435B-B625-AC534EF3920F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1623C5E-614B-435B-B625-AC534EF3920F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F1623C5E-614B-435B-B625-AC534EF3920F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {4107248F-F2FC-4152-816B-E8633C4913F5} = {68E63FC4-249A-4EFE-A8D8-5154DA8E3415}
+ EndGlobalSection
+EndGlobal
diff --git a/Backup/BuildMonitorPackage/BuildMonitorPackage.cs b/Backup/BuildMonitorPackage/BuildMonitorPackage.cs
new file mode 100644
index 0000000..e3bb449
--- /dev/null
+++ b/Backup/BuildMonitorPackage/BuildMonitorPackage.cs
@@ -0,0 +1,231 @@
+using System;
+using System.ComponentModel.Design;
+using System.Runtime.InteropServices;
+using BuildMonitor;
+using BuildMonitor.Domain;
+using BuildMonitor.UI;
+using EnvDTE;
+using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Shell;
+using Constants = EnvDTE.Constants;
+
+namespace BuildMonitorPackage
+{
+ [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
+ [Guid(GuidList.guidBuildMonitorPackagePkgString)]
+ [PackageRegistration(UseManagedResourcesOnly = true)]
+ [ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}")]
+ sealed class BuildMonitorPackage : Package, IVsUpdateSolutionEvents2
+ {
+ private DTE dte;
+ private readonly Monitor monitor;
+ private readonly DataAdjusterWithLogging dataAdjuster;
+ private BuildMonitor.Domain.Solution solution;
+
+ private IVsSolutionBuildManager2 sbm;
+ private uint updateSolutionEventsCookie;
+ private OutputWindowPane outputWindowPane;
+ private SolutionEvents events;
+ private IVsSolution2 vsSolution;
+
+ public BuildMonitorPackage()
+ {
+ Settings.CreateApplicationFolderIfNotExist();
+
+ var factory = new BuildFactory();
+ var repository = new BuildRepository(Settings.RepositoryPath);
+
+ monitor = new Monitor(factory, repository);
+ dataAdjuster = new DataAdjusterWithLogging(repository, PrintLine);
+ }
+
+ protected override void Initialize()
+ {
+ base.Initialize();
+
+ //if invalid data, adjust it
+ dataAdjuster.Adjust();
+
+ // Get solution build manager
+ sbm = ServiceProvider.GlobalProvider.GetService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager2;
+ if (sbm != null)
+ {
+ sbm.AdviseUpdateSolutionEvents(this, out updateSolutionEventsCookie);
+ }
+
+ // Must hold a reference to the solution events object or the events wont fire, garbage collection related
+ events = GetDTE().Events.SolutionEvents;
+ events.Opened += Solution_Opened;
+
+ PrintLine("Build monitor initialized");
+ PrintLine("Path to persist data: {0}", Settings.RepositoryPath);
+
+ monitor.SolutionBuildFinished = b =>
+ {
+ Print("[{0}] Time Elapsed: {1} \t\t", b.SessionBuildCount, b.SolutionBuildTime.ToTime());
+ PrintLine("Session build time: {0}\n", b.SessionMillisecondsElapsed.ToTime());
+ };
+
+ monitor.ProjectBuildFinished = b => PrintLine(" - {0}\t-- {1} --", b.MillisecondsElapsed.ToTime(), b.ProjectName);
+ }
+
+ private void Solution_Opened()
+ {
+ solution = new BuildMonitor.Domain.Solution { Name = GetSolutionName() };
+ PrintLine("\nSolution loaded: \t{0}", solution.Name);
+ PrintLine("{0}", 60.Times("-"));
+ }
+
+ #region Print to output window pane
+
+ private OutputWindowPane GetOutputWindowPane()
+ {
+ if (outputWindowPane == null)
+ {
+ var outputWindow = (OutputWindow)GetDTE().Windows.Item(Constants.vsWindowKindOutput).Object;
+ outputWindowPane = outputWindow.OutputWindowPanes.Add("Build monitor");
+ }
+ return outputWindowPane;
+ }
+
+ private void Print(string format, params object[] args)
+ {
+ GetOutputWindowPane().OutputString(string.Format(format, args));
+ }
+
+ private void PrintLine(string format, params object[] args)
+ {
+ Print(format + Environment.NewLine, args);
+ }
+
+ #endregion
+
+ #region Get objects from vs
+
+ private DTE GetDTE()
+ {
+ if (dte == null)
+ {
+ var serviceContainer = this as IServiceContainer;
+ dte = serviceContainer.GetService(typeof(SDTE)) as DTE;
+ }
+ return dte;
+ }
+
+ private void SetVsSolution()
+ {
+ if (vsSolution == null)
+ vsSolution = ServiceProvider.GlobalProvider.GetService(typeof(SVsSolution)) as IVsSolution2;
+ }
+
+ private string GetSolutionName()
+ {
+ SetVsSolution();
+ object solutionName;
+ vsSolution.GetProperty((int)__VSPROPID.VSPROPID_SolutionBaseName, out solutionName);
+ return (string)solutionName;
+ }
+
+ private IProject GetProject(IVsHierarchy pHierProj)
+ {
+ object n;
+ pHierProj.GetProperty((uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_Name, out n);
+ var name = n as string;
+
+ Guid id;
+ vsSolution.GetGuidOfProject(pHierProj, out id);
+
+ return new BuildMonitor.Domain.Project { Name = name, Id = id };
+ }
+
+ #endregion
+
+ int IVsUpdateSolutionEvents.UpdateSolution_Begin(ref int pfCancelUpdate)
+ {
+ // This method is called when the entire solution starts to build.
+ monitor.SolutionBuildStart(solution);
+
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents2.UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel)
+ {
+ // This method is called when a specific project begins building.
+ var project = GetProject(pHierProj);
+ monitor.ProjectBuildStart(project);
+
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents2.UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel)
+ {
+ // This method is called when a specific project finishes building.
+ var project = GetProject(pHierProj);
+ monitor.ProjectBuildStop(project);
+
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand)
+ {
+ // This method is called when the entire solution is done building.
+ monitor.SolutionBuildStop();
+
+ return VSConstants.S_OK;
+ }
+
+ #region empty impl. of solution events interface
+
+ int IVsUpdateSolutionEvents2.UpdateSolution_StartUpdate(ref int pfCancelUpdate)
+ {
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents2.UpdateSolution_Cancel()
+ {
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents2.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy)
+ {
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents2.UpdateSolution_Begin(ref int pfCancelUpdate)
+ {
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents2.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand)
+ {
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents.UpdateSolution_StartUpdate(ref int pfCancelUpdate)
+ {
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents.UpdateSolution_Cancel()
+ {
+ return VSConstants.S_OK;
+ }
+
+ int IVsUpdateSolutionEvents.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy)
+ {
+ return VSConstants.S_OK;
+ }
+
+ #endregion
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ // Unadvise all events
+ if (sbm != null && updateSolutionEventsCookie != 0)
+ sbm.UnadviseUpdateSolutionEvents(updateSolutionEventsCookie);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/BuildMonitorPackage.csproj b/Backup/BuildMonitorPackage/BuildMonitorPackage.csproj
new file mode 100644
index 0000000..a358092
--- /dev/null
+++ b/Backup/BuildMonitorPackage/BuildMonitorPackage.csproj
@@ -0,0 +1,181 @@
+
+
+
+ 12.0
+ 11.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ 4.0
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {F1623C5E-614B-435B-B625-AC534EF3920F}
+ {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ Library
+ Properties
+ BuildMonitorPackage
+ BuildMonitorPackage
+ True
+ Key.snk
+ v4.5
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}
+ 8
+ 0
+ 0
+ primary
+ False
+ False
+
+
+ {26AD1324-4B7C-44BC-84F8-B86AED45729F}
+ 10
+ 0
+ 0
+ primary
+ False
+ False
+
+
+ {1A31287A-4D7D-413E-8E32-3B374931BD89}
+ 8
+ 0
+ 0
+ primary
+ False
+ False
+
+
+ {2CE2370E-D744-4936-A090-3FFFE667B0E1}
+ 9
+ 0
+ 0
+ primary
+ False
+ False
+
+
+ {1CBA492E-7263-47BB-87FE-639000619B15}
+ 8
+ 0
+ 0
+ primary
+ False
+ False
+
+
+ {00020430-0000-0000-C000-000000000046}
+ 2
+ 0
+ 0
+ primary
+ False
+ False
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ true
+ VSPackage
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+ Always
+ true
+
+
+
+
+
+ {700a9d28-e9de-428b-a8f2-b40baf4a3e87}
+ BuildMonitor
+
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/DataAdjusterWithLogging.cs b/Backup/BuildMonitorPackage/DataAdjusterWithLogging.cs
new file mode 100644
index 0000000..92a3789
--- /dev/null
+++ b/Backup/BuildMonitorPackage/DataAdjusterWithLogging.cs
@@ -0,0 +1,17 @@
+using BuildMonitor.Domain;
+using BuildMonitor.LocalData;
+
+namespace BuildMonitorPackage
+{
+ public class DataAdjusterWithLogging : DataAdjuster
+ {
+ public delegate void LogAction(string s, params object[] args);
+
+ public DataAdjusterWithLogging(IBuildRepository repository, LogAction log) : base(repository)
+ {
+ OnFoundInvalidData = data => log("-- Found invalid json-data in file: {0}", data.Source);
+ OnFixedInvalidData = () => log("-- Successfully converted invalid json data to valid");
+ OnCouldNotConvertData = e => log("-- Could not convert json-data : {0}", e.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/GlobalSuppressions.cs b/Backup/BuildMonitorPackage/GlobalSuppressions.cs
new file mode 100644
index 0000000..a893f9d
--- /dev/null
+++ b/Backup/BuildMonitorPackage/GlobalSuppressions.cs
@@ -0,0 +1,11 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project. Project-level
+// suppressions either have no target or are given a specific target
+// and scoped to a namespace, type, member, etc.
+//
+// To add a suppression to this file, right-click the message in the
+// Error List, point to "Suppress Message(s)", and click "In Project
+// Suppression File". You do not need to add suppressions to this
+// file manually.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]
diff --git a/Backup/BuildMonitorPackage/Guids.cs b/Backup/BuildMonitorPackage/Guids.cs
new file mode 100644
index 0000000..80b5b9c
--- /dev/null
+++ b/Backup/BuildMonitorPackage/Guids.cs
@@ -0,0 +1,15 @@
+// Guids.cs
+// MUST match guids.h
+
+using System;
+
+namespace BuildMonitorPackage
+{
+ static class GuidList
+ {
+ public const string guidBuildMonitorPackagePkgString = "d350a95e-5d09-4b5d-9075-0a5f7bb6b1dc";
+ public const string guidBuildMonitorPackageCmdSetString = "ec1339d5-c4f2-44de-9908-af4b79ce6f74";
+
+ public static readonly Guid guidBuildMonitorPackageCmdSet = new Guid(guidBuildMonitorPackageCmdSetString);
+ };
+}
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/Key.snk b/Backup/BuildMonitorPackage/Key.snk
new file mode 100644
index 0000000..0a721d8
Binary files /dev/null and b/Backup/BuildMonitorPackage/Key.snk differ
diff --git a/Backup/BuildMonitorPackage/Properties/AssemblyInfo.cs b/Backup/BuildMonitorPackage/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..0f12b42
--- /dev/null
+++ b/Backup/BuildMonitorPackage/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BuildMonitorPackage")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Daniel Vinntreus")]
+[assembly: AssemblyProduct("BuildMonitorPackage")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+[assembly: CLSCompliant(false)]
+[assembly: NeutralResourcesLanguage("en-US")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+
+
+
diff --git a/Backup/BuildMonitorPackage/Resources.Designer.cs b/Backup/BuildMonitorPackage/Resources.Designer.cs
new file mode 100644
index 0000000..abbe023
--- /dev/null
+++ b/Backup/BuildMonitorPackage/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.34003
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace BuildMonitorPackage {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BuildMonitorPackage.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Backup/BuildMonitorPackage/Resources.resx b/Backup/BuildMonitorPackage/Resources.resx
new file mode 100644
index 0000000..891c592
--- /dev/null
+++ b/Backup/BuildMonitorPackage/Resources.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/Resources/Package.ico b/Backup/BuildMonitorPackage/Resources/Package.ico
new file mode 100644
index 0000000..449296f
Binary files /dev/null and b/Backup/BuildMonitorPackage/Resources/Package.ico differ
diff --git a/Backup/BuildMonitorPackage/Settings.cs b/Backup/BuildMonitorPackage/Settings.cs
new file mode 100644
index 0000000..372846d
--- /dev/null
+++ b/Backup/BuildMonitorPackage/Settings.cs
@@ -0,0 +1,35 @@
+using System;
+using System.IO;
+
+// ReSharper disable InconsistentNaming
+namespace BuildMonitorPackage
+{
+ public static class OptionKey
+ {
+ public const string SolutionId = "bm_solution_id";
+ }
+
+ public static class Settings
+ {
+ public static string RepositoryPath = string.Format("{0}\\{1}\\{2}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ApplicationFolderName, JsonFileName);
+
+ private static string ApplicationFolder = string.Format("{0}\\{1}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ApplicationFolderName);
+
+ private const string ApplicationFolderName = "Build Monitor";
+ private const string JsonFileName = "buildtimes.json";
+
+
+ public static void CreateApplicationFolderIfNotExist()
+ {
+ if(!Directory.Exists(ApplicationFolder))
+ {
+ Directory.CreateDirectory(ApplicationFolder);
+ }
+ if(!File.Exists(RepositoryPath))
+ {
+ using(var f = File.Create(RepositoryPath)){}
+ }
+ }
+ }
+}
+// ReSharper restore InconsistentNaming
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/VSPackage.resx b/Backup/BuildMonitorPackage/VSPackage.resx
new file mode 100644
index 0000000..bba6465
--- /dev/null
+++ b/Backup/BuildMonitorPackage/VSPackage.resx
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ BuildMonitorPackage
+
+
+ Monitor builds in Visual Studio.
+
+
+ Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/license.txt b/Backup/BuildMonitorPackage/license.txt
new file mode 100644
index 0000000..426c72c
--- /dev/null
+++ b/Backup/BuildMonitorPackage/license.txt
@@ -0,0 +1,13 @@
+Copyright 2012 Daniel Vinntreus
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/Backup/BuildMonitorPackage/source.extension.vsixmanifest b/Backup/BuildMonitorPackage/source.extension.vsixmanifest
new file mode 100644
index 0000000..b232ae7
--- /dev/null
+++ b/Backup/BuildMonitorPackage/source.extension.vsixmanifest
@@ -0,0 +1,27 @@
+
+
+
+
+ Build Monitor
+ Measure how long it takes to build your solution and individual projects in Visual Studio.
+ http://visualstudiogallery.msdn.microsoft.com/b0c87e47-f4ee-4935-9a59-f2c81ce692ab
+ license.txt
+ Build, Monitor, Build Monitor, Build times
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj b/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj
index 9c7cd44..fb9120e 100644
--- a/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj
+++ b/BuildMonitor.UnitTests/BuildMonitor.UnitTests.csproj
@@ -41,6 +41,8 @@
+
+
diff --git a/BuildMonitor.UnitTests/LocalData/AnalyseBuildTimesTests.cs b/BuildMonitor.UnitTests/LocalData/AnalyseBuildTimesTests.cs
new file mode 100644
index 0000000..d4f1429
--- /dev/null
+++ b/BuildMonitor.UnitTests/LocalData/AnalyseBuildTimesTests.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using BuildMonitor.LocalData;
+using System.Collections;
+
+namespace BuildMonitor.UnitTests.LocalData
+{
+ internal struct SolutionBuildTime
+ {
+ public string Solution { get; set; }
+ public DateTime BuildDateTime { get; set; }
+ public int BuildTimeInMilliseconds { get; set; }
+ }
+
+ [TestFixture]
+ public class AnalyseBuildTimesTests
+ {
+ [Test]
+ public void IgnoreNullSolutionNames()
+ {
+ const string json =
+@"[{
+ 'Start': '2016-01-22T18:53:33.6172723+00:00',
+ 'Time': 2,
+ 'Solution': {
+ }
+}]";
+
+ var buildTimes = new AnalyseBuildTimes().Calculate(json);
+
+ Assert.AreEqual(0, buildTimes.AvailableSolutions.Count());
+ }
+
+ [Test]
+ public void AvailableMonths()
+ {
+ var solutionbuilds = CreateBuilds(
+ CreateBuild(buildDateTime: JANUARY1())
+ , CreateBuild(buildDateTime: JANUARY31())
+ , CreateBuild(buildDateTime: MARCH())
+ );
+
+ var json = CreateBuildsJSON(solutionbuilds);
+
+ var buildTimes = new AnalyseBuildTimes().Calculate(json);
+
+ var expected = CreateSolutionMonths(CreateSolutionMonth(JANUARY1()), CreateSolutionMonth(MARCH()));
+
+ CollectionAssert.AreEqual(expected, buildTimes.AvailableMonths);
+ }
+
+ [Test]
+ public void AvailableSolutions()
+ {
+ const string CEDD = "Cedd";
+ const string BUILDMONITOR = "BuildMonitor";
+
+ var solutionbuilds = CreateBuilds(
+ CreateBuild(solution: CEDD)
+ , CreateBuild(solution: BUILDMONITOR)
+ , CreateBuild(solution: CEDD)
+ );
+
+ var json = CreateBuildsJSON(solutionbuilds);
+
+ var buildTimes = new AnalyseBuildTimes().Calculate(json);
+
+ var expected = new List() { CEDD, BUILDMONITOR };
+
+ CollectionAssert.AreEqual(expected, buildTimes.AvailableSolutions);
+ }
+
+ [TestCase(19366)]
+ [TestCase(1)]
+ [TestCase(395)]
+ public void OneBuildTimeTotal(int singleBuildTimeInMilliseconds)
+ {
+ var json = CreateBuildsJSON(CreateBuildJSON(CreateBuild(buildTimeInMilliseconds: singleBuildTimeInMilliseconds)));
+
+ var buildTimes = new AnalyseBuildTimes().Calculate(json);
+
+ Assert.That(buildTimes.Total == TimeSpan.FromMilliseconds(singleBuildTimeInMilliseconds));
+ }
+
+ [TestCase(1, 2)]
+ [TestCase(21341, 123642, 21389734)]
+ [TestCase(543, 0, 34534567)]
+ public void MultipleBuildsSameSolution(params int[] buildTimeInMilliseconds)
+ {
+ var json = CreateBuildsJSON(buildTimeInMilliseconds.Select(b => CreateBuild(buildTimeInMilliseconds: b)));
+
+ var buildTimes = new AnalyseBuildTimes().Calculate(json);
+
+ Assert.That(buildTimes.Total == TimeSpan.FromMilliseconds(buildTimeInMilliseconds.Sum()));
+ }
+
+ // Add a testcasesource here if more test cases are wanted
+ [Test]
+ public void MultipleBuildsMultipleSolutions()
+ {
+ var solutionbuilds = CreateBuilds(
+ CreateBuild("BuildMonitor", 1)
+ , CreateBuild("Cedd", 2)
+ , CreateBuild("BuildMonitor", 982734789)
+ , CreateBuild("Cedd", 83468)
+ );
+
+ var json = CreateBuildsJSON(solutionbuilds);
+
+ var buildTimes = new AnalyseBuildTimes().Calculate(json);
+
+ foreach (var solution in solutionbuilds.Select(s => s.Solution).Distinct())
+ Assert.That(buildTimes.Solution(solution) == TimeSpan.FromMilliseconds(solutionbuilds.Where(s => s.Solution == solution).Sum(s => s.BuildTimeInMilliseconds)));
+ }
+
+ // Add a testcasesource here if more test cases are wanted
+ [Test]
+ public void MultipleBuildsMultipleMonthsMultipleSolutions()
+ {
+ var solutionbuilds = CreateBuilds(
+ CreateBuild("BuildMonitor", 1, JANUARY1())
+ , CreateBuild("Cedd", 2, JANUARY31())
+ , CreateBuild("BuildMonitor", 982734789, FEBRUARY())
+ , CreateBuild("Cedd", 83468, MARCH())
+ );
+
+ var json = CreateBuildsJSON(solutionbuilds);
+
+ //act
+ var buildTimes = new AnalyseBuildTimes().Calculate(json);
+
+ // assert
+ var solutionMonths = solutionbuilds.Select(s => new SolutionMonth(solution: s.Solution, month: s.BuildDateTime.Month, year: s.BuildDateTime.Year)).Distinct();
+ foreach (var solutionMonth in solutionMonths)
+ Assert.That(buildTimes.SolutionMonth(solutionMonth.Solution, solutionMonth.Month, solutionMonth.Year) == TimeSpan.FromMilliseconds(solutionbuilds.Where(s => s.Solution == solutionMonth.Solution && s.BuildDateTime.Month == solutionMonth.Month && s.BuildDateTime.Year == solutionMonth.Year).Sum(s => s.BuildTimeInMilliseconds)));
+ }
+
+ private static string CreateBuildsJSON(string build)
+ {
+ return CreateBuildsJSON(new List() { build });
+ }
+
+ private static string CreateBuildsJSON(IEnumerable builds)
+ {
+ return "[" + string.Join("\n,\n", builds) + "]";
+ }
+
+ private static string CreateBuildsJSON(IEnumerable solutionbuilds)
+ {
+ return CreateBuildsJSON(solutionbuilds.Select(b => CreateBuildJSON(b)));
+ }
+
+ private static string CreateBuildJSON(SolutionBuildTime build)
+ {
+ return @"{
+ 'Start': '" + build.BuildDateTime.ToString() + @"',
+ 'Time': " + build.BuildTimeInMilliseconds.ToString() + @",
+ 'Solution': {
+ 'Name': '" + build.Solution + @"'
+ },
+ 'Projects': [{
+ 'Start': '2016-01-21T18:53:33.6172723+00:00',
+ 'Time': 2,
+ 'Project': {
+ 'Name': 'blah',
+ 'Id': '700a9d28-e9de-428b-a8f2-b40baf4a3e87'
+ }
+ },
+ {
+ 'Start': '2016-01-22T18:53:33.6172723+00:00',
+ 'Time': 4,
+ 'Project': {
+ 'Name': 'blah2',
+ 'Id': '700a9d28-e9de-428b-a8f2-b40baf4a3e88'
+ }
+ }]
+}";
+ }
+
+ private static IEnumerable CreateBuilds(params SolutionBuildTime[] solutionBuildTimes)
+ {
+ return solutionBuildTimes;
+ }
+
+ private static SolutionBuildTime CreateBuild(string solution = "", int buildTimeInMilliseconds = 0, DateTime buildDateTime = new DateTime())
+ {
+ return new SolutionBuildTime() { Solution = solution, BuildTimeInMilliseconds = buildTimeInMilliseconds, BuildDateTime = buildDateTime };
+ }
+
+ private IEnumerable CreateSolutionMonths(params SolutionMonth[] solutionMonths)
+ {
+ return solutionMonths;
+ }
+
+ private SolutionMonth CreateSolutionMonth(DateTime dateTime)
+ {
+ return new SolutionMonth("", dateTime.Year, dateTime.Month);
+ }
+
+ private DateTime MARCH()
+ {
+ return new DateTime(2000, 3, 1);
+ }
+
+ private DateTime FEBRUARY()
+ {
+ return new DateTime(2000, 2, 1);
+ }
+
+ private DateTime JANUARY1()
+ {
+ return new DateTime(2000, 1, 1);
+ }
+
+ private DateTime JANUARY31()
+ {
+ return new DateTime(2000, 1, 31);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/BuildMonitor.UnitTests/LocalData/BuildTimesTests.cs b/BuildMonitor.UnitTests/LocalData/BuildTimesTests.cs
new file mode 100644
index 0000000..0afe816
--- /dev/null
+++ b/BuildMonitor.UnitTests/LocalData/BuildTimesTests.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using BuildMonitor.LocalData;
+
+namespace BuildMonitor.UnitTests.LocalData
+{
+ [TestFixture]
+ public class BuildTimesTests
+ {
+ [Test]
+ public void ReturnZeroForMissingMonth()
+ {
+ var buildTimes = new BuildTimes(TimeSpan.FromSeconds(0), new Dictionary(), new Dictionary());
+
+ Assert.AreEqual(TimeSpan.FromSeconds(0), buildTimes.SolutionMonth("", 0, 0));
+ }
+ }
+}
diff --git a/BuildMonitor.UnitTests/packages.config b/BuildMonitor.UnitTests/packages.config
index 62adf5a..09bcfd1 100644
--- a/BuildMonitor.UnitTests/packages.config
+++ b/BuildMonitor.UnitTests/packages.config
@@ -1,4 +1,5 @@
+
\ No newline at end of file
diff --git a/BuildMonitor.sln b/BuildMonitor.sln
index 18202dc..c1c3d94 100644
--- a/BuildMonitor.sln
+++ b/BuildMonitor.sln
@@ -1,18 +1,20 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
+# Visual Studio 14
+VisualStudioVersion = 14.0.23107.0
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{4FDB4354-8389-4675-ACED-6CA6DF570561}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{68E63FC4-249A-4EFE-A8D8-5154DA8E3415}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildMonitor", "BuildMonitor\BuildMonitor.csproj", "{700A9D28-E9DE-428B-A8F2-B40BAF4A3E87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildMonitor.UnitTests", "BuildMonitor.UnitTests\BuildMonitor.UnitTests.csproj", "{4107248F-F2FC-4152-816B-E8633C4913F5}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{68E63FC4-249A-4EFE-A8D8-5154DA8E3415}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildMonitorPackage", "BuildMonitorPackage\BuildMonitorPackage.csproj", "{F1623C5E-614B-435B-B625-AC534EF3920F}"
EndProject
Global
diff --git a/BuildMonitor/BuildMonitor.csproj b/BuildMonitor/BuildMonitor.csproj
index f8916ed..7e3ef91 100644
--- a/BuildMonitor/BuildMonitor.csproj
+++ b/BuildMonitor/BuildMonitor.csproj
@@ -35,9 +35,10 @@
true
- key.pfx
+ cedd.snk
+
..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
@@ -62,15 +63,22 @@
+
+
+
+
+
+
+
diff --git a/BuildMonitor/LocalData/AnalyseBuildTimes.cs b/BuildMonitor/LocalData/AnalyseBuildTimes.cs
new file mode 100644
index 0000000..30c3e5f
--- /dev/null
+++ b/BuildMonitor/LocalData/AnalyseBuildTimes.cs
@@ -0,0 +1,53 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BuildMonitor.LocalData
+{
+ public class AnalyseBuildTimes
+ {
+ public IBuildTimes Calculate(string json)
+ {
+ var solutionMonths = new Dictionary();
+ var solutions = new Dictionary();
+ int total = 0;
+
+ foreach (var jsonSolutionBuildTime in ReadJSON(json))
+ {
+ UpdateSolutionMonths(jsonSolutionBuildTime, solutionMonths);
+ UpdateSolutions(jsonSolutionBuildTime, solutions);
+ total += jsonSolutionBuildTime.Time;
+ }
+
+ return new BuildTimes(TimeSpan.FromMilliseconds(total), solutions, solutionMonths);
+ }
+
+ private static IEnumerable ReadJSON(string json)
+ {
+ var jsonSerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
+ jsonSerializerSettings.Converters.Add(new IsoDateTimeConverter());
+ var jsonSolutionBuildTimes = JsonConvert.DeserializeObject>(json, jsonSerializerSettings);
+ return jsonSolutionBuildTimes.Where(b => b.Name != null);
+ }
+
+ private void UpdateSolutions(JSONSolutionTimes jsonSolutionBuildTime, Dictionary solutions)
+ {
+ if (solutions.ContainsKey(jsonSolutionBuildTime.Name))
+ solutions[jsonSolutionBuildTime.Name] = solutions[jsonSolutionBuildTime.Name] + TimeSpan.FromMilliseconds(jsonSolutionBuildTime.Time);
+ else
+ solutions.Add(jsonSolutionBuildTime.Name, TimeSpan.FromMilliseconds(jsonSolutionBuildTime.Time));
+ }
+
+ private void UpdateSolutionMonths(JSONSolutionTimes jsonSolutionBuildTime, Dictionary solutionMonths)
+ {
+ var solutionMonth = new SolutionMonth( solution: jsonSolutionBuildTime.Name, month: jsonSolutionBuildTime.Start.Month, year: jsonSolutionBuildTime.Start.Year );
+
+ if (solutionMonths.ContainsKey(solutionMonth))
+ solutionMonths[solutionMonth] = solutionMonths[solutionMonth] + TimeSpan.FromMilliseconds(jsonSolutionBuildTime.Time);
+ else
+ solutionMonths.Add(solutionMonth, TimeSpan.FromMilliseconds(jsonSolutionBuildTime.Time));
+ }
+ }
+}
\ No newline at end of file
diff --git a/BuildMonitor/LocalData/BuildTimes.cs b/BuildMonitor/LocalData/BuildTimes.cs
new file mode 100644
index 0000000..26cc01c
--- /dev/null
+++ b/BuildMonitor/LocalData/BuildTimes.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BuildMonitor.LocalData
+{
+ public class BuildTimes : IBuildTimes
+ {
+ public BuildTimes(TimeSpan total, Dictionary solutions, Dictionary solutionMonths)
+ {
+ Total = total;
+ this.solutions = solutions;
+ this.solutionMonths = solutionMonths;
+ }
+
+ public TimeSpan Total { get; protected set; }
+
+ public IEnumerable AvailableMonths
+ {
+ get
+ {
+ return this.solutionMonths.Keys;
+ }
+ }
+
+ public IEnumerable AvailableSolutions
+ {
+ get
+ {
+ return this.solutions.Keys;
+ }
+ }
+
+ public TimeSpan SolutionMonth(string solution, int month, int year)
+ {
+ var solutionMonth = new SolutionMonth(solution: solution, month: month, year: year);
+
+ return this.solutionMonths.ContainsKey(solutionMonth) ? this.solutionMonths[solutionMonth] : TimeSpan.FromSeconds(0);
+ }
+
+ public TimeSpan Solution(string solution)
+ {
+ return this.solutions[solution];
+ }
+
+ // this is for the UI, in MVC land it would be a ViewModel, but that seems a bit excessive here.
+ public IEnumerable SolutionMonthTable()
+ {
+ var table = new List();
+
+ foreach (var solution in AvailableSolutions)
+ {
+ dynamic row = new ExpandoObject();
+ var p = row as IDictionary;
+ p["Solution"] = solution;
+ foreach (var month in AvailableMonths)
+ {
+ var buildTime = SolutionMonth(solution, month.Month, month.Year);
+
+ if (buildTime == TimeSpan.FromSeconds(0))
+ p[FormatMonth(month)] = "";
+ else
+ p[FormatMonth(month)] = $@"{buildTime:hh\:mm\:ss}";
+ }
+
+ table.Add(row);
+ }
+
+ return table;
+ }
+
+ private static string FormatMonth(SolutionMonth month)
+ {
+ return $"{new DateTime(2000, month.Month, 1):MMM} {month.Year}";
+ }
+
+ private Dictionary solutionMonths;
+ private Dictionary solutions;
+ }
+}
diff --git a/BuildMonitor/LocalData/IBuildTimes.cs b/BuildMonitor/LocalData/IBuildTimes.cs
new file mode 100644
index 0000000..5d9cdad
--- /dev/null
+++ b/BuildMonitor/LocalData/IBuildTimes.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BuildMonitor.LocalData
+{
+ public interface IBuildTimes
+ {
+ IEnumerable SolutionMonthTable();
+
+ // these are only used in testing, but I can't think of a good way to take them out of the interface and keep the rest of the code ok.
+ TimeSpan Total { get; }
+ IEnumerable AvailableMonths { get; }
+ IEnumerable AvailableSolutions { get; }
+ TimeSpan SolutionMonth(string solution, int month, int year);
+ TimeSpan Solution(string solution);
+ }
+}
diff --git a/BuildMonitor/LocalData/JSONSolutionName.cs b/BuildMonitor/LocalData/JSONSolutionName.cs
new file mode 100644
index 0000000..b9689ae
--- /dev/null
+++ b/BuildMonitor/LocalData/JSONSolutionName.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BuildMonitor.LocalData
+{
+ internal struct JSONSolutionName
+ {
+ public string Name;
+ }
+}
diff --git a/BuildMonitor/LocalData/JSONSolutionTimes.cs b/BuildMonitor/LocalData/JSONSolutionTimes.cs
new file mode 100644
index 0000000..8a93bad
--- /dev/null
+++ b/BuildMonitor/LocalData/JSONSolutionTimes.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BuildMonitor.LocalData
+{
+ internal struct JSONSolutionTimes
+ {
+ public int Time;
+ public DateTime Start;
+ public JSONSolutionName Solution;
+ public string Name { get { return Solution.Name; } }
+ }
+}
diff --git a/BuildMonitor/LocalData/SolutionMonth.cs b/BuildMonitor/LocalData/SolutionMonth.cs
new file mode 100644
index 0000000..e5d2c64
--- /dev/null
+++ b/BuildMonitor/LocalData/SolutionMonth.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BuildMonitor.LocalData
+{
+ public class SolutionMonth
+ {
+ private readonly string solution;
+ private readonly int year;
+ private readonly int month;
+
+ public SolutionMonth(string solution = "", int year = 0, int month = 0)
+ {
+ this.solution = solution ?? "";
+ this.year = year;
+ this.month = month;
+ }
+
+ public string Solution { get { return this.solution; } }
+ public int Year { get { return this.year; } }
+ public int Month { get { return this.month; } }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is SolutionMonth))
+ return base.Equals(obj);
+
+ var other = (obj as SolutionMonth);
+
+ return (Solution == other.Solution && Year == other.Year && Month == other.Month);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ int hash = 17;
+ // Suitable nullity checks etc, of course :)
+ hash = hash * 23 + Solution.GetHashCode();
+ hash = hash * 23 + Year.GetHashCode();
+ hash = hash * 23 + Month.GetHashCode();
+ return hash;
+ }
+ }
+ }
+}
diff --git a/BuildMonitor/Properties/AssemblyInfo.cs b/BuildMonitor/Properties/AssemblyInfo.cs
index c6b0fac..ab4bb43 100644
--- a/BuildMonitor/Properties/AssemblyInfo.cs
+++ b/BuildMonitor/Properties/AssemblyInfo.cs
@@ -10,7 +10,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BuildMonitor")]
-[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("1.5.0.0")]
+[assembly: AssemblyFileVersion("1.5.0.0")]
diff --git a/BuildMonitor/cedd.snk b/BuildMonitor/cedd.snk
new file mode 100644
index 0000000..2a530fd
Binary files /dev/null and b/BuildMonitor/cedd.snk differ
diff --git a/BuildMonitorPackage/AnalyseBuildTimes.xaml b/BuildMonitorPackage/AnalyseBuildTimes.xaml
new file mode 100644
index 0000000..712c8e3
--- /dev/null
+++ b/BuildMonitorPackage/AnalyseBuildTimes.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/BuildMonitorPackage/AnalyseBuildTimes.xaml.cs b/BuildMonitorPackage/AnalyseBuildTimes.xaml.cs
new file mode 100644
index 0000000..40e6021
--- /dev/null
+++ b/BuildMonitorPackage/AnalyseBuildTimes.xaml.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace BuildMonitorPackage
+{
+ ///
+ /// Interaction logic for AnalyseBuildTimes.xaml
+ ///
+ public partial class AnalyseBuildTimes : Window
+ {
+ public AnalyseBuildTimes()
+ {
+ InitializeComponent();
+ }
+
+ public AnalyseBuildTimes(IEnumerable solutionMonthTable) : this()
+ {
+ // you would have thought it would be easier to show a grid of data in 2016 ...
+ SolutionMonthDataGrid.ItemsSource = solutionMonthTable;
+
+ var rows = solutionMonthTable.OfType>();
+ var columns = rows.SelectMany(d => d.Keys).Distinct(StringComparer.OrdinalIgnoreCase);
+
+ foreach (string text in columns)
+ {
+ // now set up a column and binding for each property
+ var column = new DataGridTextColumn
+ {
+ Header = text,
+ Binding = new Binding(text)
+ };
+
+ SolutionMonthDataGrid.Columns.Add(column);
+ }
+
+ }
+
+ private void button_Click(object sender, RoutedEventArgs e)
+ {
+ this.Close();
+ }
+ }
+}
diff --git a/BuildMonitorPackage/AnalyseBuildTimesCommand.cs b/BuildMonitorPackage/AnalyseBuildTimesCommand.cs
new file mode 100644
index 0000000..307e86e
--- /dev/null
+++ b/BuildMonitorPackage/AnalyseBuildTimesCommand.cs
@@ -0,0 +1,109 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) RESGROUP. All rights reserved.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.ComponentModel.Design;
+using System.Globalization;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using System.IO;
+using BuildMonitor.LocalData;
+using System.Data;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace BuildMonitorPackage
+{
+ ///
+ /// Command handler
+ ///
+ internal sealed class AnalyseBuildTimesCommand
+ {
+ ///
+ /// Command ID.
+ ///
+ public const int CommandId = 4130;
+
+ ///
+ /// Command menu group (command set GUID).
+ ///
+ public static readonly Guid CommandSet = new Guid("6dc18c9d-0c60-4346-93be-acb15e591bf5");
+
+ ///
+ /// VS Package that provides this command, not null.
+ ///
+ private readonly Package package;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Adds our command handlers for menu (commands must exist in the command table file)
+ ///
+ /// Owner package, not null.
+ private AnalyseBuildTimesCommand(Package package)
+ {
+ if (package == null)
+ {
+ throw new ArgumentNullException("package");
+ }
+
+ this.package = package;
+
+ OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
+ if (commandService != null)
+ {
+ var menuCommandID = new CommandID(CommandSet, CommandId);
+ var menuItem = new MenuCommand(this.MenuItemCallback, menuCommandID);
+ commandService.AddCommand(menuItem);
+ }
+ }
+
+ ///
+ /// Gets the instance of the command.
+ ///
+ public static AnalyseBuildTimesCommand Instance
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the service provider from the owner package.
+ ///
+ private IServiceProvider ServiceProvider
+ {
+ get
+ {
+ return this.package;
+ }
+ }
+
+ ///
+ /// Initializes the singleton instance of the command.
+ ///
+ /// Owner package, not null.
+ public static void Initialize(Package package)
+ {
+ Instance = new AnalyseBuildTimesCommand(package);
+ }
+
+ ///
+ /// This function is the callback used to execute the command when the menu item is clicked.
+ /// See the constructor to see how the menu item is associated with this function using
+ /// OleMenuCommandService service and MenuCommand class.
+ ///
+ /// Event sender.
+ /// Event args.
+ private void MenuItemCallback(object sender, EventArgs e)
+ {
+ var form = new AnalyseBuildTimes(new BuildMonitor.LocalData.AnalyseBuildTimes().Calculate(File.ReadAllText(Settings.RepositoryPath)).SolutionMonthTable());
+
+ form.ShowDialog();
+ }
+ }
+}
diff --git a/BuildMonitorPackage/BuildMonitorPackage.cs b/BuildMonitorPackage/BuildMonitorPackage.cs
index e3bb449..fe44924 100644
--- a/BuildMonitorPackage/BuildMonitorPackage.cs
+++ b/BuildMonitorPackage/BuildMonitorPackage.cs
@@ -9,6 +9,10 @@
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using Constants = EnvDTE.Constants;
+using System.Data.SqlClient;
+using System.Data;
+using System.Security.Principal;
+using System.Threading.Tasks;
namespace BuildMonitorPackage
{
@@ -16,6 +20,7 @@ namespace BuildMonitorPackage
[Guid(GuidList.guidBuildMonitorPackagePkgString)]
[PackageRegistration(UseManagedResourcesOnly = true)]
[ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}")]
+ [ProvideMenuResource("Menus.ctmenu", 1)]
sealed class BuildMonitorPackage : Package, IVsUpdateSolutionEvents2
{
private DTE dte;
@@ -68,6 +73,7 @@ protected override void Initialize()
};
monitor.ProjectBuildFinished = b => PrintLine(" - {0}\t-- {1} --", b.MillisecondsElapsed.ToTime(), b.ProjectName);
+ AnalyseBuildTimesCommand.Initialize(this);
}
private void Solution_Opened()
diff --git a/BuildMonitorPackage/BuildMonitorPackage.csproj b/BuildMonitorPackage/BuildMonitorPackage.csproj
index a358092..202f497 100644
--- a/BuildMonitorPackage/BuildMonitorPackage.csproj
+++ b/BuildMonitorPackage/BuildMonitorPackage.csproj
@@ -1,7 +1,8 @@
+
- 12.0
+ 14.0
11.0
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
@@ -9,6 +10,8 @@
4.0
+
+
@@ -44,26 +47,94 @@
true
+
-
-
-
-
-
-
- true
-
-
-
-
-
+
+ ..\packages\Microsoft.VisualStudio.Imaging.14.0.23107\lib\net45\Microsoft.VisualStudio.Imaging.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6070\lib\Microsoft.VisualStudio.OLE.Interop.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.14.0.14.0.23107\lib\Microsoft.VisualStudio.Shell.14.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.Immutable.10.0.10.0.30319\lib\net40\Microsoft.VisualStudio.Shell.Immutable.10.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.Immutable.11.0.11.0.50727\lib\net45\Microsoft.VisualStudio.Shell.Immutable.11.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.Immutable.12.0.12.0.21003\lib\net45\Microsoft.VisualStudio.Shell.Immutable.12.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.Immutable.14.0.14.0.23107\lib\net45\Microsoft.VisualStudio.Shell.Immutable.14.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6071\lib\Microsoft.VisualStudio.Shell.Interop.dll
+ True
+
+
+ True
+ ..\packages\Microsoft.VisualStudio.Shell.Interop.10.0.10.0.30319\lib\Microsoft.VisualStudio.Shell.Interop.10.0.dll
+ True
+
+
+ True
+ ..\packages\Microsoft.VisualStudio.Shell.Interop.11.0.11.0.61030\lib\Microsoft.VisualStudio.Shell.Interop.11.0.dll
+ True
+
+
+ True
+ ..\packages\Microsoft.VisualStudio.Shell.Interop.12.0.12.0.30110\lib\Microsoft.VisualStudio.Shell.Interop.12.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.Shell.Interop.8.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Shell.Interop.9.0.9.0.30729\lib\Microsoft.VisualStudio.Shell.Interop.9.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.TextManager.Interop.8.0.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Threading.14.0.50702\lib\net45\Microsoft.VisualStudio.Threading.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Utilities.14.0.23107\lib\net45\Microsoft.VisualStudio.Utilities.dll
+ True
+
+
+ ..\packages\Microsoft.VisualStudio.Validation.14.0.50702\lib\net45\Microsoft.VisualStudio.Validation.dll
+ True
+
+
+
+
+
@@ -122,6 +193,10 @@
+
+ AnalyseBuildTimes.xaml
+
+
@@ -146,6 +221,7 @@
+
Designer
@@ -154,10 +230,15 @@
+
+ Menus.ctmenu
+ Designer
+
Always
true
+
@@ -166,11 +247,25 @@
BuildMonitor
+
+
+ Designer
+ MSBuild:Compile
+
+
true
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BuildMonitorPackage/Properties/AssemblyInfo.cs b/BuildMonitorPackage/Properties/AssemblyInfo.cs
index 0f12b42..41bc1a1 100644
--- a/BuildMonitorPackage/Properties/AssemblyInfo.cs
+++ b/BuildMonitorPackage/Properties/AssemblyInfo.cs
@@ -29,8 +29,8 @@
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("1.5.0.0")]
+[assembly: AssemblyFileVersion("1.5.0.0")]
diff --git a/BuildMonitorPackage/Resources.Designer.cs b/BuildMonitorPackage/Resources.Designer.cs
index abbe023..c61ed81 100644
--- a/BuildMonitorPackage/Resources.Designer.cs
+++ b/BuildMonitorPackage/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.34003
+// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
diff --git a/BuildMonitorPackage/Resources/AnalyseBuildTimesCommand.png b/BuildMonitorPackage/Resources/AnalyseBuildTimesCommand.png
new file mode 100644
index 0000000..b22d975
Binary files /dev/null and b/BuildMonitorPackage/Resources/AnalyseBuildTimesCommand.png differ
diff --git a/BuildMonitorPackage/packages.config b/BuildMonitorPackage/packages.config
new file mode 100644
index 0000000..ff538df
--- /dev/null
+++ b/BuildMonitorPackage/packages.config
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BuildMonitorPackage/source.extension.vsixmanifest b/BuildMonitorPackage/source.extension.vsixmanifest
index a09d9e9..475b36e 100644
--- a/BuildMonitorPackage/source.extension.vsixmanifest
+++ b/BuildMonitorPackage/source.extension.vsixmanifest
@@ -1,7 +1,7 @@
-
+
Build Monitor
Measure how long it takes to build your solution and individual projects in Visual Studio.
http://visualstudiogallery.msdn.microsoft.com/b0c87e47-f4ee-4935-9a59-f2c81ce692ab
diff --git a/BuildMonitorPackage/source.extension.vsixmanifest.orig b/BuildMonitorPackage/source.extension.vsixmanifest.orig
new file mode 100644
index 0000000..1c20c74
--- /dev/null
+++ b/BuildMonitorPackage/source.extension.vsixmanifest.orig
@@ -0,0 +1,31 @@
+
+
+
+<<<<<<< HEAD
+
+=======
+
+>>>>>>> daniel/master
+ Build Monitor
+ Measure how long it takes to build your solution and individual projects in Visual Studio.
+ http://visualstudiogallery.msdn.microsoft.com/b0c87e47-f4ee-4935-9a59-f2c81ce692ab
+ license.txt
+ Build, Monitor, Build Monitor, Build times
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UpgradeLog.htm b/UpgradeLog.htm
new file mode 100644
index 0000000..dbe4668
Binary files /dev/null and b/UpgradeLog.htm differ