Новая реализация парсинга траекторий и фабрики парсеров

This commit is contained in:
Степанов Дмитрий 2024-01-31 17:20:54 +05:00
parent 108644c13d
commit 94c7e1e7c9
5 changed files with 40 additions and 89 deletions

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.Import;
using AsbCloudApp.Services;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudApp.Services.Parser;
using AsbCloudInfrastructure.Services.Trajectory.Parser;
namespace AsbCloudInfrastructure.Services;
@ -12,19 +12,30 @@ public class ParserServiceFactory
public const int IdTrajectoryFactManualParserService = 1;
public const int IdTrajectoryPlanParserService = 2;
private readonly IDictionary<int, Func<IParserService>> parsers = new Dictionary<int, Func<IParserService>>
private readonly IDictionary<int, Func<object>> parsers = new Dictionary<int, Func<object>>
{
{ IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService() },
{ IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService() }
};
public IParserService<TDto, TOptions> Create<TDto, TOptions>(int idImportService)
public IParserService<TDto> GetParser<TDto>(int idParserService)
where TDto : class, IId
where TOptions : ParserOptionsRequestBase
{
var parser = parsers[idImportService].Invoke();
if (!parsers.TryGetValue(idParserService, out var parserService))
throw new ArgumentNullException(nameof(idParserService), "Сервис не зарегистрирован");
return parser as IParserService<TDto, TOptions>
?? throw new ArgumentNullException(nameof(idImportService), "Не удалось распознать файл");
return parserService.Invoke() as IParserService<TDto>
?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа");
}
public IParserServiceWithOptions<TDto, TOptions> GetParserWithOptions<TDto, TOptions>(int idParserService)
where TDto : class, IId
where TOptions : IParserOptionsRequest
{
if (!parsers.TryGetValue(idParserService, out var parserService))
throw new ArgumentNullException(nameof(idParserService), "Сервис не зарегистрирован");
return parserService.Invoke() as IParserServiceWithOptions<TDto, TOptions>
?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа");
}
}

View File

