Add store log analysis feature and related data structures
- Implement AnalyzeSlotUnregistrationsAsync method to retrieve and report store log data for cooperating stores in region 139. - Introduce StoreLogProfileEntry record to aggregate log information. - Add StoreLog endpoint for API access. - Create GetStoreLogAsync and GetStoreLogEntriesAsync methods for fetching and deserializing store log entries. - Update Program.cs to include the new analysis task option.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FsToolbox.Cli.Tasks
|
||||
{
|
||||
public static partial class StoreTasks
|
||||
@@ -15,6 +17,35 @@ namespace FsToolbox.Cli.Tasks
|
||||
|
||||
#endregion
|
||||
|
||||
#region Record FoodsaverProfile
|
||||
|
||||
/// <summary>
|
||||
/// Minimal foodsaver profile information for store logs.
|
||||
/// </summary>
|
||||
/// <param name="Id">The foodsaver identifier.</param>
|
||||
/// <param name="Name">The foodsaver display name.</param>
|
||||
/// <param name="Avatar">The foodsaver avatar URL (relative).</param>
|
||||
/// <param name="IsSleeping">Indicates whether the foodsaver is sleeping.</param>
|
||||
public record FoodsaverProfile(int Id, string Name, string? Avatar, bool IsSleeping);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Record StoreLogEntry
|
||||
|
||||
/// <summary>
|
||||
/// Represents a store log entry with foodsaver and action information.
|
||||
/// </summary>
|
||||
public record StoreLogEntry(
|
||||
[property: JsonPropertyName("performed_at")] DateTime PerformedAt,
|
||||
[property: JsonPropertyName("action_id")] int ActionId,
|
||||
[property: JsonPropertyName("date_reference")] DateTime? DateReference,
|
||||
[property: JsonPropertyName("content")] string? Content,
|
||||
[property: JsonPropertyName("reason")] string? Reason,
|
||||
[property: JsonPropertyName("acting_foodsaver")] FoodsaverProfile? ActingFoodsaver,
|
||||
[property: JsonPropertyName("affected_foodsaver")] FoodsaverProfile? AffectedFoodsaver);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Record Pickup
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -82,6 +82,68 @@ namespace FsToolbox.Cli.Tasks
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method GetStoreLogAsync
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the store log for a specified store and date range.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">The HTTP client used to send the request.</param>
|
||||
/// <param name="storeId">The store identifier to query.</param>
|
||||
/// <param name="fromDate">Start date (formatted for the API).</param>
|
||||
/// <param name="toDate">End date (formatted for the API).</param>
|
||||
/// <param name="storeLogActionIds">Comma-separated action IDs.</param>
|
||||
/// <returns>The raw response body from the API.</returns>
|
||||
public static async Task<string> GetStoreLogAsync(HttpClient httpClient, int storeId, string fromDate, string toDate, string storeLogActionIds)
|
||||
{
|
||||
await AuthHelper.EnsureAuthenticationAsync(httpClient);
|
||||
|
||||
var uri = string.Format(Endpoints.StoreLog, storeId, fromDate, toDate, storeLogActionIds);
|
||||
var response = await httpClient.GetAsync(uri);
|
||||
var responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// handle unsuccessful response
|
||||
if (!response.IsSuccessStatusCode)
|
||||
Logger.Error("Store log retrieval failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody);
|
||||
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method GetStoreLogEntriesAsync
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves and deserializes store log entries for a specified store and date range.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">The HTTP client used to send the request.</param>
|
||||
/// <param name="storeId">The store identifier to query.</param>
|
||||
/// <param name="fromDate">Start date (formatted for the API).</param>
|
||||
/// <param name="toDate">End date (formatted for the API).</param>
|
||||
/// <param name="storeLogActionIds">Comma-separated action IDs.</param>
|
||||
/// <returns>A list of store log entries, or an empty list when parsing fails.</returns>
|
||||
public static async Task<List<StoreLogEntry>> GetStoreLogEntriesAsync(HttpClient httpClient, int storeId, string fromDate, string toDate, string storeLogActionIds)
|
||||
{
|
||||
var responseBody = await GetStoreLogAsync(httpClient, storeId, fromDate, toDate, storeLogActionIds);
|
||||
|
||||
var opts = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
PropertyNamingPolicy = null
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<StoreLogEntry>>(responseBody, opts) ?? [];
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
Logger.Error(ex, "Failed to parse store log response.");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method PatchPickupAsync
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user