2021-10-08 11:30:06 +05:00
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using ClosedXML.Excel;
|
|
|
|
|
using AsbCloudApp.Data;
|
2021-10-08 17:00:30 +05:00
|
|
|
|
using AsbCloudDb.Model;
|
2021-10-09 20:16:22 +05:00
|
|
|
|
using Mapster;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using AsbCloudApp.Services;
|
2021-10-08 11:30:06 +05:00
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
namespace AsbCloudInfrastructure.Services.WellOperationService
|
2021-10-08 11:30:06 +05:00
|
|
|
|
{
|
2021-12-08 15:38:38 +05:00
|
|
|
|
/*
|
|
|
|
|
* password for WellOperationImportTemplate.xlsx is ASB2020!
|
|
|
|
|
*/
|
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
public class WellOperationImportService : IWellOperationImportService
|
2021-10-08 11:30:06 +05:00
|
|
|
|
{
|
|
|
|
|
private const string sheetNamePlan = "План";
|
|
|
|
|
private const string sheetNameFact = "Факт";
|
|
|
|
|
|
2021-12-08 15:29:41 +05:00
|
|
|
|
private const int headerRowsCount = 1;
|
|
|
|
|
private const int columnSection = 1;
|
|
|
|
|
private const int columnCategory = 2;
|
|
|
|
|
private const int columnCategoryInfo = 3;
|
|
|
|
|
private const int columnDepthStart = 4;
|
|
|
|
|
private const int columnDepthEnd = 5;
|
|
|
|
|
private const int columnDate = 6;
|
|
|
|
|
private const int columnDuration = 7;
|
|
|
|
|
private const int columnComment = 8;
|
2021-10-09 20:16:22 +05:00
|
|
|
|
|
|
|
|
|
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);
|
2021-10-08 17:00:30 +05:00
|
|
|
|
private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366);
|
|
|
|
|
|
|
|
|
|
private readonly IAsbCloudDbContext db;
|
|
|
|
|
|
|
|
|
|
private List<WellOperationCategory> categories = null;
|
2021-12-08 15:29:41 +05:00
|
|
|
|
public List<WellOperationCategory> Categories
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (categories is null)
|
|
|
|
|
{
|
|
|
|
|
categories = db.WellOperationCategories
|
|
|
|
|
.AsNoTracking()
|
|
|
|
|
.ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return categories;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-08 17:00:30 +05:00
|
|
|
|
|
|
|
|
|
private List<WellSectionType> sections = null;
|
|
|
|
|
public List<WellSectionType> Sections
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (sections is null)
|
2021-10-09 20:16:22 +05:00
|
|
|
|
sections = db.WellSectionTypes
|
|
|
|
|
.AsNoTracking()
|
|
|
|
|
.ToList();
|
2021-10-08 17:00:30 +05:00
|
|
|
|
return sections;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public WellOperationImportService(IAsbCloudDbContext db)
|
|
|
|
|
{
|
|
|
|
|
this.db = db;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
public void Import(int idWell, Stream stream, bool deleteWellOperationsBeforeImport = false)
|
|
|
|
|
{
|
|
|
|
|
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
|
|
|
|
|
var operations = ParseFileStream(stream);
|
|
|
|
|
foreach (var operation in operations)
|
|
|
|
|
operation.IdWell = idWell;
|
|
|
|
|
|
|
|
|
|
SaveOperations(idWell, operations, deleteWellOperationsBeforeImport);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Stream Export(int idWell)
|
|
|
|
|
{
|
|
|
|
|
var operations = db.WellOperations
|
|
|
|
|
.Include(o => o.WellSectionType)
|
|
|
|
|
.Include(o => o.OperationCategory)
|
|
|
|
|
.Where(o => o.IdWell == idWell)
|
|
|
|
|
.OrderBy(o => o.DateStart)
|
|
|
|
|
.AsNoTracking()
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
if (!operations.Any())
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
return MakeExelFileStream(operations);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-11 13:45:06 +05:00
|
|
|
|
public Stream GetExcelTemplateStream() {
|
|
|
|
|
var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
2021-10-11 15:28:21 +05:00
|
|
|
|
.GetManifestResourceStream("AsbCloudInfrastructure.Services.WellOperationService.WellOperationImportTemplate.xlsx");
|
2021-10-11 13:45:06 +05:00
|
|
|
|
return stream;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
private Stream MakeExelFileStream(IEnumerable<WellOperation> operations)
|
|
|
|
|
{
|
2021-10-11 13:45:06 +05:00
|
|
|
|
using Stream ecxelTemplateStream = GetExcelTemplateStream();
|
2021-10-09 20:16:22 +05:00
|
|
|
|
|
|
|
|
|
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);
|
|
|
|
|
AddOperationsToWorkbook(workbook, operations);
|
|
|
|
|
|
|
|
|
|
MemoryStream memoryStream = new MemoryStream();
|
2021-10-11 15:28:21 +05:00
|
|
|
|
workbook.SaveAs(memoryStream, new SaveOptions { });
|
2021-10-09 20:16:22 +05:00
|
|
|
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
return memoryStream;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-07 11:34:06 +05:00
|
|
|
|
private static void AddOperationsToWorkbook(XLWorkbook workbook, IEnumerable<WellOperation> operations)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
{
|
2021-10-09 20:16:22 +05:00
|
|
|
|
var planOperations = operations.Where(o => o.IdType == 0);
|
|
|
|
|
if (planOperations.Any())
|
|
|
|
|
{
|
|
|
|
|
var sheetPlan = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlan);
|
|
|
|
|
AddOperationsToSheet(sheetPlan, planOperations);
|
|
|
|
|
}
|
2021-10-08 11:30:06 +05:00
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
var factOperations = operations.Where(o => o.IdType == 1);
|
|
|
|
|
if (factOperations.Any())
|
|
|
|
|
{
|
|
|
|
|
var sheetFact = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNameFact);
|
|
|
|
|
AddOperationsToSheet(sheetFact, factOperations);
|
|
|
|
|
}
|
2021-10-08 17:00:30 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-07 11:34:06 +05:00
|
|
|
|
private static void AddOperationsToSheet(IXLWorksheet sheet, IEnumerable<WellOperation> operations)
|
2021-10-08 17:00:30 +05:00
|
|
|
|
{
|
2021-10-09 20:16:22 +05:00
|
|
|
|
var operationsList = operations.ToList();
|
2021-12-07 11:34:06 +05:00
|
|
|
|
for (int i = 0; i < operationsList.Count; i++)
|
2021-10-09 20:16:22 +05:00
|
|
|
|
{
|
|
|
|
|
var row = sheet.Row(1 + i + headerRowsCount);
|
|
|
|
|
AddOperationToRow(row, operationsList[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-07 11:34:06 +05:00
|
|
|
|
private static void AddOperationToRow(IXLRow row, WellOperation operation)
|
2021-10-09 20:16:22 +05:00
|
|
|
|
{
|
|
|
|
|
row.Cell(columnSection).Value = operation.WellSectionType?.Caption;
|
|
|
|
|
row.Cell(columnCategory).Value = operation.OperationCategory?.Name;
|
|
|
|
|
row.Cell(columnCategoryInfo).Value = operation.CategoryInfo;
|
|
|
|
|
row.Cell(columnDepthStart).Value = operation.DepthStart;
|
|
|
|
|
row.Cell(columnDepthEnd).Value = operation.DepthEnd;
|
|
|
|
|
row.Cell(columnDate).Value = operation.DateStart;
|
|
|
|
|
row.Cell(columnDuration).Value = operation.DurationHours;
|
|
|
|
|
row.Cell(columnComment).Value = operation.Comment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SaveOperations(int idWell, IEnumerable<WellOperationDto> operations, bool deleteWellOperationsBeforeImport = false)
|
|
|
|
|
{
|
|
|
|
|
var transaction = db.Database.BeginTransaction();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (deleteWellOperationsBeforeImport)
|
|
|
|
|
db.WellOperations.RemoveRange(db.WellOperations.Where(o => o.IdWell == idWell));
|
|
|
|
|
db.WellOperations.AddRange(operations.Adapt<WellOperation>());
|
|
|
|
|
db.SaveChanges();
|
|
|
|
|
transaction.Commit();
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
transaction.Rollback();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerable<WellOperationDto> ParseFileStream(Stream stream)
|
|
|
|
|
{
|
|
|
|
|
using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);
|
|
|
|
|
return ParseWorkbook(workbook);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerable<WellOperationDto> ParseWorkbook(IXLWorkbook workbook)
|
|
|
|
|
{
|
|
|
|
|
var sheetPlan = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlan);
|
2021-10-08 11:30:06 +05:00
|
|
|
|
if (sheetPlan is null)
|
2021-10-09 20:16:22 +05:00
|
|
|
|
throw new FileFormatException($"Книга excel не содержит листа {sheetNamePlan}.");
|
2021-10-08 17:00:30 +05:00
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
var sheetFact = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNameFact);
|
2021-10-08 11:30:06 +05:00
|
|
|
|
if (sheetFact is null)
|
2021-10-09 20:16:22 +05:00
|
|
|
|
throw new FileFormatException($"Книга excel не содержит листа {sheetNameFact}.");
|
2021-10-08 11:30:06 +05:00
|
|
|
|
|
2021-10-08 17:00:30 +05:00
|
|
|
|
//sheetPlan.RangeUsed().RangeAddress.LastAddress.ColumnNumber
|
2021-10-08 11:30:06 +05:00
|
|
|
|
var wellOperations = new List<WellOperationDto>();
|
|
|
|
|
|
2021-10-08 17:00:30 +05:00
|
|
|
|
var wellOperationsPlan = ParseSheet(sheetPlan, 0);
|
|
|
|
|
wellOperations.AddRange(wellOperationsPlan);
|
|
|
|
|
|
|
|
|
|
var wellOperationsFact = ParseSheet(sheetFact, 1);
|
|
|
|
|
wellOperations.AddRange(wellOperationsFact);
|
|
|
|
|
|
2021-10-08 11:30:06 +05:00
|
|
|
|
return wellOperations;
|
|
|
|
|
}
|
2021-10-08 17:00:30 +05:00
|
|
|
|
|
|
|
|
|
private IEnumerable<WellOperationDto> ParseSheet(IXLWorksheet sheet, int idType)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 7)
|
|
|
|
|
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцев.");
|
|
|
|
|
|
|
|
|
|
var count = sheet.RowsUsed().Count() - headerRowsCount;
|
2021-10-09 20:16:22 +05:00
|
|
|
|
|
2021-10-08 17:00:30 +05:00
|
|
|
|
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);
|
2021-10-09 20:16:22 +05:00
|
|
|
|
|
2021-10-08 17:00:30 +05:00
|
|
|
|
if (lastOperationDateStart > operation.DateStart)
|
|
|
|
|
parseErrors.Add($"Лист {sheet.Name} строка {row.RowNumber()} дата позднее даты предыдущей операции.");
|
|
|
|
|
|
|
|
|
|
lastOperationDateStart = operation.DateStart;
|
|
|
|
|
}
|
2021-10-09 20:16:22 +05:00
|
|
|
|
catch (FileFormatException ex)
|
2021-10-08 17:00:30 +05:00
|
|
|
|
{
|
|
|
|
|
parseErrors.Add(ex.Message);
|
2021-10-09 20:16:22 +05:00
|
|
|
|
}
|
2021-10-08 17:00:30 +05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (parseErrors.Any())
|
|
|
|
|
throw new FileFormatException(string.Join("\r\n", parseErrors));
|
2021-10-11 13:45:06 +05:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (operations.Any())
|
|
|
|
|
if (operations.Min(o => o.DateStart) - operations.Max(o => o.DateStart) > drillingDurationLimitMax)
|
|
|
|
|
parseErrors.Add($"Лист {sheet.Name} содержит диапазон дат больше {drillingDurationLimitMax}");
|
|
|
|
|
}
|
2021-10-08 17:00:30 +05:00
|
|
|
|
|
|
|
|
|
return operations;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private WellOperationDto ParseRow(IXLRow row, int idType)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
var operation = new WellOperationDto { IdType = idType };
|
2021-10-08 17:00:30 +05:00
|
|
|
|
|
|
|
|
|
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()} указана некорректная секция");
|
2021-10-09 20:16:22 +05:00
|
|
|
|
|
2021-10-08 17:00:30 +05:00
|
|
|
|
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());
|
2021-10-09 20:16:22 +05:00
|
|
|
|
if (category is null)
|
2021-10-08 17:00:30 +05:00
|
|
|
|
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;
|
|
|
|
|
}
|
2021-10-08 11:30:06 +05:00
|
|
|
|
}
|
|
|
|
|
}
|