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

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

View File

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

View File

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

View File

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

View File

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

View File

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