From 1b3c06c92763a4c5f5a4773b0ec9f9b3ed6652b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Thu, 8 Feb 2024 12:50:14 +0300 Subject: [PATCH 01/14] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BF=D0=B0=D1=80=D1=81=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Добавлен шаблон для сообщений 2. Поправлен naming у сервисов парсинга траекторий 3. Удалена регистрация зависимостей парсеров траекторий 4. Внутри фабрики добавлено создание отдельного scope. Фикс нейминга констант --- AsbCloudApp/Services/IParserService.cs | 3 +- AsbCloudInfrastructure/DependencyInjection.cs | 3 -- AsbCloudInfrastructure/ParserServiceBase.cs | 51 ++++++++++++++++++- .../Services/ParserServiceFactory.cs | 22 ++++++-- ...rvice.cs => TrajectoryFactManualParser.cs} | 4 +- ...ryParserService.cs => TrajectoryParser.cs} | 9 ++-- ...rserService.cs => TrajectoryPlanParser.cs} | 4 +- AsbCloudInfrastructure/XLExtentions.cs | 6 ++- .../Trajectory/TrajectoryParserTest.cs | 4 +- .../TrajectoryFactManualController.cs | 2 +- .../Trajectory/TrajectoryPlanController.cs | 2 +- 11 files changed, 82 insertions(+), 28 deletions(-) rename AsbCloudInfrastructure/Services/Trajectory/Parser/{TrajectoryFactManualParserService.cs => TrajectoryFactManualParser.cs} (85%) rename AsbCloudInfrastructure/Services/Trajectory/Parser/{TrajectoryParserService.cs => TrajectoryParser.cs} (81%) rename AsbCloudInfrastructure/Services/Trajectory/Parser/{TrajectoryPlanParserService.cs => TrajectoryPlanParser.cs} (87%) diff --git a/AsbCloudApp/Services/IParserService.cs b/AsbCloudApp/Services/IParserService.cs index 2cfd8eb7..252aee28 100644 --- a/AsbCloudApp/Services/IParserService.cs +++ b/AsbCloudApp/Services/IParserService.cs @@ -1,6 +1,4 @@ using System.IO; -using System.Threading; -using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; @@ -35,4 +33,5 @@ public interface IParserService : IParserService /// public interface IParserService { + const string MessageTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}"; } \ No newline at end of file diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 5b131299..afab0a5a 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -45,7 +45,6 @@ using AsbCloudDb.Model.WellSections; using AsbCloudInfrastructure.Services.ProcessMaps; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Requests; -using AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure { @@ -205,8 +204,6 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/AsbCloudInfrastructure/ParserServiceBase.cs b/AsbCloudInfrastructure/ParserServiceBase.cs index d98404e7..bcceb80d 100644 --- a/AsbCloudInfrastructure/ParserServiceBase.cs +++ b/AsbCloudInfrastructure/ParserServiceBase.cs @@ -21,21 +21,68 @@ public abstract class ParserServiceBase : IParserService ValidationResults { get; } = new List(); + + protected abstract string SheetName { get; } + public abstract ParserResultDto Parse(Stream file, TOptions options); + public abstract Stream GetTemplateFile(); + protected virtual ValidationResultDto ValidateRow(int rowNumber, + IDictionary columnNumbers, + TDto dto) + { + var validationContext = new ValidationContext(dto, serviceProvider: null, items: null); + + if (Validator.TryValidateObject(dto, validationContext, ValidationResults, true)) + { + var validRow = new ValidationResultDto + { + Item = dto + }; + + return validRow; + } + + var invalidRow = new ValidationResultDto + { + Item = dto, + }; + + var warnings = new List(); + + foreach (var validationResult in from v in ValidationResults + let memberNames = v.MemberNames.Where(columnNumbers.ContainsKey) + select memberNames.Select(x => + { + var columnNumber = columnNumbers[x]; + var errorMessage = v.ErrorMessage; + var warningMessage = string.Format(IParserService.MessageTemplate, SheetName, rowNumber, columnNumber, errorMessage); + var warning = new ValidationResult(warningMessage, new[] { x }); + return warning; + })) + { + warnings.AddRange(validationResult); + } + + invalidRow.Warnings = warnings; + + return invalidRow; + } + protected virtual ParserResultDto ParseExcelSheet(IXLWorksheet sheet, Func> parseRow, int columnCount, int headerRowsCount = 0) { if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount) - throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов."); + throw new FileFormatException($"Лист {SheetName} содержит меньшее количество столбцов."); var count = sheet.RowsUsed().Count() - headerRowsCount; if (count > 1024) - throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк."); + throw new FileFormatException($"Лист {SheetName} содержит слишком большое количество строк."); if (count <= 0) return new ParserResultDto(); diff --git a/AsbCloudInfrastructure/Services/ParserServiceFactory.cs b/AsbCloudInfrastructure/Services/ParserServiceFactory.cs index 5b852a8e..91fdbebc 100644 --- a/AsbCloudInfrastructure/Services/ParserServiceFactory.cs +++ b/AsbCloudInfrastructure/Services/ParserServiceFactory.cs @@ -3,23 +3,30 @@ using System.Collections.Generic; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Services; +using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; using AsbCloudInfrastructure.Services.Trajectory.Parser; +using Microsoft.Extensions.DependencyInjection; namespace AsbCloudInfrastructure.Services; -public class ParserServiceFactory +public class ParserServiceFactory : IDisposable { - public const int IdTrajectoryFactManualParserService = 1; - public const int IdTrajectoryPlanParserService = 2; + public const int IdTrajectoryFactManualParser = 1; + public const int IdTrajectoryPlanParser = 2; + public const int IdProcessMapPlanDrillingParser = 3; private readonly IDictionary> parsers; + private readonly IServiceScope serviceScope; public ParserServiceFactory(IServiceProvider serviceProvider) { + serviceScope = serviceProvider.CreateScope(); + parsers = new Dictionary> { - { IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService(serviceProvider) }, - { IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService(serviceProvider) } + { IdTrajectoryPlanParser, () => new TrajectoryPlanParser(serviceScope.ServiceProvider) }, + { IdTrajectoryFactManualParser, () => new TrajectoryFactManualParser(serviceScope.ServiceProvider) }, + { IdProcessMapPlanDrillingParser, () => new ProcessMapPlanDrillingParser(serviceScope.ServiceProvider) } }; } @@ -33,4 +40,9 @@ public class ParserServiceFactory return parserService.Invoke() as IParserService ?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа"); } + + public void Dispose() + { + serviceScope.Dispose(); + } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParserService.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs similarity index 85% rename from AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParserService.cs rename to AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs index 6e4034a5..ad173db7 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParserService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs @@ -5,12 +5,12 @@ using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public class TrajectoryFactManualParserService : TrajectoryParserService +public class TrajectoryFactManualParser : TrajectoryParser { protected override string SheetName => "Фактическая траектория"; protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx"; - public TrajectoryFactManualParserService(IServiceProvider serviceProvider) + public TrajectoryFactManualParser(IServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParserService.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs similarity index 81% rename from AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParserService.cs rename to AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs index b306e602..410b1bed 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParserService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs @@ -2,26 +2,23 @@ using AsbCloudApp.Data.Trajectory; using ClosedXML.Excel; using System.IO; -using System.Linq; using System.Reflection; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public abstract class TrajectoryParserService : ParserServiceBase +public abstract class TrajectoryParser : ParserServiceBase where T : TrajectoryGeoDto { private const int HeaderRowsCount = 2; private const int ColumnCount = 6; - protected TrajectoryParserService(IServiceProvider serviceProvider) + protected TrajectoryParser(IServiceProvider serviceProvider) : base(serviceProvider) { } - - protected abstract string SheetName { get; } - + protected abstract string TemplateFileName { get; } protected abstract ValidationResultDto ParseRow(IXLRow row); diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParserService.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs similarity index 87% rename from AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParserService.cs rename to AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs index fbf5a537..7e400024 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParserService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs @@ -5,12 +5,12 @@ using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public class TrajectoryPlanParserService : TrajectoryParserService +public class TrajectoryPlanParser : TrajectoryParser { protected override string SheetName => "Плановая траектория"; protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx"; - public TrajectoryPlanParserService(IServiceProvider serviceProvider) + public TrajectoryPlanParser(IServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs index 0aeeca00..8fc0220c 100644 --- a/AsbCloudInfrastructure/XLExtentions.cs +++ b/AsbCloudInfrastructure/XLExtentions.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Linq; +using AsbCloudApp.Services; namespace AsbCloudInfrastructure; @@ -39,8 +40,9 @@ public static class XLExtentions } catch { - throw new FileFormatException( - $"Лист '{cell.Worksheet.Name}'. {cell.Address.RowNumber} строка содержит некорректное значение в {cell.Address.ColumnNumber} столбце"); + var message = string.Format(IParserService.MessageTemplate, cell.Worksheet.Name, cell.Address.RowNumber, + cell.Address.ColumnNumber, "Содержит некорректное значение"); + throw new FileFormatException(message); } } } \ No newline at end of file diff --git a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs index 1fddd56d..2a90be08 100644 --- a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs +++ b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs @@ -37,7 +37,7 @@ public class TrajectoryParserTest Assert.Fail("Файла для импорта не существует"); var parserService = parserServiceFactory.Create( - ParserServiceFactory.IdTrajectoryPlanParserService); + ParserServiceFactory.IdTrajectoryPlanParser); var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); @@ -54,7 +54,7 @@ public class TrajectoryParserTest Assert.Fail("Файла для импорта не существует"); var parserService = parserServiceFactory.Create( - ParserServiceFactory.IdTrajectoryFactManualParserService); + ParserServiceFactory.IdTrajectoryFactManualParser); var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs index 97b84b07..9113acc8 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs @@ -24,7 +24,7 @@ public class TrajectoryFactManualController : TrajectoryEditableController Date: Fri, 9 Feb 2024 09:24:55 +0300 Subject: [PATCH 02/14] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=B8=D0=BD=D1=84=D1=80=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=8B=20=D0=B8=D0=BD=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=BE=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AssemblyExtensions.cs | 25 +++++++ .../Converters/ValidationResultConverter.cs | 68 +++++++++++++++++++ .../WebAppFactoryFixture.cs | 4 +- 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 AsbCloudWebApi.IntegrationTests/AssemblyExtensions.cs create mode 100644 AsbCloudWebApi.IntegrationTests/Converters/ValidationResultConverter.cs diff --git a/AsbCloudWebApi.IntegrationTests/AssemblyExtensions.cs b/AsbCloudWebApi.IntegrationTests/AssemblyExtensions.cs new file mode 100644 index 00000000..d4013aa1 --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/AssemblyExtensions.cs @@ -0,0 +1,25 @@ +using System.Reflection; + +namespace AsbCloudWebApi.IntegrationTests; + +internal static class AssemblyExtensions +{ + internal static Stream GetFileCopyStream(this Assembly assembly, string templateName) + { + var resourceName = assembly + .GetManifestResourceNames() + .FirstOrDefault(n => n.EndsWith(templateName)); + + if (string.IsNullOrWhiteSpace(resourceName)) + throw new ArgumentNullException(nameof(resourceName)); + + using var stream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream(resourceName); + + var memoryStream = new MemoryStream(); + stream?.CopyTo(memoryStream); + memoryStream.Position = 0; + + return memoryStream; + } +} \ No newline at end of file diff --git a/AsbCloudWebApi.IntegrationTests/Converters/ValidationResultConverter.cs b/AsbCloudWebApi.IntegrationTests/Converters/ValidationResultConverter.cs new file mode 100644 index 00000000..15423bc0 --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/Converters/ValidationResultConverter.cs @@ -0,0 +1,68 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace AsbCloudWebApi.IntegrationTests.Converters; + +public class ValidationResultConverter : JsonConverter +{ + public override ValidationResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected the start of an object."); + } + + string? errorMessage = null; + List? memberNames = null; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException($"Unexpected token type: {reader.TokenType}"); + } + + var propertyName = reader.GetString(); + reader.Read(); + + switch (propertyName) + { + case "errorMessage": + errorMessage = reader.GetString(); + break; + case "memberNames": + if (reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException("Expected the start of an array for 'memberNames'."); + } + memberNames = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + memberNames.Add(reader.GetString() ?? string.Empty); + } + break; + default: + reader.Skip(); + break; + } + } + + if (errorMessage == null) + { + throw new JsonException("Missing 'errorMessage' property."); + } + + return new ValidationResult(errorMessage, memberNames ?? Enumerable.Empty()); + } + + public override void Write(Utf8JsonWriter writer, ValidationResult value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs b/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs index af7303b6..aa8bd27e 100644 --- a/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs +++ b/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Refit; using System.Net.Http.Headers; using System.Text.Json; +using AsbCloudWebApi.IntegrationTests.Converters; using Xunit; namespace AsbCloudWebApi.IntegrationTests; @@ -18,7 +19,8 @@ public class WebAppFactoryFixture : WebApplicationFactory, private static readonly JsonSerializerOptions jsonSerializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true + PropertyNameCaseInsensitive = true, + Converters = { new ValidationResultConverter() } }; private static readonly RefitSettings refitSettings = new RefitSettings(new SystemTextJsonContentSerializer(jsonSerializerOptions)); From aff243626a80df0eb381c711c50bf7dc705e4a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 9 Feb 2024 09:26:50 +0300 Subject: [PATCH 03/14] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B0=20=D0=BF=D0=B0?= =?UTF-8?q?=D1=80=D1=81=D0=B5=D1=80=D0=B0,=20=D1=82=D0=B5=D1=81=D1=82=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0=20=D1=80=D0=B0=D1=81=D1=88?= =?UTF-8?q?=D0=B8=D1=80=D0=B5=D0=BD=D0=B8=D1=8F=20GetCellValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudInfrastructure/ParserServiceBase.cs | 8 ++++++++ AsbCloudWebApi.Tests/XLExtensionsTests.cs | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/AsbCloudInfrastructure/ParserServiceBase.cs b/AsbCloudInfrastructure/ParserServiceBase.cs index bcceb80d..c0ea8272 100644 --- a/AsbCloudInfrastructure/ParserServiceBase.cs +++ b/AsbCloudInfrastructure/ParserServiceBase.cs @@ -35,6 +35,14 @@ public abstract class ParserServiceBase : IParserService diff --git a/AsbCloudWebApi.Tests/XLExtensionsTests.cs b/AsbCloudWebApi.Tests/XLExtensionsTests.cs index 7f5fd781..601e71e0 100644 --- a/AsbCloudWebApi.Tests/XLExtensionsTests.cs +++ b/AsbCloudWebApi.Tests/XLExtensionsTests.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using AsbCloudInfrastructure; using ClosedXML.Excel; using Xunit; @@ -112,6 +113,16 @@ public class XLExtensionsTests Assert.Equal(DateTimeKind.Unspecified, actualValue.Kind); } + [Fact] + public void GetCellValue_returns_exception() + { + //arrange + SetCellValue("test"); + + //assert + Assert.Throws(() => GetCell(cellUsed).GetCellValue()); + } + [Fact] public void GetCellValue_returns_nullable() { From f2ca89dc8dee0521fef773e1b25d483a245c3c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 9 Feb 2024 09:32:31 +0300 Subject: [PATCH 04/14] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=81=D0=B8=D0=BD?= =?UTF-8?q?=D0=B3=20=D0=A0=D0=A2=D0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProcessMapPlan/ProcessMapPlanBaseDto.cs | 7 +- .../ProcessMapPlanDrillingDto.cs | 5 + .../IProcessMapPlanImportService.cs | 4 +- .../AsbCloudInfrastructure.csproj | 1 + .../Parser/ProcessMapPlanDrillingParser.cs | 139 ++++++++++++++++++ .../Parser/ProcessMapPlanParser.cs | 48 ++++++ .../ProcessMapPlanDrillingTemplate.xlsx | Bin 0 -> 12611 bytes ...ProcessMapPlanImportWellDrillingService.cs | 2 + .../ProcessMapPlanBaseController.cs | 79 ++++++++-- .../ProcessMapPlanDrillingController.cs | 17 ++- 10 files changed, 283 insertions(+), 19 deletions(-) create mode 100644 AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs create mode 100644 AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs create mode 100644 AsbCloudInfrastructure/Services/ProcessMapPlan/Templates/ProcessMapPlanDrillingTemplate.xlsx diff --git a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs index a6d4fd56..e8404c7f 100644 --- a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs +++ b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs @@ -19,6 +19,11 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat [Range(1, int.MaxValue, ErrorMessage = "Id секции скважины не может быть меньше 1")] public int IdWellSectionType { get; set; } + /// + /// Название секции + /// + public string? Section { get; set; } + /// /// Глубина по стволу от, м /// @@ -41,6 +46,6 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat public virtual IEnumerable Validate(ValidationContext validationContext) { if(DepthEnd <= DepthStart) - yield return new ("глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) }); + yield return new ("Глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) }); } } \ No newline at end of file diff --git a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs index 15f30300..7ab7db5d 100644 --- a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs +++ b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs @@ -12,6 +12,11 @@ public class ProcessMapPlanDrillingDto : ProcessMapPlanBaseDto /// [Range(1, 2, ErrorMessage = "Id режима должен быть либо 1-ротор либо 2-слайд")] public int IdMode { get; set; } + + /// + /// Название режима бурения + /// + public string? Mode { get; set; } /// /// Осевая нагрузка, т план diff --git a/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs b/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs index 7b30b80d..a0c3d4ec 100644 --- a/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs +++ b/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Threading.Tasks; using System.Threading; @@ -7,6 +8,7 @@ namespace AsbCloudApp.Services.ProcessMaps; /// /// Сервис импорта РТК /// +[Obsolete] public interface IProcessMapPlanImportService { /// diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj index cdb57ae5..ba012eb5 100644 --- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj +++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj @@ -36,6 +36,7 @@ + diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs new file mode 100644 index 00000000..cab930b4 --- /dev/null +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using AsbCloudApp.Data; +using AsbCloudApp.Data.ProcessMapPlan; +using AsbCloudApp.Repositories; +using AsbCloudApp.Services; +using ClosedXML.Excel; +using Microsoft.Extensions.DependencyInjection; + +namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; + +public class ProcessMapPlanDrillingParser : ProcessMapPlanParser +{ + #region Columns + + private const int columnSection = 1; + private const int columnMode = 2; + private const int columnDepthStart = 3; + private const int columnDepthEnd = 4; + private const int columnPressurePlan = 5; + private const int columnPressureLimitMax = 6; + private const int columnAxialLoadPlan = 7; + private const int columnAxialLoadLimitMax = 8; + private const int columnTopDriveTorquePlan = 9; + private const int columnTopDriveTorqueLimitMax = 10; + private const int columnTopDriveSpeedPlan = 11; + private const int columnTopDriveSpeedLimitMax = 12; + private const int columnFlowPlan = 13; + private const int columnFlowLimitMax = 14; + private const int columnRopPlan = 15; + private const int columnUsageSaub = 16; + private const int columnUsageSpin = 17; + private const int columnComment = 18; + + #endregion + + private readonly IEnumerable sections; + + public ProcessMapPlanDrillingParser(IServiceProvider serviceProvider) + : base(serviceProvider) + { + var wellOperationRepository = serviceProvider.GetRequiredService(); + + sections = wellOperationRepository.GetSectionTypes(); + } + + protected override string SheetName => "План"; + + protected override string TemplateFileName => "ProcessMapPlanDrillingTemplate.xlsx"; + + protected override ValidationResultDto ParseRow(IXLRow row) + { + var sectionCaption = row.Cell(columnSection).GetCellValue()?.Trim().ToLower(); + var modeName = row.Cell(columnMode).GetCellValue()?.Trim().ToLower(); + var depthStart = row.Cell(columnDepthStart).GetCellValue(); + var depthEnd = row.Cell(columnDepthEnd).GetCellValue(); + var deltaPressurePlan = row.Cell(columnPressurePlan).GetCellValue(); + var deltaPressureLimitMax = row.Cell(columnPressureLimitMax).GetCellValue(); + var axialLoadPlan = row.Cell(columnAxialLoadPlan).GetCellValue(); + var axialLoadLimitMax = row.Cell(columnAxialLoadLimitMax).GetCellValue(); + var topDriveTorquePlan = row.Cell(columnTopDriveTorquePlan).GetCellValue(); + var topDriveTorqueLimitMax = row.Cell(columnTopDriveTorqueLimitMax).GetCellValue(); + var topDriveSpeedPlan = row.Cell(columnTopDriveSpeedPlan).GetCellValue(); + var topDriveSpeedLimitMax = row.Cell(columnTopDriveSpeedLimitMax).GetCellValue(); + var flowPlan = row.Cell(columnFlowPlan).GetCellValue(); + var flowLimitMax = row.Cell(columnFlowLimitMax).GetCellValue(); + var ropPlan = row.Cell(columnRopPlan).GetCellValue(); + var usageSaub = row.Cell(columnUsageSaub).GetCellValue(); + var usageSpin = row.Cell(columnUsageSpin).GetCellValue(); + var comment = row.Cell(columnComment).GetCellValue() ?? string.Empty; + + var section = sections.FirstOrDefault(s => + string.Equals(s.Caption.Trim(), sectionCaption?.Trim(), StringComparison.CurrentCultureIgnoreCase)); + + if (section is null) + { + var message = string.Format(IParserService.MessageTemplate, SheetName, row.RowNumber(), columnSection, + "Указана некорректная секция"); + throw new FileFormatException(message); + } + + var idMode = GetIdMode(modeName); + + if (idMode is null) + { + var message = string.Format(IParserService.MessageTemplate, SheetName, row.RowNumber(), columnSection, + "Указан некорректный режим бурения"); + throw new FileFormatException(message); + } + + var dto = new ProcessMapPlanDrillingDto + { + IdWellSectionType = section.Id, + Section = section.Caption, + IdMode = idMode.Value, + Mode = modeName, + DepthStart = depthStart, + DepthEnd = depthEnd, + AxialLoadPlan = axialLoadPlan, + AxialLoadLimitMax = axialLoadLimitMax, + DeltaPressurePlan = deltaPressurePlan, + DeltaPressureLimitMax = deltaPressureLimitMax, + TopDriveTorquePlan = topDriveTorquePlan, + TopDriveTorqueLimitMax = topDriveTorqueLimitMax, + TopDriveSpeedPlan = topDriveSpeedPlan, + TopDriveSpeedLimitMax = topDriveSpeedLimitMax, + FlowPlan = flowPlan, + FlowLimitMax = flowLimitMax, + RopPlan = ropPlan, + UsageSaub = usageSaub, + UsageSpin = usageSpin, + Comment = comment + }; + + var columnNumbers = new Dictionary + { + { nameof(ProcessMapPlanDrillingDto.DepthStart), columnDepthStart }, + { nameof(ProcessMapPlanDrillingDto.DepthEnd), columnDepthEnd }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), columnPressurePlan }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), columnPressureLimitMax }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), columnAxialLoadPlan }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), columnAxialLoadLimitMax }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), columnTopDriveTorquePlan }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), columnTopDriveTorqueLimitMax }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), columnTopDriveSpeedPlan }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), columnTopDriveSpeedLimitMax }, + { nameof(ProcessMapPlanDrillingDto.FlowPlan), columnFlowPlan }, + { nameof(ProcessMapPlanDrillingDto.FlowLimitMax), columnFlowLimitMax }, + { nameof(ProcessMapPlanDrillingDto.RopPlan), columnRopPlan }, + { nameof(ProcessMapPlanDrillingDto.UsageSaub), columnUsageSaub }, + { nameof(ProcessMapPlanDrillingDto.UsageSpin), columnUsageSpin }, + { nameof(ProcessMapPlanDrillingDto.Comment), columnComment } + }; + + return ValidateRow(row.RowNumber(), columnNumbers, dto); + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs new file mode 100644 index 00000000..5b7aab2d --- /dev/null +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using System.Reflection; +using AsbCloudApp.Data; +using AsbCloudApp.Data.ProcessMapPlan; +using AsbCloudApp.Requests.ParserOptions; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; + +public abstract class ProcessMapPlanParser : ParserServiceBase + where TDto : ProcessMapPlanBaseDto +{ + protected ProcessMapPlanParser(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + + private const int HeaderRowsCount = 2; + private const int ColumnCount = 18; + + protected abstract string TemplateFileName { get; } + + protected abstract ValidationResultDto ParseRow(IXLRow row); + + public override ParserResultDto Parse(Stream file, IParserOptionsRequest options) + { + using var workbook = new XLWorkbook(file); + + var sheet = workbook.GetWorksheet(SheetName); + + var processMaps = ParseExcelSheet(sheet, ParseRow, ColumnCount, HeaderRowsCount); + return processMaps; + } + + public override Stream GetTemplateFile() => + Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName) + ?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден"); + + protected static int? GetIdMode(string? modeName) => + modeName?.Trim().ToLower() switch + { + "ручной" => 0, + "ротор" => 1, + "слайд" => 2, + _ => null + }; +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Templates/ProcessMapPlanDrillingTemplate.xlsx b/AsbCloudInfrastructure/Services/ProcessMapPlan/Templates/ProcessMapPlanDrillingTemplate.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bdf13143446f1d64a808c157f915d943dcf5d610 GIT binary patch literal 12611 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 literal 0 HcmV?d00001 diff --git a/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs b/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs index f322c798..454daa7a 100644 --- a/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs +++ b/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs @@ -18,6 +18,8 @@ namespace AsbCloudInfrastructure.Services.ProcessMaps.WellDrilling; /* * password for ProcessMapImportTemplate.xlsx is ASB2020! */ + +[Obsolete] public class ProcessMapPlanImportWellDrillingService : IProcessMapPlanImportService { private readonly IProcessMapPlanRepository processMapPlanWellDrillingRepository; diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs index c3fd0c49..fb55bcf7 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs @@ -9,8 +9,13 @@ using Microsoft.AspNetCore.Http; using AsbCloudApp.Exceptions; using AsbCloudApp.Requests; using System; +using System.IO; using AsbCloudApp.Services; using System.Linq; +using AsbCloudApp.Data; +using AsbCloudApp.Requests.ParserOptions; +using AsbCloudInfrastructure.Services; +using AsbCloudWebApi.Controllers.Interfaces; namespace AsbCloudWebApi.Controllers.ProcessMapPlan; @@ -20,17 +25,38 @@ namespace AsbCloudWebApi.Controllers.ProcessMapPlan; [ApiController] [Route("api/well/{idWell}/[controller]")] [Authorize] -public abstract class ProcessMapPlanBaseController : ControllerBase - where TDto : ProcessMapPlanBaseDto +public abstract class ProcessMapPlanBaseController : ControllerBase, + IControllerWithParser + where TDto : ProcessMapPlanBaseDto { - private readonly IChangeLogRepository repository; - private readonly IWellService wellService; + private readonly IChangeLogRepository repository; + private readonly IWellService wellService; + private readonly IParserService parserService; - public ProcessMapPlanBaseController(IChangeLogRepository repository, IWellService wellService) - { - this.repository = repository; - this.wellService = wellService; - } + protected ProcessMapPlanBaseController(IChangeLogRepository repository, + IWellService wellService, + ParserServiceFactory parserFactory, + int idParserService) + { + this.repository = repository; + this.wellService = wellService; + parserService = parserFactory.Create(idParserService); + } + + protected abstract string TemplateFileName { get; } + + ActionResult> IControllerWithParser.Parse(Stream file, IParserOptionsRequest options) + { + try + { + var parserResult = parserService.Parse(file, options); + return Ok(parserResult); + } + catch (FileFormatException ex) + { + return this.ValidationBadRequest("files", ex.Message); + } + } /// /// Добавление @@ -190,6 +216,39 @@ public abstract class ProcessMapPlanBaseController : ControllerBase var result = await repository.UpdateOrInsertRange(idUser, dtos, token); return Ok(result); } + + /// + /// Импорт РТК из excel (xlsx) файла + /// + /// + /// + /// + /// + [HttpPost("parse")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public async Task>> Parse(int idWell, + [FromForm] IFormFileCollection files, + CancellationToken token) + { + await AssertUserHasAccessToWell(idWell, token); + + return this.ParseExcelFile(files, IParserOptionsRequest.Empty()); + } + + /// + /// Получение шаблона для заполнения РТК + /// + /// + [HttpGet("template")] + [AllowAnonymous] + [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public IActionResult GetTemplate() + { + var stream = parserService.GetTemplateFile(); + return File(stream, "application/octet-stream", TemplateFileName); + } /// /// returns user id, if he has access to well @@ -221,4 +280,4 @@ public abstract class ProcessMapPlanBaseController : ControllerBase var idUser = User.GetUserId() ?? throw new ForbidException("Неизвестный пользователь"); return idUser; } -} +} \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs index b0a20f1e..8855c54f 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs @@ -2,15 +2,18 @@ using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; +using AsbCloudInfrastructure.Services; namespace AsbCloudWebApi.Controllers.ProcessMapPlan; public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController { - public ProcessMapPlanDrillingController( - IChangeLogRepository repository, - IWellService wellService) - : base(repository, wellService) - { - } -} + public ProcessMapPlanDrillingController(IChangeLogRepository repository, + IWellService wellService, + ParserServiceFactory parserFactory) + : base(repository, wellService, parserFactory, ParserServiceFactory.IdProcessMapPlanDrillingParser) + { + } + + protected override string TemplateFileName => "ЕЦП_шаблон_файла_РТК_план_бурение.xlsx"; +} \ No newline at end of file From 06d3e9851a79d1a0893b9c448bcab399591735a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 9 Feb 2024 09:33:03 +0300 Subject: [PATCH 05/14] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82=D1=8B=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3=D0=B0=20=D0=A0=D0=A2=D0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AsbCloudWebApi.IntegrationTests.csproj | 5 ++ .../Clients/IProcessMapPlanDrillingClient.cs | 8 +- .../Files/ProcessMapPlanDrillingInvalid.xlsx | Bin 0 -> 12846 bytes .../Files/ProcessMapPlanDrillingValid.xlsx | Bin 0 -> 12766 bytes .../ProcessMapPlanDrillingControllerTest.cs | 73 ++++++++++++++++-- 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/Files/ProcessMapPlanDrillingInvalid.xlsx create mode 100644 AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/Files/ProcessMapPlanDrillingValid.xlsx rename AsbCloudWebApi.IntegrationTests/Controllers/{ => ProcessMapPlan}/ProcessMapPlanDrillingControllerTest.cs (89%) diff --git a/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj b/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj index f20fc316..a9d7713a 100644 --- a/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj +++ b/AsbCloudWebApi.IntegrationTests/AsbCloudWebApi.IntegrationTests.csproj @@ -22,4 +22,9 @@ + + + + + diff --git a/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs index 7c8c65e3..e60f5e90 100644 --- a/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs +++ b/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs @@ -1,6 +1,6 @@ -using AsbCloudApp.Data.ProcessMapPlan; +using AsbCloudApp.Data; +using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Requests; -using Microsoft.AspNetCore.Mvc; using Refit; namespace AsbCloudWebApi.IntegrationTests.Clients; @@ -32,4 +32,8 @@ public interface IProcessMapPlanDrillingClient [Put(BaseRoute)] Task> UpdateOrInsertRange(int idWell, IEnumerable dtos); + + [Multipart] + [Post(BaseRoute + "/parse")] + Task>> Parse(int idWell, [AliasAs("files")] IEnumerable streams); } diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/Files/ProcessMapPlanDrillingInvalid.xlsx b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/Files/ProcessMapPlanDrillingInvalid.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..09865489e5be5982baf66a671d837f3776a46010 GIT binary patch literal 12846 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 W2Yc7#KPX|a0E>5HD5L%3?0*2zCWtZs literal 0 HcmV?d00001 diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/Files/ProcessMapPlanDrillingValid.xlsx b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/Files/ProcessMapPlanDrillingValid.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b2bdd4466b5abc39e1e1c3340008cd23609be9cb GIT binary patch literal 12766 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 F`+pnDPWS)- literal 0 HcmV?d00001 diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlanDrillingControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs similarity index 89% rename from AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlanDrillingControllerTest.cs rename to AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs index c0ba7093..67844201 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlanDrillingControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs @@ -1,31 +1,35 @@ using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Requests; using AsbCloudDb.Model.ProcessMapPlan; -using AsbCloudDb.Model.ProcessMaps; using AsbCloudWebApi.IntegrationTests.Clients; using Mapster; using Microsoft.EntityFrameworkCore; using System.Net; +using System.Reflection; +using AsbCloudDb.Model.ProcessMaps; +using AsbCloudWebApi.IntegrationTests.Data; +using Refit; using Xunit; -namespace AsbCloudWebApi.IntegrationTests.Controllers; +namespace AsbCloudWebApi.IntegrationTests.Controllers.ProcessMapPlan; public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest { - private IProcessMapPlanDrillingClient client; private readonly ProcessMapPlanDrillingDto dto = new (){ Id = 0, Creation = new(), Obsolete = null, IdState = 0, IdPrevious = null, - + IdWell = 1, - IdWellSectionType = 1, + Section = "Кондуктор", + IdWellSectionType = 3, DepthStart = 0.5, DepthEnd = 1.5, IdMode = 1, + Mode = "ротор", AxialLoadPlan = 2.718281, AxialLoadLimitMax = 3.1415926, DeltaPressurePlan = 4, @@ -73,6 +77,8 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest Comment = "это тестовая запись", }; + private IProcessMapPlanDrillingClient client; + public ProcessMapPlanDrillingControllerTest(WebAppFactoryFixture factory) : base(factory) { dbContext.CleanupDbSet(); @@ -109,6 +115,8 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest nameof(ProcessMapPlanDrillingDto.IdState), nameof(ProcessMapPlanDrillingDto.Author), nameof(ProcessMapPlanDrillingDto.Creation), + nameof(ProcessMapPlanDrillingDto.Mode), + nameof(ProcessMapPlanDrillingDto.Section) }; MatchHelper.Match(expected, actual, excludeProps); } @@ -556,4 +564,59 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest }; MatchHelper.Match(expected, actual, excludeProps); } + + [Fact] + public async Task Parse_returns_success() + { + //arrange + const string fileName = "ProcessMapPlanDrillingValid.xlsx"; + var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName); + + //act + var streamPart = new StreamPart(stream, fileName, "application/octet-stream"); + var response = await client.Parse(Defaults.Wells[0].Id, new[] { streamPart }); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var parserResult = response.Content; + + Assert.NotNull(parserResult); + Assert.Single(parserResult.Item); + Assert.True(parserResult.IsValid); + + var row = parserResult.Item.First(); + var dtoActual = row.Item; + + Assert.True(row.IsValid); + + var excludeProps = new[] { nameof(ProcessMapPlanDrillingDto.IdWell) }; + MatchHelper.Match(dto, dtoActual, excludeProps); + } + + [Fact] + public async Task Parse_returns_success_for_result_with_warnings() + { + //arrange + const string fileName = "ProcessMapPlanDrillingInvalid.xlsx"; + var stream = Assembly.GetExecutingAssembly().GetFileCopyStream(fileName); + + //act + var streamPart = new StreamPart(stream, fileName, "application/octet-stream"); + var response = await client.Parse(Defaults.Wells[0].Id, new[] { streamPart }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var parserResult = response.Content; + + Assert.NotNull(parserResult); + Assert.False(parserResult.IsValid); + Assert.Single(parserResult.Warnings); + Assert.Single(parserResult.Item); + + var row = parserResult.Item.First(); + + Assert.False(row.IsValid); + Assert.Equal(2, row.Warnings.Count()); + } } From 886abdcf06af96c0e15300157c8abd980e5a194f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Sun, 11 Feb 2024 11:21:38 +0300 Subject: [PATCH 06/14] Moved ParserServiceBase --- AsbCloudInfrastructure/{ => Services}/ParserServiceBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename AsbCloudInfrastructure/{ => Services}/ParserServiceBase.cs (98%) diff --git a/AsbCloudInfrastructure/ParserServiceBase.cs b/AsbCloudInfrastructure/Services/ParserServiceBase.cs similarity index 98% rename from AsbCloudInfrastructure/ParserServiceBase.cs rename to AsbCloudInfrastructure/Services/ParserServiceBase.cs index c0ea8272..8fa202af 100644 --- a/AsbCloudInfrastructure/ParserServiceBase.cs +++ b/AsbCloudInfrastructure/Services/ParserServiceBase.cs @@ -8,7 +8,7 @@ using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Services; using ClosedXML.Excel; -namespace AsbCloudInfrastructure; +namespace AsbCloudInfrastructure.Services; public abstract class ParserServiceBase : IParserService where TDto : class, IId From 4fb94a443f65da1e1611016394d635a40a7531d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Mon, 12 Feb 2024 15:25:18 +0300 Subject: [PATCH 07/14] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Trajectory/TrajectoryGeoDto.cs | 11 +- AsbCloudApp/Services/IParserService.cs | 1 - .../Services/ParserExcelService.cs | 114 ++++++++++++++++ .../Services/ParserServiceBase.cs | 127 ------------------ .../Parser/ProcessMapPlanDrillingParser.cs | 97 ++++++------- .../Parser/ProcessMapPlanParser.cs | 8 +- .../Parser/TrajectoryFactManualParser.cs | 55 +++++--- .../Trajectory/Parser/TrajectoryParser.cs | 6 +- .../Trajectory/Parser/TrajectoryPlanParser.cs | 58 +++++--- .../ValidationExtensions.cs | 17 +++ AsbCloudInfrastructure/XLExtentions.cs | 5 +- AsbCloudInfrastructure/XLMessageTemplates.cs | 12 ++ 12 files changed, 273 insertions(+), 238 deletions(-) create mode 100644 AsbCloudInfrastructure/Services/ParserExcelService.cs delete mode 100644 AsbCloudInfrastructure/Services/ParserServiceBase.cs create mode 100644 AsbCloudInfrastructure/ValidationExtensions.cs create mode 100644 AsbCloudInfrastructure/XLMessageTemplates.cs diff --git a/AsbCloudApp/Data/Trajectory/TrajectoryGeoDto.cs b/AsbCloudApp/Data/Trajectory/TrajectoryGeoDto.cs index e47a66e8..237c20b2 100644 --- a/AsbCloudApp/Data/Trajectory/TrajectoryGeoDto.cs +++ b/AsbCloudApp/Data/Trajectory/TrajectoryGeoDto.cs @@ -1,11 +1,14 @@ using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; namespace AsbCloudApp.Data.Trajectory { /// /// Базовая географическая траектория /// - public abstract class TrajectoryGeoDto : IId + public abstract class TrajectoryGeoDto : IId, IValidatableObject { /// /// ИД строки с координатами @@ -49,5 +52,11 @@ namespace AsbCloudApp.Data.Trajectory /// ИД пользователя /// public int IdUser { get; set; } + + /// + public IEnumerable Validate(ValidationContext validationContext) + { + return Enumerable.Empty(); + } } } diff --git a/AsbCloudApp/Services/IParserService.cs b/AsbCloudApp/Services/IParserService.cs index 252aee28..6fa095cd 100644 --- a/AsbCloudApp/Services/IParserService.cs +++ b/AsbCloudApp/Services/IParserService.cs @@ -33,5 +33,4 @@ public interface IParserService : IParserService /// public interface IParserService { - const string MessageTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}"; } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ParserExcelService.cs b/AsbCloudInfrastructure/Services/ParserExcelService.cs new file mode 100644 index 00000000..a9a28d9e --- /dev/null +++ b/AsbCloudInfrastructure/Services/ParserExcelService.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +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.Services; + +public abstract class ParserExcelService : IParserService + where TDto : class, IValidatableObject, IId + where TOptions : IParserOptionsRequest +{ + protected readonly IServiceProvider serviceProvider; + + protected ParserExcelService(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider; + } + + protected abstract string SheetName { get; } + + protected abstract IDictionary PropertyColumnNumbers { get; } + + public abstract ParserResultDto Parse(Stream file, TOptions options); + + public abstract Stream GetTemplateFile(); + + protected abstract TDto ParseRow(IXLRow xlRow); + + private ValidationResultDto Validate(TDto dto, int rowNumber) + { + var validationResults = new List(); + + var isValid = dto.Validate(validationResults); + + if (isValid) + { + var validDto = new ValidationResultDto() + { + Item = dto + }; + + return validDto; + } + + var invalidDto = new ValidationResultDto + { + Item = dto, + Warnings = validationResults + .SelectMany(v => v.MemberNames + .Where(PropertyColumnNumbers.ContainsKey) + .Select(m => + { + var columnNumber = PropertyColumnNumbers[m]; + var errorMessage = v.ErrorMessage; + var warningMessage = string.Format(XLMessageTemplates.ProblemDetailsTemplate, SheetName, rowNumber, columnNumber, errorMessage); + var warning = new ValidationResult(warningMessage, new[] { m }); + return warning; + })) + }; + + return invalidDto; + } + + protected virtual ParserResultDto ParseExcelSheet(IXLWorksheet sheet, + int columnCount, + int headerRowsCount = 0) + { + if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount) + throw new FileFormatException(string.Format(XLMessageTemplates.FewerColumnsTemplate, SheetName)); + + var count = sheet.RowsUsed().Count() - headerRowsCount; + + if (count > 1024) + throw new FileFormatException(string.Format(XLMessageTemplates.ExceedingMaxRowLimitTemplate, SheetName)); + + if (count <= 0) + return new ParserResultDto(); + + var valiationResults = new List>(count); + var warnings = new List(); + + for (var i = 0; i < count; i++) + { + var row = sheet.Row(1 + i + headerRowsCount); + + try + { + var dto = ParseRow(row); + var validationResult = Validate(dto, row.RowNumber()); + valiationResults.Add(validationResult); + } + catch (FileFormatException ex) + { + var warning = new ValidationResult(ex.Message); + warnings.Add(warning); + } + } + + var parserResult = new ParserResultDto + { + Item = valiationResults + }; + + if (warnings.Any()) + parserResult.Warnings = warnings; + + return parserResult; + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ParserServiceBase.cs b/AsbCloudInfrastructure/Services/ParserServiceBase.cs deleted file mode 100644 index 8fa202af..00000000 --- a/AsbCloudInfrastructure/Services/ParserServiceBase.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -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.Services; - -public abstract class ParserServiceBase : IParserService - where TDto : class, IId - where TOptions : IParserOptionsRequest -{ - protected readonly IServiceProvider serviceProvider; - - protected ParserServiceBase(IServiceProvider serviceProvider) - { - this.serviceProvider = serviceProvider; - } - - protected virtual ICollection ValidationResults { get; } = new List(); - - protected abstract string SheetName { get; } - - public abstract ParserResultDto Parse(Stream file, TOptions options); - - public abstract Stream GetTemplateFile(); - - protected virtual ValidationResultDto ValidateRow(int rowNumber, - IDictionary columnNumbers, - TDto dto) - { - var validationContext = new ValidationContext(dto, serviceProvider: null, items: null); - - if (dto is IValidatableObject dtoWithValidateMethod) - { - var validationResults = dtoWithValidateMethod.Validate(validationContext); - - foreach (var validationResult in validationResults) - ValidationResults.Add(validationResult); - } - - if (Validator.TryValidateObject(dto, validationContext, ValidationResults, true)) - { - var validRow = new ValidationResultDto - { - Item = dto - }; - - return validRow; - } - - var invalidRow = new ValidationResultDto - { - Item = dto, - }; - - var warnings = new List(); - - foreach (var validationResult in from v in ValidationResults - let memberNames = v.MemberNames.Where(columnNumbers.ContainsKey) - select memberNames.Select(x => - { - var columnNumber = columnNumbers[x]; - var errorMessage = v.ErrorMessage; - var warningMessage = string.Format(IParserService.MessageTemplate, SheetName, rowNumber, columnNumber, errorMessage); - var warning = new ValidationResult(warningMessage, new[] { x }); - return warning; - })) - { - warnings.AddRange(validationResult); - } - - invalidRow.Warnings = warnings; - - return invalidRow; - } - - protected virtual ParserResultDto ParseExcelSheet(IXLWorksheet sheet, - Func> parseRow, - int columnCount, - int headerRowsCount = 0) - { - if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount) - throw new FileFormatException($"Лист {SheetName} содержит меньшее количество столбцов."); - - var count = sheet.RowsUsed().Count() - headerRowsCount; - - if (count > 1024) - throw new FileFormatException($"Лист {SheetName} содержит слишком большое количество строк."); - - 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); - - try - { - var dto = parseRow.Invoke(row); - dtos.Add(dto); - } - catch (FileFormatException ex) - { - var warning = new ValidationResult(ex.Message); - warnings.Add(warning); - } - } - - var parserResult = new ParserResultDto - { - Item = dtos - }; - - if (warnings.Any()) - parserResult.Warnings = warnings; - - return parserResult; - } -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs index cab930b4..5ad9e2ea 100644 --- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs @@ -5,7 +5,6 @@ using System.Linq; using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Repositories; -using AsbCloudApp.Services; using ClosedXML.Excel; using Microsoft.Extensions.DependencyInjection; @@ -48,35 +47,39 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser "План"; + protected override IDictionary PropertyColumnNumbers => new Dictionary + { + { nameof(ProcessMapPlanDrillingDto.DepthStart), columnDepthStart }, + { nameof(ProcessMapPlanDrillingDto.DepthEnd), columnDepthEnd }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), columnPressurePlan }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), columnPressureLimitMax }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), columnAxialLoadPlan }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), columnAxialLoadLimitMax }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), columnTopDriveTorquePlan }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), columnTopDriveTorqueLimitMax }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), columnTopDriveSpeedPlan }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), columnTopDriveSpeedLimitMax }, + { nameof(ProcessMapPlanDrillingDto.FlowPlan), columnFlowPlan }, + { nameof(ProcessMapPlanDrillingDto.FlowLimitMax), columnFlowLimitMax }, + { nameof(ProcessMapPlanDrillingDto.RopPlan), columnRopPlan }, + { nameof(ProcessMapPlanDrillingDto.UsageSaub), columnUsageSaub }, + { nameof(ProcessMapPlanDrillingDto.UsageSpin), columnUsageSpin }, + { nameof(ProcessMapPlanDrillingDto.Comment), columnComment } + }; + protected override string TemplateFileName => "ProcessMapPlanDrillingTemplate.xlsx"; - protected override ValidationResultDto ParseRow(IXLRow row) + protected override ProcessMapPlanDrillingDto ParseRow(IXLRow row) { var sectionCaption = row.Cell(columnSection).GetCellValue()?.Trim().ToLower(); var modeName = row.Cell(columnMode).GetCellValue()?.Trim().ToLower(); - var depthStart = row.Cell(columnDepthStart).GetCellValue(); - var depthEnd = row.Cell(columnDepthEnd).GetCellValue(); - var deltaPressurePlan = row.Cell(columnPressurePlan).GetCellValue(); - var deltaPressureLimitMax = row.Cell(columnPressureLimitMax).GetCellValue(); - var axialLoadPlan = row.Cell(columnAxialLoadPlan).GetCellValue(); - var axialLoadLimitMax = row.Cell(columnAxialLoadLimitMax).GetCellValue(); - var topDriveTorquePlan = row.Cell(columnTopDriveTorquePlan).GetCellValue(); - var topDriveTorqueLimitMax = row.Cell(columnTopDriveTorqueLimitMax).GetCellValue(); - var topDriveSpeedPlan = row.Cell(columnTopDriveSpeedPlan).GetCellValue(); - var topDriveSpeedLimitMax = row.Cell(columnTopDriveSpeedLimitMax).GetCellValue(); - var flowPlan = row.Cell(columnFlowPlan).GetCellValue(); - var flowLimitMax = row.Cell(columnFlowLimitMax).GetCellValue(); - var ropPlan = row.Cell(columnRopPlan).GetCellValue(); - var usageSaub = row.Cell(columnUsageSaub).GetCellValue(); - var usageSpin = row.Cell(columnUsageSpin).GetCellValue(); - var comment = row.Cell(columnComment).GetCellValue() ?? string.Empty; var section = sections.FirstOrDefault(s => string.Equals(s.Caption.Trim(), sectionCaption?.Trim(), StringComparison.CurrentCultureIgnoreCase)); if (section is null) { - var message = string.Format(IParserService.MessageTemplate, SheetName, row.RowNumber(), columnSection, + var message = string.Format(XLMessageTemplates.ProblemDetailsTemplate, SheetName, row.RowNumber(), columnSection, "Указана некорректная секция"); throw new FileFormatException(message); } @@ -85,7 +88,7 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser(), + DepthEnd = row.Cell(columnDepthEnd).GetCellValue(), + AxialLoadPlan = row.Cell(columnAxialLoadPlan).GetCellValue(), + AxialLoadLimitMax = row.Cell(columnAxialLoadLimitMax).GetCellValue(), + DeltaPressurePlan = row.Cell(columnPressurePlan).GetCellValue(), + DeltaPressureLimitMax = row.Cell(columnPressureLimitMax).GetCellValue(), + TopDriveTorquePlan = row.Cell(columnTopDriveTorquePlan).GetCellValue(), + TopDriveTorqueLimitMax = row.Cell(columnTopDriveTorqueLimitMax).GetCellValue(), + TopDriveSpeedPlan = row.Cell(columnTopDriveSpeedPlan).GetCellValue(), + TopDriveSpeedLimitMax = row.Cell(columnTopDriveSpeedLimitMax).GetCellValue(), + FlowPlan = row.Cell(columnFlowPlan).GetCellValue(), + FlowLimitMax = row.Cell(columnFlowLimitMax).GetCellValue(), + RopPlan = row.Cell(columnRopPlan).GetCellValue(), + UsageSaub = row.Cell(columnUsageSaub).GetCellValue(), + UsageSpin = row.Cell(columnUsageSpin).GetCellValue(), + Comment = row.Cell(columnComment).GetCellValue() ?? string.Empty }; - var columnNumbers = new Dictionary - { - { nameof(ProcessMapPlanDrillingDto.DepthStart), columnDepthStart }, - { nameof(ProcessMapPlanDrillingDto.DepthEnd), columnDepthEnd }, - { nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), columnPressurePlan }, - { nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), columnPressureLimitMax }, - { nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), columnAxialLoadPlan }, - { nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), columnAxialLoadLimitMax }, - { nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), columnTopDriveTorquePlan }, - { nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), columnTopDriveTorqueLimitMax }, - { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), columnTopDriveSpeedPlan }, - { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), columnTopDriveSpeedLimitMax }, - { nameof(ProcessMapPlanDrillingDto.FlowPlan), columnFlowPlan }, - { nameof(ProcessMapPlanDrillingDto.FlowLimitMax), columnFlowLimitMax }, - { nameof(ProcessMapPlanDrillingDto.RopPlan), columnRopPlan }, - { nameof(ProcessMapPlanDrillingDto.UsageSaub), columnUsageSaub }, - { nameof(ProcessMapPlanDrillingDto.UsageSpin), columnUsageSpin }, - { nameof(ProcessMapPlanDrillingDto.Comment), columnComment } - }; - - return ValidateRow(row.RowNumber(), columnNumbers, dto); + return dto; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs index 5b7aab2d..ff76debb 100644 --- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs @@ -8,7 +8,7 @@ using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; -public abstract class ProcessMapPlanParser : ParserServiceBase +public abstract class ProcessMapPlanParser : ParserExcelService where TDto : ProcessMapPlanBaseDto { protected ProcessMapPlanParser(IServiceProvider serviceProvider) @@ -20,16 +20,14 @@ public abstract class ProcessMapPlanParser : ParserServiceBase ParseRow(IXLRow row); - + public override ParserResultDto Parse(Stream file, IParserOptionsRequest options) { using var workbook = new XLWorkbook(file); var sheet = workbook.GetWorksheet(SheetName); - var processMaps = ParseExcelSheet(sheet, ParseRow, ColumnCount, HeaderRowsCount); + var processMaps = ParseExcelSheet(sheet, ColumnCount, HeaderRowsCount); return processMaps; } diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs index ad173db7..e48fb575 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs @@ -1,5 +1,5 @@ using System; -using AsbCloudApp.Data; +using System.Collections.Generic; using AsbCloudApp.Data.Trajectory; using ClosedXML.Excel; @@ -7,33 +7,48 @@ namespace AsbCloudInfrastructure.Services.Trajectory.Parser; public class TrajectoryFactManualParser : TrajectoryParser { - protected override string SheetName => "Фактическая траектория"; - protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx"; + #region Columns + + private const int columnWellboreDepth = 1; + private const int columnZenithAngle = 2; + private const int columnAzimuthGeo = 3; + private const int columnAzimuthMagnetic = 4; + private const int columnVerticalDepth = 5; + private const int columnComment = 6; + + #endregion public TrajectoryFactManualParser(IServiceProvider serviceProvider) : base(serviceProvider) { } - - protected override ValidationResultDto ParseRow(IXLRow row) + + protected override string SheetName => "Фактическая траектория"; + + protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx"; + + protected override IDictionary PropertyColumnNumbers => new Dictionary { - var trajectoryRow = new TrajectoryGeoFactDto + { nameof(TrajectoryGeoFactDto.WellboreDepth), columnWellboreDepth }, + { nameof(TrajectoryGeoFactDto.ZenithAngle), columnZenithAngle }, + { nameof(TrajectoryGeoFactDto.AzimuthGeo), columnAzimuthGeo }, + { nameof(TrajectoryGeoFactDto.AzimuthMagnetic), columnAzimuthMagnetic }, + { nameof(TrajectoryGeoFactDto.VerticalDepth), columnVerticalDepth }, + { nameof(TrajectoryGeoFactDto.Comment), columnComment } + }; + + protected override TrajectoryGeoFactDto ParseRow(IXLRow row) + { + var dto = new TrajectoryGeoFactDto { - WellboreDepth = row.Cell(1).GetCellValue(), - ZenithAngle = row.Cell(2).GetCellValue(), - AzimuthGeo = row.Cell(3).GetCellValue(), - AzimuthMagnetic = row.Cell(4).GetCellValue(), - VerticalDepth = row.Cell(5).GetCellValue(), - Comment = row.Cell(6).GetCellValue() + WellboreDepth = row.Cell(columnWellboreDepth).GetCellValue(), + ZenithAngle = row.Cell(columnZenithAngle).GetCellValue(), + AzimuthGeo = row.Cell(columnAzimuthGeo).GetCellValue(), + AzimuthMagnetic = row.Cell(columnAzimuthMagnetic).GetCellValue(), + VerticalDepth = row.Cell(columnVerticalDepth).GetCellValue(), + Comment = row.Cell(columnComment).GetCellValue() }; - //TODO: Добавить валидацию модели - - var validationResult = new ValidationResultDto - { - Item = trajectoryRow - }; - - return validationResult; + return dto; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs index 410b1bed..c82cfc5c 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs @@ -8,7 +8,7 @@ using AsbCloudApp.Requests.ParserOptions; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public abstract class TrajectoryParser : ParserServiceBase +public abstract class TrajectoryParser : ParserExcelService where T : TrajectoryGeoDto { private const int HeaderRowsCount = 2; @@ -21,8 +21,6 @@ public abstract class TrajectoryParser : ParserServiceBase ParseRow(IXLRow row); - public override Stream GetTemplateFile() => Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName) ?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден"); @@ -33,7 +31,7 @@ public abstract class TrajectoryParser : ParserServiceBase { - protected override string SheetName => "Плановая траектория"; - protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx"; + #region Columns + + private const int columnWellboreDepth = 1; + private const int columnZenithAngle = 2; + private const int columnAzimuthGeo = 3; + private const int columnAzimuthMagnetic = 4; + private const int columnVerticalDepth = 5; + private const int columnRadius = 6; + private const int columnComment = 7; + + #endregion public TrajectoryPlanParser(IServiceProvider serviceProvider) : base(serviceProvider) { } - - protected override ValidationResultDto ParseRow(IXLRow row) + + protected override string SheetName => "Плановая траектория"; + + protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx"; + + protected override IDictionary PropertyColumnNumbers => new Dictionary { - var trajectoryRow = new TrajectoryGeoPlanDto + { nameof(TrajectoryGeoPlanDto.WellboreDepth), columnWellboreDepth }, + { nameof(TrajectoryGeoPlanDto.ZenithAngle), columnZenithAngle }, + { nameof(TrajectoryGeoPlanDto.AzimuthGeo), columnAzimuthGeo }, + { nameof(TrajectoryGeoPlanDto.AzimuthMagnetic), columnAzimuthMagnetic }, + { nameof(TrajectoryGeoPlanDto.VerticalDepth), columnVerticalDepth }, + { nameof(TrajectoryGeoPlanDto.Radius), columnRadius }, + { nameof(TrajectoryGeoPlanDto.Comment), columnComment } + }; + + protected override TrajectoryGeoPlanDto ParseRow(IXLRow row) + { + var dto = new TrajectoryGeoPlanDto { - WellboreDepth = row.Cell(1).GetCellValue(), - ZenithAngle = row.Cell(2).GetCellValue(), - AzimuthGeo = row.Cell(3).GetCellValue(), - AzimuthMagnetic = row.Cell(4).GetCellValue(), - VerticalDepth = row.Cell(5).GetCellValue(), - Radius = row.Cell(6).GetCellValue(), - Comment = row.Cell(7).GetCellValue() + WellboreDepth = row.Cell(columnWellboreDepth).GetCellValue(), + ZenithAngle = row.Cell(columnZenithAngle).GetCellValue(), + AzimuthGeo = row.Cell(columnAzimuthGeo).GetCellValue(), + AzimuthMagnetic = row.Cell(columnAzimuthMagnetic).GetCellValue(), + VerticalDepth = row.Cell(columnVerticalDepth).GetCellValue(), + Radius = row.Cell(columnRadius).GetCellValue(), + Comment = row.Cell(columnComment).GetCellValue() }; - //TODO: Добавить валидацию модели - - var validationResult = new ValidationResultDto - { - Item = trajectoryRow - }; - - return validationResult; + return dto; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/ValidationExtensions.cs b/AsbCloudInfrastructure/ValidationExtensions.cs new file mode 100644 index 00000000..feb9c06b --- /dev/null +++ b/AsbCloudInfrastructure/ValidationExtensions.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace AsbCloudInfrastructure; + +public static class ValidationExtensions +{ + public static bool Validate(this IValidatableObject validatableObject, ICollection validationResults) + { + var validationContext = new ValidationContext(validatableObject, serviceProvider: null, items: null); + + foreach (var validationResult in validatableObject.Validate(validationContext)) + validationResults.Add(validationResult); + + return Validator.TryValidateObject(validatableObject, validationContext, validationResults, true); + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs index 8fc0220c..51545ff8 100644 --- a/AsbCloudInfrastructure/XLExtentions.cs +++ b/AsbCloudInfrastructure/XLExtentions.cs @@ -2,7 +2,6 @@ using System; using System.IO; using System.Linq; -using AsbCloudApp.Services; namespace AsbCloudInfrastructure; @@ -10,7 +9,7 @@ public static class XLExtentions { public static IXLWorksheet GetWorksheet(this IXLWorkbook workbook, string sheetName) => workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase)) - ?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}."); + ?? throw new FileFormatException(string.Format(XLMessageTemplates.NotFoundSheetTemplate, sheetName)); public static IXLCell SetCellValue(this IXLCell cell, T value) { @@ -40,7 +39,7 @@ public static class XLExtentions } catch { - var message = string.Format(IParserService.MessageTemplate, cell.Worksheet.Name, cell.Address.RowNumber, + var message = string.Format(XLMessageTemplates.ProblemDetailsTemplate, cell.Worksheet.Name, cell.Address.RowNumber, cell.Address.ColumnNumber, "Содержит некорректное значение"); throw new FileFormatException(message); } diff --git a/AsbCloudInfrastructure/XLMessageTemplates.cs b/AsbCloudInfrastructure/XLMessageTemplates.cs new file mode 100644 index 00000000..edb8eba9 --- /dev/null +++ b/AsbCloudInfrastructure/XLMessageTemplates.cs @@ -0,0 +1,12 @@ +namespace AsbCloudInfrastructure; + +public static class XLMessageTemplates +{ + public const string ProblemDetailsTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}"; + + public const string NotFoundSheetTemplate = "Книга excel не содержит листа {0}"; + + public const string ExceedingMaxRowLimitTemplate = "Лист {0} содержит слишком большое количество строк"; + + public const string FewerColumnsTemplate = "Лист {0} содержит меньшее количество столбцов"; +} \ No newline at end of file From 14f4ffb6b9333f4b585bb95e369a69a4ff600f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Tue, 13 Feb 2024 14:05:16 +0300 Subject: [PATCH 08/14] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D0=BF=D0=B0=D1=80=D1=81=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=20excel,=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3=20=D0=BF=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3?= =?UTF-8?q?=D0=B0=20=D0=A0=D0=A2=D0=9A=20=D0=B8=20=D1=82=D1=80=D0=B0=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudInfrastructure/DependencyInjection.cs | 1 + .../Services/Parser/Data/Cell.cs | 17 +++ .../Services/Parser/Data/Row.cs | 17 +++ .../{ => Parser}/ParserExcelService.cs | 82 +++++++++---- .../{ => Parser}/ParserServiceFactory.cs | 2 +- .../Parser/ProcessMapPlanDrillingParser.cs | 110 ++++++------------ .../Parser/ProcessMapPlanParser.cs | 22 +--- .../Parser/TrajectoryFactManualParser.cs | 47 +++----- .../Trajectory/Parser/TrajectoryParser.cs | 37 ------ .../Trajectory/Parser/TrajectoryPlanParser.cs | 52 +++------ AsbCloudInfrastructure/XLExtentions.cs | 34 +++++- AsbCloudInfrastructure/XLMessageTemplates.cs | 12 -- .../ProcessMapPlanDrillingControllerTest.cs | 2 +- .../Trajectory/TrajectoryParserTest.cs | 2 +- .../ProcessMapPlanBaseController.cs | 2 +- .../ProcessMapPlanDrillingController.cs | 2 +- .../TrajectoryEditableController.cs | 2 +- .../TrajectoryFactManualController.cs | 2 +- .../Trajectory/TrajectoryPlanController.cs | 2 +- 19 files changed, 195 insertions(+), 252 deletions(-) create mode 100644 AsbCloudInfrastructure/Services/Parser/Data/Cell.cs create mode 100644 AsbCloudInfrastructure/Services/Parser/Data/Row.cs rename AsbCloudInfrastructure/Services/{ => Parser}/ParserExcelService.cs (50%) rename AsbCloudInfrastructure/Services/{ => Parser}/ParserServiceFactory.cs (97%) delete mode 100644 AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs delete mode 100644 AsbCloudInfrastructure/XLMessageTemplates.cs diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index afab0a5a..363ec8ee 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -45,6 +45,7 @@ using AsbCloudDb.Model.WellSections; using AsbCloudInfrastructure.Services.ProcessMaps; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Requests; +using AsbCloudInfrastructure.Services.Parser; namespace AsbCloudInfrastructure { diff --git a/AsbCloudInfrastructure/Services/Parser/Data/Cell.cs b/AsbCloudInfrastructure/Services/Parser/Data/Cell.cs new file mode 100644 index 00000000..a70f004e --- /dev/null +++ b/AsbCloudInfrastructure/Services/Parser/Data/Cell.cs @@ -0,0 +1,17 @@ +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.Parser.Data; + +public class Cell +{ + public Cell(int columnNumber, + XLDataType type) + { + ColumnNumber = columnNumber; + Type = type; + } + + public int ColumnNumber { get; } + + public XLDataType Type { get; } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Parser/Data/Row.cs b/AsbCloudInfrastructure/Services/Parser/Data/Row.cs new file mode 100644 index 00000000..c66ce1fd --- /dev/null +++ b/AsbCloudInfrastructure/Services/Parser/Data/Row.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace AsbCloudInfrastructure.Services.Parser.Data; + +public class Row +{ + public Row(int number, + IDictionary cells) + { + Number = number; + Cells = cells; + } + + public int Number { get; } + + public IDictionary Cells { get; } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ParserExcelService.cs b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs similarity index 50% rename from AsbCloudInfrastructure/Services/ParserExcelService.cs rename to AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs index a9a28d9e..bb717b56 100644 --- a/AsbCloudInfrastructure/Services/ParserExcelService.cs +++ b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs @@ -3,12 +3,15 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; +using System.Reflection; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Services; +using AsbCloudInfrastructure.Services.Parser.Data; using ClosedXML.Excel; +using Mapster; -namespace AsbCloudInfrastructure.Services; +namespace AsbCloudInfrastructure.Services.Parser; public abstract class ParserExcelService : IParserService where TDto : class, IValidatableObject, IId @@ -22,42 +25,79 @@ public abstract class ParserExcelService : IParserService 0; - protected abstract IDictionary PropertyColumnNumbers { get; } + protected abstract string TemplateFileName { get; } - public abstract ParserResultDto Parse(Stream file, TOptions options); + protected abstract IDictionary Cells { get; } - public abstract Stream GetTemplateFile(); + public virtual ParserResultDto Parse(Stream file, TOptions options) + { + using var workbook = new XLWorkbook(file); + var sheet = workbook.GetWorksheet(SheetName); + var dtos = ParseExcelSheet(sheet); + return dtos; + } - protected abstract TDto ParseRow(IXLRow xlRow); + public virtual Stream GetTemplateFile() => + Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName) + ?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден"); + + + protected virtual Row ParseRow(IXLRow xlRow) + { + var cells = Cells.ToDictionary(x => x.Key, x => + { + var columnNumber = x.Value.ColumnNumber; + var type = x.Value.Type; + var cellValue = xlRow.Cell(columnNumber).GetCellValue(type); + + return (x.Value, cellValue); + }); + + var row = new Row(xlRow.RowNumber(), cells); + + return row; + } + + protected virtual TDto BuildDto(Row row) + { + var propertiesDict = row.Cells.ToDictionary(x => x.Key, x => x.Value.CellValue); + + return propertiesDict.Adapt(); + } private ValidationResultDto Validate(TDto dto, int rowNumber) { var validationResults = new List(); - + var isValid = dto.Validate(validationResults); if (isValid) { - var validDto = new ValidationResultDto() + var validDto = new ValidationResultDto { Item = dto }; return validDto; } - + + var columnsDict = Cells.ToDictionary(x => x.Key, x => x.Value.ColumnNumber); + var invalidDto = new ValidationResultDto { Item = dto, Warnings = validationResults .SelectMany(v => v.MemberNames - .Where(PropertyColumnNumbers.ContainsKey) + .Where(columnsDict.ContainsKey) .Select(m => { - var columnNumber = PropertyColumnNumbers[m]; + var columnNumber = columnsDict[m]; var errorMessage = v.ErrorMessage; - var warningMessage = string.Format(XLMessageTemplates.ProblemDetailsTemplate, SheetName, rowNumber, columnNumber, errorMessage); + var warningMessage = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, columnNumber, + errorMessage); var warning = new ValidationResult(warningMessage, new[] { m }); return warning; })) @@ -66,18 +106,9 @@ public abstract class ParserExcelService : IParserService ParseExcelSheet(IXLWorksheet sheet, - int columnCount, - int headerRowsCount = 0) + protected virtual ParserResultDto ParseExcelSheet(IXLWorksheet sheet) { - if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount) - throw new FileFormatException(string.Format(XLMessageTemplates.FewerColumnsTemplate, SheetName)); - - var count = sheet.RowsUsed().Count() - headerRowsCount; - - if (count > 1024) - throw new FileFormatException(string.Format(XLMessageTemplates.ExceedingMaxRowLimitTemplate, SheetName)); - + var count = sheet.RowsUsed().Count() - HeaderRowsCount; if (count <= 0) return new ParserResultDto(); @@ -86,12 +117,13 @@ public abstract class ParserExcelService : IParserService { - #region Columns - - private const int columnSection = 1; - private const int columnMode = 2; - private const int columnDepthStart = 3; - private const int columnDepthEnd = 4; - private const int columnPressurePlan = 5; - private const int columnPressureLimitMax = 6; - private const int columnAxialLoadPlan = 7; - private const int columnAxialLoadLimitMax = 8; - private const int columnTopDriveTorquePlan = 9; - private const int columnTopDriveTorqueLimitMax = 10; - private const int columnTopDriveSpeedPlan = 11; - private const int columnTopDriveSpeedLimitMax = 12; - private const int columnFlowPlan = 13; - private const int columnFlowLimitMax = 14; - private const int columnRopPlan = 15; - private const int columnUsageSaub = 16; - private const int columnUsageSpin = 17; - private const int columnComment = 18; - - #endregion - private readonly IEnumerable sections; public ProcessMapPlanDrillingParser(IServiceProvider serviceProvider) @@ -44,78 +22,60 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser "План"; - - protected override IDictionary PropertyColumnNumbers => new Dictionary - { - { nameof(ProcessMapPlanDrillingDto.DepthStart), columnDepthStart }, - { nameof(ProcessMapPlanDrillingDto.DepthEnd), columnDepthEnd }, - { nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), columnPressurePlan }, - { nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), columnPressureLimitMax }, - { nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), columnAxialLoadPlan }, - { nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), columnAxialLoadLimitMax }, - { nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), columnTopDriveTorquePlan }, - { nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), columnTopDriveTorqueLimitMax }, - { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), columnTopDriveSpeedPlan }, - { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), columnTopDriveSpeedLimitMax }, - { nameof(ProcessMapPlanDrillingDto.FlowPlan), columnFlowPlan }, - { nameof(ProcessMapPlanDrillingDto.FlowLimitMax), columnFlowLimitMax }, - { nameof(ProcessMapPlanDrillingDto.RopPlan), columnRopPlan }, - { nameof(ProcessMapPlanDrillingDto.UsageSaub), columnUsageSaub }, - { nameof(ProcessMapPlanDrillingDto.UsageSpin), columnUsageSpin }, - { nameof(ProcessMapPlanDrillingDto.Comment), columnComment } - }; - protected override string TemplateFileName => "ProcessMapPlanDrillingTemplate.xlsx"; - protected override ProcessMapPlanDrillingDto ParseRow(IXLRow row) + private const int ColumnSection = 1; + private const int ColumnMode = 2; + + protected override IDictionary Cells => new Dictionary { - var sectionCaption = row.Cell(columnSection).GetCellValue()?.Trim().ToLower(); - var modeName = row.Cell(columnMode).GetCellValue()?.Trim().ToLower(); + { nameof(ProcessMapPlanDrillingDto.Section), new Cell(ColumnSection, XLDataType.Text) }, + { nameof(ProcessMapPlanDrillingDto.Mode), new Cell(ColumnMode, XLDataType.Text) }, + { nameof(ProcessMapPlanDrillingDto.DepthStart), new Cell(3, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.DepthEnd), new Cell(4, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), new Cell(5, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), new Cell(6, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), new Cell(7, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), new Cell(8, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), new Cell(9, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), new Cell(10, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), new Cell(11, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), new Cell(12, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.FlowPlan), new Cell(13, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.FlowLimitMax), new Cell(14, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.RopPlan), new Cell(15, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.UsageSaub), new Cell(16, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.UsageSpin), new Cell(17, XLDataType.Number) }, + { nameof(ProcessMapPlanDrillingDto.Comment), new Cell(18, XLDataType.Text) } + }; + + protected override ProcessMapPlanDrillingDto BuildDto(Row row) + { + var dto = base.BuildDto(row); var section = sections.FirstOrDefault(s => - string.Equals(s.Caption.Trim(), sectionCaption?.Trim(), StringComparison.CurrentCultureIgnoreCase)); + string.Equals(s.Caption.Trim(), dto.Section?.Trim(), StringComparison.CurrentCultureIgnoreCase)); if (section is null) { - var message = string.Format(XLMessageTemplates.ProblemDetailsTemplate, SheetName, row.RowNumber(), columnSection, + var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, row.Number, ColumnSection, "Указана некорректная секция"); throw new FileFormatException(message); } - var idMode = GetIdMode(modeName); + var idMode = GetIdMode(dto.Mode); if (idMode is null) { - var message = string.Format(XLMessageTemplates.ProblemDetailsTemplate, SheetName, row.RowNumber(), columnSection, + var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, row.Number, ColumnSection, "Указан некорректный режим бурения"); throw new FileFormatException(message); } - var dto = new ProcessMapPlanDrillingDto - { - IdWellSectionType = section.Id, - Section = section.Caption, - IdMode = idMode.Value, - Mode = modeName, - DepthStart = row.Cell(columnDepthStart).GetCellValue(), - DepthEnd = row.Cell(columnDepthEnd).GetCellValue(), - AxialLoadPlan = row.Cell(columnAxialLoadPlan).GetCellValue(), - AxialLoadLimitMax = row.Cell(columnAxialLoadLimitMax).GetCellValue(), - DeltaPressurePlan = row.Cell(columnPressurePlan).GetCellValue(), - DeltaPressureLimitMax = row.Cell(columnPressureLimitMax).GetCellValue(), - TopDriveTorquePlan = row.Cell(columnTopDriveTorquePlan).GetCellValue(), - TopDriveTorqueLimitMax = row.Cell(columnTopDriveTorqueLimitMax).GetCellValue(), - TopDriveSpeedPlan = row.Cell(columnTopDriveSpeedPlan).GetCellValue(), - TopDriveSpeedLimitMax = row.Cell(columnTopDriveSpeedLimitMax).GetCellValue(), - FlowPlan = row.Cell(columnFlowPlan).GetCellValue(), - FlowLimitMax = row.Cell(columnFlowLimitMax).GetCellValue(), - RopPlan = row.Cell(columnRopPlan).GetCellValue(), - UsageSaub = row.Cell(columnUsageSaub).GetCellValue(), - UsageSpin = row.Cell(columnUsageSpin).GetCellValue(), - Comment = row.Cell(columnComment).GetCellValue() ?? string.Empty - }; + dto.IdWellSectionType = section.Id; + dto.IdMode = idMode.Value; return dto; } diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs index ff76debb..3a427738 100644 --- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs @@ -1,10 +1,9 @@ using System; using System.IO; -using System.Reflection; using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Requests.ParserOptions; -using ClosedXML.Excel; +using AsbCloudInfrastructure.Services.Parser; namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; @@ -16,24 +15,7 @@ public abstract class ProcessMapPlanParser : ParserExcelService Parse(Stream file, IParserOptionsRequest options) - { - using var workbook = new XLWorkbook(file); - - var sheet = workbook.GetWorksheet(SheetName); - - var processMaps = ParseExcelSheet(sheet, ColumnCount, HeaderRowsCount); - return processMaps; - } - - public override Stream GetTemplateFile() => - Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName) - ?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден"); + protected override int HeaderRowsCount => 2; protected static int? GetIdMode(string? modeName) => modeName?.Trim().ToLower() switch diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs index e48fb575..37bad537 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs @@ -1,23 +1,15 @@ using System; using System.Collections.Generic; using AsbCloudApp.Data.Trajectory; +using AsbCloudApp.Requests.ParserOptions; +using AsbCloudInfrastructure.Services.Parser; +using AsbCloudInfrastructure.Services.Parser.Data; using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public class TrajectoryFactManualParser : TrajectoryParser +public class TrajectoryFactManualParser : ParserExcelService { - #region Columns - - private const int columnWellboreDepth = 1; - private const int columnZenithAngle = 2; - private const int columnAzimuthGeo = 3; - private const int columnAzimuthMagnetic = 4; - private const int columnVerticalDepth = 5; - private const int columnComment = 6; - - #endregion - public TrajectoryFactManualParser(IServiceProvider serviceProvider) : base(serviceProvider) { @@ -25,30 +17,17 @@ public class TrajectoryFactManualParser : TrajectoryParser protected override string SheetName => "Фактическая траектория"; + protected override int HeaderRowsCount => 2; + protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx"; - protected override IDictionary PropertyColumnNumbers => new Dictionary + protected override IDictionary Cells => new Dictionary() { - { nameof(TrajectoryGeoFactDto.WellboreDepth), columnWellboreDepth }, - { nameof(TrajectoryGeoFactDto.ZenithAngle), columnZenithAngle }, - { nameof(TrajectoryGeoFactDto.AzimuthGeo), columnAzimuthGeo }, - { nameof(TrajectoryGeoFactDto.AzimuthMagnetic), columnAzimuthMagnetic }, - { nameof(TrajectoryGeoFactDto.VerticalDepth), columnVerticalDepth }, - { nameof(TrajectoryGeoFactDto.Comment), columnComment } + { nameof(TrajectoryGeoFactDto.WellboreDepth), new Cell(1, XLDataType.Number) }, + { nameof(TrajectoryGeoFactDto.ZenithAngle), new Cell(2, XLDataType.Number) }, + { nameof(TrajectoryGeoFactDto.AzimuthGeo), new Cell(3, XLDataType.Number) }, + { nameof(TrajectoryGeoFactDto.AzimuthMagnetic), new Cell(4, XLDataType.Number) }, + { nameof(TrajectoryGeoFactDto.VerticalDepth), new Cell(5, XLDataType.Number) }, + { nameof(TrajectoryGeoFactDto.Comment), new Cell(6, XLDataType.Text) } }; - - protected override TrajectoryGeoFactDto ParseRow(IXLRow row) - { - var dto = new TrajectoryGeoFactDto - { - WellboreDepth = row.Cell(columnWellboreDepth).GetCellValue(), - ZenithAngle = row.Cell(columnZenithAngle).GetCellValue(), - AzimuthGeo = row.Cell(columnAzimuthGeo).GetCellValue(), - AzimuthMagnetic = row.Cell(columnAzimuthMagnetic).GetCellValue(), - VerticalDepth = row.Cell(columnVerticalDepth).GetCellValue(), - Comment = row.Cell(columnComment).GetCellValue() - }; - - return dto; - } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs deleted file mode 100644 index c82cfc5c..00000000 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryParser.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using AsbCloudApp.Data.Trajectory; -using ClosedXML.Excel; -using System.IO; -using System.Reflection; -using AsbCloudApp.Data; -using AsbCloudApp.Requests.ParserOptions; - -namespace AsbCloudInfrastructure.Services.Trajectory.Parser; - -public abstract class TrajectoryParser : ParserExcelService - where T : TrajectoryGeoDto -{ - private const int HeaderRowsCount = 2; - private const int ColumnCount = 6; - - protected TrajectoryParser(IServiceProvider serviceProvider) - : base(serviceProvider) - { - } - - protected abstract string TemplateFileName { get; } - - 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); - - var sheet = workbook.GetWorksheet(SheetName); - - var trajectoryRows = ParseExcelSheet(sheet, ColumnCount, HeaderRowsCount); - return trajectoryRows; - } -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs index de8db5e1..99ec5ae6 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs @@ -1,25 +1,15 @@ using System; using System.Collections.Generic; -using AsbCloudApp.Data; using AsbCloudApp.Data.Trajectory; +using AsbCloudApp.Requests.ParserOptions; +using AsbCloudInfrastructure.Services.Parser; +using AsbCloudInfrastructure.Services.Parser.Data; using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public class TrajectoryPlanParser : TrajectoryParser +public class TrajectoryPlanParser : ParserExcelService { - #region Columns - - private const int columnWellboreDepth = 1; - private const int columnZenithAngle = 2; - private const int columnAzimuthGeo = 3; - private const int columnAzimuthMagnetic = 4; - private const int columnVerticalDepth = 5; - private const int columnRadius = 6; - private const int columnComment = 7; - - #endregion - public TrajectoryPlanParser(IServiceProvider serviceProvider) : base(serviceProvider) { @@ -27,32 +17,18 @@ public class TrajectoryPlanParser : TrajectoryParser protected override string SheetName => "Плановая траектория"; + protected override int HeaderRowsCount => 2; + protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx"; - protected override IDictionary PropertyColumnNumbers => new Dictionary + protected override IDictionary Cells => new Dictionary { - { nameof(TrajectoryGeoPlanDto.WellboreDepth), columnWellboreDepth }, - { nameof(TrajectoryGeoPlanDto.ZenithAngle), columnZenithAngle }, - { nameof(TrajectoryGeoPlanDto.AzimuthGeo), columnAzimuthGeo }, - { nameof(TrajectoryGeoPlanDto.AzimuthMagnetic), columnAzimuthMagnetic }, - { nameof(TrajectoryGeoPlanDto.VerticalDepth), columnVerticalDepth }, - { nameof(TrajectoryGeoPlanDto.Radius), columnRadius }, - { nameof(TrajectoryGeoPlanDto.Comment), columnComment } + { nameof(TrajectoryGeoPlanDto.WellboreDepth), new Cell(1, XLDataType.Number) }, + { nameof(TrajectoryGeoPlanDto.ZenithAngle), new Cell(2, XLDataType.Number) }, + { nameof(TrajectoryGeoPlanDto.AzimuthGeo), new Cell(3, XLDataType.Number) }, + { nameof(TrajectoryGeoPlanDto.AzimuthMagnetic), new Cell(4, XLDataType.Number) }, + { nameof(TrajectoryGeoPlanDto.VerticalDepth), new Cell(5, XLDataType.Number) }, + { nameof(TrajectoryGeoPlanDto.Radius), new Cell(6, XLDataType.Number) }, + { nameof(TrajectoryGeoPlanDto.Comment), new Cell(7, XLDataType.Text) } }; - - protected override TrajectoryGeoPlanDto ParseRow(IXLRow row) - { - var dto = new TrajectoryGeoPlanDto - { - WellboreDepth = row.Cell(columnWellboreDepth).GetCellValue(), - ZenithAngle = row.Cell(columnZenithAngle).GetCellValue(), - AzimuthGeo = row.Cell(columnAzimuthGeo).GetCellValue(), - AzimuthMagnetic = row.Cell(columnAzimuthMagnetic).GetCellValue(), - VerticalDepth = row.Cell(columnVerticalDepth).GetCellValue(), - Radius = row.Cell(columnRadius).GetCellValue(), - Comment = row.Cell(columnComment).GetCellValue() - }; - - return dto; - } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs index 51545ff8..481ea891 100644 --- a/AsbCloudInfrastructure/XLExtentions.cs +++ b/AsbCloudInfrastructure/XLExtentions.cs @@ -7,9 +7,15 @@ namespace AsbCloudInfrastructure; public static class XLExtentions { + public const string ProblemDetailsTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}"; + + public const string NotFoundSheetTemplate = "Книга excel не содержит листа {0}"; + + public const string InvalidValueTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. Содержит некорректное значение"; + public static IXLWorksheet GetWorksheet(this IXLWorkbook workbook, string sheetName) => workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase)) - ?? throw new FileFormatException(string.Format(XLMessageTemplates.NotFoundSheetTemplate, sheetName)); + ?? throw new FileFormatException(string.Format(NotFoundSheetTemplate, sheetName)); public static IXLCell SetCellValue(this IXLCell cell, T value) { @@ -28,6 +34,29 @@ public static class XLExtentions return cell; } + public static object? GetCellValue(this IXLCell cell, XLDataType type) + { + try + { + return type switch + { + XLDataType.Blank => null, + XLDataType.Boolean => cell.Value.GetBoolean(), + XLDataType.Number => cell.Value.GetNumber(), + XLDataType.Text => cell.Value.GetText(), + XLDataType.Error => cell.Value.GetError(), + XLDataType.DateTime => cell.Value.GetDateTime(), + XLDataType.TimeSpan => cell.Value.GetTimeSpan(), + _ => throw new InvalidCastException() + }; + } + catch + { + var message = string.Format(InvalidValueTemplate, cell.Worksheet.Name, cell.Address.RowNumber, cell.Address.ColumnNumber); + throw new FileFormatException(message); + } + } + public static T? GetCellValue(this IXLCell cell) { try @@ -39,8 +68,7 @@ public static class XLExtentions } catch { - var message = string.Format(XLMessageTemplates.ProblemDetailsTemplate, cell.Worksheet.Name, cell.Address.RowNumber, - cell.Address.ColumnNumber, "Содержит некорректное значение"); + var message = string.Format(InvalidValueTemplate, cell.Worksheet.Name, cell.Address.RowNumber, cell.Address.ColumnNumber); throw new FileFormatException(message); } } diff --git a/AsbCloudInfrastructure/XLMessageTemplates.cs b/AsbCloudInfrastructure/XLMessageTemplates.cs deleted file mode 100644 index edb8eba9..00000000 --- a/AsbCloudInfrastructure/XLMessageTemplates.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace AsbCloudInfrastructure; - -public static class XLMessageTemplates -{ - public const string ProblemDetailsTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}"; - - public const string NotFoundSheetTemplate = "Книга excel не содержит листа {0}"; - - public const string ExceedingMaxRowLimitTemplate = "Лист {0} содержит слишком большое количество строк"; - - public const string FewerColumnsTemplate = "Лист {0} содержит меньшее количество столбцов"; -} \ No newline at end of file diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs index 67844201..f06e2133 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs @@ -29,7 +29,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest DepthEnd = 1.5, IdMode = 1, - Mode = "ротор", + Mode = "Ротор", AxialLoadPlan = 2.718281, AxialLoadLimitMax = 3.1415926, DeltaPressurePlan = 4, diff --git a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs index 2a90be08..9e2ebd7e 100644 --- a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs +++ b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs @@ -2,7 +2,7 @@ using System.Linq; using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Requests.ParserOptions; -using AsbCloudInfrastructure.Services; +using AsbCloudInfrastructure.Services.Parser; using Microsoft.Extensions.DependencyInjection; using NSubstitute; using Xunit; diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs index fb55bcf7..ca54ef62 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs @@ -14,7 +14,7 @@ using AsbCloudApp.Services; using System.Linq; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; -using AsbCloudInfrastructure.Services; +using AsbCloudInfrastructure.Services.Parser; using AsbCloudWebApi.Controllers.Interfaces; namespace AsbCloudWebApi.Controllers.ProcessMapPlan; diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs index 8855c54f..9e9a7175 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs @@ -2,7 +2,7 @@ using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; -using AsbCloudInfrastructure.Services; +using AsbCloudInfrastructure.Services.Parser; namespace AsbCloudWebApi.Controllers.ProcessMapPlan; diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs index 7a04e7fc..30251923 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs @@ -11,7 +11,7 @@ using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; -using AsbCloudInfrastructure.Services; +using AsbCloudInfrastructure.Services.Parser; using AsbCloudWebApi.Controllers.Interfaces; namespace AsbCloudWebApi.Controllers.Trajectory diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs index 9113acc8..2bc3c769 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs @@ -1,7 +1,7 @@ using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Repositories; using AsbCloudApp.Services; -using AsbCloudInfrastructure.Services; +using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.Trajectory.Export; using Microsoft.AspNetCore.Mvc; diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs index 49e1c1ba..c22876dc 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using AsbCloudInfrastructure.Services; +using AsbCloudInfrastructure.Services.Parser; namespace AsbCloudWebApi.Controllers.Trajectory { From 7090daf494024cb1c1986bae2f6f979be2388b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 14 Feb 2024 12:13:43 +0300 Subject: [PATCH 09/14] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BF=D1=80=D0=B0=D1=81=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3=D0=B0=20Excel=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudInfrastructure/DependencyInjection.cs | 17 +++- .../Services/Parser/Cell.cs | 94 +++++++++++++++++++ .../Services/Parser/Data/Cell.cs | 17 ---- .../Services/Parser/Data/Row.cs | 17 ---- .../Services/Parser/ParserExcelService.cs | 26 +++-- .../Services/Parser/ParserIds.cs | 8 ++ .../Services/Parser/ParserServiceFactory.cs | 26 +---- .../Parser/ProcessMapPlanDrillingParser.cs | 47 +++++----- .../Parser/TrajectoryFactManualParser.cs | 16 ++-- .../Trajectory/Parser/TrajectoryPlanParser.cs | 16 ++-- AsbCloudInfrastructure/XLExtentions.cs | 25 ----- .../Trajectory/TrajectoryParserTest.cs | 21 +---- .../ProcessMapPlanDrillingController.cs | 2 +- .../TrajectoryFactManualController.cs | 3 +- .../Trajectory/TrajectoryPlanController.cs | 2 +- 15 files changed, 176 insertions(+), 161 deletions(-) create mode 100644 AsbCloudInfrastructure/Services/Parser/Cell.cs delete mode 100644 AsbCloudInfrastructure/Services/Parser/Data/Cell.cs delete mode 100644 AsbCloudInfrastructure/Services/Parser/Data/Row.cs create mode 100644 AsbCloudInfrastructure/Services/Parser/ParserIds.cs diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 92d305c5..1aaeab0c 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using AsbCloudApp.Data; using AsbCloudApp.Data.DrillTestReport; using AsbCloudApp.Data.Manuals; @@ -46,6 +47,8 @@ using AsbCloudInfrastructure.Services.ProcessMaps; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Requests; using AsbCloudInfrastructure.Services.Parser; +using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; +using AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudInfrastructure { @@ -329,8 +332,20 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + + + services.AddTransient(serviceProvider => + { + var parsers = new Dictionary> + { + { ParserIds.IdTrajectoryPlanParser, () => new TrajectoryPlanParser(serviceProvider) }, + { ParserIds.IdTrajectoryFactManualParser, () => new TrajectoryFactManualParser(serviceProvider) }, + { ParserIds.IdProcessMapPlanDrillingParser, () => new ProcessMapPlanDrillingParser(serviceProvider) } + }; - services.AddSingleton(); + var factory = new ParserServiceFactory(parsers); + return factory; + }); return services; } diff --git a/AsbCloudInfrastructure/Services/Parser/Cell.cs b/AsbCloudInfrastructure/Services/Parser/Cell.cs new file mode 100644 index 00000000..381ca57b --- /dev/null +++ b/AsbCloudInfrastructure/Services/Parser/Cell.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.IO; +using ClosedXML.Excel; + +namespace AsbCloudInfrastructure.Services.Parser; + +public class Cell +{ + private static IDictionary> converters = new Dictionary>() + { + { typeof(bool), cell => cell.GetBoolean() }, + { typeof(double), cell => cell.GetValue() }, + { typeof(float), cell => cell.GetValue() }, + { typeof(long), cell => cell.GetValue() }, + { typeof(ulong), cell => cell.GetValue() }, + { typeof(int), cell => cell.GetValue() }, + { typeof(uint), cell => cell.GetValue() }, + { typeof(short), cell => cell.GetValue() }, + { typeof(ushort), cell => cell.GetValue() }, + { typeof(string), cell => cell.GetString() }, + + { + typeof(DateTime), cell => + { + if (cell.DataType == XLDataType.DateTime) + return cell.GetDateTime(); + + var stringValue = cell.GetString(); + return DateTime.Parse(stringValue); + } + }, + + { + typeof(DateTimeOffset), cell => + { + var stringValue = cell.GetString(); + return DateTimeOffset.Parse(stringValue); + } + }, + + { + typeof(DateOnly), cell => + { + var stringValue = cell.GetString(); + return DateOnly.Parse(stringValue); + } + }, + + { + typeof(TimeOnly), cell => + { + var stringValue = cell.GetString(); + return TimeOnly.Parse(stringValue); + } + }, + + { + typeof(TimeSpan), cell => + { + if (cell.DataType == XLDataType.TimeSpan) + return cell.GetTimeSpan(); + + var stringValue = cell.GetString(); + return TimeSpan.Parse(stringValue); + } + }, + }; + + private readonly Type type; + + public Cell(int columnNumber, + Type type) + { + ColumnNumber = columnNumber; + this.type = type; + } + + public int ColumnNumber { get; } + + public object? GetValueFromCell(IXLCell cell) + { + try + { + return converters[type].Invoke(cell); + } + catch + { + var message = string.Format(XLExtentions.InvalidValueTemplate, cell.Worksheet.Name, cell.Address.RowNumber, + cell.Address.ColumnNumber); + throw new FileFormatException(message); + } + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Parser/Data/Cell.cs b/AsbCloudInfrastructure/Services/Parser/Data/Cell.cs deleted file mode 100644 index a70f004e..00000000 --- a/AsbCloudInfrastructure/Services/Parser/Data/Cell.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ClosedXML.Excel; - -namespace AsbCloudInfrastructure.Services.Parser.Data; - -public class Cell -{ - public Cell(int columnNumber, - XLDataType type) - { - ColumnNumber = columnNumber; - Type = type; - } - - public int ColumnNumber { get; } - - public XLDataType Type { get; } -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Parser/Data/Row.cs b/AsbCloudInfrastructure/Services/Parser/Data/Row.cs deleted file mode 100644 index c66ce1fd..00000000 --- a/AsbCloudInfrastructure/Services/Parser/Data/Row.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace AsbCloudInfrastructure.Services.Parser.Data; - -public class Row -{ - public Row(int number, - IDictionary cells) - { - Number = number; - Cells = cells; - } - - public int Number { get; } - - public IDictionary Cells { get; } -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs index bb717b56..dabf5d50 100644 --- a/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs +++ b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs @@ -7,7 +7,6 @@ using System.Reflection; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Services; -using AsbCloudInfrastructure.Services.Parser.Data; using ClosedXML.Excel; using Mapster; @@ -45,27 +44,23 @@ public abstract class ParserExcelService : IParserService ParseRow(IXLRow xlRow) { var cells = Cells.ToDictionary(x => x.Key, x => { var columnNumber = x.Value.ColumnNumber; - var type = x.Value.Type; - var cellValue = xlRow.Cell(columnNumber).GetCellValue(type); - - return (x.Value, cellValue); + var xlCell = xlRow.Cell(columnNumber); + var cellValue = x.Value.GetValueFromCell(xlCell); + return cellValue; }); - var row = new Row(xlRow.RowNumber(), cells); - - return row; + return cells; } - protected virtual TDto BuildDto(Row row) + protected virtual TDto BuildDto(IDictionary row, int rowNumber) { - var propertiesDict = row.Cells.ToDictionary(x => x.Key, x => x.Value.CellValue); - - return propertiesDict.Adapt(); + var dto = row.Adapt(); + return dto; } private ValidationResultDto Validate(TDto dto, int rowNumber) @@ -118,12 +113,13 @@ public abstract class ParserExcelService : IParserService> parsers; - private readonly IServiceScope serviceScope; - public ParserServiceFactory(IServiceProvider serviceProvider) + public ParserServiceFactory(IDictionary> parsers) { - serviceScope = serviceProvider.CreateScope(); - - parsers = new Dictionary> - { - { IdTrajectoryPlanParser, () => new TrajectoryPlanParser(serviceScope.ServiceProvider) }, - { IdTrajectoryFactManualParser, () => new TrajectoryFactManualParser(serviceScope.ServiceProvider) }, - { IdProcessMapPlanDrillingParser, () => new ProcessMapPlanDrillingParser(serviceScope.ServiceProvider) } - }; + this.parsers = parsers; } public IParserService Create(int idParserService) @@ -40,9 +25,4 @@ public class ParserServiceFactory : IDisposable return parserService.Invoke() as IParserService ?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа"); } - - public void Dispose() - { - serviceScope.Dispose(); - } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs index 6af94311..e0073bee 100644 --- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs @@ -5,8 +5,7 @@ using System.Linq; using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Repositories; -using AsbCloudInfrastructure.Services.Parser.Data; -using ClosedXML.Excel; +using AsbCloudInfrastructure.Services.Parser; using Microsoft.Extensions.DependencyInjection; namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; @@ -31,36 +30,36 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser Cells => new Dictionary { - { nameof(ProcessMapPlanDrillingDto.Section), new Cell(ColumnSection, XLDataType.Text) }, - { nameof(ProcessMapPlanDrillingDto.Mode), new Cell(ColumnMode, XLDataType.Text) }, - { nameof(ProcessMapPlanDrillingDto.DepthStart), new Cell(3, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.DepthEnd), new Cell(4, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), new Cell(5, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), new Cell(6, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), new Cell(7, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), new Cell(8, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), new Cell(9, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), new Cell(10, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), new Cell(11, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), new Cell(12, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.FlowPlan), new Cell(13, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.FlowLimitMax), new Cell(14, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.RopPlan), new Cell(15, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.UsageSaub), new Cell(16, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.UsageSpin), new Cell(17, XLDataType.Number) }, - { nameof(ProcessMapPlanDrillingDto.Comment), new Cell(18, XLDataType.Text) } + { nameof(ProcessMapPlanDrillingDto.Section), new Cell(ColumnSection, typeof(string)) }, + { nameof(ProcessMapPlanDrillingDto.Mode), new Cell(ColumnMode, typeof(string)) }, + { nameof(ProcessMapPlanDrillingDto.DepthStart), new Cell(3, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.DepthEnd), new Cell(4, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), new Cell(5, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), new Cell(6, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), new Cell(7, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), new Cell(8, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), new Cell(9, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), new Cell(10, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), new Cell(11, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), new Cell(12, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.FlowPlan), new Cell(13, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.FlowLimitMax), new Cell(14, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.RopPlan), new Cell(15, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.UsageSaub), new Cell(16, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.UsageSpin), new Cell(17, typeof(double)) }, + { nameof(ProcessMapPlanDrillingDto.Comment), new Cell(18, typeof(string)) } }; - protected override ProcessMapPlanDrillingDto BuildDto(Row row) + protected override ProcessMapPlanDrillingDto BuildDto(IDictionary row, int rowNumber) { - var dto = base.BuildDto(row); + var dto = base.BuildDto(row, rowNumber); var section = sections.FirstOrDefault(s => string.Equals(s.Caption.Trim(), dto.Section?.Trim(), StringComparison.CurrentCultureIgnoreCase)); if (section is null) { - var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, row.Number, ColumnSection, + var message = string.Format(XLExtentions.ProblemDetailsTemplate, SheetName, rowNumber, ColumnSection, "Указана некорректная секция"); throw new FileFormatException(message); } @@ -69,7 +68,7 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser "TrajectoryFactManualTemplate.xlsx"; - protected override IDictionary Cells => new Dictionary() + protected override IDictionary Cells => new Dictionary { - { nameof(TrajectoryGeoFactDto.WellboreDepth), new Cell(1, XLDataType.Number) }, - { nameof(TrajectoryGeoFactDto.ZenithAngle), new Cell(2, XLDataType.Number) }, - { nameof(TrajectoryGeoFactDto.AzimuthGeo), new Cell(3, XLDataType.Number) }, - { nameof(TrajectoryGeoFactDto.AzimuthMagnetic), new Cell(4, XLDataType.Number) }, - { nameof(TrajectoryGeoFactDto.VerticalDepth), new Cell(5, XLDataType.Number) }, - { nameof(TrajectoryGeoFactDto.Comment), new Cell(6, XLDataType.Text) } + { nameof(TrajectoryGeoFactDto.WellboreDepth), new Cell(1, typeof(double)) }, + { nameof(TrajectoryGeoFactDto.ZenithAngle), new Cell(2, typeof(double)) }, + { nameof(TrajectoryGeoFactDto.AzimuthGeo), new Cell(3, typeof(double)) }, + { nameof(TrajectoryGeoFactDto.AzimuthMagnetic), new Cell(4, typeof(double)) }, + { nameof(TrajectoryGeoFactDto.VerticalDepth), new Cell(5, typeof(double)) }, + { nameof(TrajectoryGeoFactDto.Comment), new Cell(6, typeof(string)) } }; } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs index 99ec5ae6..b7a03696 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; -using AsbCloudInfrastructure.Services.Parser.Data; -using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; @@ -23,12 +21,12 @@ public class TrajectoryPlanParser : ParserExcelService Cells => new Dictionary { - { nameof(TrajectoryGeoPlanDto.WellboreDepth), new Cell(1, XLDataType.Number) }, - { nameof(TrajectoryGeoPlanDto.ZenithAngle), new Cell(2, XLDataType.Number) }, - { nameof(TrajectoryGeoPlanDto.AzimuthGeo), new Cell(3, XLDataType.Number) }, - { nameof(TrajectoryGeoPlanDto.AzimuthMagnetic), new Cell(4, XLDataType.Number) }, - { nameof(TrajectoryGeoPlanDto.VerticalDepth), new Cell(5, XLDataType.Number) }, - { nameof(TrajectoryGeoPlanDto.Radius), new Cell(6, XLDataType.Number) }, - { nameof(TrajectoryGeoPlanDto.Comment), new Cell(7, XLDataType.Text) } + { nameof(TrajectoryGeoPlanDto.WellboreDepth), new Cell(1, typeof(double)) }, + { nameof(TrajectoryGeoPlanDto.ZenithAngle), new Cell(2, typeof(double)) }, + { nameof(TrajectoryGeoPlanDto.AzimuthGeo), new Cell(3, typeof(double)) }, + { nameof(TrajectoryGeoPlanDto.AzimuthMagnetic), new Cell(4, typeof(double)) }, + { nameof(TrajectoryGeoPlanDto.VerticalDepth), new Cell(5, typeof(double)) }, + { nameof(TrajectoryGeoPlanDto.Radius), new Cell(6, typeof(double)) }, + { nameof(TrajectoryGeoPlanDto.Comment), new Cell(7, typeof(string)) } }; } \ No newline at end of file diff --git a/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs index 481ea891..927c90a8 100644 --- a/AsbCloudInfrastructure/XLExtentions.cs +++ b/AsbCloudInfrastructure/XLExtentions.cs @@ -8,9 +8,7 @@ namespace AsbCloudInfrastructure; public static class XLExtentions { public const string ProblemDetailsTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. {3}"; - public const string NotFoundSheetTemplate = "Книга excel не содержит листа {0}"; - public const string InvalidValueTemplate = "Лист: {0}, Строка: {1}, Столбец: {2}. Содержит некорректное значение"; public static IXLWorksheet GetWorksheet(this IXLWorkbook workbook, string sheetName) => @@ -34,29 +32,6 @@ public static class XLExtentions return cell; } - public static object? GetCellValue(this IXLCell cell, XLDataType type) - { - try - { - return type switch - { - XLDataType.Blank => null, - XLDataType.Boolean => cell.Value.GetBoolean(), - XLDataType.Number => cell.Value.GetNumber(), - XLDataType.Text => cell.Value.GetText(), - XLDataType.Error => cell.Value.GetError(), - XLDataType.DateTime => cell.Value.GetDateTime(), - XLDataType.TimeSpan => cell.Value.GetTimeSpan(), - _ => throw new InvalidCastException() - }; - } - catch - { - var message = string.Format(InvalidValueTemplate, cell.Worksheet.Name, cell.Address.RowNumber, cell.Address.ColumnNumber); - throw new FileFormatException(message); - } - } - public static T? GetCellValue(this IXLCell cell) { try diff --git a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs index 9e2ebd7e..dcda8a58 100644 --- a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs +++ b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs @@ -1,10 +1,7 @@ -using System; -using System.Linq; +using System.Linq; using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; -using Microsoft.Extensions.DependencyInjection; -using NSubstitute; using Xunit; namespace AsbCloudWebApi.Tests.Services.Trajectory; @@ -12,21 +9,9 @@ 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 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() { @@ -37,7 +22,7 @@ public class TrajectoryParserTest Assert.Fail("Файла для импорта не существует"); var parserService = parserServiceFactory.Create( - ParserServiceFactory.IdTrajectoryPlanParser); + ParserIds.IdTrajectoryPlanParser); var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); @@ -54,7 +39,7 @@ public class TrajectoryParserTest Assert.Fail("Файла для импорта не существует"); var parserService = parserServiceFactory.Create( - ParserServiceFactory.IdTrajectoryFactManualParser); + ParserIds.IdTrajectoryFactManualParser); var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs index 9e9a7175..ed2e9b35 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs @@ -11,7 +11,7 @@ public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController repository, IWellService wellService, ParserServiceFactory parserFactory) - : base(repository, wellService, parserFactory, ParserServiceFactory.IdProcessMapPlanDrillingParser) + : base(repository, wellService, parserFactory, ParserIds.IdProcessMapPlanDrillingParser) { } diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs index 2bc3c769..2317c1ff 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs @@ -1,6 +1,7 @@ using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Repositories; using AsbCloudApp.Services; +using AsbCloudInfrastructure; using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.Trajectory.Export; using Microsoft.AspNetCore.Mvc; @@ -24,7 +25,7 @@ public class TrajectoryFactManualController : TrajectoryEditableController Date: Thu, 15 Feb 2024 10:00:17 +0300 Subject: [PATCH 10/14] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D0=BE=D0=B2=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D1=83=D1=8E=D1=89=D0=B8=D0=B9=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3=20Excel=20=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Interfaces/IControllerWithParser.cs | 15 ++++++++-- .../ProcessMapPlanBaseController.cs | 26 +++++------------ .../TrajectoryEditableController.cs | 28 +++++-------------- AsbCloudWebApi/Extensions.cs | 4 +-- 4 files changed, 28 insertions(+), 45 deletions(-) diff --git a/AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs b/AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs index d11ac201..e47d644b 100644 --- a/AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs +++ b/AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs @@ -1,13 +1,22 @@ -using System.IO; +using System.Threading; +using System.Threading.Tasks; using AsbCloudApp.Data; +using AsbCloudApp.Requests.ParserOptions; +using AsbCloudApp.Services; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace AsbCloudWebApi.Controllers.Interfaces; -public interface IControllerWithParser +public interface IWellControllerWithParser where TDto : class, IId + where TOptions : class, IParserOptionsRequest { - ActionResult> Parse(Stream file, TOptions options); + IParserService ParserService { get; } + + Task>> Parse(int idWell, + [FromForm] IFormFileCollection files, + CancellationToken token); IActionResult GetTemplate(); } \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs index ca54ef62..f630059c 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Http; using AsbCloudApp.Exceptions; using AsbCloudApp.Requests; using System; -using System.IO; using AsbCloudApp.Services; using System.Linq; using AsbCloudApp.Data; @@ -26,12 +25,11 @@ namespace AsbCloudWebApi.Controllers.ProcessMapPlan; [Route("api/well/{idWell}/[controller]")] [Authorize] public abstract class ProcessMapPlanBaseController : ControllerBase, - IControllerWithParser + IWellControllerWithParser where TDto : ProcessMapPlanBaseDto { private readonly IChangeLogRepository repository; private readonly IWellService wellService; - private readonly IParserService parserService; protected ProcessMapPlanBaseController(IChangeLogRepository repository, IWellService wellService, @@ -40,24 +38,14 @@ public abstract class ProcessMapPlanBaseController : ControllerBase, { this.repository = repository; this.wellService = wellService; - parserService = parserFactory.Create(idParserService); + + ParserService = parserFactory.Create(idParserService); } + public IParserService ParserService { get; } + protected abstract string TemplateFileName { get; } - - ActionResult> IControllerWithParser.Parse(Stream file, IParserOptionsRequest options) - { - try - { - var parserResult = parserService.Parse(file, options); - return Ok(parserResult); - } - catch (FileFormatException ex) - { - return this.ValidationBadRequest("files", ex.Message); - } - } - + /// /// Добавление /// @@ -246,7 +234,7 @@ public abstract class ProcessMapPlanBaseController : ControllerBase, [ProducesResponseType(StatusCodes.Status204NoContent)] public IActionResult GetTemplate() { - var stream = parserService.GetTemplateFile(); + var stream = ParserService.GetTemplateFile(); return File(stream, "application/octet-stream", TemplateFileName); } diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs index 30251923..d96b1491 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; -using System.IO; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; @@ -23,10 +22,9 @@ namespace AsbCloudWebApi.Controllers.Trajectory [ApiController] [Authorize] public abstract class TrajectoryEditableController : TrajectoryController, - IControllerWithParser + IWellControllerWithParser where TDto : TrajectoryGeoDto - { - private readonly IParserService parserService; + { private readonly ITrajectoryEditableRepository trajectoryRepository; protected TrajectoryEditableController(IWellService wellService, @@ -39,25 +37,13 @@ namespace AsbCloudWebApi.Controllers.Trajectory trajectoryExportService, trajectoryRepository) { - parserService = parserServiceFactory.Create(idParserService); this.trajectoryRepository = trajectoryRepository; - } - - ActionResult> IControllerWithParser.Parse(Stream file, - IParserOptionsRequest options) - { - try - { - var parserResult = parserService.Parse(file, options); - return Ok(parserResult); - } - catch (FileFormatException ex) - { - return this.ValidationBadRequest("files", ex.Message); - } + ParserService = parserServiceFactory.Create(idParserService); } - /// + public IParserService ParserService { get; } + + /// /// Возвращает excel шаблон для заполнения строк траектории /// /// Запрашиваемый файл @@ -67,7 +53,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory [ProducesResponseType(StatusCodes.Status204NoContent)] public IActionResult GetTemplate() { - var stream = parserService.GetTemplateFile(); + var stream = ParserService.GetTemplateFile(); return File(stream, "application/octet-stream", fileName); } diff --git a/AsbCloudWebApi/Extensions.cs b/AsbCloudWebApi/Extensions.cs index 489d6149..b78a9745 100644 --- a/AsbCloudWebApi/Extensions.cs +++ b/AsbCloudWebApi/Extensions.cs @@ -105,7 +105,7 @@ public static class Extensions /// /// public static ActionResult> ParseExcelFile( - this IControllerWithParser controller, + this IWellControllerWithParser controller, IFormFileCollection files, TOptions options) where TDto : class, IId @@ -120,6 +120,6 @@ public static class Extensions var stream = file.OpenReadStream(); - return controller.Parse(stream, options); + return controller.ParserService.Parse(stream, options); } } \ No newline at end of file From 63e2e1b18078d55b3f8dc93744f2d4765ad17bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 16 Feb 2024 11:53:10 +0300 Subject: [PATCH 11/14] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=D0=BE=D0=B2=20=D0=BF=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Services/IParserService.cs | 7 ++--- AsbCloudInfrastructure/DependencyInjection.cs | 18 +++--------- .../Services/Parser/ParserExcelService.cs | 13 ++------- .../Services/Parser/ParserIds.cs | 8 ------ .../Services/Parser/ParserServiceFactory.cs | 28 ------------------- .../Parser/ProcessMapPlanDrillingParser.cs | 5 +--- .../Parser/ProcessMapPlanParser.cs | 10 +------ .../Parser/TrajectoryFactManualParser.cs | 11 ++------ .../Trajectory/Parser/TrajectoryPlanParser.cs | 11 ++------ .../Trajectory/TrajectoryParserTest.cs | 16 ++++------- 10 files changed, 22 insertions(+), 105 deletions(-) delete mode 100644 AsbCloudInfrastructure/Services/Parser/ParserIds.cs delete mode 100644 AsbCloudInfrastructure/Services/Parser/ParserServiceFactory.cs diff --git a/AsbCloudApp/Services/IParserService.cs b/AsbCloudApp/Services/IParserService.cs index 6fa095cd..8c01af5a 100644 --- a/AsbCloudApp/Services/IParserService.cs +++ b/AsbCloudApp/Services/IParserService.cs @@ -8,10 +8,8 @@ namespace AsbCloudApp.Services; /// Сервис парсинга /// /// -/// -public interface IParserService : IParserService +public interface IParserService : IParserService where TDto : class, IId - where TOptions : IParserOptionsRequest { /// /// Распарсить файл @@ -19,7 +17,8 @@ public interface IParserService : IParserService /// /// /// - ParserResultDto Parse(Stream file, TOptions options); + ParserResultDto Parse(Stream file, TOptions options) + where TOptions : IParserOptionsRequest; /// /// Получение шаблона для заполнения diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 1aaeab0c..7356f9a3 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -332,21 +332,11 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - - - services.AddTransient(serviceProvider => - { - var parsers = new Dictionary> - { - { ParserIds.IdTrajectoryPlanParser, () => new TrajectoryPlanParser(serviceProvider) }, - { ParserIds.IdTrajectoryFactManualParser, () => new TrajectoryFactManualParser(serviceProvider) }, - { ParserIds.IdProcessMapPlanDrillingParser, () => new ProcessMapPlanDrillingParser(serviceProvider) } - }; - var factory = new ParserServiceFactory(parsers); - return factory; - }); - + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + return services; } } diff --git a/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs index dabf5d50..661c729f 100644 --- a/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs +++ b/AsbCloudInfrastructure/Services/Parser/ParserExcelService.cs @@ -12,17 +12,9 @@ using Mapster; namespace AsbCloudInfrastructure.Services.Parser; -public abstract class ParserExcelService : IParserService +public abstract class ParserExcelService : IParserService where TDto : class, IValidatableObject, IId - where TOptions : IParserOptionsRequest { - protected readonly IServiceProvider serviceProvider; - - protected ParserExcelService(IServiceProvider serviceProvider) - { - this.serviceProvider = serviceProvider; - } - protected abstract string SheetName { get; } protected virtual int HeaderRowsCount => 0; @@ -31,7 +23,8 @@ public abstract class ParserExcelService : IParserService Cells { get; } - public virtual ParserResultDto Parse(Stream file, TOptions options) + public virtual ParserResultDto Parse(Stream file, TOptions options) + where TOptions : IParserOptionsRequest { using var workbook = new XLWorkbook(file); var sheet = workbook.GetWorksheet(SheetName); diff --git a/AsbCloudInfrastructure/Services/Parser/ParserIds.cs b/AsbCloudInfrastructure/Services/Parser/ParserIds.cs deleted file mode 100644 index 45b9d915..00000000 --- a/AsbCloudInfrastructure/Services/Parser/ParserIds.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace AsbCloudInfrastructure.Services.Parser; - -public static class ParserIds -{ - public const int IdTrajectoryFactManualParser = 1; - public const int IdTrajectoryPlanParser = 2; - public const int IdProcessMapPlanDrillingParser = 3; -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Parser/ParserServiceFactory.cs b/AsbCloudInfrastructure/Services/Parser/ParserServiceFactory.cs deleted file mode 100644 index 36718cfd..00000000 --- a/AsbCloudInfrastructure/Services/Parser/ParserServiceFactory.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using AsbCloudApp.Data; -using AsbCloudApp.Requests.ParserOptions; -using AsbCloudApp.Services; - -namespace AsbCloudInfrastructure.Services.Parser; - -public class ParserServiceFactory -{ - private readonly IDictionary> parsers; - - public ParserServiceFactory(IDictionary> parsers) - { - this.parsers = parsers; - } - - public IParserService Create(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 IParserService - ?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа"); - } -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs index e0073bee..2268b821 100644 --- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs @@ -14,11 +14,8 @@ public class ProcessMapPlanDrillingParser : ProcessMapPlanParser sections; - public ProcessMapPlanDrillingParser(IServiceProvider serviceProvider) - : base(serviceProvider) + public ProcessMapPlanDrillingParser(IWellOperationRepository wellOperationRepository) { - var wellOperationRepository = serviceProvider.GetRequiredService(); - sections = wellOperationRepository.GetSectionTypes(); } diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs index 3a427738..c08ec6e8 100644 --- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs @@ -1,20 +1,12 @@ using System; -using System.IO; -using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMapPlan; -using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; -public abstract class ProcessMapPlanParser : ParserExcelService +public abstract class ProcessMapPlanParser : ParserExcelService where TDto : ProcessMapPlanBaseDto { - protected ProcessMapPlanParser(IServiceProvider serviceProvider) - : base(serviceProvider) - { - } - protected override int HeaderRowsCount => 2; protected static int? GetIdMode(string? modeName) => diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs index 4b7ddb53..5de4e2c5 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryFactManualParser.cs @@ -1,18 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using AsbCloudApp.Data.Trajectory; -using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public class TrajectoryFactManualParser : ParserExcelService +public class TrajectoryFactManualParser : ParserExcelService { - public TrajectoryFactManualParser(IServiceProvider serviceProvider) - : base(serviceProvider) - { - } - protected override string SheetName => "Фактическая траектория"; protected override int HeaderRowsCount => 2; diff --git a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs index b7a03696..4c100766 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/Parser/TrajectoryPlanParser.cs @@ -1,18 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using AsbCloudApp.Data.Trajectory; -using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; namespace AsbCloudInfrastructure.Services.Trajectory.Parser; -public class TrajectoryPlanParser : ParserExcelService +public class TrajectoryPlanParser : ParserExcelService { - public TrajectoryPlanParser(IServiceProvider serviceProvider) - : base(serviceProvider) - { - } - protected override string SheetName => "Плановая траектория"; protected override int HeaderRowsCount => 2; diff --git a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs index dcda8a58..8921d982 100644 --- a/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs +++ b/AsbCloudWebApi.Tests/Services/Trajectory/TrajectoryParserTest.cs @@ -2,6 +2,7 @@ using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; +using AsbCloudInfrastructure.Services.Trajectory.Parser; using Xunit; namespace AsbCloudWebApi.Tests.Services.Trajectory; @@ -10,8 +11,9 @@ public class TrajectoryParserTest { private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates"; - private readonly ParserServiceFactory parserServiceFactory; - + private readonly TrajectoryPlanParser trajectoryPlanParser = new(); + private readonly TrajectoryFactManualParser trajectoryFactManualParser = new(); + [Fact] public void Parse_trajectory_plan() { @@ -20,11 +22,8 @@ public class TrajectoryParserTest if (stream is null) Assert.Fail("Файла для импорта не существует"); - - var parserService = parserServiceFactory.Create( - ParserIds.IdTrajectoryPlanParser); - var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); + var trajectoryRows = trajectoryPlanParser.Parse(stream, IParserOptionsRequest.Empty()); Assert.Equal(3, trajectoryRows.Item.Count()); } @@ -38,10 +37,7 @@ public class TrajectoryParserTest if (stream is null) Assert.Fail("Файла для импорта не существует"); - var parserService = parserServiceFactory.Create( - ParserIds.IdTrajectoryFactManualParser); - - var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty()); + var trajectoryRows = trajectoryFactManualParser.Parse(stream, IParserOptionsRequest.Empty()); Assert.Equal(4, trajectoryRows.Item.Count()); } From e2764504aa80d6b4aaa6027e9358762dd37eead9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 16 Feb 2024 11:54:46 +0300 Subject: [PATCH 12/14] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Interfaces/IControllerWithParser.cs | 22 ---------- .../ProcessMapPlanBaseController.cs | 38 +++++++++------- .../ProcessMapPlanDrillingController.cs | 6 +-- .../TrajectoryEditableController.cs | 43 +++++++++++-------- .../TrajectoryFactManualController.cs | 11 ++--- .../Trajectory/TrajectoryPlanController.cs | 10 ++--- AsbCloudWebApi/Extensions.cs | 25 ++++------- 7 files changed, 63 insertions(+), 92 deletions(-) delete mode 100644 AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs diff --git a/AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs b/AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs deleted file mode 100644 index e47d644b..00000000 --- a/AsbCloudWebApi/Controllers/Interfaces/IControllerWithParser.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using AsbCloudApp.Data; -using AsbCloudApp.Requests.ParserOptions; -using AsbCloudApp.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace AsbCloudWebApi.Controllers.Interfaces; - -public interface IWellControllerWithParser - where TDto : class, IId - where TOptions : class, IParserOptionsRequest -{ - IParserService ParserService { get; } - - Task>> Parse(int idWell, - [FromForm] IFormFileCollection files, - CancellationToken token); - - IActionResult GetTemplate(); -} \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs index f630059c..9c0c8594 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs @@ -9,12 +9,12 @@ using Microsoft.AspNetCore.Http; using AsbCloudApp.Exceptions; using AsbCloudApp.Requests; using System; +using System.IO; using AsbCloudApp.Services; using System.Linq; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; -using AsbCloudWebApi.Controllers.Interfaces; namespace AsbCloudWebApi.Controllers.ProcessMapPlan; @@ -24,29 +24,25 @@ namespace AsbCloudWebApi.Controllers.ProcessMapPlan; [ApiController] [Route("api/well/{idWell}/[controller]")] [Authorize] -public abstract class ProcessMapPlanBaseController : ControllerBase, - IWellControllerWithParser +public abstract class ProcessMapPlanBaseController : ControllerBase where TDto : ProcessMapPlanBaseDto { private readonly IChangeLogRepository repository; private readonly IWellService wellService; - + private readonly ParserExcelService parserService; + protected ProcessMapPlanBaseController(IChangeLogRepository repository, IWellService wellService, - ParserServiceFactory parserFactory, - int idParserService) + ParserExcelService parserService) { this.repository = repository; this.wellService = wellService; - - ParserService = parserFactory.Create(idParserService); + this.parserService = parserService; } - - public IParserService ParserService { get; } - - protected abstract string TemplateFileName { get; } - /// + protected abstract string TemplateFileName { get; } + + /// /// Добавление /// /// @@ -220,8 +216,18 @@ public abstract class ProcessMapPlanBaseController : ControllerBase, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); - - return this.ParseExcelFile(files, IParserOptionsRequest.Empty()); + + var stream = files.GetExcelFile(); + + try + { + var dto = parserService.Parse(stream, IParserOptionsRequest.Empty()); + return Ok(dto); + } + catch (FileFormatException ex) + { + return this.ValidationBadRequest(nameof(files), ex.Message); + } } /// @@ -234,7 +240,7 @@ public abstract class ProcessMapPlanBaseController : ControllerBase, [ProducesResponseType(StatusCodes.Status204NoContent)] public IActionResult GetTemplate() { - var stream = ParserService.GetTemplateFile(); + var stream = parserService.GetTemplateFile(); return File(stream, "application/octet-stream", TemplateFileName); } diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs index ed2e9b35..a22676d8 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs @@ -2,7 +2,7 @@ using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; -using AsbCloudInfrastructure.Services.Parser; +using AsbCloudInfrastructure.Services.ProcessMapPlan.Parser; namespace AsbCloudWebApi.Controllers.ProcessMapPlan; @@ -10,8 +10,8 @@ public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController repository, IWellService wellService, - ParserServiceFactory parserFactory) - : base(repository, wellService, parserFactory, ParserIds.IdProcessMapPlanDrillingParser) + ProcessMapPlanDrillingParser parserService) + : base(repository, wellService, parserService) { } diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs index d96b1491..344ed707 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryEditableController.cs @@ -1,4 +1,5 @@ -using AsbCloudApp.Data.Trajectory; +using System; +using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudInfrastructure.Services.Trajectory.Export; @@ -6,12 +7,12 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Requests.ParserOptions; using AsbCloudInfrastructure.Services.Parser; -using AsbCloudWebApi.Controllers.Interfaces; namespace AsbCloudWebApi.Controllers.Trajectory { @@ -21,28 +22,22 @@ namespace AsbCloudWebApi.Controllers.Trajectory /// [ApiController] [Authorize] - public abstract class TrajectoryEditableController : TrajectoryController, - IWellControllerWithParser - where TDto : TrajectoryGeoDto - { + public abstract class TrajectoryEditableController : TrajectoryController + where TDto : TrajectoryGeoDto + { + private readonly ParserExcelService parserService; private readonly ITrajectoryEditableRepository trajectoryRepository; protected TrajectoryEditableController(IWellService wellService, - ParserServiceFactory parserServiceFactory, + ParserExcelService parserService, TrajectoryExportService trajectoryExportService, - ITrajectoryEditableRepository trajectoryRepository, - int idParserService) - : base( - wellService, - trajectoryExportService, - trajectoryRepository) - { + ITrajectoryEditableRepository trajectoryRepository) + : base(wellService, trajectoryExportService, trajectoryRepository) + { + this.parserService = parserService; this.trajectoryRepository = trajectoryRepository; - ParserService = parserServiceFactory.Create(idParserService); } - public IParserService ParserService { get; } - /// /// Возвращает excel шаблон для заполнения строк траектории /// @@ -53,7 +48,7 @@ namespace AsbCloudWebApi.Controllers.Trajectory [ProducesResponseType(StatusCodes.Status204NoContent)] public IActionResult GetTemplate() { - var stream = ParserService.GetTemplateFile(); + var stream = parserService.GetTemplateFile(); return File(stream, "application/octet-stream", fileName); } @@ -79,7 +74,17 @@ namespace AsbCloudWebApi.Controllers.Trajectory if (!await CanUserAccessToWellAsync(idWell, token)) return Forbid(); - return this.ParseExcelFile(files, IParserOptionsRequest.Empty()); + var stream = files.GetExcelFile(); + + try + { + var dto = parserService.Parse(stream, IParserOptionsRequest.Empty()); + return Ok(dto); + } + catch (FileFormatException ex) + { + return this.ValidationBadRequest(nameof(files), ex.Message); + } } /// diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs index 2317c1ff..aa1a60dc 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryFactManualController.cs @@ -1,9 +1,8 @@ using AsbCloudApp.Data.Trajectory; using AsbCloudApp.Repositories; using AsbCloudApp.Services; -using AsbCloudInfrastructure; -using AsbCloudInfrastructure.Services.Parser; using AsbCloudInfrastructure.Services.Trajectory.Export; +using AsbCloudInfrastructure.Services.Trajectory.Parser; using Microsoft.AspNetCore.Mvc; namespace AsbCloudWebApi.Controllers.Trajectory; @@ -19,13 +18,9 @@ public class TrajectoryFactManualController : TrajectoryEditableController trajectoryRepository) - : base(wellService, - parserServiceFactory, - trajectoryExportService, - trajectoryRepository, - ParserIds.IdTrajectoryFactManualParser) + : base(wellService, parserService, trajectoryExportService, trajectoryRepository) { } } \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs index 248730ba..bffd7d15 100644 --- a/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs +++ b/AsbCloudWebApi/Controllers/Trajectory/TrajectoryPlanController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using AsbCloudInfrastructure.Services.Parser; +using AsbCloudInfrastructure.Services.Trajectory.Parser; namespace AsbCloudWebApi.Controllers.Trajectory { @@ -23,15 +23,11 @@ namespace AsbCloudWebApi.Controllers.Trajectory protected override string fileName => "ЕЦП_шаблон_файла_плановая_траектория.xlsx"; public TrajectoryPlanController(IWellService wellService, + TrajectoryPlanParser parserService, TrajectoryPlanExportService trajectoryExportService, - ParserServiceFactory parserServiceFactory, ITrajectoryEditableRepository trajectoryRepository, TrajectoryService trajectoryVisualizationService) - : base(wellService, - parserServiceFactory, - trajectoryExportService, - trajectoryRepository, - ParserIds.IdTrajectoryPlanParser) + : base(wellService, parserService, trajectoryExportService, trajectoryRepository) { this.trajectoryVisualizationService = trajectoryVisualizationService; } diff --git a/AsbCloudWebApi/Extensions.cs b/AsbCloudWebApi/Extensions.cs index b78a9745..59c72e37 100644 --- a/AsbCloudWebApi/Extensions.cs +++ b/AsbCloudWebApi/Extensions.cs @@ -8,8 +8,9 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Security.Claims; using AsbCloudApp.Data; +using AsbCloudApp.Exceptions; using AsbCloudApp.Requests.ParserOptions; -using AsbCloudWebApi.Controllers.Interfaces; +using AsbCloudInfrastructure.Services.Parser; using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Mvc; @@ -96,30 +97,20 @@ public static class Extensions } /// - /// Вызов парсера со стандартной валидацией входного файла + /// Получение Excel /// - /// - /// - /// /// - /// /// - public static ActionResult> ParseExcelFile( - this IWellControllerWithParser controller, - IFormFileCollection files, - TOptions options) - where TDto : class, IId - where TOptions : class, IParserOptionsRequest + /// + public static Stream GetExcelFile(this IFormFileCollection files) { if (files.Count < 1) - return MakeBadRequestObjectResult(nameof(files), "Нет файла"); + throw new ArgumentInvalidException(nameof(files), "Нет файла"); var file = files[0]; if (Path.GetExtension(file.FileName).ToLower() != ".xlsx") - return MakeBadRequestObjectResult(nameof(files), "Требуется .xlsx файл."); + throw new ArgumentInvalidException(nameof(files), "Требуется .xlsx файл."); - var stream = file.OpenReadStream(); - - return controller.ParserService.Parse(stream, options); + return file.OpenReadStream(); } } \ No newline at end of file From 855034287991bb2cb16a3a52e4746105e0679870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 16 Feb 2024 12:00:46 +0300 Subject: [PATCH 13/14] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20ChangeLogRepos?= =?UTF-8?q?itory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repository/ChangeLogRepositoryAbstract.cs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs index f9265ba8..61e5bdcb 100644 --- a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs +++ b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs @@ -30,7 +30,6 @@ public abstract class ChangeLogRepositoryAbstract : ICh var result = 0; if (dtos.Any()) { - using var transaction = await db.Database.BeginTransactionAsync(token); var entities = dtos.Select(Convert); var creation = DateTimeOffset.UtcNow; var dbSet = db.Set(); @@ -47,7 +46,6 @@ public abstract class ChangeLogRepositoryAbstract : ICh } result += await SaveChangesWithExceptionHandling(token); - await transaction.CommitAsync(token); } return result; } @@ -104,8 +102,17 @@ public abstract class ChangeLogRepositoryAbstract : ICh } result += await SaveChangesWithExceptionHandling(token); - await transaction.CommitAsync(token); - return result; + + try + { + await transaction.CommitAsync(token); + return result; + } + catch + { + await transaction.RollbackAsync(token); + throw; + } } public async Task UpdateOrInsertRange(int idUser, IEnumerable dtos, CancellationToken token) @@ -148,8 +155,16 @@ public abstract class ChangeLogRepositoryAbstract : ICh using var transaction = await db.Database.BeginTransactionAsync(token); result += await Clear(idUser, request, token); result += await InsertRange(idUser, dtos, token); - await transaction.CommitAsync(token); - return result; + try + { + await transaction.CommitAsync(token); + return result; + } + catch + { + await transaction.RollbackAsync(token); + throw; + } } public async Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token) From b121310819deeb3cd3679b5c228f288c774b1b4c Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 16 Feb 2024 15:38:27 +0500 Subject: [PATCH 14/14] ChangeLogRepositoryAbstract nit --- .../Repository/ChangeLogRepositoryAbstract.cs | 51 ++++++++++--------- .../Parser/ProcessMapPlanParser.cs | 1 - 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs index 61e5bdcb..7a01f7df 100644 --- a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs +++ b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs @@ -80,31 +80,31 @@ public abstract class ChangeLogRepositoryAbstract : ICh } using var transaction = db.Database.BeginTransaction(); - foreach (var entity in entitiesToDelete) - { - entity.IdState = ChangeLogAbstract.IdStateReplaced; - entity.Obsolete = updateTime; - entity.IdEditor = idUser; - } - result += await db.SaveChangesAsync(token); - - var entitiesNew = dtos.Select(Convert); - foreach (var entity in entitiesNew) - { - entity.IdPrevious = entity.Id; - entity.Id = default; - entity.Creation = updateTime; - entity.IdAuthor = idUser; - entity.Obsolete = null; - entity.IdEditor = null; - entity.IdState = ChangeLogAbstract.IdStateActual; - dbSet.Add(entity); - } - - result += await SaveChangesWithExceptionHandling(token); - try { + foreach (var entity in entitiesToDelete) + { + entity.IdState = ChangeLogAbstract.IdStateReplaced; + entity.Obsolete = updateTime; + entity.IdEditor = idUser; + } + result += await db.SaveChangesAsync(token); + + var entitiesNew = dtos.Select(Convert); + foreach (var entity in entitiesNew) + { + entity.IdPrevious = entity.Id; + entity.Id = default; + entity.Creation = updateTime; + entity.IdAuthor = idUser; + entity.Obsolete = null; + entity.IdEditor = null; + entity.IdState = ChangeLogAbstract.IdStateActual; + dbSet.Add(entity); + } + + result += await SaveChangesWithExceptionHandling(token); + await transaction.CommitAsync(token); return result; } @@ -153,10 +153,11 @@ public abstract class ChangeLogRepositoryAbstract : ICh { var result = 0; using var transaction = await db.Database.BeginTransactionAsync(token); - result += await Clear(idUser, request, token); - result += await InsertRange(idUser, dtos, token); try { + result += await Clear(idUser, request, token); + result += await InsertRange(idUser, dtos, token); + await transaction.CommitAsync(token); return result; } diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs index c08ec6e8..496010c4 100644 --- a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs +++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs @@ -12,7 +12,6 @@ public abstract class ProcessMapPlanParser : ParserExcelService protected static int? GetIdMode(string? modeName) => modeName?.Trim().ToLower() switch { - "ручной" => 0, "ротор" => 1, "слайд" => 2, _ => null