using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; using ClosedXML.Excel; using Mapster; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace AsbCloudInfrastructure.Services.PlannedTrajectory { #nullable enable public class PlannedTrajectoryImportService : IPlannedTrajectoryImportService { /* * password for PlannedTrajectoryTemplate.xlsx is Drill2022 */ private readonly IAsbCloudDbContext db; private readonly IWellService wellService; private const string sheetNamePlannedTrajectory = "Плановая траектория"; private const int headerRowsCount = 2; private const int ColumnWellboreDepth = 1; private const int ColumnZenithAngle = 2; private const int ColumnAzimuthGeo = 3; private const int ColumnAzimuthMagnetic = 4; private const int ColumnVerticalDepth = 5; private const int ColumnAbsoluteMark = 6; private const int ColumnNorthOrifice = 7; private const int ColumnEastOrifice = 8; private const int ColumnEastCartographic = 9; private const int ColumnNorthCartographic = 10; private const int ColumnSpatialIntensity = 11; private const int ColumnAngleIntensity = 12; private const int ColumnAzimuthIntensity = 13; private const int ColumnOrificeOffset = 14; private const int ColumnComment = 15; public PlannedTrajectoryImportService(IAsbCloudDbContext db, IWellService wellService) { this.db = db; this.wellService = wellService; } public Stream GetTemplateFile() { var stream = System.Reflection.Assembly.GetExecutingAssembly() .GetManifestResourceStream("AsbCloudInfrastructure.Services.PlannedTrajectoryService.PlannedTrajectoryTemplate.xlsx"); return stream; } public Stream Export(int idWell) { var plannedTrajectorys = db.PlannedTrajectorys .Where(o => o.IdWell == idWell) .AsNoTracking() .ToList(); if (!plannedTrajectorys.Any()) return null; var timezone = wellService.GetTimezone(idWell); return MakeExelFileStream(plannedTrajectorys); } private Stream MakeExelFileStream(IEnumerable plannedTrajectories) { using Stream ecxelTemplateStream = GetTemplateFile(); using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled); AddOperationsToWorkbook(workbook, plannedTrajectories); MemoryStream memoryStream = new MemoryStream(); workbook.SaveAs(memoryStream, new SaveOptions { }); memoryStream.Seek(0, SeekOrigin.Begin); return memoryStream; } private static void AddOperationsToWorkbook(XLWorkbook workbook, IEnumerable plannedTrajectories) { if (plannedTrajectories.Any()) { var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlannedTrajectory); AddOperationsToSheet(sheet, plannedTrajectories); } } private static void AddOperationsToSheet(IXLWorksheet sheet, IEnumerable plannedTrajectories) { var operationsList = plannedTrajectories.ToList(); for (int i = 0; i < operationsList.Count; i++) { var row = sheet.Row(1 + i + headerRowsCount); AddOperationToRow(row, operationsList[i]); } } private static void AddOperationToRow(IXLRow row, AsbCloudDb.Model.PlannedTrajectory trajectory) { row.Cell(ColumnWellboreDepth).Value = trajectory.WellboreDepth; row.Cell(ColumnZenithAngle).Value = trajectory.ZenithAngle; row.Cell(ColumnAzimuthGeo).Value = trajectory.AzimuthGeo; row.Cell(ColumnAzimuthMagnetic).Value = trajectory.AzimuthMagnetic; row.Cell(ColumnVerticalDepth).Value = trajectory.VerticalDepth; row.Cell(ColumnAbsoluteMark).Value = trajectory.AbsoluteMark; row.Cell(ColumnNorthOrifice).Value = trajectory.NorthOrifice; row.Cell(ColumnEastOrifice).Value = trajectory.EastOrifice; row.Cell(ColumnEastCartographic).Value = trajectory.EastCartographic; row.Cell(ColumnNorthCartographic).Value = trajectory.NorthCartographic; row.Cell(ColumnSpatialIntensity).Value = trajectory.SpatialIntensity; row.Cell(ColumnAngleIntensity).Value = trajectory.AngleIntensity; row.Cell(ColumnAzimuthIntensity).Value = trajectory.AzimuthIntensity; row.Cell(ColumnOrificeOffset).Value = trajectory.OrificeOffset; row.Cell(ColumnComment).Value = trajectory.Comment; } public void Import(int idWell, int idUser, Stream stream, bool deletePrevRows = false) { using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled); var trajectoryRows = ParseFileStream(stream); foreach (var row in trajectoryRows) row.IdWell = idWell; SaveOperations(idWell, idUser, trajectoryRows, deletePrevRows); } private void SaveOperations(int idWell, int idUser, IEnumerable newRows, bool deletePrevRow = false) { var timezone = wellService.GetTimezone(idWell); var transaction = db.Database.BeginTransaction(); try { if (deletePrevRow) db.WellOperations.RemoveRange(db.WellOperations.Where(o => o.IdWell == idWell)); var entities = newRows.Select(o => { var entity = o.Adapt(); entity.IdWell = idWell; entity.LoadDate = DateTime.Today.ToUtcDateTimeOffset(timezone.Hours); entity.IdUser = idUser; return entity; }); db.PlannedTrajectorys.AddRange(entities); db.SaveChanges(); transaction.Commit(); } catch { transaction.Rollback(); throw; } } private IEnumerable ParseFileStream(Stream stream) { using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled); return ParseWorkbook(workbook); } private IEnumerable ParseWorkbook(IXLWorkbook workbook) { var sheetPlannedTrajectory = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNamePlannedTrajectory); if (sheetPlannedTrajectory is null) throw new FileFormatException($"Книга excel не содержит листа {sheetNamePlannedTrajectory}."); var plannedTrajectoryRows = ParseSheet(sheetPlannedTrajectory); return plannedTrajectoryRows; } private IEnumerable ParseSheet(IXLWorksheet sheet) { if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < 15) throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов."); var count = sheet.RowsUsed().Count() - headerRowsCount; if (count > 1024) throw new FileFormatException($"Лист {sheet.Name} содержит слишком большое количество операций."); if (count <= 0) throw new FileFormatException($"Лист {sheet.Name} некорректного формата либо пустой"); var trajectoryRows = new List(count); var parseErrors = new List(); for (int i = 0; i < count; i++) { var row = sheet.Row(1 + i + headerRowsCount); try { var trajectoryRow = ParseRow(row); trajectoryRows.Add(trajectoryRow); } catch (FileFormatException ex) { parseErrors.Add(ex.Message); } }; return trajectoryRows; } private PlannedTrajectoryDto ParseRow(IXLRow row) { var _wellboreDepth = row.Cell(ColumnWellboreDepth).Value; var _zenithAngle = row.Cell(ColumnZenithAngle).Value; var _azimuthGeo = row.Cell(ColumnAzimuthGeo).Value; var _azimuthMagnetic = row.Cell(ColumnAzimuthMagnetic).Value; var _verticalDepth = row.Cell(ColumnVerticalDepth).Value; var _absoluteMark = row.Cell(ColumnAbsoluteMark).Value; var _northOrifice = row.Cell(ColumnNorthOrifice).Value; var _eastOrifice = row.Cell(ColumnEastOrifice).Value; var _eastCartographic = row.Cell(ColumnEastCartographic).Value; var _northCartographic = row.Cell(ColumnNorthCartographic).Value; var _spatialIntensity = row.Cell(ColumnSpatialIntensity).Value; var _angleIntensity = row.Cell(ColumnAngleIntensity).Value; var _azimuthIntensity = row.Cell(ColumnAzimuthIntensity).Value; var _orificeOffset = row.Cell(ColumnOrificeOffset).Value; var _comment = row.Cell(ColumnComment).Value; var trajectoryRow = new PlannedTrajectoryDto(); static bool isCorrectValue(object value) { if (value is null) return true; if (value is double) return true; return false; } static double? getDoubleValue(object value, string nameParam, IXLRow row) { if (!isCorrectValue(value)) throw new FileFormatException($"Лист {row.Worksheet.Name}. Строка {row.RowNumber()} - некорректные данные - {nameParam}"); if (value is double _value) return _value; return null; } trajectoryRow.WellboreDepth = getDoubleValue(_wellboreDepth, "Глубина по стволу", row); trajectoryRow.ZenithAngle = getDoubleValue(_zenithAngle, "Зенитный угол", row); trajectoryRow.AzimuthGeo = getDoubleValue(_azimuthGeo, "Азимут географический", row); trajectoryRow.AzimuthMagnetic = getDoubleValue(_azimuthMagnetic, "Азимут магнитный", row); trajectoryRow.VerticalDepth = getDoubleValue(_verticalDepth, "Глубина вертикальная", row); trajectoryRow.AbsoluteMark = getDoubleValue(_absoluteMark, "Абсолютная отметка", row); trajectoryRow.NorthOrifice = getDoubleValue(_northOrifice, "Север относительно устья", row); trajectoryRow.EastOrifice = getDoubleValue(_eastOrifice, "Восток относительно устья", row); trajectoryRow.EastCartographic = getDoubleValue(_eastCartographic, "Восток картографический", row); trajectoryRow.NorthCartographic = getDoubleValue(_northCartographic, "Север картографический", row); trajectoryRow.SpatialIntensity = getDoubleValue(_spatialIntensity, "Простр. интенсивность", row); trajectoryRow.AngleIntensity = getDoubleValue(_angleIntensity, "Интенсивность по углу", row); trajectoryRow.AzimuthIntensity = getDoubleValue(_azimuthIntensity, "Интенсивность по азимуту", row); trajectoryRow.OrificeOffset = getDoubleValue(_orificeOffset, "Смещение от устья", row); if (_comment is not null) trajectoryRow.Comment = _comment.ToString(); return trajectoryRow; } } #nullable disable }