VEU.WathBody/3-app/2-cli/Server.WatchBody.Import/ProgramPackageImporter.cs

121 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text.Json;
using Server.WatchBody.Core.Dal.Ref.Repository;
using Server.WatchBody.Core.Dal.Src.Domain;
using Server.WatchBody.Core.Dal.Src.Repository;
using Server.WatchBody.Core.Programs;
namespace Server.WatchBody.Import;
/// <summary>Нарушение формата пакета program-import.json (spec 03 §2).</summary>
public class ImportPackageException(string message) : Exception(message);
/// <summary>
/// Импорт пакета программы (spec 03 m06, формат program-import-format.md):
/// program по external_id, версия через ProgramStore (повтор пакета —
/// новая версия max+1), отсутствующие упражнения создаются. 🛡
/// Существующие версии не переписываются; статус draft.
/// </summary>
public class ProgramPackageImporter(
IProgramRepository programs,
IExerciseRepository exercises,
IExerciseGroupRepository groups,
ProgramStore store)
{
public Guid Import(int athleteId, string packageJson)
{
JsonDocument doc;
try
{
doc = JsonDocument.Parse(packageJson);
}
catch (JsonException e)
{
throw new ImportPackageException($"пакет не JSON: {e.Message}");
}
using (doc)
{
var root = doc.RootElement;
if (root.ValueKind != JsonValueKind.Object ||
!root.TryGetProperty("weeks", out var weeks) ||
weeks.ValueKind != JsonValueKind.Array || weeks.GetArrayLength() == 0)
throw new ImportPackageException("пакет без weeks — это не программа");
if (!root.TryGetProperty("program_id", out var programIdEl) ||
programIdEl.GetString() is not { Length: > 0 } externalId)
throw new ImportPackageException("пакет без program_id");
var author = root.TryGetProperty("author", out var authorEl)
? authorEl.GetString() : null;
if (author is not ("user" or "admin"))
throw new ImportPackageException($"author '{author}' не допускается (user|admin)");
// программа по external_id (З-35) или создание
var program = programs.Select(athleteId).FirstOrDefault(p => p.ExternalId == externalId)
?? CreateProgram(athleteId, externalId);
// отсутствующие упражнения библиотеки (format разд. 2)
var groupByFocus = GroupByFocus();
EnsureExercises(athleteId, weeks, groupByFocus);
var rationale = root.TryGetProperty("rationale", out var rationaleEl) &&
rationaleEl.ValueKind == JsonValueKind.String
? rationaleEl.GetString() ?? string.Empty
: string.Empty;
var version = store.CreateDraft(athleteId, root.GetRawText(), author, rationale);
return version.Id;
}
}
private Program CreateProgram(int athleteId, string externalId)
{
var id = programs.Insert(new Program
{
AthleteId = athleteId,
Title = externalId,
ExternalId = externalId,
});
return new Program { Id = id, AthleteId = athleteId, Title = externalId, ExternalId = externalId };
}
private Dictionary<string, int> GroupByFocus() =>
groups.Select().ToDictionary(g => g.Code, g => g.Id, StringComparer.Ordinal);
private void EnsureExercises(int athleteId, JsonElement weeks, Dictionary<string, int> groupIds)
{
var known = exercises.Select(new ExerciseFilter { AthleteId = athleteId })
.Select(e => e.Name).ToHashSet(StringComparer.Ordinal);
foreach (var session in weeks.EnumerateArray()
.SelectMany(w => w.TryGetProperty("sessions", out var s)
? s.EnumerateArray() : Enumerable.Empty<JsonElement>()))
{
var focus = session.TryGetProperty("focus", out var f) ? f.GetString() ?? "" : "";
var groupId = FocusGroup(focus, groupIds);
if (!session.TryGetProperty("exercises", out var exerciseNodes)) continue;
foreach (var exercise in exerciseNodes.EnumerateArray())
{
if (!exercise.TryGetProperty("exercise_id", out var idEl) ||
idEl.GetString() is not { Length: > 0 } name || known.Contains(name)) continue;
exercises.Insert(new Exercise
{
Id = Guid.NewGuid(),
AthleteId = athleteId,
Name = name,
GroupId = groupId,
IsBase = true,
IsActive = true,
ReplacementIds = [],
});
known.Add(name);
}
}
}
private static int FocusGroup(string focus, Dictionary<string, int> groupIds)
{
foreach (var (code, id) in groupIds)
if (focus.Contains(code, StringComparison.OrdinalIgnoreCase))
return id;
return groupIds.GetValueOrDefault("core", 4);
}
}