Сделал рефакторинг сервисов для парсинга

This commit is contained in:
parent 7d00cfde1c
commit 92a909a029
6 changed files with 65 additions and 89 deletions

View File

@ -1,29 +1,20 @@
using System.Collections.Generic;
using System.IO; using System.IO;
using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Data.WellOperationImport;
using AsbCloudApp.Data.WellOperationImport.Options;
namespace AsbCloudApp.Services.WellOperationImport; namespace AsbCloudApp.Services.WellOperationImport;
/// <summary> /// <summary>
/// Парсинг операций из excel файла /// Парсинг операций из excel файла
/// </summary> /// </summary>
public interface IWellOperationExcelParser public interface IWellOperationExcelParser<in TOptions>
where TOptions : IWellOperationImportOptions
{ {
/// <summary> /// <summary>
/// Id шаблона /// Метод парсинга документа
/// </summary> /// </summary>
int IdTemplate { get; } /// <param name="stream"></param>
/// <param name="options"></param>
/// <summary> /// <returns></returns>
/// Типы операций, которые можно получить из файла SheetDto Parse(Stream stream, TOptions options);
/// </summary>
IEnumerable<int> IdTypes { get; }
/// <summary>
/// Метод парсинга документа
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
IEnumerable<RowDto> Parse(Stream stream, WellOperationParserOptionsDto options);
} }

View File

@ -1,4 +1,3 @@
using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Data.WellOperationImport;
@ -10,15 +9,14 @@ namespace AsbCloudApp.Services.WellOperationImport;
/// </summary> /// </summary>
public interface IWellOperationImportService public interface IWellOperationImportService
{ {
/// <summary> /// <summary>
/// Загрузить из excel список операций /// Загрузить из excel список операций
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="stream"></param> /// <param name="idUser"></param>
/// <param name="idUser"></param> /// <param name="idType"></param>
/// <param name="deleteBeforeImport"></param> /// <param name="sheet"></param>
/// <param name="cancellationToken"></param> /// <param name="deleteBeforeImport"></param>
/// <param name="options"></param> /// <param name="cancellationToken"></param>
Task ImportAsync(int idWell, int idUser, Stream stream, WellOperationParserOptionsDto options, bool deleteBeforeImport, Task ImportAsync(int idWell, int idUser, int idType, SheetDto sheet, bool deleteBeforeImport, CancellationToken cancellationToken);
CancellationToken cancellationToken);
} }

View File

