diff --git a/AsbCloudApp/Data/WellOperationImport/Options/IWellOperationImportOptions.cs b/AsbCloudApp/Data/WellOperationImport/Options/IWellOperationImportOptions.cs new file mode 100644 index 00000000..30b9cf8c --- /dev/null +++ b/AsbCloudApp/Data/WellOperationImport/Options/IWellOperationImportOptions.cs @@ -0,0 +1,12 @@ +namespace AsbCloudApp.Data.WellOperationImport.Options; + +/// +/// Опции для парсинга +/// +public interface IWellOperationImportOptions +{ + /// + /// Тип операции + /// + int IdType { get; } +} \ No newline at end of file diff --git a/AsbCloudApp/Data/WellOperationImport/Options/WellOperationImportDefaultOptionsDto.cs b/AsbCloudApp/Data/WellOperationImport/Options/WellOperationImportDefaultOptionsDto.cs new file mode 100644 index 00000000..a1db3d35 --- /dev/null +++ b/AsbCloudApp/Data/WellOperationImport/Options/WellOperationImportDefaultOptionsDto.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace AsbCloudApp.Data.WellOperationImport.Options; + +/// +/// Опции для парсинга дефолтного шаблона +/// +public class WellOperationImportDefaultOptionsDto : IWellOperationImportOptions +{ + /// + /// Тип операции + /// 0 - плановая операция + /// 1 - фактическая операция + /// + [Required] + [Range(0, 1, ErrorMessage = "Тип операции недопустим. Допустимые: 0, 1")] + public int IdType { get; set; } +} \ No newline at end of file diff --git a/AsbCloudApp/Data/WellOperationImport/Options/WellOperationImportGazpromKhantosOptionsDto.cs b/AsbCloudApp/Data/WellOperationImport/Options/WellOperationImportGazpromKhantosOptionsDto.cs new file mode 100644 index 00000000..d8b094c4 --- /dev/null +++ b/AsbCloudApp/Data/WellOperationImport/Options/WellOperationImportGazpromKhantosOptionsDto.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations; + +namespace AsbCloudApp.Data.WellOperationImport.Options; + +/// +/// Опции для настройки парсинга документа ГПНХ(Хантос) +/// +public class WellOperationImportGazpromKhantosOptionsDto : IWellOperationImportOptions +{ + /// + /// Название листа + /// + [Required] + [StringLength(250, MinimumLength = 1, ErrorMessage = "Название листа должно быть задано")] + public string SheetName { get; set; } = null!; + + /// + /// Тип операции + /// 0 - плановая операция + /// + [Required] + [Range(0, 0, ErrorMessage = "Тип операции недопустим. Допустимый: 0")] + public int IdType { get; set; } + + /// + /// Начальная строка + /// + [Required] + public int StartRow { get; set; } + + /// + /// Конечная строка + /// + [Required] + public int EndRow { get; set; } +} \ No newline at end of file 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/SheetDto.cs b/AsbCloudApp/Data/WellOperationImport/SheetDto.cs new file mode 100644 index 00000000..32091283 --- /dev/null +++ b/AsbCloudApp/Data/WellOperationImport/SheetDto.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; + +namespace AsbCloudApp.Data.WellOperationImport; + +/// +/// Лист полученный из файла Excel +/// +public class SheetDto +{ + /// + /// Название листа + /// + public string Name { get; set; } = null!; + + /// + /// Строки + /// + public IEnumerable Rows { get; set; } = Enumerable.Empty(); +} \ No newline at end of file diff --git a/AsbCloudApp/Data/WellOperationImport/WellOperationImportOptionsDto.cs b/AsbCloudApp/Data/WellOperationImport/WellOperationImportOptionsDto.cs deleted file mode 100644 index b085754f..00000000 --- a/AsbCloudApp/Data/WellOperationImport/WellOperationImportOptionsDto.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace AsbCloudApp.Data.WellOperationImport; - -/// -/// Опции для настройки парсинга документа -/// -public class WellOperationParserOptionsDto -{ - /// - /// Название листа - /// - [StringLength(250, MinimumLength =1, ErrorMessage = "Название листа должно быть задано")] - public string SheetName { get; set; } = null!; - - /// - /// Id шаблона - /// 0 - Дефолтный шаблон - /// 1 - Газпром хантос - /// - public int IdTemplate { get; set; } - - /// - /// Начальная строка - /// - public int? StartRow { get; set; } - - /// - /// Конечная строка - /// - public int? EndRow { get; set; } -} \ No newline at end of file diff --git a/AsbCloudApp/Services/WellOperationImport/IWellOperationExcelParser.cs b/AsbCloudApp/Services/WellOperationImport/IWellOperationExcelParser.cs index c8c8042a..7a695beb 100644 --- a/AsbCloudApp/Services/WellOperationImport/IWellOperationExcelParser.cs +++ b/AsbCloudApp/Services/WellOperationImport/IWellOperationExcelParser.cs @@ -1,29 +1,20 @@ -using System.Collections.Generic; using System.IO; using AsbCloudApp.Data.WellOperationImport; +using AsbCloudApp.Data.WellOperationImport.Options; namespace AsbCloudApp.Services.WellOperationImport; /// /// Парсинг операций из excel файла /// -public interface IWellOperationExcelParser +public interface IWellOperationExcelParser + where TOptions : IWellOperationImportOptions { - /// - /// Id шаблона - /// - int IdTemplate { get; } - - /// - /// Типы операций, которые можно получить из файла - /// - IEnumerable IdTypes { get; } - - /// - /// Метод парсинга документа - /// - /// - /// - /// - IEnumerable Parse(Stream stream, WellOperationParserOptionsDto options); + /// + /// Метод парсинга документа + /// + /// + /// + /// + SheetDto Parse(Stream stream, TOptions options); } \ No newline at end of file diff --git a/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs b/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs index 1da68cf8..54e813d8 100644 --- a/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs +++ b/AsbCloudApp/Services/WellOperationImport/IWellOperationImportService.cs @@ -1,4 +1,3 @@ -using System.IO; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data.WellOperationImport; @@ -10,17 +9,14 @@ namespace AsbCloudApp.Services.WellOperationImport; /// public interface IWellOperationImportService { - /// - /// Загрузить из excel список операций - /// - /// - /// - /// - /// - /// - /// - /// - Task ImportAsync(int idWell, int idUser, int idType, Stream stream, WellOperationParserOptionsDto options, - bool deleteWellOperationsBeforeImport, - CancellationToken cancellationToken); + /// + /// Загрузить из excel список операций + /// + /// + /// + /// + /// + /// + /// + Task ImportAsync(int idWell, int idUser, int idType, SheetDto sheet, bool deleteBeforeImport, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 36a91cf3..01eea5cc 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -31,6 +31,7 @@ using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; using AsbCloudApp.Services.WellOperationImport; using AsbCloudInfrastructure.Services.WellOperationImport; using AsbCloudInfrastructure.Services.ProcessMap.ProcessMapWellboreDevelopment; +using AsbCloudApp.Data.WellOperationImport.Options; using AsbCloudInfrastructure.Services.WellOperationImport.FileParser; namespace AsbCloudInfrastructure @@ -241,8 +242,8 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient, WellOperationDefaultExcelParser>(); + services.AddTransient, WellOperationGazpromKhantosExcelParser>(); return services; } diff --git a/AsbCloudInfrastructure/Services/DailyReport/XLExtentions.cs b/AsbCloudInfrastructure/Services/DailyReport/XLExtentions.cs deleted file mode 100644 index 30eee683..00000000 --- a/AsbCloudInfrastructure/Services/DailyReport/XLExtentions.cs +++ /dev/null @@ -1,161 +0,0 @@ -using ClosedXML.Excel; -using System; - -namespace AsbCloudInfrastructure.Services.DailyReport -{ - - internal static class XLExtentions - { - public static IXLRange _SetValue(this IXLRange range, object value) - { - var mergedRange = range.Merge(); - mergedRange.FirstCell()._SetValue(value); - var colWidth = mergedRange.FirstCell().WorksheetColumn().Width; - var maxCharsToWrap = colWidth / (0.1d * mergedRange.FirstCell().Style.Font.FontSize); - if (value is string valueString && valueString.Length > maxCharsToWrap) - { - var row = mergedRange.FirstCell().WorksheetRow(); - var baseHeight = row.Height; - row.Height = 0.5d * baseHeight * Math.Ceiling(1d + valueString.Length / maxCharsToWrap); - } - mergedRange.Style.SetAllBorders() - .Alignment.SetWrapText(true); - return mergedRange; - } - - public static IXLCell _SetValue(this IXLCell cell, object value) - { - switch (value) - { - case DateTime dateTime: - cell._SetValue(dateTime); - break; - case IFormattable formattable: - cell._SetValue(formattable); - break; - case string valueString: - cell._SetValue(valueString); - break; - default: - cell.Value = value; - break; - } - - return cell; - } - - public static IXLCell _SetValue(this IXLCell cell, string value, bool adaptRowHeight = false) - { - cell.Value = value; - cell.Style - .SetAllBorders() - .Alignment.WrapText = true; - - cell.Value = value; - if (adaptRowHeight) - { - var colWidth = cell.WorksheetColumn().Width; - var maxCharsToWrap = colWidth / (0.1d * cell.Style.Font.FontSize); - if (value.Length > maxCharsToWrap) - { - var row = cell.WorksheetRow(); - var baseHeight = row.Height; - row.Height = 0.5d * baseHeight * Math.Ceiling(1d + value.Length / maxCharsToWrap); - } - } - - return cell; - } - - public static IXLCell _ValueNoBorder(this IXLCell cell, string value, bool adaptRowHeight = false) - { - cell.Value = value; - cell.Style.Alignment.WrapText = true; - - cell.Value = value; - if (adaptRowHeight) - { - var colWidth = cell.WorksheetColumn().Width; - var maxCharsToWrap = colWidth / (0.1d * cell.Style.Font.FontSize); - if (value.Length > maxCharsToWrap) - { - var row = cell.WorksheetRow(); - var baseHeight = row.Height; - row.Height = 0.5d * baseHeight * Math.Ceiling(1d + value.Length / maxCharsToWrap); - } - } - - return cell; - } - - - - - public static IXLCell _SetValue(this IXLCell cell, DateTime value, string dateFormat = "DD.MM.YYYY HH:MM:SS") - { - cell.Value = value; - cell.Style - .SetAllBorders() - .Alignment.WrapText = true; - - cell.Value = value; - - cell.DataType = XLDataType.DateTime; - cell.Style.DateFormat.Format = "DD.MM.YYYY HH:MM:SS"; - - return cell; - } - - public static IXLCell _SetValue(this IXLCell cell, IFormattable value, string format = "0.00") - { - cell.Value = value; - cell.Style - .SetAllBorders() - .Alignment.WrapText = true; - - cell.Value = value; - - cell.DataType = XLDataType.Number; - cell.Style.NumberFormat.Format = "0.00"; - - return cell; - } - - public static IXLStyle SetAllBorders(this IXLStyle style, XLBorderStyleValues borderStyle = XLBorderStyleValues.Thin) - { - style.Border.RightBorder = borderStyle; - style.Border.LeftBorder = borderStyle; - style.Border.TopBorder = borderStyle; - style.Border.BottomBorder = borderStyle; - style.Border.InsideBorder = borderStyle; - style.Border.OutsideBorder = borderStyle; - return style; - } - - public static IXLStyle SetBaseFont(this IXLStyle style) - { - style.Font.FontName = "Calibri"; - style.Font.FontSize = 10; - return style; - } - - public static IXLStyle SetH1(this IXLStyle style) - { - style.Font.FontName = "Calibri"; - style.Font.FontSize = 14; - return style; - } - - /// - /// Костыль исправляющий проблему в библиотеке IXLRange Range(this IXLWorksheet, IXLAddress, IXLAddress) с кастингом IXLAddress к XLAddress. - /// - /// - /// - /// - /// - public static IXLRange _Range(this IXLWorksheet sheet, CellAddress begin, CellAddress end) - => sheet.Range(begin.RowNumber, begin.ColumnNumber, end.RowNumber, end.ColumnNumber); - - } - -} diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs index e8185a34..7ff6ecf2 100644 --- a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs +++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs @@ -187,23 +187,23 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService private ProcessMapPlanDto ParseRow(IXLRow row) { - var wellSectionTypeCaption = GetCellValue(row, columnWellSectionType).Trim().ToLower(); - var modeName = GetCellValue(row, columnMode).Trim().ToLower(); - var depthStart = GetCellValue(row, columnDepthStart); - var depthEnd = GetCellValue(row, columnDepthEnd); - var pressurePlan = GetCellValue(row, columnPressurePlan); - var pressureLimitMax = GetCellValue(row, columnPressureLimitMax); - var axialLoadPlan = GetCellValue(row, columnAxialLoadPlan); - var axialLoadLimitMax = GetCellValue(row, columnAxialLoadLimitMax); - var topDriveTorquePlan = GetCellValue(row, columnTopDriveTorquePlan); - var topDriveTorqueLimitMax = GetCellValue(row, columnTopDriveTorqueLimitMax); - var topDriveSpeedPlan = GetCellValue(row, columnTopDriveSpeedPlan); - var topDriveSpeedLimitMax = GetCellValue(row, columnTopDriveSpeedLimitMax); - var flowPlan = GetCellValue(row, columnFlowPlan); - var flowLimitMax = GetCellValue(row, columnFlowLimitMax); - var ropPlan = GetCellValue(row, columnRopPlan); - var usageSaub = GetCellValue(row, columnUsageSaub); - var usageSpin = GetCellValue(row, columnUsageSpin); + var wellSectionTypeCaption = row.Cell(columnWellSectionType).GetCellValue().Trim().ToLower(); + var modeName = row.Cell(columnMode).GetCellValue().Trim().ToLower(); + var depthStart = row.Cell(columnDepthStart).GetCellValue(); + var depthEnd = row.Cell(columnDepthEnd).GetCellValue(); + var pressurePlan = row.Cell(columnPressurePlan).GetCellValue(); + var pressureLimitMax = 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 wellSection = sections.FirstOrDefault(s => s.Caption.Trim().ToLower() == wellSectionTypeCaption) ?? throw new FileFormatException( @@ -342,19 +342,4 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService 2 => "Слайд", _ => "Ручной", }; - - //TODO: вынести в метод расширения - private static T GetCellValue(IXLRow row, int columnNumber) - { - try - { - var cell = row.Cell(columnNumber); - return (T)Convert.ChangeType(cell.Value, typeof(T)); - } - catch - { - throw new FileFormatException( - $"Лист {row.Worksheet.Name}. Ячейка: ({row.RowNumber()},{columnNumber}) содержит некорректное значение"); - } - } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Trajectory/PlannedTrajectoryImportService.cs b/AsbCloudInfrastructure/Services/Trajectory/PlannedTrajectoryImportService.cs index 6ea64efb..771396dc 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/PlannedTrajectoryImportService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/PlannedTrajectoryImportService.cs @@ -177,32 +177,17 @@ namespace AsbCloudInfrastructure.Services.Trajectory private TrajectoryGeoPlanDto ParseRow(IXLRow row) { - var _wellboreDepth = row.Cell(ColumnWellboreDepth).Value; - var _zenithAngle = row.Cell(ColumnZenithAngle).Value; - var _azimuthGeo = row.Cell(ColumnAzimuthGeo).Value; - var _azimuthMagnetic = row.Cell(ColumnAzimuthMagnetic).Value; - var _verticalDepth = row.Cell(ColumnVerticalDepth).Value; - var _radius = row.Cell(ColumnRadius).Value; - var _comment = row.Cell(ColumnComment).Value; - - var trajectoryRow = new TrajectoryGeoPlanDto(); - - static double getDoubleValue(object value, string nameParam, IXLRow row) + var trajectoryRow = new TrajectoryGeoPlanDto { - if (value is double _value) - return _value; - throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} - некорректные данные - {nameParam}"); - } - - trajectoryRow.WellboreDepth = getDoubleValue(_wellboreDepth, "Глубина по стволу", row); - trajectoryRow.ZenithAngle = getDoubleValue(_zenithAngle, "Зенитный угол", row); - trajectoryRow.AzimuthGeo = getDoubleValue(_azimuthGeo, "Азимут географический", row); - trajectoryRow.AzimuthMagnetic = getDoubleValue(_azimuthMagnetic, "Азимут магнитный", row); - trajectoryRow.VerticalDepth = getDoubleValue(_verticalDepth, "Глубина вертикальная", row); - trajectoryRow.Radius = getDoubleValue(_radius, "Радиус цели", row); - - if (_comment is not null) - trajectoryRow.Comment = _comment.ToString(); + 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 trajectoryRow; } } diff --git a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs index f5f56612..8fd86f2c 100644 --- a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs +++ b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationDefaultExcelParser.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using AsbCloudApp.Data.WellOperationImport; -using AsbCloudApp.Exceptions; +using AsbCloudApp.Data.WellOperationImport.Options; using AsbCloudApp.Services.WellOperationImport; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.WellOperationImport.Constants; @@ -11,31 +11,29 @@ using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.WellOperationImport.FileParser; -public class WellOperationDefaultExcelParser : IWellOperationExcelParser +public class WellOperationDefaultExcelParser : IWellOperationExcelParser { - public int IdTemplate => Templates.IdDefaultTemplate; - public IEnumerable IdTypes => new[] { WellOperation.IdOperationTypePlan, WellOperation.IdOperationTypeFact }; - - public IEnumerable Parse(Stream stream, WellOperationParserOptionsDto options) + public SheetDto Parse(Stream stream, WellOperationImportDefaultOptionsDto options) { using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled); return ParseWorkbook(workbook, options); } - private static IEnumerable ParseWorkbook(IXLWorkbook workbook, WellOperationParserOptionsDto options) + private static SheetDto ParseWorkbook(IXLWorkbook workbook, WellOperationImportDefaultOptionsDto 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)) - ?? throw new FileFormatException($"Книга excel не содержит листа '{options.SheetName}'"); + string.Equals(ws.Name, sheetName, StringComparison.CurrentCultureIgnoreCase)) + ?? throw new FileFormatException($"Книга excel не содержит листа '{sheetName}'"); return ParseSheet(sheet); } - private static IEnumerable ParseSheet(IXLWorksheet sheet) + private static SheetDto ParseSheet(IXLWorksheet sheet) { if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 7) throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов."); @@ -47,7 +45,7 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser case > 1024: throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество операций."); case <= 0: - return Enumerable.Empty(); + return new SheetDto { Name = sheet.Name }; } var rows = new RowDto[count]; @@ -71,7 +69,11 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser if (cellValuesErrors.Any()) throw new FileFormatException(string.Join("\r\n", cellValuesErrors)); - return rows; + return new SheetDto + { + Name = sheet.Name, + Rows = rows + }; } private static RowDto ParseRow(IXLRow xlRow) @@ -79,28 +81,14 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser return new RowDto { Number = xlRow.RowNumber(), - Section = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnSection)), - Category = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnCategory)), - CategoryInfo = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnCategoryInfo)), - DepthStart = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnDepthStart)), - DepthEnd = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnDepthEnd)), - Date = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnDate)), - Duration = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnDuration)), - Comment = GetCellValue(xlRow.Cell(DefaultTemplateInfo.ColumnComment)) + Section = xlRow.Cell(DefaultTemplateInfo.ColumnSection).GetCellValue(), + Category = xlRow.Cell(DefaultTemplateInfo.ColumnCategory).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() }; } - - //TODO: вынести в метод расширения - private static T GetCellValue(IXLCell cell) - { - try - { - return (T)Convert.ChangeType(cell.Value, typeof(T)); - } - catch - { - throw new FileFormatException( - $"Лист '{cell.Worksheet.Name}'. Ячейка: ({cell.Address.RowNumber},{cell.Address.ColumnNumber}) содержит некорректное значение"); - } - } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs index 6f0b412d..1b41ebd3 100644 --- a/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs +++ b/AsbCloudInfrastructure/Services/WellOperationImport/FileParser/WellOperationGazpromKhantosExcelParser.cs @@ -1,26 +1,25 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using AsbCloudApp.Data.WellOperationImport; +using AsbCloudApp.Data.WellOperationImport.Options; using AsbCloudApp.Exceptions; using AsbCloudApp.Services.WellOperationImport; -using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.WellOperationImport.Constants; using AsbCloudInfrastructure.Services.WellOperationImport.FileParser.StringSimilarity; using ClosedXML.Excel; namespace AsbCloudInfrastructure.Services.WellOperationImport.FileParser; -public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser +public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser { private class Operation { public int RowNumber { get; set; } - public string CategoryInfo { get; set; } = null!; + public string? CategoryInfo { get; set; } public double SectionDiameter { get; set; } @@ -31,38 +30,25 @@ 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; - - public IEnumerable IdTypes => new[] { WellOperation.IdOperationTypePlan }; - - public IEnumerable Parse(Stream stream, WellOperationParserOptionsDto options) + public SheetDto Parse(Stream stream, WellOperationImportGazpromKhantosOptionsDto options) { using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled); return ParseWorkBook(workbook, options); - } + } - private IEnumerable ParseWorkBook(IXLWorkbook workbook, WellOperationParserOptionsDto options) + private SheetDto ParseWorkBook(IXLWorkbook workbook, WellOperationImportGazpromKhantosOptionsDto options) { - if (string.IsNullOrWhiteSpace(options.SheetName)) - throw new ArgumentInvalidException(nameof(options.SheetName), "Не указано название листа"); - - if (options.StartRow is null or < 1 or > 1048576) + if (options.StartRow is < 1 or > 1048576) throw new ArgumentInvalidException(nameof(options.StartRow), "Некорректное значение начальной строки"); - if (options.EndRow is null or < 1 or > 1048576) + if (options.EndRow is < 1 or > 1048576) throw new ArgumentInvalidException(nameof(options.EndRow), "Некорректное значение конечной строки"); if (options.EndRow < options.StartRow) @@ -72,15 +58,15 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser string.Equals(ws.Name, options.SheetName, StringComparison.CurrentCultureIgnoreCase)) ?? throw new FileFormatException($"Книга excel не содержит листа '{options.SheetName}'"); - return ParseSheet(sheet, options.StartRow.Value, options.EndRow.Value); + return ParseSheet(sheet, options.StartRow, options.EndRow); } - private IEnumerable ParseSheet(IXLWorksheet sheet, int startRow, int endRow) + private SheetDto ParseSheet(IXLWorksheet sheet, int startRow, int endRow) { - var operationAttributes = GetOperationAttributes(sheet.RowsUsed()); + var operationAttributes = GetOperationAttributes(sheet.RowsUsed()); if (operationAttributes is null) - return Enumerable.Empty(); + return new SheetDto { Name = sheet.Name }; var rowsCount = endRow - startRow + 1; @@ -97,11 +83,11 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser operations.Add(new Operation { RowNumber = xlRow.RowNumber(), - CategoryInfo = GetCellValue(xlRow.Cell(operationAttributes[OperationAttributes.CategoryInfo])), - SectionDiameter = GetCellValue(xlRow.Cell(operationAttributes[OperationAttributes.SectionDiameter])), - Depth = GetCellValue(xlRow.Cell(operationAttributes[OperationAttributes.Depth])), - Duration = GetCellValue(xlRow.Cell(operationAttributes[OperationAttributes.Duration])), - Date = GetCellValue(xlRow.Cell(operationAttributes[OperationAttributes.Date])) + 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(), + Date = xlRow.Cell(operationAttributes[OperationAttributes.Date]).GetCellValue() }); } catch (FileFormatException ex) @@ -113,7 +99,11 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser if (cellValuesErrors.Any()) throw new FileFormatException(string.Join("\r\n", cellValuesErrors)); - return BuildRows(); + return new SheetDto() + { + Name = sheet.Name, + Rows = BuildRows() + }; IEnumerable<(double Diameter, string Name)> BuildSections() { @@ -183,7 +173,7 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser foreach (var cell in cells) { - var operationAttribute = GetValueDictionary(operationAttributesDict, GetCellValue(cell), 0.7); + var operationAttribute = GetValueDictionary(operationAttributesDict, cell.GetCellValue(), 0.7); if (operationAttribute is null || operationAttributes.Any(a => a.Key == operationAttribute)) continue; @@ -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); @@ -234,21 +227,4 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser .Select(line => line.Split(separator)) .ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim()); } - - //TODO: вынести в метод расширения - private static T GetCellValue(IXLCell cell) - { - try - { - if (typeof(T) != typeof(DateTime)) - return (T)Convert.ChangeType(cell.GetFormattedString(), typeof(T), CultureInfo.InvariantCulture); - - return (T)(object)DateTime.FromOADate((double)cell.Value); - } - catch - { - throw new FileFormatException( - $"Лист '{cell.Worksheet.Name}'. Ячейка: ({cell.Address.RowNumber},{cell.Address.ColumnNumber}) содержит некорректное значение"); - } - } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs b/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs index af84f3a9..9da293c6 100644 --- a/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationImport/WellOperationImportService.cs @@ -6,64 +6,35 @@ using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Data.WellOperationImport; -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; public class WellOperationImportService : IWellOperationImportService { - private readonly IEnumerable excelParsers; private readonly IWellOperationRepository wellOperationRepository; private static readonly DateTime dateLimitMin = new(2001, 1, 1, 0, 0, 0); private static readonly DateTime dateLimitMax = new(2099, 1, 1, 0, 0, 0); private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366); - public WellOperationImportService(IEnumerable excelParsers, - IWellOperationRepository wellOperationRepository) + public WellOperationImportService(IWellOperationRepository wellOperationRepository) { - this.excelParsers = excelParsers; 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, int idType, SheetDto sheet, 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 validationErrors = new List(); var sections = wellOperationRepository.GetSectionTypes(); var categories = wellOperationRepository.GetCategories(false); - switch (options.IdTemplate) - { - 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; - } - var operations = new List(); - foreach (var row in rows) + foreach (var row in sheet.Rows) { try { @@ -71,28 +42,32 @@ public class WellOperationImportService : IWellOperationImportService string.Equals(s.Caption, row.Section, StringComparison.CurrentCultureIgnoreCase)); if (section is null) - throw new FileFormatException($"Лист '{options.SheetName}'. В строке '{row.Number}' не удалось определить секцию"); + throw new FileFormatException($"Лист '{sheet.Name}'. В строке '{row.Number}' не удалось определить секцию"); var category = categories.FirstOrDefault(c => string.Equals(c.Name, row.Category, StringComparison.CurrentCultureIgnoreCase)); if (category is null) - throw new FileFormatException($"Лист '{options.SheetName}'. В строке '{row.Number}' не удалось определить операцию"); + throw new FileFormatException($"Лист '{sheet.Name}'. В строке '{row.Number}' не удалось определить операцию"); if (row.DepthStart is not (>= 0d and <= 20_000d)) - throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на начало операции"); + throw new FileFormatException( + $"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная глубина на начало операции"); if (row.DepthEnd is not (>= 0d and <= 20_000d)) - throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на конец операции"); + throw new FileFormatException( + $"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная глубина на конец операции"); if (row.Date < dateLimitMin && row.Date > dateLimitMax) - throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' неправильно получена дата начала операции"); + throw new FileFormatException( + $"Лист '{sheet.Name}'. Строка '{row.Number}' неправильно получена дата начала операции"); if (operations.LastOrDefault()?.DateStart > row.Date) - throw new FileFormatException($"Лист '{options.SheetName}' строка '{row.Number}' дата позднее даты предыдущей операции"); + throw new FileFormatException( + $"Лист '{sheet.Name}' строка '{row.Number}' дата позднее даты предыдущей операции"); if (row.Duration is not (>= 0d and <= 240d)) - throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная длительность операции"); + throw new FileFormatException($"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная длительность операции"); operations.Add(new WellOperationDto { @@ -115,24 +90,25 @@ public class WellOperationImportService : IWellOperationImportService } if (operations.Any() && operations.Min(o => o.DateStart) - operations.Max(o => o.DateStart) > drillingDurationLimitMax) - validationErrors.Add($"Лист {options.SheetName} содержит диапазон дат больше {drillingDurationLimitMax}"); + validationErrors.Add($"Лист {sheet.Name} содержит диапазон дат больше {drillingDurationLimitMax}"); 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 + IdWell = idWell, + OperationType = idType }, 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/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs new file mode 100644 index 00000000..24043605 --- /dev/null +++ b/AsbCloudInfrastructure/XLExtentions.cs @@ -0,0 +1,182 @@ +using ClosedXML.Excel; +using System; +using System.Globalization; +using System.IO; +using AsbCloudInfrastructure.Services.DailyReport; + +namespace AsbCloudInfrastructure; + +internal static class XLExtentions +{ + internal static IXLRange _SetValue(this IXLRange range, object value) + { + var mergedRange = range.Merge(); + mergedRange.FirstCell()._SetValue(value); + var colWidth = mergedRange.FirstCell().WorksheetColumn().Width; + var maxCharsToWrap = colWidth / (0.1d * mergedRange.FirstCell().Style.Font.FontSize); + if (value is string valueString && valueString.Length > maxCharsToWrap) + { + var row = mergedRange.FirstCell().WorksheetRow(); + var baseHeight = row.Height; + row.Height = 0.5d * baseHeight * Math.Ceiling(1d + valueString.Length / maxCharsToWrap); + } + + mergedRange.Style.SetAllBorders() + .Alignment.SetWrapText(true); + return mergedRange; + } + + internal static IXLCell _SetValue(this IXLCell cell, object value) + { + switch (value) + { + case DateTime dateTime: + cell._SetValue(dateTime); + break; + case IFormattable formattable: + cell._SetValue(formattable); + break; + case string valueString: + cell._SetValue(valueString); + break; + default: + cell.Value = value; + break; + } + + return cell; + } + + internal static IXLCell _SetValue(this IXLCell cell, string value, bool adaptRowHeight = false) + { + cell.Value = value; + cell.Style + .SetAllBorders() + .Alignment.WrapText = true; + + cell.Value = value; + if (adaptRowHeight) + { + var colWidth = cell.WorksheetColumn().Width; + var maxCharsToWrap = colWidth / (0.1d * cell.Style.Font.FontSize); + if (value.Length > maxCharsToWrap) + { + var row = cell.WorksheetRow(); + var baseHeight = row.Height; + row.Height = 0.5d * baseHeight * Math.Ceiling(1d + value.Length / maxCharsToWrap); + } + } + + return cell; + } + + internal static IXLCell _ValueNoBorder(this IXLCell cell, string value, bool adaptRowHeight = false) + { + cell.Value = value; + cell.Style.Alignment.WrapText = true; + + cell.Value = value; + if (adaptRowHeight) + { + var colWidth = cell.WorksheetColumn().Width; + var maxCharsToWrap = colWidth / (0.1d * cell.Style.Font.FontSize); + if (value.Length > maxCharsToWrap) + { + var row = cell.WorksheetRow(); + var baseHeight = row.Height; + row.Height = 0.5d * baseHeight * Math.Ceiling(1d + value.Length / maxCharsToWrap); + } + } + + return cell; + } + + + internal static IXLCell _SetValue(this IXLCell cell, DateTime value, string dateFormat = "DD.MM.YYYY HH:MM:SS") + { + cell.Value = value; + cell.Style + .SetAllBorders() + .Alignment.WrapText = true; + + cell.Value = value; + + cell.DataType = XLDataType.DateTime; + cell.Style.DateFormat.Format = "DD.MM.YYYY HH:MM:SS"; + + return cell; + } + + internal static IXLCell _SetValue(this IXLCell cell, IFormattable value, string format = "0.00") + { + cell.Value = value; + cell.Style + .SetAllBorders() + .Alignment.WrapText = true; + + cell.Value = value; + + cell.DataType = XLDataType.Number; + cell.Style.NumberFormat.Format = "0.00"; + + return cell; + } + + internal static IXLStyle SetAllBorders(this IXLStyle style, XLBorderStyleValues borderStyle = XLBorderStyleValues.Thin) + { + style.Border.RightBorder = borderStyle; + style.Border.LeftBorder = borderStyle; + style.Border.TopBorder = borderStyle; + style.Border.BottomBorder = borderStyle; + style.Border.InsideBorder = borderStyle; + style.Border.OutsideBorder = borderStyle; + return style; + } + + internal static IXLStyle SetBaseFont(this IXLStyle style) + { + style.Font.FontName = "Calibri"; + style.Font.FontSize = 10; + return style; + } + + internal static IXLStyle SetH1(this IXLStyle style) + { + style.Font.FontName = "Calibri"; + style.Font.FontSize = 14; + return style; + } + + /// + /// Костыль исправляющий проблему в библиотеке IXLRange Range(this IXLWorksheet, IXLAddress, IXLAddress) с кастингом IXLAddress к XLAddress. + /// + /// + /// + /// + /// + internal static IXLRange _Range(this IXLWorksheet sheet, CellAddress begin, CellAddress end) + => sheet.Range(begin.RowNumber, begin.ColumnNumber, end.RowNumber, end.ColumnNumber); + + + internal static T? GetCellValue(this IXLCell cell) + { + try + { + if (cell.IsEmpty() && default(T) == null) + return default; + + if (typeof(T) != typeof(DateTime)) + return (T)Convert.ChangeType(cell.GetFormattedString(), typeof(T), CultureInfo.InvariantCulture); + + if (cell.Value is DateTime dateTime) + return (T)(object)dateTime; + + return (T)(object)DateTime.FromOADate((double)cell.Value); + } + catch + { + throw new FileFormatException( + $"Лист '{cell.Worksheet.Name}'. Ячейка: ({cell.Address.RowNumber},{cell.Address.ColumnNumber}) содержит некорректное значение"); + } + } +} \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs index 189b9747..54270f49 100644 --- a/AsbCloudWebApi/Controllers/WellOperationController.cs +++ b/AsbCloudWebApi/Controllers/WellOperationController.cs @@ -11,8 +11,9 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading; using System.Threading.Tasks; -using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Services.WellOperationImport; +using AsbCloudApp.Data.WellOperationImport.Options; +using AsbCloudApp.Exceptions; namespace AsbCloudWebApi.Controllers { @@ -30,6 +31,8 @@ namespace AsbCloudWebApi.Controllers private readonly IWellOperationExportService wellOperationExportService; private readonly IWellOperationImportTemplateService wellOperationImportTemplateService; private readonly IWellOperationImportService wellOperationImportService; + private readonly IWellOperationExcelParser wellOperationDefaultExcelParser; + private readonly IWellOperationExcelParser wellOperationGazpromKhantosExcelParser; private readonly IUserRepository userRepository; public WellOperationController(IWellOperationRepository operationRepository, @@ -37,6 +40,8 @@ namespace AsbCloudWebApi.Controllers IWellOperationImportTemplateService wellOperationImportTemplateService, IWellOperationExportService wellOperationExportService, IWellOperationImportService wellOperationImportService, + IWellOperationExcelParser wellOperationDefaultExcelParser, + IWellOperationExcelParser wellOperationGazpromKhantosExcelParser, IUserRepository userRepository) { this.operationRepository = operationRepository; @@ -44,6 +49,8 @@ namespace AsbCloudWebApi.Controllers this.wellOperationImportTemplateService = wellOperationImportTemplateService; this.wellOperationExportService = wellOperationExportService; this.wellOperationImportService = wellOperationImportService; + this.wellOperationDefaultExcelParser = wellOperationDefaultExcelParser; + this.wellOperationGazpromKhantosExcelParser = wellOperationGazpromKhantosExcelParser; this.userRepository = userRepository; } @@ -287,45 +294,28 @@ namespace AsbCloudWebApi.Controllers /// - /// Импорт плановых операций из excel (xlsx) файла + /// Импорт операций из excel (xlsx) файла. Стандартный заполненный шаблон /// /// id скважины - /// Тип операции - /// Начальная строка - /// Конечная строка + /// Параметры для парсинга файла /// Коллекция из одного файла xlsx - /// Удалить операции перед импортом = 1, если файл валидный - /// Название листа - /// Токен отмены задачи - /// Шаблон файла. 0 - стандартный, 1 - Газпромнефть Хантос + /// Удалить операции перед импортом = 1, если файл валидный + /// /// - [HttpPost("import/{options}")] + [HttpPost("import/default/{deleteBeforeImport}")] [Permission] - public async Task ImportAsync(int idWell, - [Required] int idType, - string? sheetName, - [Required] int idTemplate, - int? startRow, - int? endRow, + public async Task ImportDefaultExcelFileAsync(int idWell, + [FromQuery] WellOperationImportDefaultOptionsDto options, [FromForm] IFormFileCollection files, - int options, + [Range(0, 1, ErrorMessage = "Недопустимое значение. Допустимые: 0, 1")] int deleteBeforeImport, CancellationToken token) { - var idCompany = User.GetCompanyId(); var idUser = User.GetUserId(); - if (idCompany is null || idUser is null) - return Forbid(); + if (!idUser.HasValue) + throw new ForbidException("Неизвестный пользователь"); - if (!await CanUserAccessToWellAsync(idWell, token)) - return Forbid(); - - if (!await CanUserEditWellOperationsAsync(idWell, token)) - return Forbid(); - - if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, - idWell, token).ConfigureAwait(false)) - return Forbid(); + await AssertUserHasAccessToImportWellOperationsAsync(idWell, token); if (files.Count < 1) return this.ValidationBadRequest(nameof(files), "Нет файла"); @@ -338,13 +328,56 @@ 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); + var sheet = wellOperationDefaultExcelParser.Parse(stream, options); + + await wellOperationImportService.ImportAsync(idWell, idUser.Value, options.IdType, sheet, (deleteBeforeImport & 1) > 0, token); + } + catch (FileFormatException ex) + { + return this.ValidationBadRequest(nameof(files), ex.Message); + } + + return Ok(); + } + + /// + /// Импорт операций из excel (xlsx) файла. ГПНХ (Хантос) + /// + /// id скважины + /// Параметры для парсинга файла + /// Коллекция из одного файла xlsx + /// Удалить операции перед импортом = 1, если файл валидный + /// + /// + [HttpPost("import/gazpromKhantos/{deleteBeforeImport}")] + [Permission] + public async Task ImportGazpromKhantosExcelFileAsync(int idWell, + [FromQuery] WellOperationImportGazpromKhantosOptionsDto options, + [FromForm] IFormFileCollection files, + [Range(0, 1, ErrorMessage = "Недопустимое значение. Допустимые: 0, 1")] int deleteBeforeImport, + CancellationToken token) + { + var idUser = User.GetUserId(); + + if (!idUser.HasValue) + throw new ForbidException("Неизвестный пользователь"); + + await AssertUserHasAccessToImportWellOperationsAsync(idWell, token); + + if (files.Count < 1) + return this.ValidationBadRequest(nameof(files), "Нет файла"); + + var file = files[0]; + if (Path.GetExtension(file.FileName).ToLower() != ".xlsx") + return this.ValidationBadRequest(nameof(files), "Требуется xlsx файл."); + + using Stream stream = file.OpenReadStream(); + + try + { + var sheet = wellOperationGazpromKhantosExcelParser.Parse(stream, options); + + await wellOperationImportService.ImportAsync(idWell, idUser.Value, options.IdType, sheet, (deleteBeforeImport & 1) > 0, token); } catch (FileFormatException ex) { @@ -420,6 +453,24 @@ namespace AsbCloudWebApi.Controllers return File(stream, "application/octet-stream", fileName); } + private async Task AssertUserHasAccessToImportWellOperationsAsync(int idWell, CancellationToken token) + { + var idCompany = User.GetCompanyId(); + var idUser = User.GetUserId(); + + if (!idCompany.HasValue || !idUser.HasValue) + throw new ForbidException("Неизвестный пользователь"); + + if (!await CanUserAccessToWellAsync(idWell, token)) + throw new ForbidException("Нет доступа к скважине"); + + if (!await CanUserEditWellOperationsAsync(idWell, token)) + throw new ForbidException("Недостаточно прав для редактирования ГГД на завершенной скважине"); + + if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, token)) + throw new ForbidException("Скважина недоступна для компании"); + } + private async Task CanUserEditWellOperationsAsync(int idWell, CancellationToken token) { var idUser = User.GetUserId();