Парсинг РТК

This commit is contained in:
Степанов Дмитрий 2024-02-09 09:32:31 +03:00
parent aff243626a
commit f2ca89dc8d
10 changed files with 283 additions and 19 deletions

View File

@ -19,6 +19,11 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat
[Range(1, int.MaxValue, ErrorMessage = "Id секции скважины не может быть меньше 1")]
public int IdWellSectionType { get; set; }
/// <summary>
/// Название секции
/// </summary>
public string? Section { get; set; }
/// <summary>
/// Глубина по стволу от, м
/// <para>
@ -41,6 +46,6 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(DepthEnd <= DepthStart)
yield return new ("глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) });
yield return new ("Глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) });
}
}

View File

@ -13,6 +13,11 @@ public class ProcessMapPlanDrillingDto : ProcessMapPlanBaseDto
[Range(1, 2, ErrorMessage = "Id режима должен быть либо 1-ротор либо 2-слайд")]
public int IdMode { get; set; }
/// <summary>
/// Название режима бурения
/// </summary>
public string? Mode { get; set; }
/// <summary>
/// Осевая нагрузка, т план
/// </summary>

View File

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
@ -7,6 +8,7 @@ namespace AsbCloudApp.Services.ProcessMaps;
/// <summary>
/// Сервис импорта РТК
/// </summary>
[Obsolete]
public interface IProcessMapPlanImportService
{
/// <summary>

View File

@ -36,6 +36,7 @@
<EmbeddedResource Include="Services\DetectOperations\DetectOperations.xlsx" />
<EmbeddedResource Include="Services\DailyReport\DailyReportTemplate.xlsx" />
<EmbeddedResource Include="Services\DrillTestReport\DrillTestReportTemplate.xlsx" />
<EmbeddedResource Include="Services\ProcessMapPlan\Templates\ProcessMapPlanDrillingTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryFactNnbTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryFactManualTemplate.xlsx" />
<EmbeddedResource Include="Services\Trajectory\Templates\TrajectoryPlanTemplate.xlsx" />

View File

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMapPlan;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using Microsoft.Extensions.DependencyInjection;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
public class ProcessMapPlanDrillingParser : ProcessMapPlanParser<ProcessMapPlanDrillingDto>
{
#region Columns
private const int columnSection = 1;
private const int columnMode = 2;
private const int columnDepthStart = 3;
private const int columnDepthEnd = 4;
private const int columnPressurePlan = 5;
private const int columnPressureLimitMax = 6;
private const int columnAxialLoadPlan = 7;
private const int columnAxialLoadLimitMax = 8;
private const int columnTopDriveTorquePlan = 9;
private const int columnTopDriveTorqueLimitMax = 10;
private const int columnTopDriveSpeedPlan = 11;
private const int columnTopDriveSpeedLimitMax = 12;
private const int columnFlowPlan = 13;
private const int columnFlowLimitMax = 14;
private const int columnRopPlan = 15;
private const int columnUsageSaub = 16;
private const int columnUsageSpin = 17;
private const int columnComment = 18;
#endregion
private readonly IEnumerable<WellSectionTypeDto> sections;
public ProcessMapPlanDrillingParser(IServiceProvider serviceProvider)
: base(serviceProvider)
{
var wellOperationRepository = serviceProvider.GetRequiredService<IWellOperationRepository>();
sections = wellOperationRepository.GetSectionTypes();
}
protected override string SheetName => "План";
protected override string TemplateFileName => "ProcessMapPlanDrillingTemplate.xlsx";
protected override ValidationResultDto<ProcessMapPlanDrillingDto> ParseRow(IXLRow row)
{
var sectionCaption = row.Cell(columnSection).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 deltaPressurePlan = row.Cell(columnPressurePlan).GetCellValue<double>();
var deltaPressureLimitMax = 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 comment = row.Cell(columnComment).GetCellValue<string>() ?? string.Empty;
var section = sections.FirstOrDefault(s =>
string.Equals(s.Caption.Trim(), sectionCaption?.Trim(), StringComparison.CurrentCultureIgnoreCase));
if (section is null)
{
var message = string.Format(IParserService.MessageTemplate, SheetName, row.RowNumber(), columnSection,
"Указана некорректная секция");
throw new FileFormatException(message);
}
var idMode = GetIdMode(modeName);
if (idMode is null)
{
var message = string.Format(IParserService.MessageTemplate, SheetName, row.RowNumber(), columnSection,
"Указан некорректный режим бурения");
throw new FileFormatException(message);
}
var dto = new ProcessMapPlanDrillingDto
{
IdWellSectionType = section.Id,
Section = section.Caption,
IdMode = idMode.Value,
Mode = modeName,
DepthStart = depthStart,
DepthEnd = depthEnd,
AxialLoadPlan = axialLoadPlan,
AxialLoadLimitMax = axialLoadLimitMax,
DeltaPressurePlan = deltaPressurePlan,
DeltaPressureLimitMax = deltaPressureLimitMax,
TopDriveTorquePlan = topDriveTorquePlan,
TopDriveTorqueLimitMax = topDriveTorqueLimitMax,
TopDriveSpeedPlan = topDriveSpeedPlan,
TopDriveSpeedLimitMax = topDriveSpeedLimitMax,
FlowPlan = flowPlan,
FlowLimitMax = flowLimitMax,
RopPlan = ropPlan,
UsageSaub = usageSaub,
UsageSpin = usageSpin,
Comment = comment
};
var columnNumbers = new Dictionary<string, int>
{
{ nameof(ProcessMapPlanDrillingDto.DepthStart), columnDepthStart },
{ nameof(ProcessMapPlanDrillingDto.DepthEnd), columnDepthEnd },
{ nameof(ProcessMapPlanDrillingDto.DeltaPressurePlan), columnPressurePlan },
{ nameof(ProcessMapPlanDrillingDto.DeltaPressureLimitMax), columnPressureLimitMax },
{ nameof(ProcessMapPlanDrillingDto.AxialLoadPlan), columnAxialLoadPlan },
{ nameof(ProcessMapPlanDrillingDto.AxialLoadLimitMax), columnAxialLoadLimitMax },
{ nameof(ProcessMapPlanDrillingDto.TopDriveTorquePlan), columnTopDriveTorquePlan },
{ nameof(ProcessMapPlanDrillingDto.TopDriveTorqueLimitMax), columnTopDriveTorqueLimitMax },
{ nameof(ProcessMapPlanDrillingDto.TopDriveSpeedPlan), columnTopDriveSpeedPlan },
{ nameof(ProcessMapPlanDrillingDto.TopDriveSpeedLimitMax), columnTopDriveSpeedLimitMax },
{ nameof(ProcessMapPlanDrillingDto.FlowPlan), columnFlowPlan },
{ nameof(ProcessMapPlanDrillingDto.FlowLimitMax), columnFlowLimitMax },
{ nameof(ProcessMapPlanDrillingDto.RopPlan), columnRopPlan },
{ nameof(ProcessMapPlanDrillingDto.UsageSaub), columnUsageSaub },
{ nameof(ProcessMapPlanDrillingDto.UsageSpin), columnUsageSpin },
{ nameof(ProcessMapPlanDrillingDto.Comment), columnComment }
};
return ValidateRow(row.RowNumber(), columnNumbers, dto);
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.IO;
using System.Reflection;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMapPlan;
using AsbCloudApp.Requests.ParserOptions;
using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.ProcessMapPlan.Parser;
public abstract class ProcessMapPlanParser<TDto> : ParserServiceBase<TDto, IParserOptionsRequest>
where TDto : ProcessMapPlanBaseDto
{
protected ProcessMapPlanParser(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
private const int HeaderRowsCount = 2;
private const int ColumnCount = 18;
protected abstract string TemplateFileName { get; }
protected abstract ValidationResultDto<TDto> ParseRow(IXLRow row);
public override ParserResultDto<TDto> Parse(Stream file, IParserOptionsRequest options)
{
using var workbook = new XLWorkbook(file);
var sheet = workbook.GetWorksheet(SheetName);
var processMaps = ParseExcelSheet(sheet, ParseRow, ColumnCount, HeaderRowsCount);
return processMaps;
}
public override Stream GetTemplateFile() =>
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName)
?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден");
protected static int? GetIdMode(string? modeName) =>
modeName?.Trim().ToLower() switch
{
"ручной" => 0,
"ротор" => 1,
"слайд" => 2,
_ => null
};
}

View File

@ -18,6 +18,8 @@ namespace AsbCloudInfrastructure.Services.ProcessMaps.WellDrilling;
/*
* password for ProcessMapImportTemplate.xlsx is ASB2020!
*/
[Obsolete]
public class ProcessMapPlanImportWellDrillingService : IProcessMapPlanImportService
{
private readonly IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto> processMapPlanWellDrillingRepository;

View File

@ -9,8 +9,13 @@ using Microsoft.AspNetCore.Http;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Requests;
using System;
using System.IO;
using AsbCloudApp.Services;
using System.Linq;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services;
using AsbCloudWebApi.Controllers.Interfaces;
namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
@ -20,17 +25,38 @@ namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
[ApiController]
[Route("api/well/{idWell}/[controller]")]
[Authorize]
public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
where TDto : ProcessMapPlanBaseDto
public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase,
IControllerWithParser<TDto, IParserOptionsRequest>
where TDto : ProcessMapPlanBaseDto
{
private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository;
private readonly IWellService wellService;
private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository;
private readonly IWellService wellService;
private readonly IParserService<TDto, IParserOptionsRequest> parserService;
public ProcessMapPlanBaseController(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository, IWellService wellService)
{
this.repository = repository;
this.wellService = wellService;
}
protected ProcessMapPlanBaseController(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository,
IWellService wellService,
ParserServiceFactory parserFactory,
int idParserService)
{
this.repository = repository;
this.wellService = wellService;
parserService = parserFactory.Create<TDto, IParserOptionsRequest>(idParserService);
}
protected abstract string TemplateFileName { get; }
ActionResult<ParserResultDto<TDto>> IControllerWithParser<TDto, IParserOptionsRequest>.Parse(Stream file, IParserOptionsRequest options)
{
try
{
var parserResult = parserService.Parse(file, options);
return Ok(parserResult);
}
catch (FileFormatException ex)
{
return this.ValidationBadRequest("files", ex.Message);
}
}
/// <summary>
/// Добавление
@ -191,6 +217,39 @@ public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
return Ok(result);
}
/// <summary>
/// Импорт РТК из excel (xlsx) файла
/// </summary>
/// <param name="idWell"></param>
/// <param name="files"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("parse")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ParserResultDto<TDto>>> Parse(int idWell,
[FromForm] IFormFileCollection files,
CancellationToken token)
{
await AssertUserHasAccessToWell(idWell, token);
return this.ParseExcelFile(files, IParserOptionsRequest.Empty());
}
/// <summary>
/// Получение шаблона для заполнения РТК
/// </summary>
/// <returns></returns>
[HttpGet("template")]
[AllowAnonymous]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public IActionResult GetTemplate()
{
var stream = parserService.GetTemplateFile();
return File(stream, "application/octet-stream", TemplateFileName);
}
/// <summary>
/// returns user id, if he has access to well
/// </summary>

View File

@ -2,15 +2,18 @@
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services;
namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController<ProcessMapPlanDrillingDto>
{
public ProcessMapPlanDrillingController(
IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> repository,
IWellService wellService)
: base(repository, wellService)
{
}
public ProcessMapPlanDrillingController(IChangeLogRepository<ProcessMapPlanDrillingDto, ProcessMapPlanBaseRequestWithWell> repository,
IWellService wellService,
ParserServiceFactory parserFactory)
: base(repository, wellService, parserFactory, ParserServiceFactory.IdProcessMapPlanDrillingParser)
{
}
protected override string TemplateFileName => "ЕЦП_шаблон_файла_РТК_план_бурение.xlsx";
}