diff --git a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs
index a6d4fd56..e8404c7f 100644
--- a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs
+++ b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanBaseDto.cs
@@ -19,6 +19,11 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat
[Range(1, int.MaxValue, ErrorMessage = "Id секции скважины не может быть меньше 1")]
public int IdWellSectionType { get; set; }
+ ///
+ /// Название секции
+ ///
+ public string? Section { get; set; }
+
///
/// Глубина по стволу от, м
///
@@ -41,6 +46,6 @@ public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelat
public virtual IEnumerable Validate(ValidationContext validationContext)
{
if(DepthEnd <= DepthStart)
- yield return new ("глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) });
+ yield return new ("Глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) });
}
}
\ No newline at end of file
diff --git a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs
index 15f30300..7ab7db5d 100644
--- a/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs
+++ b/AsbCloudApp/Data/ProcessMapPlan/ProcessMapPlanDrillingDto.cs
@@ -12,6 +12,11 @@ public class ProcessMapPlanDrillingDto : ProcessMapPlanBaseDto
///
[Range(1, 2, ErrorMessage = "Id режима должен быть либо 1-ротор либо 2-слайд")]
public int IdMode { get; set; }
+
+ ///
+ /// Название режима бурения
+ ///
+ public string? Mode { get; set; }
///
/// Осевая нагрузка, т план
diff --git a/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs b/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs
index 7b30b80d..a0c3d4ec 100644
--- a/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs
+++ b/AsbCloudApp/Services/ProcessMaps/IProcessMapPlanImportService.cs
@@ -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;
///
/// Сервис импорта РТК
///
+[Obsolete]
public interface IProcessMapPlanImportService
{
///
diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
index cdb57ae5..ba012eb5 100644
--- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
+++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
@@ -36,6 +36,7 @@
+
diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs
new file mode 100644
index 00000000..cab930b4
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanDrillingParser.cs
@@ -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
+{
+ #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 sections;
+
+ public ProcessMapPlanDrillingParser(IServiceProvider serviceProvider)
+ : base(serviceProvider)
+ {
+ var wellOperationRepository = serviceProvider.GetRequiredService();
+
+ sections = wellOperationRepository.GetSectionTypes();
+ }
+
+ protected override string SheetName => "План";
+
+ protected override string TemplateFileName => "ProcessMapPlanDrillingTemplate.xlsx";
+
+ protected override ValidationResultDto ParseRow(IXLRow row)
+ {
+ var sectionCaption = row.Cell(columnSection).GetCellValue()?.Trim().ToLower();
+ var modeName = row.Cell(columnMode).GetCellValue()?.Trim().ToLower();
+ var depthStart = row.Cell(columnDepthStart).GetCellValue();
+ var depthEnd = row.Cell(columnDepthEnd).GetCellValue();
+ var deltaPressurePlan = row.Cell(columnPressurePlan).GetCellValue();
+ var deltaPressureLimitMax = 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 comment = row.Cell(columnComment).GetCellValue() ?? 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
+ {
+ { 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);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs
new file mode 100644
index 00000000..5b7aab2d
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/ProcessMapPlan/Parser/ProcessMapPlanParser.cs
@@ -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 : ParserServiceBase
+ 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 ParseRow(IXLRow row);
+
+ public override ParserResultDto 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
+ };
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/ProcessMapPlan/Templates/ProcessMapPlanDrillingTemplate.xlsx b/AsbCloudInfrastructure/Services/ProcessMapPlan/Templates/ProcessMapPlanDrillingTemplate.xlsx
new file mode 100644
index 00000000..bdf13143
Binary files /dev/null and b/AsbCloudInfrastructure/Services/ProcessMapPlan/Templates/ProcessMapPlanDrillingTemplate.xlsx differ
diff --git a/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs b/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs
index f322c798..454daa7a 100644
--- a/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs
+++ b/AsbCloudInfrastructure/Services/ProcessMaps/WellDrilling/ProcessMapPlanImportWellDrillingService.cs
@@ -18,6 +18,8 @@ namespace AsbCloudInfrastructure.Services.ProcessMaps.WellDrilling;
/*
* password for ProcessMapImportTemplate.xlsx is ASB2020!
*/
+
+[Obsolete]
public class ProcessMapPlanImportWellDrillingService : IProcessMapPlanImportService
{
private readonly IProcessMapPlanRepository processMapPlanWellDrillingRepository;
diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs
index c3fd0c49..fb55bcf7 100644
--- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs
+++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs
@@ -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 : ControllerBase
- where TDto : ProcessMapPlanBaseDto
+public abstract class ProcessMapPlanBaseController : ControllerBase,
+ IControllerWithParser
+ where TDto : ProcessMapPlanBaseDto
{
- private readonly IChangeLogRepository repository;
- private readonly IWellService wellService;
+ private readonly IChangeLogRepository repository;
+ private readonly IWellService wellService;
+ private readonly IParserService parserService;
- public ProcessMapPlanBaseController(IChangeLogRepository repository, IWellService wellService)
- {
- this.repository = repository;
- this.wellService = wellService;
- }
+ protected ProcessMapPlanBaseController(IChangeLogRepository repository,
+ IWellService wellService,
+ ParserServiceFactory parserFactory,
+ int idParserService)
+ {
+ this.repository = repository;
+ this.wellService = wellService;
+ parserService = parserFactory.Create(idParserService);
+ }
+
+ protected abstract string TemplateFileName { get; }
+
+ ActionResult> IControllerWithParser.Parse(Stream file, IParserOptionsRequest options)
+ {
+ try
+ {
+ var parserResult = parserService.Parse(file, options);
+ return Ok(parserResult);
+ }
+ catch (FileFormatException ex)
+ {
+ return this.ValidationBadRequest("files", ex.Message);
+ }
+ }
///
/// Добавление
@@ -190,6 +216,39 @@ public abstract class ProcessMapPlanBaseController : ControllerBase
var result = await repository.UpdateOrInsertRange(idUser, dtos, token);
return Ok(result);
}
+
+ ///
+ /// Импорт РТК из excel (xlsx) файла
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpPost("parse")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ public async Task>> Parse(int idWell,
+ [FromForm] IFormFileCollection files,
+ CancellationToken token)
+ {
+ await AssertUserHasAccessToWell(idWell, token);
+
+ return this.ParseExcelFile(files, IParserOptionsRequest.Empty());
+ }
+
+ ///
+ /// Получение шаблона для заполнения РТК
+ ///
+ ///
+ [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);
+ }
///
/// returns user id, if he has access to well
@@ -221,4 +280,4 @@ public abstract class ProcessMapPlanBaseController : ControllerBase
var idUser = User.GetUserId() ?? throw new ForbidException("Неизвестный пользователь");
return idUser;
}
-}
+}
\ No newline at end of file
diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs
index b0a20f1e..8855c54f 100644
--- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs
+++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs
@@ -2,15 +2,18 @@
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
+using AsbCloudInfrastructure.Services;
namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController
{
- public ProcessMapPlanDrillingController(
- IChangeLogRepository repository,
- IWellService wellService)
- : base(repository, wellService)
- {
- }
-}
+ public ProcessMapPlanDrillingController(IChangeLogRepository repository,
+ IWellService wellService,
+ ParserServiceFactory parserFactory)
+ : base(repository, wellService, parserFactory, ParserServiceFactory.IdProcessMapPlanDrillingParser)
+ {
+ }
+
+ protected override string TemplateFileName => "ЕЦП_шаблон_файла_РТК_план_бурение.xlsx";
+}
\ No newline at end of file