DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/WellOperationService/ScheduleReportService.cs
2022-03-17 16:56:13 +05:00

313 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AsbCloudApp.Data;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.WellOperationService
{
public class ScheduleReportService: IScheduleReportService
{
private readonly IOperationsStatService operationsStatService;
private readonly IWellService wellService;
const string sheetNameSchedule = "Сетевой график";
const string sheetNameTvd = "ГГД";
const int maxChartsToWrap = 89;
public ScheduleReportService(IOperationsStatService operationsStatService, IWellService wellService)
{
this.operationsStatService = operationsStatService;
this.wellService = wellService;
}
public async Task<Stream> MakeReportAsync(int idWell, CancellationToken token = default)
{
var tvd = await operationsStatService.GetTvdAsync(idWell, token);
if (!tvd.Any())
return null;
var well = await wellService.GetAsync(idWell, token);
var ecxelTemplateStream = GetExcelTemplateStream();
using var workbook = new XLWorkbook(ecxelTemplateStream, XLEventTracking.Disabled);
FillScheduleSheetToWorkbook(workbook, tvd, well);
FillTvdSheetToWorkbook(workbook, tvd, well);
MemoryStream memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private void FillScheduleSheetToWorkbook(XLWorkbook workbook, IEnumerable<PlanFactPredictBase<WellOperationDto>> tvd, WellDto well)
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNameSchedule);
if (sheet is null)
return;
const int headerRowsCount = 6;
const int rowTitle = 3;
const int columnRowNumber = 2;
const int columnCaption = 3;
const int columnWellDepthPlan = 4;
const int columnWellDepthFact = 5;
const int columnWellDepthPredict = 6;
const int columnDeltaWellDepthPerDay = 7;
const int columnDurationPlan = 8;
const int columnDurationFact = 9;
const int columnDurationPredict = 10;
const int columnDateEndPlan = 11;
const int columnDateEndFact = 12;
const int columnDateEndPredict = 13;
const int columnGuilty = 14;
const int columnNpt = 15;
var subTitle = $"на строительство скважины №{well.Caption}, куст: {well.Cluster}, м/р: {well.Deposit}";
sheet.Row(rowTitle).Cell(3).Value = subTitle;
var tvdList = tvd.ToList();
double dayStartDepth = 0d;
double dayNum = 0;
int i = 0;
for (; i < tvdList.Count; i++)
{
var tvdItem = tvdList[i];
var operation = tvdItem.Fact ?? tvdItem.Plan;
if (operation is null)
continue;
var row = sheet.Row(1 + i + headerRowsCount);
SetCell(row, columnRowNumber, $"{1 + i}");
SetCell(row, columnCaption, $"{operation.CategoryName} {operation.CategoryInfo}".Trim());
SetCell(row, columnWellDepthPlan, tvdItem.Plan?.DepthEnd);
SetCell(row, columnWellDepthFact, tvdItem.Fact?.DepthEnd);
SetCell(row, columnWellDepthPredict, tvdItem.Predict?.DepthEnd);
SetCell(row, columnDeltaWellDepthPerDay, null);
if (tvdItem.Fact is not null)
{
if (dayStartDepth == 0d)
{
dayStartDepth = tvdItem.Fact.DepthStart;
dayNum = tvdItem.Fact.DateStart.DayOfYear;
}
if (i > 0 && tvdItem.Fact.DateStart.DayOfYear > dayNum)
{
double? delta = tvdItem.Fact.DepthStart - dayStartDepth;
delta = delta > 0 ? delta : null;
SetCell(sheet.Row(0 + i + headerRowsCount), columnDeltaWellDepthPerDay, delta);
dayStartDepth = tvdItem.Fact.DepthStart;
dayNum = tvdItem.Fact.DateStart.DayOfYear;
}
}
SetCell(row, columnDurationPlan, tvdItem.Plan?.DurationHours);
SetCell(row, columnDurationFact, tvdItem.Fact?.DurationHours);
SetCell(row, columnDurationPredict, tvdItem.Predict?.DurationHours);
SetCell(row, columnDateEndPlan, tvdItem.Plan?.DateStart.AddHours(tvdItem.Plan?.DurationHours ?? 0));
SetCell(row, columnDateEndFact, tvdItem.Fact?.DateStart.AddHours(tvdItem.Fact?.DurationHours ?? 0));
SetCell(row, columnDateEndPredict, tvdItem.Predict?.DateStart.AddHours(tvdItem.Predict?.DurationHours ?? 0));
if (tvdItem.Fact?.IdCategory == WellOperationService.idOperationNonProductiveTime)
{
SetCell(row, columnGuilty, tvdItem.Fact.Comment);
SetCell(row, columnNpt, tvdItem.Fact.DurationHours);
row.Row(columnRowNumber, columnNpt).Style.Fill.BackgroundColor = XLColor.Red;
}
else
{
SetCell(row, columnGuilty, null);
SetCell(row, columnNpt, null);
}
}
var rowNumSummary = 1 + i + headerRowsCount;
var rowNumStart = 1 + headerRowsCount;
var rowNumEnd = i + headerRowsCount;
string MakeRangeFunction(string funcName, int column)
=> $"={funcName}({GetColunmLetter(column)}{rowNumStart}:{GetColunmLetter(column)}{rowNumEnd})";
IXLCell AddRangeFormula(IXLRow row, string funcName, int column)
{
var cell = row.Cell(column);
cell.FormulaA1 = MakeRangeFunction(funcName, column);
return cell;
}
var rowSummary = sheet.Row(rowNumSummary);
rowSummary.Style.Font.Bold = true;
rowSummary.Cell(columnCaption).Value = "Итого:";
AddRangeFormula(rowSummary, "sum", columnDeltaWellDepthPerDay);
AddRangeFormula(rowSummary, "sum", columnDurationPlan);
AddRangeFormula(rowSummary, "sum", columnDurationFact);
var cell = AddRangeFormula(rowSummary, "max", columnDateEndPlan);
SetDateTime(cell);
cell = AddRangeFormula(rowSummary, "max", columnDateEndFact);
SetDateTime(cell);
AddRangeFormula(rowSummary, "sum", columnNpt);
SetBorder(rowSummary.Cells(true).Style);
var rowSummary2 = sheet.Row(rowNumSummary + 1);
rowSummary2.DataType = XLDataType.Number;
rowSummary2.Style.NumberFormat.Format = "0,00";
rowSummary2.Cell(columnCaption).Value = "в сутках:";
rowSummary2.Cell(columnDurationPlan).FormulaA1 = $"={GetColunmLetter(columnDurationPlan)}{rowNumSummary}/24";
SetNumber(rowSummary2.Cell(columnDurationPlan));
rowSummary2.Cell(columnDurationFact).FormulaA1 = $"={GetColunmLetter(columnDurationFact)}{rowNumSummary}/24";
SetNumber(rowSummary2.Cell(columnDurationFact));
rowSummary2.Cell(columnNpt).FormulaA1 = $"={GetColunmLetter(columnNpt)}{rowNumSummary}/24";
SetNumber(rowSummary2.Cell(columnNpt));
SetBorder(rowSummary2.Cells(true).Style);
}
private void FillTvdSheetToWorkbook(XLWorkbook workbook, IEnumerable<PlanFactPredictBase<WellOperationDto>> tvd, WellDto well)
{
var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetNameTvd);
if (sheet is null)
return;
const int rowTitle = 2;
const int rowSubtitle = 3;
const int colTitle = 5;
const int rowTopStatTitle = 2;
const int colTopStatvalue = 10;
const int colBottomStatvalue = 3;
const int rowStartDateFact = 43;
const int rowEndDatePlan = 44;
const int rowEndDateFact = 45;
sheet.Row(rowSubtitle).Cell(colTitle).Value
= $"скважины №{well.Caption}, куст: {well.Cluster}, м/р: {well.Deposit}";
SetCell(sheet.Row(rowTitle), colTopStatvalue, DateTime.Now);
var Plan = tvd.Where(t => t.Plan is not null)
.Select(t => t.Plan);
var Fact = tvd.Where(t => t.Fact is not null)
.Select(t => t.Fact);
var Predict = tvd.Where(t => t.Predict is not null)
.Select(t => t.Predict);
var startDateFact = Fact.FirstOrDefault()?.DateStart;
var planLast = Plan.LastOrDefault();
var factLast = Fact.LastOrDefault();
var predictLast = Predict.LastOrDefault();
DateTime GetEndDate(WellOperationDto operation)
=> operation is not null
? operation.DateStart.AddHours(operation.DurationHours)
: default;
var endDatePlan = GetEndDate(planLast);
var endDateFact = GetEndDate(factLast);
var endDatePredict = GetEndDate(predictLast);
var endDate = endDatePredict > endDateFact
? endDatePredict
: endDateFact;
if(startDateFact is not null)
{
SetCell(sheet.Row(rowStartDateFact), colBottomStatvalue, startDateFact);
SetCell(sheet.Row(rowEndDatePlan), colBottomStatvalue, endDatePlan);
SetCell(sheet.Row(rowEndDateFact), colBottomStatvalue, endDate);
if(endDate != default)
{
var deltaEndDate = (endDatePlan - endDate).TotalDays;
SetCell(sheet.Row(rowTopStatTitle + 1), colTopStatvalue, Math.Abs(deltaEndDate));
if(deltaEndDate >= 0)
SetCell(sheet.Row(rowTopStatTitle + 1), colTopStatvalue - 1, "+")
.Style.Font.SetFontColor(XLColor.Green);
else
SetCell(sheet.Row(rowTopStatTitle + 1), colTopStatvalue - 1, "-")
.Style.Font.SetFontColor(XLColor.Red);
}
}
}
private static string GetColunmLetter(int columnNumber)
{
string letter = "";
while (columnNumber > 0)
{
int modulo = (columnNumber - 1) % 26;
letter = Convert.ToChar('A' + modulo) + letter;
columnNumber = (columnNumber - modulo) / 26;
}
return letter;
}
private static IXLStyle SetBorder(IXLStyle style)
{
style.Border.RightBorder = XLBorderStyleValues.Thin;
style.Border.LeftBorder = XLBorderStyleValues.Thin;
style.Border.TopBorder = XLBorderStyleValues.Thin;
style.Border.BottomBorder = XLBorderStyleValues.Thin;
style.Border.InsideBorder = XLBorderStyleValues.Thin;
return style;
}
private static IXLCell SetDateTime(IXLCell cell)
{
cell.DataType = XLDataType.DateTime;
cell.Style.DateFormat.Format = "DD.MM.YYYY HH:MM:SS";
return cell;
}
private static IXLCell SetNumber(IXLCell cell)
{
cell.DataType = XLDataType.Number;
cell.Style.NumberFormat.Format = "0.00";
return cell;
}
private static IXLCell SetCell(IXLRow row, int colunm, object value)
{
var cell = row.Cell(colunm);
cell.Value = value;
SetBorder(cell.Style);
cell.Style.Alignment.WrapText = true;
if (value is string valueString && valueString.Length > maxChartsToWrap)
{
var baseHeight = row.Height;
row.Height = Math.Ceiling(0.85d * valueString.Length / maxChartsToWrap) * baseHeight;
}
if (value is DateTime)
{
SetDateTime(cell);
}
else if (value is IFormattable)
{
SetNumber(cell);
}
return cell;
}
private static Stream GetExcelTemplateStream()
{
var stream = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream("AsbCloudInfrastructure.Services.WellOperationService.ScheduleReportTemplate.xlsx");
return stream;
}
}
}