DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/WellOperationImportService.cs

201 lines
8.7 KiB
C#
Raw Normal View History

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using ClosedXML.Excel;
using AsbCloudApp.Data;
using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services
{
public class WellOperationImportService
{
private const string sheetNamePlan = "План";
private const string sheetNameFact = "Факт";
private static readonly DateTime dateLimitMin = new DateTime(2001,1,1,0,0,0);
private static readonly DateTime dateLimitMax = new DateTime(2099,1,1,0,0,0);
private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366);
private readonly IAsbCloudDbContext db;
private List<WellOperationCategory> categories = null;
public List<WellOperationCategory> Categories {
get {
if (categories is null)
categories = db.WellOperationCategories.ToList();
return categories;
}
}
private List<WellSectionType> sections = null;
public List<WellSectionType> Sections
{
get
{
if (sections is null)
sections = db.WellSectionTypes.ToList();
return sections;
}
}
public WellOperationImportService(IAsbCloudDbContext db)
{
this.db = db;
}
public IEnumerable<WellOperationDto> ParseFile(string excelFilePath)
{
if (!File.Exists(excelFilePath))
throw new FileNotFoundException($"Файл {excelFilePath} не найден.");
return ParseWorkbook(excelFilePath);
}
private IEnumerable<WellOperationDto> ParseWorkbook(string excelFilePath)
{
using var sourceExcelWorkbook = new XLWorkbook(excelFilePath, XLEventTracking.Disabled);
var sheetPlan = sourceExcelWorkbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlan);
if (sheetPlan is null)
throw new FileFormatException($"Файл {excelFilePath} не не содержит листа {sheetNamePlan}.");
var sheetFact = sourceExcelWorkbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNameFact);
if (sheetFact is null)
throw new FileFormatException($"Файл {excelFilePath} не не содержит листа {sheetNameFact}.");
//sheetPlan.RangeUsed().RangeAddress.LastAddress.ColumnNumber
var wellOperations = new List<WellOperationDto>();
var wellOperationsPlan = ParseSheet(sheetPlan, 0);
wellOperations.AddRange(wellOperationsPlan);
var wellOperationsFact = ParseSheet(sheetFact, 1);
wellOperations.AddRange(wellOperationsFact);
return wellOperations;
}
private IEnumerable<WellOperationDto> ParseSheet(IXLWorksheet sheet, int idType)
{
const int headerRowsCount = 1;
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 7)
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцев.");
var count = sheet.RowsUsed().Count() - headerRowsCount;
if (count > 1024)
throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество операций.");
if (count <= 0)
return new List<WellOperationDto>();
var operations = new List<WellOperationDto>(count);
var parseErrors = new List<string>();
DateTime lastOperationDateStart = new DateTime();
for (int i = 0; i < count; i++)
{
var row = sheet.Row(1 + i + headerRowsCount);
try
{
var operation = ParseRow(row, idType);
operations.Add(operation);
if (lastOperationDateStart > operation.DateStart)
parseErrors.Add($"Лист {sheet.Name} строка {row.RowNumber()} дата позднее даты предыдущей операции.");
lastOperationDateStart = operation.DateStart;
}
catch(FileFormatException ex)
{
parseErrors.Add(ex.Message);
}
};
// проверка диапазона дат
if(operations.Min(o => o.DateStart) - operations.Max(o => o.DateStart) > drillingDurationLimitMax)
parseErrors.Add($"Лист {sheet.Name} содержит диапазон дат больше {drillingDurationLimitMax}");
if (parseErrors.Any())
throw new FileFormatException(string.Join("\r\n", parseErrors));
return operations;
}
private WellOperationDto ParseRow(IXLRow row, int idType)
{
const int columnSection = 1;
const int columnCategory = 2;
const int columnCategoryInfo = 3;
const int columnDepthStart = 4;
const int columnDepthEnd = 5;
const int columnDate = 6;
const int columnDuration = 7;
const int columnComment = 8;
var vSection = row.Cell(columnSection).Value;
var vCategory = row.Cell(columnCategory).Value;
var vCategoryInfo = row.Cell(columnCategoryInfo).Value;
var vDepthStart = row.Cell(columnDepthStart).Value;
var vDepthEnd = row.Cell(columnDepthEnd).Value;
var vDate = row.Cell(columnDate).Value;
var vDuration = row.Cell(columnDuration).Value;
var vComment = row.Cell(columnComment).Value;
var operation = new WellOperationDto{IdType = idType};
if (vSection is string sectionName)
{
var section = Sections.Find(c => c.Caption.ToLower() == sectionName.ToLower());
if (section is null)
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} указана некорректная секция");
operation.IdWellSectionType = section.Id;
operation.WellSectionTypeName = section.Caption;
}
else
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} не указана секция");
if (vCategory is string categoryName)
{
var category = Categories.Find(c => c.Name.ToLower() == categoryName.ToLower());
if(category is null)
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} указана некорректная операция");
operation.IdCategory = category.Id;
operation.CategoryName = category.Name;
}
else
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} не указана операция");
if (vCategoryInfo is not null)
operation.CategoryInfo = vCategoryInfo.ToString();
if (vDepthStart is double depthStart && depthStart >= 0d && depthStart <= 20_000d)
operation.DepthStart = depthStart;
else
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} не указана глубина на начало операции");
if (vDepthEnd is double depthEnd && depthEnd >= 0d && depthEnd <= 20_000d)
operation.DepthEnd = depthEnd;
else
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} не указана глубина при завершении операции");
if (vDate is DateTime date && date > dateLimitMin && date < dateLimitMax)
operation.DateStart = date;
else
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} неправильно указана дата/время начала операции");
if (vDuration is double duration && duration >= 0d && duration <= 240d)
operation.DurationHours = duration;
else
throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} не указана длительность операции");
if (vComment is not null)
operation.Comment = vComment.ToString();
return operation;
}
}
}