From 14615517d638a14aa3bf5750ba4fe3e7859633d3 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, 2 Oct 2023 09:27:20 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82?= =?UTF-8?q?=D0=B0=20=D0=93=D0=93=D0=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/WellOperationImport/RowDto.cs | 2 +- .../WellOperationImportOptionsDto.cs | 30 ++++++++-- .../IWellOperationImportService.cs | 6 +- .../WellOperationDefaultExcelParser.cs | 12 ++-- .../WellOperationGazpromKhantosExcelParser.cs | 21 +++---- .../WellOperationImportService.cs | 58 ++++++++----------- .../Controllers/WellOperationController.cs | 28 +++------ 7 files changed, 72 insertions(+), 85 deletions(-) diff --git a/AsbCloudApp/Data/WellOperationImport/RowDto.cs b/AsbCloudApp/Data/WellOperationImport/RowDto.cs index 5b6691f4..8f15c3bf 100644 --- a/AsbCloudApp/Data/WellOperationImport/RowDto.cs +++ b/AsbCloudApp/Data/WellOperationImport/RowDto.cs @@ -25,7 +25,7 @@ public class RowDto /// /// Описание категории /// - public string CategoryInfo { get; set; } = null!; + public string? CategoryInfo { get; set; } /// /// Начальная глубина операции diff --git a/AsbCloudApp/Data/WellOperationImport/WellOperationImportOptionsDto.cs b/AsbCloudApp/Data/WellOperationImport/WellOperationImportOptionsDto.cs index b085754f..285f9d67 100644 --- a/AsbCloudApp/Data/WellOperationImport/WellOperationImportOptionsDto.cs +++ b/AsbCloudApp/Data/WellOperationImport/WellOperationImportOptionsDto.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace AsbCloudApp.Data.WellOperationImport; @@ -5,28 +6,45 @@ namespace AsbCloudApp.Data.WellOperationImport; /// /// Опции для настройки парсинга документа /// -public class WellOperationParserOptionsDto +public class WellOperationParserOptionsDto : IValidatableObject { /// /// Название листа /// - [StringLength(250, MinimumLength =1, ErrorMessage = "Название листа должно быть задано")] - public string SheetName { get; set; } = null!; - + public string? SheetName { get; set; } + /// - /// Id шаблона + /// Тип операции + /// 0 - плановая операция + /// 1 - фактическая операция + /// + [Required] + [Range(0, 1, ErrorMessage = "Тип операции недопустим. Допустимые: 0, 1")] + public int IdType { get; set; } + + /// + /// Тип шаблона /// 0 - Дефолтный шаблон /// 1 - Газпром хантос /// + [Required] + [Range(0, 1, ErrorMessage = "Тип шаблона недопустим. Допустимые: 0, 1")] public int IdTemplate { get; set; } /// /// Начальная строка /// public int? StartRow { get; set; } - + /// /// Конечная строка /// public int? EndRow { get; set; } + + /// + public IEnumerable Validate(ValidationContext validationContext) + { + if (IdTemplate != 0 && string.IsNullOrWhiteSpace(SheetName)) + yield return new ValidationResult("Название листа должно быть задано", new[] { nameof(SheetName) }); + } } \ No newline at end of file diff --git a/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs b/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs index 1da68cf8..7a122062 100644 --- a/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs +++ b/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs @@ -14,13 +14,11 @@ public interface IWellOperationImportService /// Загрузить из excel список операций /// /// - /// /// /// - /// + /// /// /// - Task ImportAsync(int idWell, int idUser, int idType, Stream stream, WellOperationParserOptionsDto options, - bool deleteWellOperationsBeforeImport, + Task ImportAsync(int idWell, int idUser, Stream stream, WellOperationParserOptionsDto options, bool deleteBeforeImport, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs index ef9ed499..d4890934 100644 --- a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs +++ b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using AsbCloudApp.Data.WellOperationImport; -using AsbCloudApp.Exceptions; using AsbCloudApp.Services.WellOperationImport; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.WellOperationImport.Constants; @@ -25,11 +24,12 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser private static IEnumerable ParseWorkbook(IXLWorkbook workbook, WellOperationParserOptionsDto options) { - if (string.IsNullOrWhiteSpace(options.SheetName)) - throw new ArgumentInvalidException(nameof(options.SheetName), "Не указано название листа"); + var sheetName = options.IdType == WellOperation.IdOperationTypePlan + ? DefaultTemplateInfo.SheetNamePlan + : DefaultTemplateInfo.SheetNameFact; var sheet = workbook.Worksheets.FirstOrDefault(ws => - string.Equals(ws.Name, options.SheetName, StringComparison.CurrentCultureIgnoreCase)) + string.Equals(ws.Name, sheetName, StringComparison.CurrentCultureIgnoreCase)) ?? throw new FileFormatException($"Книга excel не содержит листа '{options.SheetName}'"); return ParseSheet(sheet); @@ -81,12 +81,12 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser Number = xlRow.RowNumber(), Section = xlRow.Cell(DefaultTemplateInfo.ColumnSection).GetCellValue(), Category = xlRow.Cell(DefaultTemplateInfo.ColumnCategory).GetCellValue(), - CategoryInfo = xlRow.Cell(DefaultTemplateInfo.ColumnCategoryInfo).GetCellValue(), + CategoryInfo = xlRow.Cell(DefaultTemplateInfo.ColumnCategoryInfo).GetCellValue(), DepthStart = xlRow.Cell(DefaultTemplateInfo.ColumnDepthStart).GetCellValue(), DepthEnd = xlRow.Cell(DefaultTemplateInfo.ColumnDepthEnd).GetCellValue(), Date = xlRow.Cell(DefaultTemplateInfo.ColumnDate).GetCellValue(), Duration = xlRow.Cell(DefaultTemplateInfo.ColumnDuration).GetCellValue(), - Comment = xlRow.Cell(DefaultTemplateInfo.ColumnComment).GetCellValue() + Comment = xlRow.Cell(DefaultTemplateInfo.ColumnComment).GetCellValue() }; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs index d1ce47cd..a484a2da 100644 --- a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs +++ b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -20,7 +19,7 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser { public int RowNumber { get; set; } - public string CategoryInfo { get; set; } = null!; + public string? CategoryInfo { get; set; } public double SectionDiameter { get; set; } @@ -31,17 +30,11 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser public DateTime Date { get; set; } } - private readonly CosineSimilarity cosineSimilarity; + private readonly CosineSimilarity cosineSimilarity = new(); private readonly Dictionary operationDict = InitDict("Operations.txt", '='); private readonly Dictionary sectionDict = InitDict("Sections.txt", '='); private readonly Dictionary operationAttributesDict = InitDict("OperationAttributes.txt", '='); - - - public WellOperationGazpromKhantosExcelParser() - { - cosineSimilarity = new CosineSimilarity(); - } public int IdTemplate => Templates.IdGazpromKhantosTemplate; @@ -56,9 +49,6 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser private IEnumerable ParseWorkBook(IXLWorkbook workbook, WellOperationParserOptionsDto options) { - if (string.IsNullOrWhiteSpace(options.SheetName)) - throw new ArgumentInvalidException(nameof(options.SheetName), "Не указано название листа"); - if (options.StartRow is null or < 1 or > 1048576) throw new ArgumentInvalidException(nameof(options.StartRow), "Некорректное значение начальной строки"); @@ -97,7 +87,7 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser operations.Add(new Operation { RowNumber = xlRow.RowNumber(), - CategoryInfo = xlRow.Cell(operationAttributes[OperationAttributes.CategoryInfo]).GetCellValue(), + CategoryInfo = xlRow.Cell(operationAttributes[OperationAttributes.CategoryInfo]).GetCellValue(), SectionDiameter =xlRow.Cell(operationAttributes[OperationAttributes.SectionDiameter]).GetCellValue(), Depth = xlRow.Cell(operationAttributes[OperationAttributes.Depth]).GetCellValue(), Duration = xlRow.Cell(operationAttributes[OperationAttributes.Duration]).GetCellValue(), @@ -198,8 +188,11 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser return operationAttributes is not null && operationAttributes.Count == countOperationAttributes ? operationAttributes : null; } - private string? GetValueDictionary(IDictionary dict, string cellValue, double? minSimilarity) + private string? GetValueDictionary(IDictionary dict, string? cellValue, double? minSimilarity) { + if (string.IsNullOrWhiteSpace(cellValue)) + return null; + var similarValues = new List<(double similarity, string value)>(); var profile1 = cosineSimilarity.GetProfile(cellValue); diff --git a/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs b/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs index af84f3a9..b7e79b4a 100644 --- a/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs @@ -10,8 +10,6 @@ using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services.WellOperationImport; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.WellOperationImport.Constants; namespace AsbCloudInfrastructure.Services.WellOperationImport; @@ -31,35 +29,25 @@ public class WellOperationImportService : IWellOperationImportService this.wellOperationRepository = wellOperationRepository; } - public async Task ImportAsync(int idWell, int idUser, int idType, Stream stream, WellOperationParserOptionsDto options, - bool deleteWellOperationsBeforeImport, CancellationToken cancellationToken) + public async Task ImportAsync(int idWell, int idUser, Stream stream, WellOperationParserOptionsDto options, bool deleteBeforeImport, + CancellationToken cancellationToken) { - var excelParser = excelParsers.FirstOrDefault(p => p.IdTemplate == options.IdTemplate && p.IdTypes.Contains(idType)) - ?? throw new ArgumentInvalidException(nameof(options.IdTemplate), "Невозможно импортировать файл"); - - if (idType != WellOperation.IdOperationTypePlan && idType != WellOperation.IdOperationTypeFact) - throw new ArgumentInvalidException(nameof(idType), "Операции не существует"); - - RowDto[] rows; + var excelParser = excelParsers.FirstOrDefault(p => + p.IdTemplate == options.IdTemplate && + p.IdTypes.Contains(options.IdType)) + ?? throw new ArgumentInvalidException(nameof(options.IdTemplate), "Невозможно импортировать файл"); + + IEnumerable rows; var validationErrors = new List(); var sections = wellOperationRepository.GetSectionTypes(); var categories = wellOperationRepository.GetCategories(false); - switch (options.IdTemplate) + rows = options.IdTemplate switch { - case 0: - options.SheetName = idType == WellOperation.IdOperationTypePlan - ? DefaultTemplateInfo.SheetNamePlan - : DefaultTemplateInfo.SheetNameFact; - rows = excelParser.Parse(stream, options).ToArray(); - break; - default: - if (string.IsNullOrWhiteSpace(options.SheetName)) - throw new FileFormatException("Не указано название листа"); - rows = excelParser.Parse(stream, options).ToArray(); - break; - } + 0 => excelParser.Parse(stream, options), + _ => excelParser.Parse(stream, options) + }; var operations = new List(); @@ -80,16 +68,20 @@ public class WellOperationImportService : IWellOperationImportService throw new FileFormatException($"Лист '{options.SheetName}'. В строке '{row.Number}' не удалось определить операцию"); if (row.DepthStart is not (>= 0d and <= 20_000d)) - throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на начало операции"); + throw new FileFormatException( + $"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на начало операции"); if (row.DepthEnd is not (>= 0d and <= 20_000d)) - throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на конец операции"); + throw new FileFormatException( + $"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на конец операции"); if (row.Date < dateLimitMin && row.Date > dateLimitMax) - throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' неправильно получена дата начала операции"); + throw new FileFormatException( + $"Лист '{options.SheetName}'. Строка '{row.Number}' неправильно получена дата начала операции"); if (operations.LastOrDefault()?.DateStart > row.Date) - throw new FileFormatException($"Лист '{options.SheetName}' строка '{row.Number}' дата позднее даты предыдущей операции"); + throw new FileFormatException( + $"Лист '{options.SheetName}' строка '{row.Number}' дата позднее даты предыдущей операции"); if (row.Duration is not (>= 0d and <= 240d)) throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная длительность операции"); @@ -98,7 +90,7 @@ public class WellOperationImportService : IWellOperationImportService { IdWell = idWell, IdUser = idUser, - IdType = idType, + IdType = options.IdType, IdWellSectionType = section.Id, IdCategory = category.Id, CategoryInfo = row.CategoryInfo, @@ -120,19 +112,19 @@ public class WellOperationImportService : IWellOperationImportService if (validationErrors.Any()) throw new FileFormatException(string.Join("\r\n", validationErrors)); - if(!operations.Any()) + if (!operations.Any()) return; - if (deleteWellOperationsBeforeImport) + if (deleteBeforeImport) { var existingOperations = await wellOperationRepository.GetAsync(new WellOperationRequest { IdWell = idWell }, cancellationToken); - + await wellOperationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken); } - await wellOperationRepository.InsertRangeAsync(operations, cancellationToken); + await wellOperationRepository.InsertRangeAsync(operations, cancellationToken); } } \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs index 189b9747..f2c2b695 100644 --- a/AsbCloudWebApi/Controllers/WellOperationController.cs +++ b/AsbCloudWebApi/Controllers/WellOperationController.cs @@ -290,25 +290,17 @@ namespace AsbCloudWebApi.Controllers /// Импорт плановых операций из excel (xlsx) файла /// /// id скважины - /// Тип операции - /// Начальная строка - /// Конечная строка + /// Параметры для парсинга файла /// Коллекция из одного файла xlsx - /// Удалить операции перед импортом = 1, если файл валидный - /// Название листа - /// Токен отмены задачи - /// Шаблон файла. 0 - стандартный, 1 - Газпромнефть Хантос + /// Удалить операции перед импортом = 1, если файл валидный + /// /// - [HttpPost("import/{options}")] + [HttpPost("import/{deleteBeforeImport}")] [Permission] public async Task ImportAsync(int idWell, - [Required] int idType, - string? sheetName, - [Required] int idTemplate, - int? startRow, - int? endRow, + [FromQuery] WellOperationParserOptionsDto options, [FromForm] IFormFileCollection files, - int options, + [Range(0, 1, ErrorMessage = "Недопустимое значение. Допустимые: 0, 1")] int deleteBeforeImport, CancellationToken token) { var idCompany = User.GetCompanyId(); @@ -338,13 +330,7 @@ namespace AsbCloudWebApi.Controllers try { - await wellOperationImportService.ImportAsync(idWell, idUser.Value, idType, stream, new WellOperationParserOptionsDto - { - SheetName = sheetName, - IdTemplate = idTemplate, - StartRow = startRow, - EndRow = endRow - }, (options & 1) > 0, token); + await wellOperationImportService.ImportAsync(idWell, idUser.Value, stream, options, (deleteBeforeImport & 1) > 0, token); } catch (FileFormatException ex) {