forked from ddrilling/AsbCloudServer
Merge pull request 'Рефакторинг импорта ГГД' (#119) from feature/well_operation_import into dev
Reviewed-on: http://test.digitaldrilling.ru:8080/DDrilling/AsbCloudServer/pulls/119
This commit is contained in:
commit
771ba06a6f
@ -0,0 +1,12 @@
|
||||
namespace AsbCloudApp.Data.WellOperationImport.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Опции для парсинга
|
||||
/// </summary>
|
||||
public interface IWellOperationImportOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип операции
|
||||
/// </summary>
|
||||
int IdType { get; }
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace AsbCloudApp.Data.WellOperationImport.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Опции для парсинга дефолтного шаблона
|
||||
/// </summary>
|
||||
public class WellOperationImportDefaultOptionsDto : IWellOperationImportOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип операции
|
||||
/// 0 - плановая операция
|
||||
/// 1 - фактическая операция
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Range(0, 1, ErrorMessage = "Тип операции недопустим. Допустимые: 0, 1")]
|
||||
public int IdType { get; set; }
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace AsbCloudApp.Data.WellOperationImport.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Опции для настройки парсинга документа ГПНХ(Хантос)
|
||||
/// </summary>
|
||||
public class WellOperationImportGazpromKhantosOptionsDto : IWellOperationImportOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Название листа
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(250, MinimumLength = 1, ErrorMessage = "Название листа должно быть задано")]
|
||||
public string SheetName { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Тип операции
|
||||
/// 0 - плановая операция
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Range(0, 0, ErrorMessage = "Тип операции недопустим. Допустимый: 0")]
|
||||
public int IdType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Начальная строка
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int StartRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Конечная строка
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int EndRow { get; set; }
|
||||
}
|
@ -25,7 +25,7 @@ public class RowDto
|
||||
/// <summary>
|
||||
/// Описание категории
|
||||
/// </summary>
|
||||
public string CategoryInfo { get; set; } = null!;
|
||||
public string? CategoryInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Начальная глубина операции
|
||||
|
20
AsbCloudApp/Data/WellOperationImport/SheetDto.cs
Normal file
20
AsbCloudApp/Data/WellOperationImport/SheetDto.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AsbCloudApp.Data.WellOperationImport;
|
||||
|
||||
/// <summary>
|
||||
/// Лист полученный из файла Excel
|
||||
/// </summary>
|
||||
public class SheetDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Название листа
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Строки
|
||||
/// </summary>
|
||||
public IEnumerable<RowDto> Rows { get; set; } = Enumerable.Empty<RowDto>();
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace AsbCloudApp.Data.WellOperationImport;
|
||||
|
||||
/// <summary>
|
||||
/// Опции для настройки парсинга документа
|
||||
/// </summary>
|
||||
public class WellOperationParserOptionsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Название листа
|
||||
/// </summary>
|
||||
[StringLength(250, MinimumLength =1, ErrorMessage = "Название листа должно быть задано")]
|
||||
public string SheetName { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Id шаблона
|
||||
/// 0 - Дефолтный шаблон
|
||||
/// 1 - Газпром хантос
|
||||
/// </summary>
|
||||
public int IdTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Начальная строка
|
||||
/// </summary>
|
||||
public int? StartRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Конечная строка
|
||||
/// </summary>
|
||||
public int? EndRow { get; set; }
|
||||
}
|
@ -1,29 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using AsbCloudApp.Data.WellOperationImport;
|
||||
using AsbCloudApp.Data.WellOperationImport.Options;
|
||||
|
||||
namespace AsbCloudApp.Services.WellOperationImport;
|
||||
|
||||
/// <summary>
|
||||
/// Парсинг операций из excel файла
|
||||
/// </summary>
|
||||
public interface IWellOperationExcelParser
|
||||
public interface IWellOperationExcelParser<in TOptions>
|
||||
where TOptions : IWellOperationImportOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Id шаблона
|
||||
/// </summary>
|
||||
int IdTemplate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Типы операций, которые можно получить из файла
|
||||
/// </summary>
|
||||
IEnumerable<int> IdTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Метод парсинга документа
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<RowDto> Parse(Stream stream, WellOperationParserOptionsDto options);
|
||||
/// <summary>
|
||||
/// Метод парсинга документа
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
SheetDto Parse(Stream stream, TOptions options);
|
||||
}
|
@ -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;
|
||||
/// </summary>
|
||||
public interface IWellOperationImportService
|
||||
{
|
||||
/// <summary>
|
||||
/// Загрузить из excel список операций
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="idType"></param>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="idUser"></param>
|
||||
/// <param name="deleteWellOperationsBeforeImport"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <param name="options"></param>
|
||||
Task ImportAsync(int idWell, int idUser, int idType, Stream stream, WellOperationParserOptionsDto options,
|
||||
bool deleteWellOperationsBeforeImport,
|
||||
CancellationToken cancellationToken);
|
||||
/// <summary>
|
||||
/// Загрузить из excel список операций
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="idUser"></param>
|
||||
/// <param name="idType"></param>
|
||||
/// <param name="sheet"></param>
|
||||
/// <param name="deleteBeforeImport"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
Task ImportAsync(int idWell, int idUser, int idType, SheetDto sheet, bool deleteBeforeImport, CancellationToken cancellationToken);
|
||||
}
|
@ -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<IWellOperationImportService, WellOperationImportService>();
|
||||
services.AddTransient<IWellOperationImportTemplateService, WellOperationImportTemplateService>();
|
||||
|
||||
services.AddTransient<IWellOperationExcelParser, WellOperationDefaultExcelParser>();
|
||||
services.AddTransient<IWellOperationExcelParser, WellOperationGazpromKhantosExcelParser>();
|
||||
services.AddTransient<IWellOperationExcelParser<WellOperationImportDefaultOptionsDto>, WellOperationDefaultExcelParser>();
|
||||
services.AddTransient<IWellOperationExcelParser<WellOperationImportGazpromKhantosOptionsDto>, WellOperationGazpromKhantosExcelParser>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Костыль исправляющий проблему в библиотеке IXLRange Range(this IXLWorksheet, IXLAddress, IXLAddress) с кастингом IXLAddress к XLAddress.
|
||||
/// </summary>
|
||||
/// <param name="sheet"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <returns></returns>
|
||||
public static IXLRange _Range(this IXLWorksheet sheet, CellAddress begin, CellAddress end)
|
||||
=> sheet.Range(begin.RowNumber, begin.ColumnNumber, end.RowNumber, end.ColumnNumber);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -187,23 +187,23 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService
|
||||
|
||||
private ProcessMapPlanDto ParseRow(IXLRow row)
|
||||
{
|
||||
var wellSectionTypeCaption = GetCellValue<string>(row, columnWellSectionType).Trim().ToLower();
|
||||
var modeName = GetCellValue<string>(row, columnMode).Trim().ToLower();
|
||||
var depthStart = GetCellValue<double>(row, columnDepthStart);
|
||||
var depthEnd = GetCellValue<double>(row, columnDepthEnd);
|
||||
var pressurePlan = GetCellValue<double>(row, columnPressurePlan);
|
||||
var pressureLimitMax = GetCellValue<double>(row, columnPressureLimitMax);
|
||||
var axialLoadPlan = GetCellValue<double>(row, columnAxialLoadPlan);
|
||||
var axialLoadLimitMax = GetCellValue<double>(row, columnAxialLoadLimitMax);
|
||||
var topDriveTorquePlan = GetCellValue<double>(row, columnTopDriveTorquePlan);
|
||||
var topDriveTorqueLimitMax = GetCellValue<double>(row, columnTopDriveTorqueLimitMax);
|
||||
var topDriveSpeedPlan = GetCellValue<double>(row, columnTopDriveSpeedPlan);
|
||||
var topDriveSpeedLimitMax = GetCellValue<double>(row, columnTopDriveSpeedLimitMax);
|
||||
var flowPlan = GetCellValue<double>(row, columnFlowPlan);
|
||||
var flowLimitMax = GetCellValue<double>(row, columnFlowLimitMax);
|
||||
var ropPlan = GetCellValue<double>(row, columnRopPlan);
|
||||
var usageSaub = GetCellValue<double>(row, columnUsageSaub);
|
||||
var usageSpin = GetCellValue<double>(row, columnUsageSpin);
|
||||
var wellSectionTypeCaption = row.Cell(columnWellSectionType).GetCellValue<string>().Trim().ToLower();
|
||||
var modeName = row.Cell(columnMode).GetCellValue<string>().Trim().ToLower();
|
||||
var depthStart = row.Cell(columnDepthStart).GetCellValue<double>();
|
||||
var depthEnd = row.Cell(columnDepthEnd).GetCellValue<double>();
|
||||
var pressurePlan = row.Cell(columnPressurePlan).GetCellValue<double>();
|
||||
var pressureLimitMax = row.Cell(columnPressureLimitMax).GetCellValue<double>();
|
||||
var axialLoadPlan = row.Cell(columnAxialLoadPlan).GetCellValue<double>();
|
||||
var axialLoadLimitMax = row.Cell(columnAxialLoadLimitMax).GetCellValue<double>();
|
||||
var topDriveTorquePlan = row.Cell(columnTopDriveTorquePlan).GetCellValue<double>();
|
||||
var topDriveTorqueLimitMax = row.Cell(columnTopDriveTorqueLimitMax).GetCellValue<double>();
|
||||
var topDriveSpeedPlan = row.Cell(columnTopDriveSpeedPlan).GetCellValue<double>();
|
||||
var topDriveSpeedLimitMax = row.Cell(columnTopDriveSpeedLimitMax).GetCellValue<double>();
|
||||
var flowPlan = row.Cell(columnFlowPlan).GetCellValue<double>();
|
||||
var flowLimitMax = row.Cell(columnFlowLimitMax).GetCellValue<double>();
|
||||
var ropPlan = row.Cell(columnRopPlan).GetCellValue<double>();
|
||||
var usageSaub = row.Cell(columnUsageSaub).GetCellValue<double>();
|
||||
var usageSpin = row.Cell(columnUsageSpin).GetCellValue<double>();
|
||||
|
||||
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<T>(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}) содержит некорректное значение");
|
||||
}
|
||||
}
|
||||
}
|
@ -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<double>(),
|
||||
ZenithAngle = row.Cell(ColumnZenithAngle).GetCellValue<double>(),
|
||||
AzimuthGeo = row.Cell(ColumnAzimuthGeo).GetCellValue<double>(),
|
||||
AzimuthMagnetic = row.Cell(ColumnAzimuthMagnetic).GetCellValue<double>(),
|
||||
VerticalDepth = row.Cell(ColumnVerticalDepth).GetCellValue<double>(),
|
||||
Radius = row.Cell(ColumnRadius).GetCellValue<double>(),
|
||||
Comment = row.Cell(ColumnComment).GetCellValue<string?>()
|
||||
};
|
||||
|
||||
return trajectoryRow;
|
||||
}
|
||||
}
|
||||
|
@ -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<WellOperationImportDefaultOptionsDto>
|
||||
{
|
||||
public int IdTemplate => Templates.IdDefaultTemplate;
|
||||
public IEnumerable<int> IdTypes => new[] { WellOperation.IdOperationTypePlan, WellOperation.IdOperationTypeFact };
|
||||
|
||||
public IEnumerable<RowDto> 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<RowDto> 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<RowDto> 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<RowDto>();
|
||||
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<string>(xlRow.Cell(DefaultTemplateInfo.ColumnSection)),
|
||||
Category = GetCellValue<string>(xlRow.Cell(DefaultTemplateInfo.ColumnCategory)),
|
||||
CategoryInfo = GetCellValue<string>(xlRow.Cell(DefaultTemplateInfo.ColumnCategoryInfo)),
|
||||
DepthStart = GetCellValue<double>(xlRow.Cell(DefaultTemplateInfo.ColumnDepthStart)),
|
||||
DepthEnd = GetCellValue<double>(xlRow.Cell(DefaultTemplateInfo.ColumnDepthEnd)),
|
||||
Date = GetCellValue<DateTime>(xlRow.Cell(DefaultTemplateInfo.ColumnDate)),
|
||||
Duration = GetCellValue<double>(xlRow.Cell(DefaultTemplateInfo.ColumnDuration)),
|
||||
Comment = GetCellValue<string>(xlRow.Cell(DefaultTemplateInfo.ColumnComment))
|
||||
Section = xlRow.Cell(DefaultTemplateInfo.ColumnSection).GetCellValue<string>(),
|
||||
Category = xlRow.Cell(DefaultTemplateInfo.ColumnCategory).GetCellValue<string>(),
|
||||
CategoryInfo = xlRow.Cell(DefaultTemplateInfo.ColumnCategoryInfo).GetCellValue<string?>(),
|
||||
DepthStart = xlRow.Cell(DefaultTemplateInfo.ColumnDepthStart).GetCellValue<double>(),
|
||||
DepthEnd = xlRow.Cell(DefaultTemplateInfo.ColumnDepthEnd).GetCellValue<double>(),
|
||||
Date = xlRow.Cell(DefaultTemplateInfo.ColumnDate).GetCellValue<DateTime>(),
|
||||
Duration = xlRow.Cell(DefaultTemplateInfo.ColumnDuration).GetCellValue<double>(),
|
||||
Comment = xlRow.Cell(DefaultTemplateInfo.ColumnComment).GetCellValue<string?>()
|
||||
};
|
||||
}
|
||||
|
||||
//TODO: вынести в метод расширения
|
||||
private static T GetCellValue<T>(IXLCell cell)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (T)Convert.ChangeType(cell.Value, typeof(T));
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new FileFormatException(
|
||||
$"Лист '{cell.Worksheet.Name}'. Ячейка: ({cell.Address.RowNumber},{cell.Address.ColumnNumber}) содержит некорректное значение");
|
||||
}
|
||||
}
|
||||
}
|
@ -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<WellOperationImportGazpromKhantosOptionsDto>
|
||||
{
|
||||
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<string, string> operationDict = InitDict("Operations.txt", '=');
|
||||
private readonly Dictionary<string, string> sectionDict = InitDict("Sections.txt", '=');
|
||||
private readonly Dictionary<string, string> operationAttributesDict = InitDict("OperationAttributes.txt", '=');
|
||||
|
||||
|
||||
public WellOperationGazpromKhantosExcelParser()
|
||||
{
|
||||
cosineSimilarity = new CosineSimilarity();
|
||||
}
|
||||
|
||||
public int IdTemplate => Templates.IdGazpromKhantosTemplate;
|
||||
|
||||
public IEnumerable<int> IdTypes => new[] { WellOperation.IdOperationTypePlan };
|
||||
|
||||
public IEnumerable<RowDto> 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<RowDto> 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<RowDto> 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<RowDto>();
|
||||
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<string>(xlRow.Cell(operationAttributes[OperationAttributes.CategoryInfo])),
|
||||
SectionDiameter = GetCellValue<double>(xlRow.Cell(operationAttributes[OperationAttributes.SectionDiameter])),
|
||||
Depth = GetCellValue<double>(xlRow.Cell(operationAttributes[OperationAttributes.Depth])),
|
||||
Duration = GetCellValue<double>(xlRow.Cell(operationAttributes[OperationAttributes.Duration])),
|
||||
Date = GetCellValue<DateTime>(xlRow.Cell(operationAttributes[OperationAttributes.Date]))
|
||||
CategoryInfo = xlRow.Cell(operationAttributes[OperationAttributes.CategoryInfo]).GetCellValue<string?>(),
|
||||
SectionDiameter =xlRow.Cell(operationAttributes[OperationAttributes.SectionDiameter]).GetCellValue<double>(),
|
||||
Depth = xlRow.Cell(operationAttributes[OperationAttributes.Depth]).GetCellValue<double>(),
|
||||
Duration = xlRow.Cell(operationAttributes[OperationAttributes.Duration]).GetCellValue<double>(),
|
||||
Date = xlRow.Cell(operationAttributes[OperationAttributes.Date]).GetCellValue<DateTime>()
|
||||
});
|
||||
}
|
||||
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<string>(cell), 0.7);
|
||||
var operationAttribute = GetValueDictionary(operationAttributesDict, cell.GetCellValue<string>(), 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<string, string> dict, string cellValue, double? minSimilarity)
|
||||
private string? GetValueDictionary(IDictionary<string, string> 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<T>(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}) содержит некорректное значение");
|
||||
}
|
||||
}
|
||||
}
|
@ -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<IWellOperationExcelParser> 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<IWellOperationExcelParser> 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<string>();
|
||||
|
||||
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<WellOperationDto>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
182
AsbCloudInfrastructure/XLExtentions.cs
Normal file
182
AsbCloudInfrastructure/XLExtentions.cs
Normal file
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Костыль исправляющий проблему в библиотеке IXLRange Range(this IXLWorksheet, IXLAddress, IXLAddress) с кастингом IXLAddress к XLAddress.
|
||||
/// </summary>
|
||||
/// <param name="sheet"></param>
|
||||
/// <param name="begin"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <returns></returns>
|
||||
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<T>(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}) содержит некорректное значение");
|
||||
}
|
||||
}
|
||||
}
|
@ -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<WellOperationImportDefaultOptionsDto> wellOperationDefaultExcelParser;
|
||||
private readonly IWellOperationExcelParser<WellOperationImportGazpromKhantosOptionsDto> wellOperationGazpromKhantosExcelParser;
|
||||
private readonly IUserRepository userRepository;
|
||||
|
||||
public WellOperationController(IWellOperationRepository operationRepository,
|
||||
@ -37,6 +40,8 @@ namespace AsbCloudWebApi.Controllers
|
||||
IWellOperationImportTemplateService wellOperationImportTemplateService,
|
||||
IWellOperationExportService wellOperationExportService,
|
||||
IWellOperationImportService wellOperationImportService,
|
||||
IWellOperationExcelParser<WellOperationImportDefaultOptionsDto> wellOperationDefaultExcelParser,
|
||||
IWellOperationExcelParser<WellOperationImportGazpromKhantosOptionsDto> 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
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Импорт плановых операций из excel (xlsx) файла
|
||||
/// Импорт операций из excel (xlsx) файла. Стандартный заполненный шаблон
|
||||
/// </summary>
|
||||
/// <param name="idWell">id скважины</param>
|
||||
/// <param name="idType">Тип операции</param>
|
||||
/// <param name="startRow">Начальная строка</param>
|
||||
/// <param name="endRow">Конечная строка</param>
|
||||
/// <param name="options">Параметры для парсинга файла</param>
|
||||
/// <param name="files">Коллекция из одного файла xlsx</param>
|
||||
/// <param name="options">Удалить операции перед импортом = 1, если файл валидный</param>
|
||||
/// <param name="sheetName">Название листа</param>
|
||||
/// <param name="token">Токен отмены задачи </param>
|
||||
/// <param name="idTemplate">Шаблон файла. 0 - стандартный, 1 - Газпромнефть Хантос</param>
|
||||
/// <param name="deleteBeforeImport">Удалить операции перед импортом = 1, если файл валидный</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("import/{options}")]
|
||||
[HttpPost("import/default/{deleteBeforeImport}")]
|
||||
[Permission]
|
||||
public async Task<IActionResult> ImportAsync(int idWell,
|
||||
[Required] int idType,
|
||||
string? sheetName,
|
||||
[Required] int idTemplate,
|
||||
int? startRow,
|
||||
int? endRow,
|
||||
public async Task<IActionResult> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт операций из excel (xlsx) файла. ГПНХ (Хантос)
|
||||
/// </summary>
|
||||
/// <param name="idWell">id скважины</param>
|
||||
/// <param name="options">Параметры для парсинга файла</param>
|
||||
/// <param name="files">Коллекция из одного файла xlsx</param>
|
||||
/// <param name="deleteBeforeImport">Удалить операции перед импортом = 1, если файл валидный</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("import/gazpromKhantos/{deleteBeforeImport}")]
|
||||
[Permission]
|
||||
public async Task<IActionResult> 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<bool> CanUserEditWellOperationsAsync(int idWell, CancellationToken token)
|
||||
{
|
||||
var idUser = User.GetUserId();
|
||||
|
Loading…
Reference in New Issue
Block a user