@ -6,6 +6,8 @@ namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public class TrajectoryFactManualParserService : TrajectoryParserService<TrajectoryGeoFactDto>
{
protected override string SheetName => "Фактическая траектория";
protected override ValidationResultDto<TrajectoryGeoFactDto> ParseRow(IXLRow row)
{
var trajectoryRow = new TrajectoryGeoFactDto

View File

@ -1,78 +1,31 @@
using AsbCloudApp.Data.Trajectory;
using ClosedXML.Excel;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.Import;
using AsbCloudApp.Services;
using AsbCloudApp.Services.Parser;
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public abstract class TrajectoryParserService<T> : IParserService<T, TrajectoryParserRequest>
public abstract class TrajectoryParserService<T> : IParserService<T>
where T : TrajectoryGeoDto
{
private const int HeaderRowsCount = 2;
private const int ColumnCount = 6;
protected abstract string SheetName { get; }
protected abstract ValidationResultDto<T> ParseRow(IXLRow row);
public ParserResultDto<T> Parse(Stream file, TrajectoryParserRequest options)
public ParserResultDto<T> Parse(Stream file)
{
using var workbook = new XLWorkbook(file, XLEventTracking.Disabled);
var trajectoryRows = ParseFileStream(file, options);
var sheet = workbook.Worksheets.FirstOrDefault(ws =>
ws.Name.ToLower().Trim() == SheetName.ToLower().Trim())
?? throw new FileFormatException($"Книга excel не содержит листа {SheetName}.");
var trajectoryRows = sheet.Parse(ParseRow, ColumnCount, HeaderRowsCount);
return trajectoryRows;
}
private ParserResultDto<T> ParseFileStream(Stream stream, TrajectoryParserRequest options)
{
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
return ParseWorkbook(workbook, options);
}
private ParserResultDto<T> ParseWorkbook(IXLWorkbook workbook, TrajectoryParserRequest options)
{
var sheetTrajectory = workbook.Worksheets.FirstOrDefault(ws =>
ws.Name.ToLower().Trim() == options.SheetName.ToLower().Trim());
if (sheetTrajectory is null)
throw new FileFormatException($"Книга excel не содержит листа {options.SheetName}.");
var trajectoryRows = ParseSheet(sheetTrajectory, options);
return trajectoryRows;
}
private ParserResultDto<T> ParseSheet(IXLWorksheet sheet, TrajectoryParserRequest options)
{
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 6)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
var count = sheet.RowsUsed().Count() - options.HeaderRowsCount;
if (count > 1024)
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк.");
if (count <= 0)
throw new FileFormatException($"Лист {sheet.Name} некорректного формата либо пустой");
var trajectoryRows = new List<ValidationResultDto<T>>(count);
var parseErrors = new List<string>();
for (int i = 0; i < count; i++)
{
var row = sheet.Row(1 + i + options.HeaderRowsCount);
try
{
var trajectoryRow = ParseRow(row);
trajectoryRows.Add(trajectoryRow);
}
catch (FileFormatException ex)
{
parseErrors.Add(ex.Message);
}
}
if (parseErrors.Any())
throw new FileFormatException(string.Join("\r\n", parseErrors));
return new ParserResultDto<T>
{
Item = trajectoryRows
};
}
}

View File

@ -6,6 +6,8 @@ namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeoPlanDto>
{
protected override string SheetName => "Плановая траектория";
protected override ValidationResultDto<TrajectoryGeoPlanDto> ParseRow(IXLRow row)
{
var trajectoryRow = new TrajectoryGeoPlanDto

View File

@ -1,6 +1,5 @@
using System.Linq;
using AsbCloudApp.Data.Trajectory;
using AsbCloudApp.Requests.Import;
using AsbCloudInfrastructure.Services;
using Xunit;
@ -10,20 +9,6 @@ public class TrajectoryParserTest
{
private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates";
private readonly TrajectoryParserRequest planTrajectoryParserOptions = new()
{
IdParserService = ParserServiceFactory.IdTrajectoryPlanParserService,
SheetName = "Плановая траектория",
HeaderRowsCount = 2
};
private readonly TrajectoryParserRequest factTrajectoryParserOptions = new()
{
IdParserService = ParserServiceFactory.IdTrajectoryFactManualParserService,
SheetName = "Фактическая траектория",
HeaderRowsCount = 2
};
private static readonly ParserServiceFactory parserServiceFactory = new();
[Fact]
@ -35,9 +20,8 @@ public class TrajectoryParserTest
if (stream is null)
Assert.Fail("Файла для импорта не существует");
var parserService = parserServiceFactory.Create<TrajectoryGeoPlanDto, TrajectoryParserRequest>(
planTrajectoryParserOptions.IdParserService);
var trajectoryRows = parserService.Parse(stream, planTrajectoryParserOptions);
var parserService = parserServiceFactory.GetParser<TrajectoryGeoPlanDto>(ParserServiceFactory.IdTrajectoryPlanParserService);
var trajectoryRows = parserService.Parse(stream);
Assert.Equal(3, trajectoryRows.Item.Count());
}
@ -51,9 +35,8 @@ public class TrajectoryParserTest
if (stream is null)
Assert.Fail("Файла для импорта не существует");
var parserService = parserServiceFactory.Create<TrajectoryGeoFactDto, TrajectoryParserRequest>(
factTrajectoryParserOptions.IdParserService);
var trajectoryRows = parserService.Parse(stream, factTrajectoryParserOptions);
var parserService = parserServiceFactory.GetParser<TrajectoryGeoFactDto>(ParserServiceFactory.IdTrajectoryFactManualParserService);
var trajectoryRows = parserService.Parse(stream);
Assert.Equal(4, trajectoryRows.Item.Count());
}