@ -32,6 +32,7 @@ using AsbCloudInfrastructure.Services.AutoGeneratedDailyReports;
using AsbCloudInfrastructure.Services.WellOperationImport; using AsbCloudInfrastructure.Services.WellOperationImport;
using AsbCloudInfrastructure.Services.WellOperationImport.FileParser; using AsbCloudInfrastructure.Services.WellOperationImport.FileParser;
using AsbCloudInfrastructure.Services.ProcessMap.ProcessMapWellboreDevelopment; using AsbCloudInfrastructure.Services.ProcessMap.ProcessMapWellboreDevelopment;
using AsbCloudApp.Data.WellOperationImport.Options;
namespace AsbCloudInfrastructure namespace AsbCloudInfrastructure
{ {
@ -239,8 +240,8 @@ namespace AsbCloudInfrastructure
services.AddTransient<IWellOperationImportService, WellOperationImportService>(); services.AddTransient<IWellOperationImportService, WellOperationImportService>();
services.AddTransient<IWellOperationImportTemplateService, WellOperationImportTemplateService>(); services.AddTransient<IWellOperationImportTemplateService, WellOperationImportTemplateService>();
services.AddTransient<IWellOperationExcelParser, WellOperationDefaultExcelParser>(); services.AddTransient<IWellOperationExcelParser<WellOperationImportDefaultOptionsDto>, WellOperationDefaultExcelParser>();
services.AddTransient<IWellOperationExcelParser, WellOperationGazpromKhantosExcelParser>(); services.AddTransient<IWellOperationExcelParser<WellOperationImportGazpromKhantosOptionsDto>, WellOperationGazpromKhantosExcelParser>();
return services; return services;
} }

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Data.WellOperationImport;
using AsbCloudApp.Data.WellOperationImport.Options;
using AsbCloudApp.Services.WellOperationImport; using AsbCloudApp.Services.WellOperationImport;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.WellOperationImport.Constants; using AsbCloudInfrastructure.Services.WellOperationImport.Constants;
@ -10,19 +11,16 @@ using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.WellOperationImport.FileParser; namespace AsbCloudInfrastructure.Services.WellOperationImport.FileParser;
public class WellOperationDefaultExcelParser : IWellOperationExcelParser public class WellOperationDefaultExcelParser : IWellOperationExcelParser<WellOperationImportDefaultOptionsDto>
{ {
public int IdTemplate => Templates.IdDefaultTemplate; public SheetDto Parse(Stream stream, WellOperationImportDefaultOptionsDto options)
public IEnumerable<int> IdTypes => new[] { WellOperation.IdOperationTypePlan, WellOperation.IdOperationTypeFact };
public IEnumerable<RowDto> Parse(Stream stream, WellOperationParserOptionsDto options)
{ {
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled); using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
return ParseWorkbook(workbook, options); return ParseWorkbook(workbook, options);
} }
private static IEnumerable<RowDto> ParseWorkbook(IXLWorkbook workbook, WellOperationParserOptionsDto options) private static SheetDto ParseWorkbook(IXLWorkbook workbook, WellOperationImportDefaultOptionsDto options)
{ {
var sheetName = options.IdType == WellOperation.IdOperationTypePlan var sheetName = options.IdType == WellOperation.IdOperationTypePlan
? DefaultTemplateInfo.SheetNamePlan ? DefaultTemplateInfo.SheetNamePlan
@ -30,12 +28,12 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser
var sheet = workbook.Worksheets.FirstOrDefault(ws => var sheet = workbook.Worksheets.FirstOrDefault(ws =>
string.Equals(ws.Name, sheetName, StringComparison.CurrentCultureIgnoreCase)) string.Equals(ws.Name, sheetName, StringComparison.CurrentCultureIgnoreCase))
?? throw new FileFormatException($"Книга excel не содержит листа '{options.SheetName}'"); ?? throw new FileFormatException($"Книга excel не содержит листа '{sheetName}'");
return ParseSheet(sheet); return ParseSheet(sheet);
} }
private static IEnumerable<RowDto> ParseSheet(IXLWorksheet sheet) private static SheetDto ParseSheet(IXLWorksheet sheet)
{ {
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 7) if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 7)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов."); throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
@ -47,7 +45,7 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser
case > 1024: case > 1024:
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество операций."); throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество операций.");
case <= 0: case <= 0:
return Enumerable.Empty<RowDto>(); return new SheetDto { Name = sheet.Name };
} }
var rows = new RowDto[count]; var rows = new RowDto[count];
@ -71,7 +69,11 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser
if (cellValuesErrors.Any()) if (cellValuesErrors.Any())
throw new FileFormatException(string.Join("\r\n", cellValuesErrors)); throw new FileFormatException(string.Join("\r\n", cellValuesErrors));
return rows; return new SheetDto
{
Name = sheet.Name,
Rows = rows
};
} }
private static RowDto ParseRow(IXLRow xlRow) private static RowDto ParseRow(IXLRow xlRow)

View File

