forked from ddrilling/AsbCloudServer
Сервисы парсинга траекторий
1. Сделан рефакторинг сервисов парсинга траекторий 2. Добавлена фабрика создания парсеров 3. Рефакторинг тестов
This commit is contained in:
parent
a726602be8
commit
82650b1cfb
@ -30,7 +30,6 @@ using AsbCloudInfrastructure.Services.SAUB;
|
||||
using AsbCloudInfrastructure.Services.Subsystems;
|
||||
using AsbCloudInfrastructure.Services.Trajectory;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Export;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Import;
|
||||
using AsbCloudInfrastructure.Services.WellOperationImport;
|
||||
using AsbCloudInfrastructure.Services.WellOperationImport.FileParser;
|
||||
using AsbCloudInfrastructure.Services.WellOperationService;
|
||||
@ -45,6 +44,7 @@ using AsbCloudDb.Model.DailyReports.Blocks.TimeBalance;
|
||||
using AsbCloudDb.Model.WellSections;
|
||||
using AsbCloudInfrastructure.Services.ProcessMaps;
|
||||
using AsbCloudApp.Data.ProcessMapPlan;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure
|
||||
{
|
||||
@ -325,6 +325,8 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<IProcessMapPlanService<ProcessMapPlanWellReamDto>, ProcessMapPlanService<ProcessMapPlanWellReamDto>>();
|
||||
|
||||
services.AddTransient<IWellSectionPlanRepository, WellSectionPlanRepository>();
|
||||
|
||||
services.AddSingleton<ParserServiceFactory>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
30
AsbCloudInfrastructure/Services/ParserServiceFactory.cs
Normal file
30
AsbCloudInfrastructure/Services/ParserServiceFactory.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.Import;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services;
|
||||
|
||||
public class ParserServiceFactory
|
||||
{
|
||||
public const int IdTrajectoryFactManualParserService = 1;
|
||||
public const int IdTrajectoryPlanParserService = 2;
|
||||
|
||||
private readonly IDictionary<int, Func<IParserService>> parsers = new Dictionary<int, Func<IParserService>>
|
||||
{
|
||||
{ IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService() },
|
||||
{ IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService() }
|
||||
};
|
||||
|
||||
public IParserService<TDto, TOptions> Create<TDto, TOptions>(int idImportService)
|
||||
where TDto : class, IId
|
||||
where TOptions : ParserOptionsRequestBase
|
||||
{
|
||||
var parser = parsers[idImportService].Invoke();
|
||||
|
||||
return parser as IParserService<TDto, TOptions>
|
||||
?? throw new ArgumentNullException(nameof(idImportService), "Не удалось распознать файл");
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Import
|
||||
{
|
||||
|
||||
public class TrajectoryFactManualParserService : TrajectoryParserService<TrajectoryGeoFactDto>
|
||||
{
|
||||
public override string templateFileName { get; } = "TrajectoryFactManualTemplate.xlsx";
|
||||
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
|
||||
public override string sheetName { get; } = "Фактическая траектория";
|
||||
public override int headerRowsCount { get; } = 2;
|
||||
|
||||
protected override TrajectoryGeoFactDto ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoFactDto
|
||||
{
|
||||
WellboreDepth = row.Cell(1).GetCellValue<double>(),
|
||||
ZenithAngle = row.Cell(2).GetCellValue<double>(),
|
||||
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
|
||||
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
|
||||
VerticalDepth = row.Cell(5).GetCellValue<double>(),
|
||||
Comment = row.Cell(6).GetCellValue<string?>()
|
||||
};
|
||||
//TODO: Добавить валидацию модели IValidatableObject
|
||||
return trajectoryRow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Import
|
||||
{
|
||||
public abstract class TrajectoryParserService<T>
|
||||
where T : TrajectoryGeoDto
|
||||
{
|
||||
public abstract string templateFileName { get; }
|
||||
public abstract string usingTemplateFile { get; }
|
||||
public abstract string sheetName { get; }
|
||||
public abstract int headerRowsCount { get; }
|
||||
|
||||
protected abstract T ParseRow(IXLRow row);
|
||||
|
||||
public IEnumerable<T> Import(Stream stream)
|
||||
{
|
||||
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
|
||||
var trajectoryRows = ParseFileStream(stream);
|
||||
|
||||
return trajectoryRows;
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<T> ParseFileStream(Stream stream)
|
||||
{
|
||||
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
|
||||
return ParseWorkbook(workbook);
|
||||
}
|
||||
|
||||
private IEnumerable<T> ParseWorkbook(IXLWorkbook workbook)
|
||||
{
|
||||
var sheetTrajectory = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName);
|
||||
if (sheetTrajectory is null)
|
||||
throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
|
||||
var trajectoryRows = ParseSheet(sheetTrajectory);
|
||||
return trajectoryRows;
|
||||
}
|
||||
|
||||
private IEnumerable<T> ParseSheet(IXLWorksheet sheet)
|
||||
{
|
||||
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 6)
|
||||
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
|
||||
|
||||
var count = sheet.RowsUsed().Count() - headerRowsCount;
|
||||
|
||||
if (count > 1024)
|
||||
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк.");
|
||||
|
||||
if (count <= 0)
|
||||
throw new FileFormatException($"Лист {sheet.Name} некорректного формата либо пустой");
|
||||
|
||||
var trajectoryRows = new List<T>(count);
|
||||
var parseErrors = new List<string>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var row = sheet.Row(1 + i + headerRowsCount);
|
||||
try
|
||||
{
|
||||
var trajectoryRow = ParseRow(row);
|
||||
trajectoryRows.Add(trajectoryRow);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
parseErrors.Add(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (parseErrors.Any())
|
||||
throw new FileFormatException(string.Join("\r\n", parseErrors));
|
||||
|
||||
return trajectoryRows;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Import
|
||||
{
|
||||
|
||||
public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeoPlanDto>
|
||||
{
|
||||
public override string templateFileName { get; } = "TrajectoryPlanTemplate.xlsx";
|
||||
public override string usingTemplateFile { get; } = "AsbCloudInfrastructure.Services.Trajectory.Templates";
|
||||
public override string sheetName { get; } = "Плановая траектория";
|
||||
public override int headerRowsCount { get; } = 2;
|
||||
|
||||
protected override TrajectoryGeoPlanDto ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoPlanDto
|
||||
{
|
||||
WellboreDepth = row.Cell(1).GetCellValue<double>(),
|
||||
ZenithAngle = row.Cell(2).GetCellValue<double>(),
|
||||
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
|
||||
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
|
||||
VerticalDepth = row.Cell(5).GetCellValue<double>(),
|
||||
Radius = row.Cell(6).GetCellValue<double>(),
|
||||
Comment = row.Cell(7).GetCellValue<string?>()
|
||||
};
|
||||
|
||||
//TODO: Добавить валидацию модели IValidatableObject
|
||||
return trajectoryRow;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public class TrajectoryFactManualParserService : TrajectoryParserService<TrajectoryGeoFactDto>
|
||||
{
|
||||
protected override ValidationResultDto<TrajectoryGeoFactDto> ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoFactDto
|
||||
{
|
||||
WellboreDepth = row.Cell(1).GetCellValue<double>(),
|
||||
ZenithAngle = row.Cell(2).GetCellValue<double>(),
|
||||
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
|
||||
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
|
||||
VerticalDepth = row.Cell(5).GetCellValue<double>(),
|
||||
Comment = row.Cell(6).GetCellValue<string?>()
|
||||
};
|
||||
|
||||
//TODO: Добавить валидацию модели
|
||||
|
||||
return new ValidationResultDto<TrajectoryGeoFactDto>
|
||||
{
|
||||
Item = trajectoryRow
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.Import;
|
||||
using AsbCloudApp.Services;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public abstract class TrajectoryParserService<T> : IParserService<T, TrajectoryParserRequest>
|
||||
where T : TrajectoryGeoDto
|
||||
{
|
||||
protected abstract ValidationResultDto<T> ParseRow(IXLRow row);
|
||||
|
||||
public ParserResultDto<T> Parse(Stream file, TrajectoryParserRequest options)
|
||||
{
|
||||
using var workbook = new XLWorkbook(file, XLEventTracking.Disabled);
|
||||
var trajectoryRows = ParseFileStream(file, options);
|
||||
|
||||
return trajectoryRows;
|
||||
}
|
||||
|
||||
private ParserResultDto<T> ParseFileStream(Stream stream, TrajectoryParserRequest options)
|
||||
{
|
||||
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
|
||||
return ParseWorkbook(workbook, options);
|
||||
}
|
||||
|
||||
private ParserResultDto<T> ParseWorkbook(IXLWorkbook workbook, TrajectoryParserRequest options)
|
||||
{
|
||||
var sheetTrajectory = workbook.Worksheets.FirstOrDefault(ws =>
|
||||
ws.Name.ToLower().Trim() == options.SheetName.ToLower().Trim());
|
||||
if (sheetTrajectory is null)
|
||||
throw new FileFormatException($"Книга excel не содержит листа {options.SheetName}.");
|
||||
var trajectoryRows = ParseSheet(sheetTrajectory, options);
|
||||
return trajectoryRows;
|
||||
}
|
||||
|
||||
private ParserResultDto<T> ParseSheet(IXLWorksheet sheet, TrajectoryParserRequest options)
|
||||
{
|
||||
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 6)
|
||||
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
|
||||
|
||||
var count = sheet.RowsUsed().Count() - options.HeaderRowsCount;
|
||||
|
||||
if (count > 1024)
|
||||
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество строк.");
|
||||
|
||||
if (count <= 0)
|
||||
throw new FileFormatException($"Лист {sheet.Name} некорректного формата либо пустой");
|
||||
|
||||
var trajectoryRows = new List<ValidationResultDto<T>>(count);
|
||||
var parseErrors = new List<string>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var row = sheet.Row(1 + i + options.HeaderRowsCount);
|
||||
try
|
||||
{
|
||||
var trajectoryRow = ParseRow(row);
|
||||
trajectoryRows.Add(trajectoryRow);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
parseErrors.Add(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (parseErrors.Any())
|
||||
throw new FileFormatException(string.Join("\r\n", parseErrors));
|
||||
|
||||
return new ParserResultDto<T>
|
||||
{
|
||||
Item = trajectoryRows
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeoPlanDto>
|
||||
{
|
||||
protected override ValidationResultDto<TrajectoryGeoPlanDto> ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoPlanDto
|
||||
{
|
||||
WellboreDepth = row.Cell(1).GetCellValue<double>(),
|
||||
ZenithAngle = row.Cell(2).GetCellValue<double>(),
|
||||
AzimuthGeo = row.Cell(3).GetCellValue<double>(),
|
||||
AzimuthMagnetic = row.Cell(4).GetCellValue<double>(),
|
||||
VerticalDepth = row.Cell(5).GetCellValue<double>(),
|
||||
Radius = row.Cell(6).GetCellValue<double>(),
|
||||
Comment = row.Cell(7).GetCellValue<string?>()
|
||||
};
|
||||
|
||||
//TODO: Добавить валидацию модели
|
||||
|
||||
return new ValidationResultDto<TrajectoryGeoPlanDto>
|
||||
{
|
||||
Item = trajectoryRow
|
||||
};
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Import;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services.Trajectory
|
||||
{
|
||||
public class TrajectoryImportTest
|
||||
{
|
||||
private readonly TrajectoryPlanParserService trajectoryPlanImportService;
|
||||
private readonly TrajectoryFactManualParserService trajectoryFactManualImportService;
|
||||
|
||||
private string usingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates";
|
||||
|
||||
public TrajectoryImportTest()
|
||||
{
|
||||
trajectoryPlanImportService = new TrajectoryPlanParserService();
|
||||
trajectoryFactManualImportService = new TrajectoryFactManualParserService();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Import_trajectory_plan()
|
||||
{
|
||||
var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream($"{usingTemplateFile}.TrajectoryPlanTemplate.xlsx");
|
||||
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var trajectoryRows = trajectoryPlanImportService.Import(stream);
|
||||
|
||||
Assert.Equal(3, trajectoryRows.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Import_trajectory_fact_manual()
|
||||
{
|
||||
var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream($"{usingTemplateFile}.TrajectoryFactManualTemplate.xlsx");
|
||||
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var trajectoryRows = trajectoryFactManualImportService.Import(stream);
|
||||
|
||||
Assert.Equal(4, trajectoryRows.Count());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using AsbCloudApp.Requests.Import;
|
||||
using AsbCloudInfrastructure.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services.Trajectory;
|
||||
|
||||
public class TrajectoryParserTest
|
||||
{
|
||||
private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates";
|
||||
|
||||
private readonly TrajectoryParserRequest planTrajectoryParserOptions = new()
|
||||
{
|
||||
IdParserService = ParserServiceFactory.IdTrajectoryPlanParserService,
|
||||
SheetName = "Плановая траектория",
|
||||
HeaderRowsCount = 2
|
||||
};
|
||||
|
||||
private readonly TrajectoryParserRequest factTrajectoryParserOptions = new()
|
||||
{
|
||||
IdParserService = ParserServiceFactory.IdTrajectoryFactManualParserService,
|
||||
SheetName = "Фактическая траектория",
|
||||
HeaderRowsCount = 2
|
||||
};
|
||||
|
||||
private static readonly ParserServiceFactory parserServiceFactory = new();
|
||||
|
||||
[Fact]
|
||||
public void Parse_trajectory_plan()
|
||||
{
|
||||
var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream($"{UsingTemplateFile}.TrajectoryPlanTemplate.xlsx");
|
||||
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var parserService = parserServiceFactory.Create<TrajectoryGeoPlanDto, TrajectoryParserRequest>(
|
||||
planTrajectoryParserOptions.IdParserService);
|
||||
var trajectoryRows = parserService.Parse(stream, planTrajectoryParserOptions);
|
||||
|
||||
Assert.Equal(3, trajectoryRows.Item.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_trajectory_fact_manual()
|
||||
{
|
||||
var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream($"{UsingTemplateFile}.TrajectoryFactManualTemplate.xlsx");
|
||||
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var parserService = parserServiceFactory.Create<TrajectoryGeoFactDto, TrajectoryParserRequest>(
|
||||
factTrajectoryParserOptions.IdParserService);
|
||||
var trajectoryRows = parserService.Parse(stream, factTrajectoryParserOptions);
|
||||
|
||||
Assert.Equal(4, trajectoryRows.Item.Count());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user