diff --git a/AsbCloudApp/Requests/ParserOptions/IParserOptionsRequest.cs b/AsbCloudApp/Requests/ParserOptions/IParserOptionsRequest.cs index dd8f8e4f..b3b12f51 100644 --- a/AsbCloudApp/Requests/ParserOptions/IParserOptionsRequest.cs +++ b/AsbCloudApp/Requests/ParserOptions/IParserOptionsRequest.cs @@ -1,8 +1,19 @@ namespace AsbCloudApp.Requests.ParserOptions; /// -/// Интерфейс для параметров парсера +/// Параметры парсинга /// public interface IParserOptionsRequest { + private static DummyOptions empty => new(); + + private class DummyOptions : IParserOptionsRequest + { + } + + /// + /// Получение пустого объекта опций + /// + /// + public static IParserOptionsRequest Empty() => empty; } \ No newline at end of file diff --git a/AsbCloudApp/Services/Parser/IParserServiceWithOptions.cs b/AsbCloudApp/Services/IParserService.cs similarity index 50% rename from AsbCloudApp/Services/Parser/IParserServiceWithOptions.cs rename to AsbCloudApp/Services/IParserService.cs index 83e54a27..2cfd8eb7 100644 --- a/AsbCloudApp/Services/Parser/IParserServiceWithOptions.cs +++ b/AsbCloudApp/Services/IParserService.cs @@ -1,15 +1,17 @@ using System.IO; +using System.Threading; +using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; -namespace AsbCloudApp.Services.Parser; +namespace AsbCloudApp.Services; /// -/// Сервис парсинга файлов с доп. параметрами +/// Сервис парсинга /// /// /// -public interface IParserServiceWithOptions +public interface IParserService : IParserService where TDto : class, IId where TOptions : IParserOptionsRequest { @@ -20,4 +22,17 @@ public interface IParserServiceWithOptions /// /// ParserResultDto Parse(Stream file, TOptions options); + + /// + /// Получение шаблона для заполнения + /// + /// + Stream GetTemplateFile(); +} + +/// +/// Сервис парсинга(интерфейс маркер) +/// +public interface IParserService +{ } \ No newline at end of file diff --git a/AsbCloudApp/Services/Parser/IParserService.cs b/AsbCloudApp/Services/Parser/IParserService.cs deleted file mode 100644 index 628859bb..00000000 --- a/AsbCloudApp/Services/Parser/IParserService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.IO; -using AsbCloudApp.Data; - -namespace AsbCloudApp.Services.Parser; - -/// -/// Сервис парсинга файлов -/// -/// -public interface IParserService - where TDto : class, IId -{ - /// - /// Распарсить файл - /// - /// - /// - ParserResultDto Parse(Stream file); -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/XLParserExtensions.cs b/AsbCloudInfrastructure/ParserServiceBase.cs similarity index 69% rename from AsbCloudInfrastructure/XLParserExtensions.cs rename to AsbCloudInfrastructure/ParserServiceBase.cs index 28ecb089..d98404e7 100644 --- a/AsbCloudInfrastructure/XLParserExtensions.cs +++ b/AsbCloudInfrastructure/ParserServiceBase.cs @@ -4,17 +4,30 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using AsbCloudApp.Data; +using AsbCloudApp.Requests.ParserOptions; +using AsbCloudApp.Services; using ClosedXML.Excel; namespace AsbCloudInfrastructure; -public static class XLParserExtensions +public abstract class ParserServiceBase : IParserService + where TDto : class, IId + where TOptions : IParserOptionsRequest { - public static ParserResultDto Parse(this IXLWorksheet sheet, + protected readonly IServiceProvider serviceProvider; + + protected ParserServiceBase(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider; + } + + public abstract ParserResultDto Parse(Stream file, TOptions options); + public abstract Stream GetTemplateFile(); + + protected virtual ParserResultDto ParseExcelSheet(IXLWorksheet sheet, Func> parseRow, int columnCount, int headerRowsCount = 0) - where TDto : class, IId { if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount) throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов."); @@ -26,10 +39,10 @@ public static class XLParserExtensions if (count <= 0) return new ParserResultDto(); - + var dtos = new List>(count); var warnings = new List(); - + for (var i = 0; i < count; i++) { var row = sheet.Row(1 + i + headerRowsCount); @@ -45,7 +58,7 @@ public static class XLParserExtensions warnings.Add(warning); } } - + var parserResult = new ParserResultDto { Item = dtos diff --git a/AsbCloudInfrastructure/Services/ParserServiceFactory.cs b/AsbCloudInfrastructure/Services/ParserServiceFactory.cs index 7aeceb98..5b852a8e 100644 --- a/AsbCloudInfrastructure/Services/ParserServiceFactory.cs +++ b/AsbCloudInfrastructure/Services/ParserServiceFactory.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; -using AsbCloudApp.Services.Parser; +using AsbCloudApp.Services; using AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure.Services; @@ -12,30 +12,25 @@ public class ParserServiceFactory public const int IdTrajectoryFactManualParserService = 1; public const int IdTrajectoryPlanParserService = 2; - private readonly IDictionary> parsers = new Dictionary> - { - { IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService() }, - { IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService() } - }; + private readonly IDictionary> parsers; - public IParserService GetParser(int idParserService) - where TDto : class, IId + public ParserServiceFactory(IServiceProvider serviceProvider) { - if (!parsers.TryGetValue(idParserService, out var parserService)) - throw new ArgumentNullException(nameof(idParserService), "Сервис не зарегистрирован"); - - return parserService.Invoke() as IParserService - ?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа"); + parsers = new Dictionary> + { + { IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService(serviceProvider) }, + { IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService(serviceProvider) } + }; } - public IParserServiceWithOptions GetParserWithOptions(int idParserService) + public IParserService Create(int idParserService) where TDto : class, IId where TOptions : IParserOptionsRequest { if (!parsers.TryGetValue(idParserService, out var parserService)) - throw new ArgumentNullException(nameof(idParserService), "Сервис не зарегистрирован"); + throw new ArgumentNullException(nameof(idParserService), "Не правильный идентификатор парсера"); - return parserService.Invoke() as IParserServiceWithOptions + return parserService.Invoke() as IParserService ?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа"); } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParserService.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParserService.cs index d494c5b6..6e4034a5 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParserService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParserService.cs @@ -1,4 +1,5 @@ -using AsbCloudApp.Data; +using System; +using AsbCloudApp.Data; using AsbCloudApp.Data.Trajectory; using ClosedXML.Excel; @@ -7,7 +8,13 @@ namespace AsbCloudInfrastructure.Services.Trajectory.Parser; public class TrajectoryFactManualParserService : TrajectoryParserService { protected override string SheetName => "Фактическая траектория"; + protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx"; + public TrajectoryFactManualParserService(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + protected override ValidationResultDto ParseRow(IXLRow row) { var trajectoryRow = new TrajectoryGeoFactDto @@ -22,9 +29,11 @@ public class TrajectoryFactManualParserService : TrajectoryParserService + var validationResult = new ValidationResultDto { Item = trajectoryRow }; + + return validationResult; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParserService.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParserService.cs index 16dbf55d..d0afe513 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParserService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParserService.cs @@ -1,23 +1,36 @@ -using AsbCloudApp.Data.Trajectory; +using System; +using AsbCloudApp.Data.Trajectory; using ClosedXML.Excel; using System.IO; using System.Linq; +using System.Reflection; using AsbCloudApp.Data; -using AsbCloudApp.Services.Parser; +using AsbCloudApp.Requests.ParserOptions; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public abstract class TrajectoryParserService : IParserService +public abstract class TrajectoryParserService : ParserServiceBase where T : TrajectoryGeoDto { private const int HeaderRowsCount = 2; private const int ColumnCount = 6; + protected TrajectoryParserService(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + protected abstract string SheetName { get; } + protected abstract string TemplateFileName { get; } + protected abstract ValidationResultDto ParseRow(IXLRow row); - public ParserResultDto Parse(Stream file) + public override Stream GetTemplateFile() => + Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName) + ?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден"); + + public override ParserResultDto Parse(Stream file, IParserOptionsRequest options) { using var workbook = new XLWorkbook(file, XLEventTracking.Disabled); @@ -25,7 +38,7 @@ public abstract class TrajectoryParserService : IParserService ws.Name.ToLower().Trim() == SheetName.ToLower().Trim()) ?? throw new FileFormatException($"Книга excel не содержит листа {SheetName}."); - var trajectoryRows = sheet.Parse(ParseRow, ColumnCount, HeaderRowsCount); + var trajectoryRows = ParseExcelSheet(sheet, ParseRow, ColumnCount, HeaderRowsCount); return trajectoryRows; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParserService.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParserService.cs index 97d7ca29..fbf5a537 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParserService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParserService.cs @@ -1,4 +1,5 @@ -using AsbCloudApp.Data; +using System; +using AsbCloudApp.Data; using AsbCloudApp.Data.Trajectory; using ClosedXML.Excel; @@ -7,7 +8,13 @@ namespace AsbCloudInfrastructure.Services.Trajectory.Parser; public class TrajectoryPlanParserService : TrajectoryParserService { protected override string SheetName => "Плановая траектория"; + protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx"; + public TrajectoryPlanParserService(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + protected override ValidationResultDto ParseRow(IXLRow row) { var trajectoryRow = new TrajectoryGeoPlanDto @@ -23,9 +30,11 @@ public class TrajectoryPlanParserService : TrajectoryParserService + var validationResult = new ValidationResultDto { Item = trajectoryRow }; + + return validationResult; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs index 401e2423..e633a875 100644 --- a/AsbCloudInfrastructure/XLExtentions.cs +++ b/AsbCloudInfrastructure/XLExtentions.cs @@ -90,7 +90,7 @@ internal static class XLExtentions catch { throw new FileFormatException( - $"Лист '{cell.Worksheet.Name}'. Ячейка: ({cell.Address.RowNumber},{cell.Address.ColumnNumber}) содержит некорректное значение"); + $"Лист '{cell.Worksheet.Name}'. {cell.Address.RowNumber} строка содержит некорректное значение в {cell.Address.ColumnNumber} столбце"); } } } \ No newline at end of file diff --git a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs index b5c722b4..1fddd56d 100644 --- a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs +++ b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs @@ -1,6 +1,10 @@ -using System.Linq; +using System; +using System.Linq; using AsbCloudApp.Data.Trajectory; +using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; using Xunit; namespace AsbCloudWebApi.Tests.Services.Trajectory; @@ -8,8 +12,20 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory; public class TrajectoryParserTest { private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates"; + + private readonly IServiceProvider serviceProviderMock = Substitute.For(); + private readonly IServiceScope serviceScopeMock = Substitute.For(); + private readonly IServiceScopeFactory serviceScopeFactoryMock = Substitute.For(); - private static readonly ParserServiceFactory parserServiceFactory = new(); + private readonly ParserServiceFactory parserServiceFactory; + + public TrajectoryParserTest() + { + serviceScopeFactoryMock.CreateScope().Returns(serviceScopeMock); + ((ISupportRequiredService)serviceProviderMock).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactoryMock); + + parserServiceFactory = new ParserServiceFactory(serviceProviderMock); + } [Fact] public void Parse_trajectory_plan() @@ -20,8 +36,10 @@ public class TrajectoryParserTest if (stream is null) Assert.Fail("Файла для импорта не существует"); - var parserService = parserServiceFactory.GetParser(ParserServiceFactory.IdTrajectoryPlanParserService); - var trajectoryRows = parserService.Parse(stream); + var parserService = parserServiceFactory.Create( + ParserServiceFactory.IdTrajectoryPlanParserService); + + var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); Assert.Equal(3, trajectoryRows.Item.Count()); } @@ -35,8 +53,10 @@ public class TrajectoryParserTest if (stream is null) Assert.Fail("Файла для импорта не существует"); - var parserService = parserServiceFactory.GetParser(ParserServiceFactory.IdTrajectoryFactManualParserService); - var trajectoryRows = parserService.Parse(stream); + var parserService = parserServiceFactory.Create( + ParserServiceFactory.IdTrajectoryFactManualParserService); + + var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); Assert.Equal(4, trajectoryRows.Item.Count()); }