forked from ddrilling/AsbCloudServer
Рефакторинг импорта ГГД
This commit is contained in:
parent
ac578bce38
commit
14615517d6
@ -25,7 +25,7 @@ public class RowDto
|
||||
/// <summary>
|
||||
/// Описание категории
|
||||
/// </summary>
|
||||
public string CategoryInfo { get; set; } = null!;
|
||||
public string? CategoryInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Начальная глубина операции
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace AsbCloudApp.Data.WellOperationImport;
|
||||
@ -5,28 +6,45 @@ namespace AsbCloudApp.Data.WellOperationImport;
|
||||
/// <summary>
|
||||
/// Опции для настройки парсинга документа
|
||||
/// </summary>
|
||||
public class WellOperationParserOptionsDto
|
||||
public class WellOperationParserOptionsDto : IValidatableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Название листа
|
||||
/// </summary>
|
||||
[StringLength(250, MinimumLength =1, ErrorMessage = "Название листа должно быть задано")]
|
||||
public string SheetName { get; set; } = null!;
|
||||
|
||||
public string? SheetName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id шаблона
|
||||
/// Тип операции
|
||||
/// 0 - плановая операция
|
||||
/// 1 - фактическая операция
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Range(0, 1, ErrorMessage = "Тип операции недопустим. Допустимые: 0, 1")]
|
||||
public int IdType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Тип шаблона
|
||||
/// 0 - Дефолтный шаблон
|
||||
/// 1 - Газпром хантос
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Range(0, 1, ErrorMessage = "Тип шаблона недопустим. Допустимые: 0, 1")]
|
||||
public int IdTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Начальная строка
|
||||
/// </summary>
|
||||
public int? StartRow { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Конечная строка
|
||||
/// </summary>
|
||||
public int? EndRow { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (IdTemplate != 0 && string.IsNullOrWhiteSpace(SheetName))
|
||||
yield return new ValidationResult("Название листа должно быть задано", new[] { nameof(SheetName) });
|
||||
}
|
||||
}
|
@ -14,13 +14,11 @@ public interface IWellOperationImportService
|
||||
/// Загрузить из excel список операций
|
||||
/// </summary>
|
||||
/// <param name="idWell"></param>
|
||||
/// <param name="idType"></param>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="idUser"></param>
|
||||
/// <param name="deleteWellOperationsBeforeImport"></param>
|
||||
/// <param name="deleteBeforeImport"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <param name="options"></param>
|
||||
Task ImportAsync(int idWell, int idUser, int idType, Stream stream, WellOperationParserOptionsDto options,
|
||||
bool deleteWellOperationsBeforeImport,
|
||||
Task ImportAsync(int idWell, int idUser, Stream stream, WellOperationParserOptionsDto options, bool deleteBeforeImport,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data.WellOperationImport;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Services.WellOperationImport;
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudInfrastructure.Services.WellOperationImport.Constants;
|
||||
@ -25,11 +24,12 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser
|
||||
|
||||
private static IEnumerable<RowDto> ParseWorkbook(IXLWorkbook workbook, WellOperationParserOptionsDto options)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.SheetName))
|
||||
throw new ArgumentInvalidException(nameof(options.SheetName), "Не указано название листа");
|
||||
var sheetName = options.IdType == WellOperation.IdOperationTypePlan
|
||||
? DefaultTemplateInfo.SheetNamePlan
|
||||
: DefaultTemplateInfo.SheetNameFact;
|
||||
|
||||
var sheet = workbook.Worksheets.FirstOrDefault(ws =>
|
||||
string.Equals(ws.Name, options.SheetName, StringComparison.CurrentCultureIgnoreCase))
|
||||
string.Equals(ws.Name, sheetName, StringComparison.CurrentCultureIgnoreCase))
|
||||
?? throw new FileFormatException($"Книга excel не содержит листа '{options.SheetName}'");
|
||||
|
||||
return ParseSheet(sheet);
|
||||
@ -81,12 +81,12 @@ public class WellOperationDefaultExcelParser : IWellOperationExcelParser
|
||||
Number = xlRow.RowNumber(),
|
||||
Section = xlRow.Cell(DefaultTemplateInfo.ColumnSection).GetCellValue<string>(),
|
||||
Category = xlRow.Cell(DefaultTemplateInfo.ColumnCategory).GetCellValue<string>(),
|
||||
CategoryInfo = xlRow.Cell(DefaultTemplateInfo.ColumnCategoryInfo).GetCellValue<string>(),
|
||||
CategoryInfo = xlRow.Cell(DefaultTemplateInfo.ColumnCategoryInfo).GetCellValue<string?>(),
|
||||
DepthStart = xlRow.Cell(DefaultTemplateInfo.ColumnDepthStart).GetCellValue<double>(),
|
||||
DepthEnd = xlRow.Cell(DefaultTemplateInfo.ColumnDepthEnd).GetCellValue<double>(),
|
||||
Date = xlRow.Cell(DefaultTemplateInfo.ColumnDate).GetCellValue<DateTime>(),
|
||||
Duration = xlRow.Cell(DefaultTemplateInfo.ColumnDuration).GetCellValue<double>(),
|
||||
Comment = xlRow.Cell(DefaultTemplateInfo.ColumnComment).GetCellValue<string>()
|
||||
Comment = xlRow.Cell(DefaultTemplateInfo.ColumnComment).GetCellValue<string?>()
|
||||
};
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -20,7 +19,7 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
|
||||
{
|
||||
public int RowNumber { get; set; }
|
||||
|
||||
public string CategoryInfo { get; set; } = null!;
|
||||
public string? CategoryInfo { get; set; }
|
||||
|
||||
public double SectionDiameter { get; set; }
|
||||
|
||||
@ -31,17 +30,11 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
|
||||
public DateTime Date { get; set; }
|
||||
}
|
||||
|
||||
private readonly CosineSimilarity cosineSimilarity;
|
||||
private readonly CosineSimilarity cosineSimilarity = new();
|
||||
|
||||
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 WellOperationGazpromKhantosExcelParser()
|
||||
{
|
||||
cosineSimilarity = new CosineSimilarity();
|
||||
}
|
||||
|
||||
public int IdTemplate => Templates.IdGazpromKhantosTemplate;
|
||||
|
||||
@ -56,9 +49,6 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
|
||||
|
||||
private IEnumerable<RowDto> ParseWorkBook(IXLWorkbook workbook, WellOperationParserOptionsDto options)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.SheetName))
|
||||
throw new ArgumentInvalidException(nameof(options.SheetName), "Не указано название листа");
|
||||
|
||||
if (options.StartRow is null or < 1 or > 1048576)
|
||||
throw new ArgumentInvalidException(nameof(options.StartRow), "Некорректное значение начальной строки");
|
||||
|
||||
@ -97,7 +87,7 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
|
||||
operations.Add(new Operation
|
||||
{
|
||||
RowNumber = xlRow.RowNumber(),
|
||||
CategoryInfo = xlRow.Cell(operationAttributes[OperationAttributes.CategoryInfo]).GetCellValue<string>(),
|
||||
CategoryInfo = xlRow.Cell(operationAttributes[OperationAttributes.CategoryInfo]).GetCellValue<string?>(),
|
||||
SectionDiameter =xlRow.Cell(operationAttributes[OperationAttributes.SectionDiameter]).GetCellValue<double>(),
|
||||
Depth = xlRow.Cell(operationAttributes[OperationAttributes.Depth]).GetCellValue<double>(),
|
||||
Duration = xlRow.Cell(operationAttributes[OperationAttributes.Duration]).GetCellValue<double>(),
|
||||
@ -198,8 +188,11 @@ public class WellOperationGazpromKhantosExcelParser : IWellOperationExcelParser
|
||||
return operationAttributes is not null && operationAttributes.Count == countOperationAttributes ? operationAttributes : null;
|
||||
}
|
||||
|
||||
private string? GetValueDictionary(IDictionary<string, string> dict, string cellValue, double? minSimilarity)
|
||||
private string? GetValueDictionary(IDictionary<string, string> dict, string? cellValue, double? minSimilarity)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(cellValue))
|
||||
return null;
|
||||
|
||||
var similarValues = new List<(double similarity, string value)>();
|
||||
|
||||
var profile1 = cosineSimilarity.GetProfile(cellValue);
|
||||
|
@ -10,8 +10,6 @@ using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services.WellOperationImport;
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudInfrastructure.Services.WellOperationImport.Constants;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.WellOperationImport;
|
||||
|
||||
@ -31,35 +29,25 @@ public class WellOperationImportService : IWellOperationImportService
|
||||
this.wellOperationRepository = wellOperationRepository;
|
||||
}
|
||||
|
||||
public async Task ImportAsync(int idWell, int idUser, int idType, Stream stream, WellOperationParserOptionsDto options,
|
||||
bool deleteWellOperationsBeforeImport, CancellationToken cancellationToken)
|
||||
public async Task ImportAsync(int idWell, int idUser, Stream stream, WellOperationParserOptionsDto options, bool deleteBeforeImport,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var excelParser = excelParsers.FirstOrDefault(p => p.IdTemplate == options.IdTemplate && p.IdTypes.Contains(idType))
|
||||
?? throw new ArgumentInvalidException(nameof(options.IdTemplate), "Невозможно импортировать файл");
|
||||
|
||||
if (idType != WellOperation.IdOperationTypePlan && idType != WellOperation.IdOperationTypeFact)
|
||||
throw new ArgumentInvalidException(nameof(idType), "Операции не существует");
|
||||
|
||||
RowDto[] rows;
|
||||
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);
|
||||
|
||||
switch (options.IdTemplate)
|
||||
rows = options.IdTemplate switch
|
||||
{
|
||||
case 0:
|
||||
options.SheetName = idType == WellOperation.IdOperationTypePlan
|
||||
? DefaultTemplateInfo.SheetNamePlan
|
||||
: DefaultTemplateInfo.SheetNameFact;
|
||||
rows = excelParser.Parse(stream, options).ToArray();
|
||||
break;
|
||||
default:
|
||||
if (string.IsNullOrWhiteSpace(options.SheetName))
|
||||
throw new FileFormatException("Не указано название листа");
|
||||
rows = excelParser.Parse(stream, options).ToArray();
|
||||
break;
|
||||
}
|
||||
0 => excelParser.Parse(stream, options),
|
||||
_ => excelParser.Parse(stream, options)
|
||||
};
|
||||
|
||||
var operations = new List<WellOperationDto>();
|
||||
|
||||
@ -80,16 +68,20 @@ public class WellOperationImportService : IWellOperationImportService
|
||||
throw new FileFormatException($"Лист '{options.SheetName}'. В строке '{row.Number}' не удалось определить операцию");
|
||||
|
||||
if (row.DepthStart is not (>= 0d and <= 20_000d))
|
||||
throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на начало операции");
|
||||
throw new FileFormatException(
|
||||
$"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на начало операции");
|
||||
|
||||
if (row.DepthEnd is not (>= 0d and <= 20_000d))
|
||||
throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на конец операции");
|
||||
throw new FileFormatException(
|
||||
$"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на конец операции");
|
||||
|
||||
if (row.Date < dateLimitMin && row.Date > dateLimitMax)
|
||||
throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' неправильно получена дата начала операции");
|
||||
throw new FileFormatException(
|
||||
$"Лист '{options.SheetName}'. Строка '{row.Number}' неправильно получена дата начала операции");
|
||||
|
||||
if (operations.LastOrDefault()?.DateStart > row.Date)
|
||||
throw new FileFormatException($"Лист '{options.SheetName}' строка '{row.Number}' дата позднее даты предыдущей операции");
|
||||
throw new FileFormatException(
|
||||
$"Лист '{options.SheetName}' строка '{row.Number}' дата позднее даты предыдущей операции");
|
||||
|
||||
if (row.Duration is not (>= 0d and <= 240d))
|
||||
throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная длительность операции");
|
||||
@ -98,7 +90,7 @@ public class WellOperationImportService : IWellOperationImportService
|
||||
{
|
||||
IdWell = idWell,
|
||||
IdUser = idUser,
|
||||
IdType = idType,
|
||||
IdType = options.IdType,
|
||||
IdWellSectionType = section.Id,
|
||||
IdCategory = category.Id,
|
||||
CategoryInfo = row.CategoryInfo,
|
||||
@ -120,19 +112,19 @@ public class WellOperationImportService : IWellOperationImportService
|
||||
if (validationErrors.Any())
|
||||
throw new FileFormatException(string.Join("\r\n", validationErrors));
|
||||
|
||||
if(!operations.Any())
|
||||
if (!operations.Any())
|
||||
return;
|
||||
|
||||
if (deleteWellOperationsBeforeImport)
|
||||
if (deleteBeforeImport)
|
||||
{
|
||||
var existingOperations = await wellOperationRepository.GetAsync(new WellOperationRequest
|
||||
{
|
||||
IdWell = idWell
|
||||
}, cancellationToken);
|
||||
|
||||
|
||||
await wellOperationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken);
|
||||
}
|
||||
|
||||
await wellOperationRepository.InsertRangeAsync(operations, cancellationToken);
|
||||
await wellOperationRepository.InsertRangeAsync(operations, cancellationToken);
|
||||
}
|
||||
}
|
@ -290,25 +290,17 @@ namespace AsbCloudWebApi.Controllers
|
||||
/// Импорт плановых операций из excel (xlsx) файла
|
||||
/// </summary>
|
||||
/// <param name="idWell">id скважины</param>
|
||||
/// <param name="idType">Тип операции</param>
|
||||
/// <param name="startRow">Начальная строка</param>
|
||||
/// <param name="endRow">Конечная строка</param>
|
||||
/// <param name="options">Параметры для парсинга файла</param>
|
||||
/// <param name="files">Коллекция из одного файла xlsx</param>
|
||||
/// <param name="options">Удалить операции перед импортом = 1, если файл валидный</param>
|
||||
/// <param name="sheetName">Название листа</param>
|
||||
/// <param name="token">Токен отмены задачи </param>
|
||||
/// <param name="idTemplate">Шаблон файла. 0 - стандартный, 1 - Газпромнефть Хантос</param>
|
||||
/// <param name="deleteBeforeImport">Удалить операции перед импортом = 1, если файл валидный</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("import/{options}")]
|
||||
[HttpPost("import/{deleteBeforeImport}")]
|
||||
[Permission]
|
||||
public async Task<IActionResult> ImportAsync(int idWell,
|
||||
[Required] int idType,
|
||||
string? sheetName,
|
||||
[Required] int idTemplate,
|
||||
int? startRow,
|
||||
int? endRow,
|
||||
[FromQuery] WellOperationParserOptionsDto options,
|
||||
[FromForm] IFormFileCollection files,
|
||||
int options,
|
||||
[Range(0, 1, ErrorMessage = "Недопустимое значение. Допустимые: 0, 1")] int deleteBeforeImport,
|
||||
CancellationToken token)
|
||||
{
|
||||
var idCompany = User.GetCompanyId();
|
||||
@ -338,13 +330,7 @@ namespace AsbCloudWebApi.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
await wellOperationImportService.ImportAsync(idWell, idUser.Value, idType, stream, new WellOperationParserOptionsDto
|
||||
{
|
||||
SheetName = sheetName,
|
||||
IdTemplate = idTemplate,
|
||||
StartRow = startRow,
|
||||
EndRow = endRow
|
||||
}, (options & 1) > 0, token);
|
||||
await wellOperationImportService.ImportAsync(idWell, idUser.Value, stream, options, (deleteBeforeImport & 1) > 0, token);
|
||||
}
|
||||
catch (FileFormatException ex)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user