-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Minecraft stats over time, cached and background loading for fa…
…ster perf
- Loading branch information
Showing
9 changed files
with
214 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
using Hangfire.Server; | ||
using Highsoft.Web.Mvc.Charts.Rendering; | ||
using Highsoft.Web.Mvc.Charts; | ||
using StackExchange.Redis; | ||
|
||
namespace CFLookup.Jobs | ||
{ | ||
public class CacheMCOverTime | ||
{ | ||
public static async Task RunAsync(PerformContext context) | ||
{ | ||
using (var scope = Program.ServiceProvider.CreateScope()) | ||
{ | ||
var _db = scope.ServiceProvider.GetRequiredService<MSSQLDB>(); | ||
var _redis = scope.ServiceProvider.GetRequiredService<ConnectionMultiplexer>(); | ||
|
||
var _rdb = _redis.GetDatabase(5); | ||
|
||
var stats = await SharedMethods.GetMinecraftStatsOverTime(_db, CancellationToken.None, null); | ||
|
||
var renderers = new List<string>(); | ||
var ModLoaderStats = new Dictionary<string, List<Series>>(); | ||
var modloaderStats = new Dictionary<string, Dictionary<DateTimeOffset, Dictionary<string, long>>>(); | ||
|
||
foreach (var stat in stats) | ||
{ | ||
var date = stat.Key; | ||
foreach (var modloaderHolder in stat.Value) | ||
{ | ||
var gameVersion = modloaderHolder.Key; | ||
|
||
if (gameVersion.Contains("snapshot", StringComparison.InvariantCultureIgnoreCase)) continue; | ||
|
||
foreach (var gameInfo in modloaderHolder.Value) | ||
{ | ||
var modloader = gameInfo.Key; | ||
var count = gameInfo.Value; | ||
|
||
if (modloader.Contains("LiteLoader", StringComparison.InvariantCultureIgnoreCase)) continue; | ||
|
||
if (!modloaderStats.ContainsKey(modloader)) | ||
{ | ||
modloaderStats[modloader] = new Dictionary<DateTimeOffset, Dictionary<string, long>>(); | ||
} | ||
|
||
if (!modloaderStats[modloader].ContainsKey(date)) | ||
{ | ||
modloaderStats[modloader][date] = new Dictionary<string, long>(); | ||
} | ||
|
||
modloaderStats[modloader][date][gameVersion] = count; | ||
} | ||
} | ||
} | ||
|
||
// Generate different series per modloader and game version for Highstock as separate graphs, where the game versions are the line series | ||
|
||
foreach (var kv in modloaderStats) | ||
{ | ||
var loader = kv.Key; | ||
|
||
var testGraph = new Dictionary<string, List<LineSeriesData>>(); | ||
|
||
foreach (var d in kv.Value) | ||
{ | ||
var date = d.Key; | ||
var gameVersions = d.Value; | ||
|
||
foreach (var gameVersion in gameVersions) | ||
{ | ||
if (!testGraph.ContainsKey(gameVersion.Key)) | ||
{ | ||
testGraph[gameVersion.Key] = new List<LineSeriesData>(); | ||
} | ||
|
||
testGraph[gameVersion.Key].Add(new LineSeriesData { X = date.ToUnixTimeMilliseconds(), Y = gameVersion.Value }); | ||
} | ||
} | ||
|
||
var viewData = new List<Series>(); | ||
|
||
foreach (var series in testGraph) | ||
{ | ||
viewData.Add(new LineSeries | ||
{ | ||
Name = series.Key, | ||
Data = series.Value, | ||
TurboThreshold = 100, | ||
Selected = false | ||
}); | ||
} | ||
|
||
ModLoaderStats[$"{loader}Data"] = viewData; | ||
} | ||
|
||
foreach (var loaderData in ModLoaderStats) | ||
{ | ||
var loader = loaderData.Key; | ||
var chartOptions = | ||
new Highcharts | ||
{ | ||
ID = $"{loader.Replace("Data", "")}Chart", | ||
Chart = new Chart | ||
{ | ||
HeightNumber = 800, | ||
ZoomType = ChartZoomType.X | ||
}, | ||
XAxis = new List<XAxis> | ||
{ | ||
new XAxis | ||
{ | ||
Type = "datetime", | ||
MinRange = 3600000 | ||
} | ||
}, | ||
Legend = new Legend | ||
{ | ||
Enabled = true | ||
}, | ||
YAxis = new List<YAxis> | ||
{ | ||
new YAxis | ||
{ | ||
Labels = new YAxisLabels | ||
{ | ||
}, | ||
PlotLines = new List<YAxisPlotLines> | ||
{ | ||
new YAxisPlotLines | ||
{ | ||
Value = 0, | ||
Width = 2, | ||
Color = "silver" | ||
} | ||
} | ||
} | ||
}, | ||
Tooltip = new Tooltip | ||
{ | ||
PointFormat = @"<span style='color:{series.color}'>{series.name}</span>: <b>{point.y}</b><br/>", | ||
ValueDecimals = 0 | ||
}, | ||
PlotOptions = new PlotOptions | ||
{ | ||
Series = new PlotOptionsSeries | ||
{ | ||
TurboThreshold = 10000 | ||
}, | ||
Area = new PlotOptionsArea | ||
{ | ||
Marker = new PlotOptionsAreaMarker | ||
{ | ||
Radius = 2 | ||
}, | ||
LineWidth = 1, | ||
States = new PlotOptionsAreaStates | ||
{ | ||
Hover = new PlotOptionsAreaStatesHover | ||
{ | ||
LineWidth = 1 | ||
} | ||
}, | ||
Threshold = null | ||
} | ||
}, | ||
Series = loaderData.Value, | ||
Title = new Title { Text = $"Amount of mods for {loader.Replace("Data", "")} over time" } | ||
}; | ||
|
||
var renderer = new HighchartsRenderer(chartOptions); | ||
renderers.Add(renderer.RenderHtml()); | ||
} | ||
|
||
await _rdb.StringSetAsync("cf-mcmodloader-stats", string.Join("<hr />", renderers), TimeSpan.FromHours(1)); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,11 @@ | ||
@page | ||
@using Highsoft.Web.Mvc.Stocks | ||
@using Highsoft.Web.Mvc.Stocks.Rendering | ||
@model CFLookup.Pages.MinecraftModStatsOverTimeModel | ||
@{ | ||
ViewData["Title"] = "Minecraft mod stats over time"; | ||
} | ||
@section Head { | ||
<script src="https://code.highcharts.com/stock/highstock.js"></script> | ||
<script src="https://code.highcharts.com/highcharts.js"></script> | ||
<script src="https://code.highcharts.com/modules/exporting.js"></script> | ||
} | ||
|
||
@{ | ||
var chartOptions = | ||
new Highstock | ||
{ | ||
RangeSelector = new RangeSelector | ||
{ | ||
Selected = 4 | ||
}, | ||
Responsive = new Responsive { | ||
|
||
}, | ||
Chart = new Chart { | ||
HeightNumber = 800 | ||
}, | ||
XAxis = new List<XAxis> | ||
{ | ||
new XAxis | ||
{ | ||
Ordinal = true | ||
} | ||
}, | ||
Legend = new Legend | ||
{ | ||
Enabled = true | ||
}, | ||
YAxis = new List<YAxis> | ||
{ | ||
new YAxis | ||
{ | ||
Labels = new YAxisLabels | ||
{ | ||
}, | ||
PlotLines = new List<YAxisPlotLines> | ||
{ | ||
new YAxisPlotLines | ||
{ | ||
Value = 0, | ||
Width = 2, | ||
Color = "silver" | ||
} | ||
} | ||
} | ||
}, | ||
PlotOptions = new PlotOptions | ||
{ | ||
Series = new PlotOptionsSeries | ||
{ | ||
Compare = PlotOptionsSeriesCompare.Value, | ||
DataGrouping = new PlotOptionsSeriesDataGrouping | ||
{ | ||
Enabled = true | ||
} | ||
} | ||
}, | ||
Tooltip = new Tooltip | ||
{ | ||
PointFormat = @"<span style='color:{series.color}'>{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>", | ||
ValueDecimals = 2, | ||
Split = true, | ||
Outside = true | ||
}, | ||
Series = Model.ModLoaderStats["ForgeData"], | ||
}; | ||
|
||
chartOptions.ID = "forgeChart"; | ||
var renderer = new HighstockRenderer(chartOptions); | ||
} | ||
|
||
@Html.Raw(renderer.RenderHtml()) | ||
@Html.Raw(Model.ChartHtml) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,92 +1,30 @@ | ||
using Highsoft.Web.Mvc.Stocks; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
using StackExchange.Redis; | ||
|
||
namespace CFLookup.Pages | ||
{ | ||
public class MinecraftModStatsOverTimeModel : PageModel | ||
{ | ||
private readonly MSSQLDB _db; | ||
private readonly ConnectionMultiplexer _db; | ||
|
||
public Dictionary<string, Dictionary<DateTimeOffset, Dictionary<string, long>>> Stats { get; set; } = new Dictionary<string, Dictionary<DateTimeOffset, Dictionary<string, long>>>(); | ||
public string ChartHtml { get; set; } | ||
|
||
public Dictionary<string, List<Series>> ModLoaderStats = new Dictionary<string, List<Series>>(); | ||
|
||
public MinecraftModStatsOverTimeModel(MSSQLDB db) | ||
public MinecraftModStatsOverTimeModel(ConnectionMultiplexer db) | ||
{ | ||
_db = db; | ||
} | ||
|
||
public async Task OnGetAsync() | ||
public async Task OnGetAsync(CancellationToken cancellationToken) | ||
{ | ||
var stats = await SharedMethods.GetMinecraftStatsOverTime(_db, 24 * 30); | ||
|
||
var modloaderStats = new Dictionary<string, Dictionary<DateTimeOffset, Dictionary<string, long>>>(); | ||
|
||
foreach (var stat in stats) | ||
{ | ||
var date = stat.Key; | ||
foreach(var modloaderHolder in stat.Value) | ||
{ | ||
var gameVersion = modloaderHolder.Key; | ||
|
||
if(gameVersion.Contains("snapshot", StringComparison.InvariantCultureIgnoreCase)) continue; | ||
|
||
foreach(var gameInfo in modloaderHolder.Value) | ||
{ | ||
var modloader = gameInfo.Key; | ||
var count = gameInfo.Value; | ||
|
||
if(modloader.Contains("LiteLoader", StringComparison.InvariantCultureIgnoreCase)) continue; | ||
|
||
if (!modloaderStats.ContainsKey(modloader)) | ||
{ | ||
modloaderStats[modloader] = new Dictionary<DateTimeOffset, Dictionary<string, long>>(); | ||
} | ||
|
||
if (!modloaderStats[modloader].ContainsKey(date)) | ||
{ | ||
modloaderStats[modloader][date] = new Dictionary<string, long>(); | ||
} | ||
var rdb = _db.GetDatabase(5); | ||
|
||
modloaderStats[modloader][date][gameVersion] = count; | ||
} | ||
} | ||
} | ||
|
||
// Generate different series per modloader and game version for Highstock as separate graphs, where the game versions are the line series | ||
var testGraph = new Dictionary<string, List<LineSeriesData>>(); | ||
var forgeData = modloaderStats["Forge"]; | ||
var statHtml = await rdb.StringGetAsync("cf-mcmodloader-stats"); | ||
|
||
foreach(var d in forgeData) | ||
if(statHtml == RedisValue.Null) | ||
{ | ||
var date = d.Key; | ||
var gameVersions = d.Value; | ||
|
||
foreach(var gameVersion in gameVersions) | ||
{ | ||
if (!testGraph.ContainsKey(gameVersion.Key)) | ||
{ | ||
testGraph[gameVersion.Key] = new List<LineSeriesData>(); | ||
} | ||
|
||
testGraph[gameVersion.Key].Add(new LineSeriesData { X = date.ToUnixTimeMilliseconds(), Y = gameVersion.Value }); | ||
} | ||
ChartHtml = "No data loaded yet"; | ||
return; | ||
} | ||
|
||
var viewData = new List<Series>(); | ||
|
||
foreach(var series in testGraph) | ||
{ | ||
viewData.Add(new LineSeries | ||
{ | ||
Name = series.Key, | ||
Data = series.Value, | ||
TurboThreshold = 100, | ||
|
||
}); | ||
} | ||
|
||
ModLoaderStats["ForgeData"] = viewData; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.