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

255 lines
9.9 KiB
C#

using AsbCloudApp.Data;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using ClosedXML.Excel.Drawings;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services
{
public class DrillingProgramService : IDrillingProgramService
{
private readonly IFileService fileService;
private readonly IWellService wellService;
private const int idFileCategoryDrillingProgramItems = 13;
private const int idFileCategoryDrillingProgram = 14;
private const int maxAllowedColumns = 256;
public DrillingProgramService(IFileService fileService, IWellService wellService)
{
this.fileService = fileService;
this.wellService = wellService;
}
public async Task<string> GetOrCreateSharedUrlAsync(int idWell, int idUser, IFileShareService fileShareService, CancellationToken token = default)
{
var fileInfo = await GetOrCreateAsync(idWell, idUser, token)
.ConfigureAwait(false);
if (fileInfo is null)
return null;
var sharedUrl = await fileService.GetSharedUrlAsync(fileInfo, idUser, fileShareService, token)
.ConfigureAwait(false);
return sharedUrl;
}
public async Task<FileInfoDto> GetOrCreateAsync(int idWell, int idUser, CancellationToken token = default)
{
var programParts = (await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgramItems, token)
.ConfigureAwait(false))
.Where(f => f.FileMarks?.Any(m => m.IdMarkType == 1 && !m.IsDeleted)??false);
var well = await wellService.GetAsync(idWell, token)
.ConfigureAwait(false);
var programs = await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgram, token)
.ConfigureAwait(false);
if (programs is not null && programs.Any() && programParts.Any())
{
programs = programs.OrderByDescending(f => f.UploadDate);
var matchFilesIterator = programs.GetEnumerator();
matchFilesIterator.MoveNext();
var matchFile = matchFilesIterator.Current;
if (programParts.All(pp => matchFile.UploadDate > pp.UploadDate) &&
File.Exists(fileService.GetUrl(matchFile)))
return matchFile;
else
await fileService.DeleteAsync(matchFile.Id, token)
.ConfigureAwait(false);
while (matchFilesIterator.MoveNext())
await fileService.DeleteAsync(matchFilesIterator.Current.Id, token)
.ConfigureAwait(false);
}
if (!programParts.Any())
throw new FileNotFoundException("Нет частей для формирования программы бурения");
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx";
var filteredFilePaths = programParts
.Select(file => fileService.GetUrl(file));
var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName);
UniteExcelFiles(filteredFilePaths, tempResultFilePath);
var fileInfo = await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram,
resultFileName, tempResultFilePath, token).ConfigureAwait(false);
return fileInfo;
}
public async Task<int> CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token)
{
var fileInfo = await fileService.GetInfoAsync(fileMarkDto.IdFile, token)
.ConfigureAwait(false);
if (fileInfo.IdCategory != idFileCategoryDrillingProgramItems)
throw new ArgumentException($"Этот метод допустим только для файлов-частей программы бурения idCategory=={idFileCategoryDrillingProgramItems}.", nameof(fileMarkDto));
var result = await fileService.CreateFileMarkAsync(fileMarkDto, idUser, token)
.ConfigureAwait(false);
var drillingPrograms = await fileService.GetInfosByCategoryAsync(fileInfo.IdWell, idFileCategoryDrillingProgram, token)
.ConfigureAwait(false);
foreach (var drillingProgram in drillingPrograms)
await fileService.DeleteAsync(drillingProgram.Id, token)
.ConfigureAwait(false);
return result;
}
public async Task<int> MarkFileMarkAsDeletedAsync(int idMark,
CancellationToken token)
{
var fileInfo = await fileService.GetByMarkId(idMark, token)
.ConfigureAwait(false);
if (fileInfo.IdCategory != idFileCategoryDrillingProgramItems)
throw new ArgumentException($"Этот метод допустим только для файлов-частей программы бурения idCategory=={idFileCategoryDrillingProgramItems}.", nameof(idMark));
var result = await fileService.MarkFileMarkAsDeletedAsync(idMark, token)
.ConfigureAwait(false);
var drillingPrograms = await fileService.GetInfosByCategoryAsync(fileInfo.IdWell, idFileCategoryDrillingProgram, token)
.ConfigureAwait(false);
foreach (var drillingProgram in drillingPrograms)
await fileService.DeleteAsync(drillingProgram.Id, token)
.ConfigureAwait(false);
return result;
}
private static void UniteExcelFiles(IEnumerable<string> excelFilesNames, string resultExcelPath)
{
var resultExcelFile = new XLWorkbook(XLEventTracking.Disabled);
var filteredFileNames = excelFilesNames.Distinct();
foreach (var excelFileName in filteredFileNames)
{
using var workbookSrc = new XLWorkbook(excelFileName, XLEventTracking.Disabled);
foreach (var sheet in workbookSrc.Worksheets)
{
if(sheet.Visibility == XLWorksheetVisibility.Visible)
CopySheet(resultExcelFile, sheet);
}
}
resultExcelFile.SaveAs(resultExcelPath,
new SaveOptions { EvaluateFormulasBeforeSaving = true});
}
private static void CopySheet(XLWorkbook workbookDst, IXLWorksheet sheetSrc)
{
var newSheetName = sheetSrc.Name;
var suffix = "";
int index = 1;
while (workbookDst.Worksheets.Contains(newSheetName))
{
newSheetName = sheetSrc.Name;
suffix = $"_{index++}";
if (newSheetName.Length + suffix.Length >= 31)
newSheetName = newSheetName[..(31 - suffix.Length)];
newSheetName += suffix;
}
var imagesInfos = sheetSrc.Pictures.Select(p => new ImageInfo
{
Id = p.Id,
Data = p.ImageStream.GetBuffer(),
Height = p.Height,
Width = p.Width,
TopLeftCellAddress = p.TopLeftCell.Address,
Left = p.Left,
Top = p.Top
}).ToList();
IXLWorksheet resultSheet;
if (sheetSrc.Columns().Count() > maxAllowedColumns)
{
resultSheet = workbookDst.Worksheets.Add(newSheetName);
var rngData = GetCellsRange(sheetSrc);
rngData.CopyTo(resultSheet.Cell(1, 1));
var lastRowWithData = rngData.LastRowUsed().RangeAddress
.LastAddress.RowNumber;
for (int i = 1; i < lastRowWithData; i++)
{
resultSheet.Row(i).Height = sheetSrc.Row(i).Height;
resultSheet.Column(i).Width = sheetSrc.Column(i).Width;
}
}
else
{
RemovePicturesFromSheet(sheetSrc);
resultSheet = sheetSrc.CopyTo(workbookDst, newSheetName);
}
CopyImagesToAnotherSheet(imagesInfos, resultSheet);
}
private static IXLWorksheet CopyImagesToAnotherSheet(IEnumerable<ImageInfo> imagesInfos,
IXLWorksheet resultSheet)
{
foreach (var image in imagesInfos)
{
var stream = new MemoryStream();
stream.Write(image.Data, 0, image.Data.Length);
resultSheet.AddPicture(stream)
.WithPlacement(XLPicturePlacement.Move)
.WithSize(image.Width, image.Height)
.MoveTo(resultSheet.Cell(image.TopLeftCellAddress),
image.Left, image.Top);
}
return resultSheet;
}
private static void RemovePicturesFromSheet(IXLWorksheet sheet)
{
var filteredPics = sheet.Pictures.Select(p => p.Name).Distinct().ToList();
foreach (var n in filteredPics)
sheet.Pictures.Delete(n);
}
private static IXLRange GetCellsRange(IXLWorksheet sheet)
{
var firstTableCell = sheet.FirstCellUsed();
var lastTableCell = sheet.LastCellUsed();
var rngData = sheet.Range(firstTableCell.Address, lastTableCell.Address);
return rngData;
}
}
class ImageInfo
{
public int Id { get; set; }
public byte[] Data { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public IXLAddress TopLeftCellAddress { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
}