116 lines
5.1 KiB
C#
116 lines
5.1 KiB
C#
using Kit.Helpers.SqlHelperAbstract;
|
|
using Microsoft.Extensions.Logging;
|
|
using Server.WatchBody.Core.Dal.Ref.Repository;
|
|
using Server.WatchBody.Core.Dal.Src.Repository;
|
|
using Server.WatchBody.Core.Programs;
|
|
|
|
namespace Server.WatchBody.Import;
|
|
|
|
/// <summary>
|
|
/// Обход Import.Dir (spec 04 m06): подпапки = имена атлетов; .fit/.tcx/
|
|
/// .gpx → WorkoutImporter, program-import.json → ProgramPackageImporter;
|
|
/// обработанные → ArchiveDir, сбойные остаются (🛡4 — пакет
|
|
/// продолжается).
|
|
/// </summary>
|
|
public class ImportService(IConnectionString connectionString, ILogger<ImportService>? logger = null)
|
|
{
|
|
private static readonly string[] ActivityExtensions = [".fit", ".tcx", ".gpx"];
|
|
private const string PackageName = "program-import.json";
|
|
|
|
/// <summary>(обработано, сбойных). Каталог импорта не существует → (0, 0).</summary>
|
|
public (int Processed, int Failed) Run(string importDir, string archiveDir)
|
|
{
|
|
if (!Directory.Exists(importDir))
|
|
{
|
|
Log($"каталог импорта {importDir} не существует — новых файлов нет");
|
|
return (0, 0);
|
|
}
|
|
Directory.CreateDirectory(archiveDir);
|
|
|
|
// имена атлетов могут повторяться (общая тестовая БД) — первый
|
|
var athletes = new AthleteRepository(connectionString).Select(null)
|
|
.GroupBy(a => a.Name, StringComparer.Ordinal)
|
|
.ToDictionary(g => g.Key, g => g.First().Id, StringComparer.Ordinal);
|
|
var importer = new ImportContext(connectionString);
|
|
|
|
var processed = 0;
|
|
var failed = 0;
|
|
foreach (var folder in Directory.GetDirectories(importDir))
|
|
{
|
|
var athleteName = Path.GetFileName(folder);
|
|
if (!athletes.TryGetValue(athleteName, out var athleteId))
|
|
{
|
|
failed++;
|
|
Log($"атлет '{athleteName}' не найден — файлы папки не обработаны");
|
|
continue;
|
|
}
|
|
foreach (var file in Directory.GetFiles(folder))
|
|
{
|
|
var name = Path.GetFileName(file);
|
|
try
|
|
{
|
|
ProcessFile(importer, athleteId, file, athleteName);
|
|
var target = ArchivePath(archiveDir, athleteName, name);
|
|
Directory.CreateDirectory(Path.GetDirectoryName(target)!);
|
|
System.IO.File.Move(file, target);
|
|
processed++;
|
|
Log($"обработан: {athleteName}/{name}");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
failed++;
|
|
Log($"СБОЙ {athleteName}/{name}: {e.Message}");
|
|
Console.Error.WriteLine($"import error: {athleteName}/{name}: {e}");
|
|
}
|
|
}
|
|
}
|
|
foreach (var file in Directory.GetFiles(importDir))
|
|
Log($"пропущен (не в подпапке-атлете): {Path.GetFileName(file)}");
|
|
return (processed, failed);
|
|
}
|
|
|
|
private static void ProcessFile(ImportContext ctx, int athleteId, string path, string athleteName)
|
|
{
|
|
var extension = Path.GetExtension(path).ToLowerInvariant();
|
|
var name = Path.GetFileName(path);
|
|
switch (extension)
|
|
{
|
|
case ".fit":
|
|
ctx.Workouts.Import(athleteId, ctx.Fit.Parse(path));
|
|
break;
|
|
case ".tcx" or ".gpx":
|
|
ctx.Workouts.Import(athleteId, ctx.Xml.Parse(path));
|
|
break;
|
|
case ".json" when name.Equals(PackageName, StringComparison.OrdinalIgnoreCase):
|
|
ctx.Packages.Import(athleteId, System.IO.File.ReadAllText(path));
|
|
break;
|
|
default:
|
|
throw new FitParseException($"неизвестный тип файла импорта: {name}");
|
|
}
|
|
}
|
|
|
|
private static string ArchivePath(string archiveDir, string athleteName, string fileName)
|
|
{
|
|
var target = Path.Combine(archiveDir, athleteName, fileName);
|
|
if (!System.IO.File.Exists(target)) return target;
|
|
var suffix = $"-{DateTime.Now:HHmmss}";
|
|
return Path.Combine(archiveDir, athleteName,
|
|
Path.GetFileNameWithoutExtension(fileName) + suffix + Path.GetExtension(fileName));
|
|
}
|
|
|
|
private void Log(string message) => logger?.LogInformation("{Message}", message);
|
|
|
|
/// <summary>Ленивые зависимости одного прогона (соединение общее).</summary>
|
|
private sealed class ImportContext(IConnectionString cs)
|
|
{
|
|
public readonly FitParser Fit = new();
|
|
public readonly TcxGpxParser Xml = new();
|
|
public readonly WorkoutImporter Workouts = new(
|
|
new WorkoutRepository(cs));
|
|
public readonly ProgramPackageImporter Packages = new(
|
|
new ProgramRepository(cs), new ExerciseRepository(cs),
|
|
new ExerciseGroupRepository(cs),
|
|
new ProgramStore(new ProgramRepository(cs), new ProgramVersionRepository(cs)));
|
|
}
|
|
}
|