Add AnalyzeEarlySlotRegistrationsAsync method and update Program menu

This commit is contained in:
2026-02-03 07:41:37 +01:00
parent 0b1e735a9f
commit d177a25c3d
3 changed files with 144 additions and 0 deletions

View File

@@ -159,6 +159,105 @@ namespace FsToolbox.Cli
#endregion
#region Public Method AnalyzeEarlySlotRegistrationsAsync
/// <summary>
/// Identifies store log entries where a user registered earlier than the automatic slot creation interval.
/// </summary>
/// <param name="httpClient">The HTTP client used to perform the requests.</param>
public static async Task AnalyzeEarlySlotRegistrationsAsync(HttpClient httpClient)
{
await AuthHelper.EnsureAuthenticationAsync(httpClient);
const int regionId = 139;
var stores = await RegionTasks.GetStoresInRegionAsync(httpClient, regionId);
var cooperatingStores = stores.Where(x => x.CooperationStatus == RegionTasks.CooperationStatus.Cooperating).ToList();
if (cooperatingStores.Count == 0)
{
Logger.Info("No cooperating stores found in region {RegionId}.", regionId);
return;
}
var fromDate = DateTime.Today.AddMonths(-6).ToString("yyyy-MM-dd");
var toDate = DateTime.Today.ToString("yyyy-MM-dd");
const string storeLogActionIds = "11";
var ignoredStoreIds = new HashSet<int> { 51224, 61913 };
var storesToProcess = cooperatingStores
.Where(x => !ignoredStoreIds.Contains(x.Id))
.ToList();
Logger.Info("Debug mode: processing first {Count} cooperating stores.", storesToProcess.Count);
var analyzed = new List<StoreLogIntervalEntry>();
foreach (var store in storesToProcess)
{
var info = await StoreTasks.GetStoreInformationAsync(httpClient, store.Id);
if (info == null)
{
Logger.Info("Store information not available for {StoreId}.", store.Id);
continue;
}
var calendarInterval = info.CalendarInterval;
if (calendarInterval <= 0)
{
Logger.Info("Skipping {StoreName} because calendar interval is {Interval}.", store.Name, calendarInterval);
continue;
}
Logger.Info("Calendar interval for {StoreName} is {Interval} seconds.", store.Name, calendarInterval);
var logEntries = await StoreTasks.GetStoreLogEntriesAsync(httpClient, store.Id, fromDate, toDate, storeLogActionIds);
Thread.Sleep(1000);
foreach (var entry in logEntries)
{
if (entry.DateReference == null) continue;
var secondsDifference = (entry.DateReference.Value - entry.PerformedAt).TotalSeconds;
if (secondsDifference <= calendarInterval) continue;
analyzed.Add(new StoreLogIntervalEntry(store, entry, secondsDifference, calendarInterval, true));
}
}
var exceeding = analyzed.Count;
var sb = new StringBuilder();
sb.AppendLine("Early Slot Registration Report");
sb.AppendLine("Generated: " + DateTime.Now.ToString("dd.MM.yyyy HH:mm"));
sb.AppendLine($"Region: {regionId}");
sb.AppendLine($"Stores analyzed: {storesToProcess.Count}");
sb.AppendLine($"Timeframe: {fromDate} to {toDate}");
sb.AppendLine($"Total log entries: {analyzed.Count}");
sb.AppendLine($"Exceeding interval: {exceeding}");
sb.AppendLine("============================");
sb.AppendLine();
foreach (var entry in analyzed.OrderByDescending(x => x.SecondsDifference))
{
var foodsaver = entry.Entry.ActingFoodsaver;
var foodsaverName = foodsaver?.Name ?? "(unknown)";
var foodsaverId = foodsaver?.Id.ToString() ?? "n/a";
var reference = entry.Entry.DateReference?.ToString("dd.MM.yyyy - HH:mm") ?? "n/a";
var performed = entry.Entry.PerformedAt.ToString("dd.MM.yyyy - HH:mm");
var intervalWeeks = entry.CalendarIntervalSeconds / (60.0 * 60 * 24 * 7);
var earlySeconds = entry.SecondsDifference - entry.CalendarIntervalSeconds;
var earlySpan = TimeSpan.FromSeconds(earlySeconds);
sb.AppendLine($"- {foodsaverName} ({foodsaverId}) | Store: {entry.Store.Name} ({entry.Store.Id}) | Performed: {performed} | Slot Date: {reference} | Interval: {intervalWeeks:F2} weeks | Early: {earlySpan.Days}d {earlySpan.Hours}h {earlySpan.Minutes}m");
}
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
var filename = $"EarlySlotRegistrations_{timestamp}.txt";
await File.WriteAllTextAsync(filename, sb.ToString());
Logger.Info("Report saved: {FileName}", filename);
}
#endregion
#region Public Method ConfirmUnconfirmedPickupsLindenbergAsync
/// <summary>