mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Add metrics reporting, some work on the app
This commit is contained in:
parent
226af90698
commit
f8db7d4386
9
Wabbajack.App.Wpf/App.xaml
Normal file
9
Wabbajack.App.Wpf/App.xaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Application x:Class="Wabbajack.App.Wpf.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:Wabbajack.App.Wpf"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
17
Wabbajack.App.Wpf/App.xaml.cs
Normal file
17
Wabbajack.App.Wpf/App.xaml.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Wabbajack.App.Wpf
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
10
Wabbajack.App.Wpf/AssemblyInfo.cs
Normal file
10
Wabbajack.App.Wpf/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
12
Wabbajack.App.Wpf/MainWindow.xaml
Normal file
12
Wabbajack.App.Wpf/MainWindow.xaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<Window x:Class="Wabbajack.App.Wpf.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:Wabbajack.App.Wpf"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="MainWindow" Height="450" Width="800">
|
||||||
|
<Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
28
Wabbajack.App.Wpf/MainWindow.xaml.cs
Normal file
28
Wabbajack.App.Wpf/MainWindow.xaml.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
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.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace Wabbajack.App.Wpf
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
Wabbajack.App.Wpf/Support/ViewModel.cs
Normal file
34
Wabbajack.App.Wpf/Support/ViewModel.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Wabbajack.App.Wpf.Support;
|
||||||
|
|
||||||
|
public class ViewModel : ReactiveObject, IDisposable
|
||||||
|
{
|
||||||
|
private readonly Lazy<CompositeDisposable> _compositeDisposable = new();
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public CompositeDisposable CompositeDisposable => _compositeDisposable.Value;
|
||||||
|
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
if (_compositeDisposable.IsValueCreated)
|
||||||
|
{
|
||||||
|
_compositeDisposable.Value.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void RaiseAndSetIfChanged<T>(
|
||||||
|
ref T item,
|
||||||
|
T newItem,
|
||||||
|
[CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(item, newItem)) return;
|
||||||
|
item = newItem;
|
||||||
|
this.RaisePropertyChanged(propertyName);
|
||||||
|
}
|
||||||
|
}
|
19
Wabbajack.App.Wpf/Wabbajack.App.Wpf.csproj
Normal file
19
Wabbajack.App.Wpf/Wabbajack.App.Wpf.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net6.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CefNet.Wpf" Version="97.0.21326.1706" />
|
||||||
|
<PackageReference Include="ReactiveUI" Version="16.3.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Screens" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.CommandLine;
|
using System.CommandLine;
|
||||||
using System.CommandLine.Invocation;
|
using System.CommandLine.Invocation;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
@ -36,9 +37,18 @@ public class GenerateMetricsReports : IVerb
|
|||||||
private async Task<int> Run(AbsolutePath output)
|
private async Task<int> Run(AbsolutePath output)
|
||||||
{
|
{
|
||||||
var subjects = await GetMetrics("one day ago", "now", "finish_install")
|
var subjects = await GetMetrics("one day ago", "now", "finish_install")
|
||||||
|
.Where(d => d.Action != d.Subject)
|
||||||
.Select(async d => d.GroupingSubject)
|
.Select(async d => d.GroupingSubject)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
||||||
|
var allTime = await GetMetrics("10 days ago", "now", "finish_install")
|
||||||
|
.Where(d => subjects.Contains(d.GroupingSubject))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var grouped = allTime.GroupBy(g => (g.Timestamp.ToString("yyyy-MM-dd"), g.GroupingSubject)).ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
Wabbajack.Common/DateTimeExtensions.cs
Normal file
12
Wabbajack.Common/DateTimeExtensions.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Wabbajack.Common;
|
||||||
|
|
||||||
|
public static class DateTimeExtensions
|
||||||
|
{
|
||||||
|
public static DateTime TruncateToDate(this DateTime d)
|
||||||
|
{
|
||||||
|
return new DateTime(d.Year, d.Month, d.Day);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,13 +1,11 @@
|
|||||||
using System;
|
using System.Reflection;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Chronic.Core;
|
using Chronic.Core;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Nettle;
|
using Nettle;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.DTOs.ServerResponses;
|
||||||
using Wabbajack.Server.DataModels;
|
using Wabbajack.Server.DataModels;
|
||||||
using Wabbajack.Server.DTOs;
|
using Wabbajack.Server.DTOs;
|
||||||
|
|
||||||
@ -86,8 +84,9 @@ public class MetricsController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] EOL = {(byte)'\n'};
|
private static byte[] EOL = {(byte)'\n'};
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("report")]
|
[Route("dump")]
|
||||||
public async Task GetMetrics([FromQuery] string action, [FromQuery] string from, [FromQuery] string? to)
|
public async Task GetMetrics([FromQuery] string action, [FromQuery] string from, [FromQuery] string? to)
|
||||||
{
|
{
|
||||||
var parser = new Parser();
|
var parser = new Parser();
|
||||||
@ -107,6 +106,60 @@ public class MetricsController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("report")]
|
||||||
|
public async Task GetReport([FromQuery] string action, [FromQuery] string from, [FromQuery] string? to)
|
||||||
|
{
|
||||||
|
var parser = new Parser();
|
||||||
|
|
||||||
|
to ??= "now";
|
||||||
|
|
||||||
|
var toDate = parser.Parse(to).Start!.Value.TruncateToDate();
|
||||||
|
|
||||||
|
var groupFilterStart = parser.Parse("three days ago").Start!.Value.TruncateToDate();
|
||||||
|
toDate = new DateTime(toDate.Year, toDate.Month, toDate.Day);
|
||||||
|
|
||||||
|
var prefetch = await _metricsStore.GetRecords(groupFilterStart, toDate, action)
|
||||||
|
.Where((Func<MetricResult, bool>) (d => d.Action != d.Subject))
|
||||||
|
.Select(async d => d.GroupingSubject)
|
||||||
|
.ToHashSet();;
|
||||||
|
|
||||||
|
var fromDate = parser.Parse(from).Start!.Value.TruncateToDate();
|
||||||
|
|
||||||
|
var counts = new Dictionary<(DateTime, string), long>();
|
||||||
|
|
||||||
|
await foreach (var record in _metricsStore.GetRecords(fromDate, toDate, action))
|
||||||
|
{
|
||||||
|
if (record.Subject == record.Action) continue;
|
||||||
|
if (!prefetch.Contains(record.GroupingSubject)) continue;
|
||||||
|
|
||||||
|
var key = (record.Timestamp.TruncateToDate(), record.GroupingSubject);
|
||||||
|
if (counts.TryGetValue(key, out var old))
|
||||||
|
counts[key] = old + 1;
|
||||||
|
else
|
||||||
|
counts[key] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response.Headers.ContentType = "application/json";
|
||||||
|
var row = new Dictionary<string, object>();
|
||||||
|
for (var d = fromDate; d <= toDate; d = d.AddDays(1))
|
||||||
|
{
|
||||||
|
row["_Timestamp"] = d;
|
||||||
|
foreach (var group in prefetch)
|
||||||
|
{
|
||||||
|
if (counts.TryGetValue((d, group), out var found))
|
||||||
|
row[group] = found;
|
||||||
|
else
|
||||||
|
row[group] = 0;
|
||||||
|
}
|
||||||
|
await JsonSerializer.SerializeAsync(Response.Body, row);
|
||||||
|
await Response.Body.WriteAsync(EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class Result
|
public class Result
|
||||||
{
|
{
|
||||||
public DateTime Timestamp { get; set; }
|
public DateTime Timestamp { get; set; }
|
||||||
|
@ -86,7 +86,8 @@ public class Metrics
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return groupingRegex.Match(metricSubject).Groups[0].ToString();
|
var result = groupingRegex.Match(metricSubject).Groups[0].ToString();
|
||||||
|
return string.IsNullOrEmpty(result) ? metricSubject : result;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
@ -116,6 +116,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.Downloaders.GameF
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.Launcher", "Wabbajack.Launcher\Wabbajack.Launcher.csproj", "{23D49FCC-A6CB-4873-879B-F90DA1871AA3}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.Launcher", "Wabbajack.Launcher\Wabbajack.Launcher.csproj", "{23D49FCC-A6CB-4873-879B-F90DA1871AA3}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.App.Wpf", "Wabbajack.App.Wpf\Wabbajack.App.Wpf.csproj", "{638F0057-8DF2-4702-BC13-AD599E921F71}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -322,6 +324,10 @@ Global
|
|||||||
{23D49FCC-A6CB-4873-879B-F90DA1871AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{23D49FCC-A6CB-4873-879B-F90DA1871AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{23D49FCC-A6CB-4873-879B-F90DA1871AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{23D49FCC-A6CB-4873-879B-F90DA1871AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{23D49FCC-A6CB-4873-879B-F90DA1871AA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
{23D49FCC-A6CB-4873-879B-F90DA1871AA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{638F0057-8DF2-4702-BC13-AD599E921F71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{638F0057-8DF2-4702-BC13-AD599E921F71}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{638F0057-8DF2-4702-BC13-AD599E921F71}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{638F0057-8DF2-4702-BC13-AD599E921F71}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{4057B668-8595-44FE-9805-007B284A838F} = {98B731EE-4FC0-4482-A069-BCBA25497871}
|
{4057B668-8595-44FE-9805-007B284A838F} = {98B731EE-4FC0-4482-A069-BCBA25497871}
|
||||||
|
Loading…
Reference in New Issue
Block a user