using AsbCloudApp.Data; using AsbCloudApp.Services; using ClosedXML.Excel; using ClosedXML.Excel.Drawings; 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 GetAsync(int idWell, CancellationToken token = default) { var filesInfos = await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgramItems, token) .ConfigureAwait(false); var well = await wellService.GetAsync(idWell, token) .ConfigureAwait(false); var matchFiles = await fileService.GetInfosByCategoryAsync(idWell, idFileCategoryDrillingProgram, token) .ConfigureAwait(false); if (matchFiles is not null && matchFiles.Any()) { matchFiles = matchFiles.OrderByDescending(f => f.UploadDate); var matchFilesIterator = matchFiles.GetEnumerator(); matchFilesIterator.MoveNext(); var matchFile = matchFilesIterator.Current; while (matchFilesIterator.MoveNext()) await fileService.DeleteAsync(matchFilesIterator.Current.Id, token) .ConfigureAwait(false); if (filesInfos.All(f => f.UploadDate <= matchFile.UploadDate)) return matchFile; else await fileService.DeleteAsync(matchFile.Id, token) .ConfigureAwait(false); } var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx"; var fileNames = filesInfos .Where(f => f.Name != resultFileName) .Select(f => fileService.GetUrl(f)); var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName); UniteExcelFiles(fileNames, tempResultFilePath); var fileInfo = await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token).ConfigureAwait(false); return fileInfo; } private static void UniteExcelFiles(IEnumerable 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; int index = 1; if (workbookDst.Worksheets.Contains(newSheetName)) { var suffix = $"_{index++}"; if (newSheetName.Length + suffix.Length >= 31) newSheetName = sheetSrc.Name.Substring(0, (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 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; } } }