Рефакторинг импорта РТК бурение

Когда будем делать импорт для РТК проработки интерфейс IProcessMapImportService станет обобщенным
This commit is contained in:
Степанов Дмитрий 2023-10-09 17:06:29 +05:00
parent 14ea9c06f5
commit a7bc6876c4
3 changed files with 394 additions and 1 deletions

View File

@ -0,0 +1,358 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudApp.Services.ProcessMaps;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.ProcessMaps.WellDrillingProcessMap;
/*
* password for ProcessMapImportTemplate.xlsx is ASB2020!
*/
public class WellDrillingProcessMapImportService : IProcessMapImportService
{
private readonly IWellDrillingProcessMapRepository wellDrillingProcessMapRepository;
private readonly ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository;
private readonly IWellService wellService;
private const string sheetNamePlan = "План";
private const int headerRowsCount = 2;
private const int columnWellSectionType = 1;
private const int columnMode = 2;
private const int columnDepthStart = 3;
private const int columnDepthEnd = 4;
private const int columnPressurePlan = 5;
private const int columnPressureLimitMax = 6;
private const int columnAxialLoadPlan = 7;
private const int columnAxialLoadLimitMax = 8;
private const int columnTopDriveTorquePlan = 9;
private const int columnTopDriveTorqueLimitMax = 10;
private const int columnTopDriveSpeedPlan = 11;
private const int columnTopDriveSpeedLimitMax = 12;
private const int columnFlowPlan = 13;
private const int columnFlowLimitMax = 14;
private const int columnRopPlan = 15;
private const int columnUsageSaub = 16;
private const int columnUsageSpin = 17;
private WellSectionTypeDto[] sections = null!;
public WellDrillingProcessMapImportService(IWellDrillingProcessMapRepository wellDrillingProcessMapRepository,
ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository,
IWellService wellService)
{
this.wellDrillingProcessMapRepository = wellDrillingProcessMapRepository;
this.wellSectionTypeRepository = wellSectionTypeRepository;
this.wellService = wellService;
}
public async Task ImportAsync(int idWell, int idUser, bool deleteProcessMapPlansBeforeImport, Stream stream,
CancellationToken cancellationToken)
{
sections = (await wellSectionTypeRepository.GetAllAsync(cancellationToken)).ToArray();
using var workBook = new XLWorkbook(stream);
var wellDrillingProcessMaps = ParseWorkBook(workBook);
if (deleteProcessMapPlansBeforeImport)
await wellDrillingProcessMapRepository.RemoveByWellAsync(idWell, cancellationToken);
foreach (var wellDrillingProcessMap in wellDrillingProcessMaps)
{
wellDrillingProcessMap.IdWell = idWell;
wellDrillingProcessMap.IdUser = idUser;
}
await wellDrillingProcessMapRepository.InsertRangeAsync(wellDrillingProcessMaps, cancellationToken);
}
public async Task<(string Name, Stream File)> ExportAsync(int idWell, CancellationToken cancellationToken)
{
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken)
?? throw new ArgumentInvalidException(nameof(idWell), $"Скважины с {idWell} не существует");
sections = (await wellSectionTypeRepository.GetAllAsync(cancellationToken)).ToArray();
var processMapPlans = (await wellDrillingProcessMapRepository.GetByIdWellAsync(idWell,
cancellationToken)).ToArray();
var file = await GenerateExcelFileStreamAsync(processMapPlans, cancellationToken);
var fileName = $"РТК-план бурение по скважине {well.Caption} куст {well.Cluster}.xlsx";
return (fileName, file);
}
public async Task<(string Name, Stream File)> GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
{
var resourceName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(n => n.EndsWith("DrillingProcessMapTemplate.xlsx"))!;
using var stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName)!;
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
var name = "ЕЦП_шаблон_файла_РТК_бурение.xlsx";
return (name, memoryStream);
}
private void AddToWorkbook(XLWorkbook workbook, IEnumerable<WellDrillingProcessMapDto> wellDrillingProcessMaps)
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlan)
?? throw new FileFormatException($"Книга excel не содержит листа {sheetNamePlan}.");
AddToSheet(sheet, wellDrillingProcessMaps.ToArray());
}
private void AddToSheet(IXLWorksheet sheet, IList<WellDrillingProcessMapDto> wellDrillingProcessMaps)
{
if (!wellDrillingProcessMaps.Any())
return;
for (int i = 0; i < wellDrillingProcessMaps.Count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
AddToRow(row, wellDrillingProcessMaps[i]);
}
}
private void AddToRow(IXLRow row, WellDrillingProcessMapDto wellDrillingProcessMap)
{
row.Cell(columnWellSectionType).Value = sections.First(x => x.Id == wellDrillingProcessMap.IdWellSectionType).Caption;
row.Cell(columnMode).Value = GetModeCaption(wellDrillingProcessMap.IdMode);
row.Cell(columnDepthStart).Value = wellDrillingProcessMap.DepthStart;
row.Cell(columnDepthEnd).Value = wellDrillingProcessMap.DepthEnd;
row.Cell(columnPressurePlan).Value = wellDrillingProcessMap.Pressure.Plan;
row.Cell(columnPressureLimitMax).Value = wellDrillingProcessMap.Pressure.LimitMax;
row.Cell(columnAxialLoadPlan).Value = wellDrillingProcessMap.AxialLoad.Plan;
row.Cell(columnAxialLoadLimitMax).Value = wellDrillingProcessMap.AxialLoad.LimitMax;
row.Cell(columnTopDriveTorquePlan).Value = wellDrillingProcessMap.TopDriveTorque.Plan;
row.Cell(columnTopDriveTorqueLimitMax).Value = wellDrillingProcessMap.TopDriveTorque.LimitMax;
row.Cell(columnTopDriveSpeedPlan).Value = wellDrillingProcessMap.TopDriveSpeed.Plan;
row.Cell(columnTopDriveSpeedLimitMax).Value = wellDrillingProcessMap.TopDriveSpeed.LimitMax;
row.Cell(columnFlowPlan).Value = wellDrillingProcessMap.Flow.Plan;
row.Cell(columnFlowLimitMax).Value = wellDrillingProcessMap.Flow.LimitMax;
row.Cell(columnRopPlan).Value = wellDrillingProcessMap.RopPlan;
row.Cell(columnUsageSaub).Value = wellDrillingProcessMap.UsageSaub;
row.Cell(columnUsageSpin).Value = wellDrillingProcessMap.UsageSpin;
}
private IEnumerable<WellDrillingProcessMapDto> ParseWorkBook(IXLWorkbook workbook)
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlan)
?? throw new FileFormatException($"Книга excel не содержит листа {sheetNamePlan}.");
return ParseSheet(sheet);
}
private IEnumerable<WellDrillingProcessMapDto> ParseSheet(IXLWorksheet sheet)
{
const int columnsCount = 17;
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnsCount)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
var rowsCount = sheet.RowsUsed().Count() - headerRowsCount;
if (rowsCount <= 0)
return Array.Empty<WellDrillingProcessMapDto>();
var processMapPlans = new WellDrillingProcessMapDto[rowsCount];
var parseErrors = new List<string>();
for (int i = 0; i < processMapPlans.Length; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
try
{
processMapPlans[i] = ParseRow(row);
}
catch (FileFormatException ex)
{
parseErrors.Add(ex.Message);
}
}
if (parseErrors.Any())
throw new FileFormatException(string.Join("\r\n", parseErrors));
return processMapPlans;
}
private WellDrillingProcessMapDto ParseRow(IXLRow row)
{
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(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указана некорректная секция");
var idMode = GetIdMode(modeName)
?? throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указан некорректный режим");
if (depthStart is < 0 or > 99999.9)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указана некорректная стартовая глубина");
if (depthEnd is < 0 or > 99999.9)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указана некорректная конечная глубина");
if (pressurePlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение перепада давления");
if (pressureLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение перепада давления");
if (axialLoadPlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение нагрузки");
if (axialLoadLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение нагрузки");
if (topDriveTorquePlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение момента на ВСП");
if (topDriveTorqueLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение момента на ВСП");
if (topDriveSpeedPlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение оборотов на ВСП");
if (topDriveSpeedLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничения оборота на ВСП");
if (flowPlan is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение расхода");
if (flowLimitMax is < 0 or > 50000)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное ограничение расхода");
if (ropPlan is < 0 or > 99999.9)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указано некорректное плановое значение механической скорости");
if (usageSaub is < 0 or > 100)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указан некорректный плановый процент использования АКБ");
if (usageSpin is < 0 or > 100)
throw new FileFormatException(
$"Лист {row.Worksheet.Name}. В строке {row.RowNumber()} указан некорректные плановый процент использования spin master");
return new()
{
IdWellSectionType = wellSection.Id,
IdMode = idMode,
DepthStart = depthStart,
LastUpdate = DateTime.UtcNow,
DepthEnd = depthEnd,
Pressure = new()
{
Plan = pressurePlan,
LimitMax = pressureLimitMax
},
AxialLoad = new()
{
Plan = axialLoadPlan,
LimitMax = axialLoadLimitMax
},
TopDriveTorque = new()
{
Plan = topDriveTorquePlan,
LimitMax = topDriveTorqueLimitMax
},
TopDriveSpeed = new()
{
Plan = topDriveSpeedPlan,
LimitMax = topDriveSpeedLimitMax
},
Flow = new()
{
Plan = flowPlan,
LimitMax = flowLimitMax
},
RopPlan = ropPlan,
UsageSaub = usageSaub,
UsageSpin = usageSpin
};
}
private async Task<Stream> GenerateExcelFileStreamAsync(WellDrillingProcessMapDto[] wellDrillingProcessMaps,
CancellationToken cancellationToken)
{
using var excelTemplateStream = (await GetExcelTemplateStreamAsync(cancellationToken)).File;
using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
AddToWorkbook(workbook, wellDrillingProcessMaps);
MemoryStream memoryStream = new();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private static int? GetIdMode(string modeName) =>
modeName switch
{
"ручной" => 0,
"ротор" => 1,
"слайд" => 2,
_ => null
};
private static string GetModeCaption(int idMode)
=> idMode switch
{
1 => "Ротор",
2 => "Слайд",
_ => "Ручной",
};
}

View File

@ -122,7 +122,42 @@ internal static class XLExtentions
return cell;
}
internal static IXLStyle SetAllBorders(this IXLStyle style, XLBorderStyleValues borderStyle = XLBorderStyleValues.Thin)
public static IXLCell SetVal(this IXLCell cell, double? value, string format = "0.00")
{
cell.Value = (value is not null && double.IsFinite(value.Value)) ? value : null;
cell.DataType = XLDataType.Number;
cell.Style.NumberFormat.Format = format;
return cell;
}
public static IXLCell SetVal(this IXLCell cell, string value, bool adaptRowHeight = false)
{
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 SetVal(this IXLCell cell, DateTime value, string dateFormat = "DD.MM.YYYY HH:MM:SS")
{
cell.Value = value;
cell.DataType = XLDataType.DateTime;
cell.Style.DateFormat.Format = dateFormat;
return cell;
}
internal static IXLStyle SetAllBorders(this IXLStyle style, XLBorderStyleValues borderStyle = XLBorderStyleValues.Thin)
{
style.Border.RightBorder = borderStyle;
style.Border.LeftBorder = borderStyle;