@ -4,16 +4,16 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Data.WellOperationImport;
using AsbCloudApp.Data.WellOperationImport.Options;
using AsbCloudApp.Exceptions; using AsbCloudApp.Exceptions;
using AsbCloudApp.Services.WellOperationImport; using AsbCloudApp.Services.WellOperationImport;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.WellOperationImport.Constants; using AsbCloudInfrastructure.Services.WellOperationImport.Constants;
using AsbCloudInfrastructure.Services.WellOperationImport.FileParser.StringSimilarity; using AsbCloudInfrastructure.Services.WellOperationImport.FileParser.StringSimilarity;
using ClosedXML.Excel; using ClosedXML.Excel;
namespace AsbCloudInfrastructure.Services.WellOperationImport.FileParser; namespace AsbCloudInfrastructure.Services.WellOperationImport.FileParser;
public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser<WellOperationImportGazpromKhantosOptionsDto>
{ {
private class Operation private class Operation
{ {
@ -36,23 +36,19 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
private readonly Dictionary<string, string> sectionDict = InitDict("Sections.txt", '='); private readonly Dictionary<string, string> sectionDict = InitDict("Sections.txt", '=');
private readonly Dictionary<string, string> operationAttributesDict = InitDict("OperationAttributes.txt", '='); private readonly Dictionary<string, string> operationAttributesDict = InitDict("OperationAttributes.txt", '=');
public int IdTemplate => Templates.IdGazpromKhantosTemplate; public SheetDto Parse(Stream stream, WellOperationImportGazpromKhantosOptionsDto options)
public IEnumerable<int> IdTypes => new[] { WellOperation.IdOperationTypePlan };
public IEnumerable<RowDto> Parse(Stream stream, WellOperationParserOptionsDto options)
{ {
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled); using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
return ParseWorkBook(workbook, options); return ParseWorkBook(workbook, options);
} }
private IEnumerable<RowDto> ParseWorkBook(IXLWorkbook workbook, WellOperationParserOptionsDto options) private SheetDto ParseWorkBook(IXLWorkbook workbook, WellOperationImportGazpromKhantosOptionsDto options)
{ {
if (options.StartRow is null or < 1 or > 1048576) if (options.StartRow is < 1 or > 1048576)
throw new ArgumentInvalidException(nameof(options.StartRow), "Некорректное значение начальной строки"); 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), "Некорректное значение конечной строки"); throw new ArgumentInvalidException(nameof(options.EndRow), "Некорректное значение конечной строки");
if (options.EndRow < options.StartRow) if (options.EndRow < options.StartRow)
@ -62,15 +58,15 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
string.Equals(ws.Name, options.SheetName, StringComparison.CurrentCultureIgnoreCase)) string.Equals(ws.Name, options.SheetName, StringComparison.CurrentCultureIgnoreCase))
?? throw new FileFormatException($"Книга excel не содержит листа '{options.SheetName}'"); ?? 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) if (operationAttributes is null)
return Enumerable.Empty<RowDto>(); return new SheetDto { Name = sheet.Name };
var rowsCount = endRow - startRow + 1; var rowsCount = endRow - startRow + 1;
@ -103,7 +99,11 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
if (cellValuesErrors.Any()) if (cellValuesErrors.Any())
throw new FileFormatException(string.Join("\r\n", cellValuesErrors)); throw new FileFormatException(string.Join("\r\n", cellValuesErrors));
return BuildRows(); return new SheetDto()
{
Name = sheet.Name,
Rows = BuildRows()
};
IEnumerable<(double Diameter, string Name)> BuildSections() IEnumerable<(double Diameter, string Name)> BuildSections()
{ {

View File

@ -6,7 +6,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Data.WellOperationImport;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services.WellOperationImport; using AsbCloudApp.Services.WellOperationImport;
@ -15,43 +14,27 @@ namespace AsbCloudInfrastructure.Services.WellOperationImport;
public class WellOperationImportService : IWellOperationImportService public class WellOperationImportService : IWellOperationImportService
{ {
private readonly IEnumerable<IWellOperationExcelParser> excelParsers;
private readonly IWellOperationRepository wellOperationRepository; private readonly IWellOperationRepository wellOperationRepository;
private static readonly DateTime dateLimitMin = new(2001, 1, 1, 0, 0, 0); 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 DateTime dateLimitMax = new(2099, 1, 1, 0, 0, 0);
private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366); private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366);
public WellOperationImportService(IEnumerable<IWellOperationExcelParser> excelParsers, public WellOperationImportService(IWellOperationRepository wellOperationRepository)
IWellOperationRepository wellOperationRepository)
{ {
this.excelParsers = excelParsers;
this.wellOperationRepository = wellOperationRepository; this.wellOperationRepository = wellOperationRepository;
} }
public async Task ImportAsync(int idWell, int idUser, Stream stream, WellOperationParserOptionsDto options, bool deleteBeforeImport, public async Task ImportAsync(int idWell, int idUser, int idType, SheetDto sheet, bool deleteBeforeImport, CancellationToken cancellationToken)
CancellationToken cancellationToken)
{ {
var excelParser = excelParsers.FirstOrDefault(p =>
p.IdTemplate == options.IdTemplate &&
p.IdTypes.Contains(options.IdType))
?? throw new ArgumentInvalidException(nameof(options.IdTemplate), "Невозможно импортировать файл");
IEnumerable<RowDto> rows;
var validationErrors = new List<string>(); var validationErrors = new List<string>();
var sections = wellOperationRepository.GetSectionTypes(); var sections = wellOperationRepository.GetSectionTypes();
var categories = wellOperationRepository.GetCategories(false); var categories = wellOperationRepository.GetCategories(false);
rows = options.IdTemplate switch
{
0 => excelParser.Parse(stream, options),
_ => excelParser.Parse(stream, options)
};
var operations = new List<WellOperationDto>(); var operations = new List<WellOperationDto>();
foreach (var row in rows) foreach (var row in sheet.Rows)
{ {
try try
{ {
@ -59,38 +42,38 @@ public class WellOperationImportService : IWellOperationImportService
string.Equals(s.Caption, row.Section, StringComparison.CurrentCultureIgnoreCase)); string.Equals(s.Caption, row.Section, StringComparison.CurrentCultureIgnoreCase));
if (section is null) if (section is null)
throw new FileFormatException($"Лист '{options.SheetName}'. В строке '{row.Number}' не удалось определить секцию"); throw new FileFormatException($"Лист '{sheet.Name}'. В строке '{row.Number}' не удалось определить секцию");
var category = categories.FirstOrDefault(c => var category = categories.FirstOrDefault(c =>
string.Equals(c.Name, row.Category, StringComparison.CurrentCultureIgnoreCase)); string.Equals(c.Name, row.Category, StringComparison.CurrentCultureIgnoreCase));
if (category is null) 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)) if (row.DepthStart is not (>= 0d and <= 20_000d))
throw new FileFormatException( throw new FileFormatException(
$"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на начало операции"); $"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная глубина на начало операции");
if (row.DepthEnd is not (>= 0d and <= 20_000d)) if (row.DepthEnd is not (>= 0d and <= 20_000d))
throw new FileFormatException( throw new FileFormatException(
$"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на конец операции"); $"Лист '{sheet.Name}'. Строка '{row.Number}' некорректная глубина на конец операции");
if (row.Date < dateLimitMin && row.Date > dateLimitMax) if (row.Date < dateLimitMin && row.Date > dateLimitMax)
throw new FileFormatException( throw new FileFormatException(
$"Лист '{options.SheetName}'. Строка '{row.Number}' неправильно получена дата начала операции"); $"Лист '{sheet.Name}'. Строка '{row.Number}' неправильно получена дата начала операции");
if (operations.LastOrDefault()?.DateStart > row.Date) if (operations.LastOrDefault()?.DateStart > row.Date)
throw new FileFormatException( throw new FileFormatException(
$"Лист '{options.SheetName}' строка '{row.Number}' дата позднее даты предыдущей операции"); $"Лист '{sheet.Name}' строка '{row.Number}' дата позднее даты предыдущей операции");
if (row.Duration is not (>= 0d and <= 240d)) 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 operations.Add(new WellOperationDto
{ {
IdWell = idWell, IdWell = idWell,
IdUser = idUser, IdUser = idUser,
IdType = options.IdType, IdType = idType,
IdWellSectionType = section.Id, IdWellSectionType = section.Id,
IdCategory = category.Id, IdCategory = category.Id,
CategoryInfo = row.CategoryInfo, CategoryInfo = row.CategoryInfo,
@ -107,7 +90,7 @@ public class WellOperationImportService : IWellOperationImportService
} }
if (operations.Any() && operations.Min(o => o.DateStart) - operations.Max(o => o.DateStart) > drillingDurationLimitMax) 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()) if (validationErrors.Any())
throw new FileFormatException(string.Join("\r\n", validationErrors)); throw new FileFormatException(string.Join("\r\n", validationErrors));
@ -119,7 +102,8 @@ public class WellOperationImportService : IWellOperationImportService
{ {
var existingOperations = await wellOperationRepository.GetAsync(new WellOperationRequest var existingOperations = await wellOperationRepository.GetAsync(new WellOperationRequest
{ {
IdWell = idWell IdWell = idWell,
OperationType = idType
}, cancellationToken); }, cancellationToken);
await wellOperationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken); await wellOperationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken);