using ClosedXML.Excel; using DocumentFormat.OpenXml.Drawing.Charts; using DocumentFormat.OpenXml.Spreadsheet; using paperless_ngx_export.Models; using RBush; using System; using System.Collections.Generic; using System.Text; namespace paperless_ngx_export { public static class spreadsheet { public static async Task GetSummarySpreadsheetAsByteArrayAsync() { byte[] bytes = null; using (var stream = new MemoryStream()) { var workbook = await GetSummaryAsXLWorkbookAsync(); workbook.SaveAs(stream); bytes = stream.ToArray(); stream.Close(); } return bytes ?? new byte[0]; } public static void PopulateDocumentTypeWorksheets( IEnumerable documentTypes, IEnumerable tags, IEnumerable customFields, IEnumerable docs, XLWorkbook workbook, bool onlyCurrentDocuments = true) { var headings = new string[] { "Correspondent", "Role", "Specialty", "Document Type", "Expiration Date", "Tags" }; var documents = docs.Where(x => { if (!x.CustomFields.Any(xx => xx.Name.Contains("expir", StringComparison.OrdinalIgnoreCase))) return true; return onlyCurrentDocuments ? !x.Tags.Any(xx => xx.name.Contains("renewed", StringComparison.OrdinalIgnoreCase)) : true; }).ToList(); foreach (var documentType in documentTypes.OrderBy(x=>!documents.Any(d=>d.DocumentType == x.name && d.CustomFields.Any(cf=>cf.Name.Contains("expir", StringComparison.OrdinalIgnoreCase)))). ThenBy(x=>x.name)) { var sheetName = documentType.name.Replace('/', '-'); var sheet = workbook.Worksheets.FirstOrDefault(x => x.Name == sheetName); if (sheet == null) { sheet = workbook.Worksheets.Add(sheetName); sheet.TabColor = XLColor.Green; for (int i = 0; i < headings.Length; i++) { sheet.Cell(1, i + 1).Value = headings[i]; } sheet.SheetView.Freeze(1, 2); sheet.SetAutoFilter(true); sheet.Cells("A1:ZZ1").Style.Font.Bold = true; } foreach (var document in documents.Where(x => x.DocumentType == documentType.name).OrderBy(x => x.Correspondent)) { var rowColour = XLColor.Transparent; var expirationDate = document.CustomFields.FirstOrDefault(x => x.Name.Contains("expir", StringComparison.OrdinalIgnoreCase))?.DateValue; if (expirationDate != null && expirationDate <= DateTime.Today) { rowColour = XLColor.Red; } else if (expirationDate != null && expirationDate?.AddDays(-30) < DateTime.Today) { rowColour = XLColor.Orange; } if(sheet.TabColor == XLColor.Green && rowColour != XLColor.Transparent) { sheet.TabColor = sheet.TabColor == XLColor.Red ? XLColor.Red : rowColour; } var rowIndex = sheet.LastRowUsed()?.RowNumber() + 1 ?? 2; sheet.Cell(rowIndex, 1).Value = document.Correspondent; sheet.Cell(rowIndex, 2).Value = document.CustomFields.FirstOrDefault(x=>x.Name == "Role")?.StringValue; sheet.Cell(rowIndex, 3).Value = document.CustomFields.FirstOrDefault(x => x.Name == "Specialty")?.StringValue; sheet.Cell(rowIndex, 4).Value = document.DocumentType; sheet.Cell(rowIndex, 5).Value = expirationDate == null ? Blank.Value : expirationDate; sheet.Cell(rowIndex, 5).Style.Fill.BackgroundColor = rowColour == XLColor.Transparent ? sheet.Cell(rowIndex, 5).Style.Fill.BackgroundColor : rowColour; sheet.Cell(rowIndex, 6).Value = String.Concat(document.Tags.Select(x=>x.name + ' ').ToArray()).Trim(); } sheet.Columns().AdjustToContents(); } } public static void PopulateSummaryWorksheet( IEnumerable documentTypes, IEnumerable tags, IEnumerable customFields, IEnumerable docs, XLWorkbook workbook, bool onlyCurrentDocuments = true) { var headers = new string[] { "Correspondent", "Role (Specialty)", "Professional Registration", "Liability Insurance", "ACLS", "BLS", "PALS", "Vaccination Record", "CV / Resumé", "Misc Certificate", "Credentialing / Privileges", "Reference Letter" }; var documents = docs.Where(x => { if (!x.CustomFields.Any(xx => xx.Name.Contains("expir", StringComparison.OrdinalIgnoreCase))) return true; return onlyCurrentDocuments ? !x.Tags.Any(xx => xx.name.Contains("renewed", StringComparison.OrdinalIgnoreCase)) : true; }).ToList(); var summarySheet = workbook.Worksheets.Add("Summary"); summarySheet.SheetView.Freeze(1, 2); //Freeze header row and, correspondent and role columns for (int i = 1; i <= headers.Length; i++) { summarySheet.Cell(1, i).Value = headers[i - 1]; } summarySheet.Cells("A1:ZZ1").Style.Font.Bold = true; summarySheet.SetAutoFilter(); var row = 2; foreach (var correspondent in documents.Select(x => x.Correspondent).Distinct()) { summarySheet.Cell(row, 1).Value = correspondent; foreach (var doc in documents.Where(x => x.Correspondent == correspondent)) { if (summarySheet.Cell(row, 2).Value.Type == XLDataType.Blank) { var role = doc.CustomFields.FirstOrDefault(x => x.Name == "Role")?.Value?.ToString() ?? ""; var specialty = doc.CustomFields.FirstOrDefault(x => x.Name == "Specialty")?.Value?.ToString() ?? ""; role = $"{role} {(!string.IsNullOrWhiteSpace(specialty) ? $"({specialty})" : "")}".Trim(); summarySheet.Cell(row, 2).Value = role; } if (headers.IndexOf(doc.DocumentType.ToString()) + 1 is int column && column > 0) { XLCellValue cellValue = summarySheet.Cell(row, column).Value; if (!cellValue.IsBlank) { continue; } if (doc.DocumentType == "Reference Letter") { cellValue = documents.Where(x => x.Correspondent == correspondent && x.DocumentType == "Reference Letter").Count(); ; } else if (doc.CustomFields.FirstOrDefault(x => x.Name == "Expiration Date") is CustomFieldParsed customFieldParsed && customFieldParsed.DateValue > DateTime.MinValue && customFieldParsed.DateValue < DateTime.MaxValue) { cellValue = XLCellValue.FromObject(customFieldParsed.DateValue); } else if (!doc.CustomFields.Any(x => x.Name == "Expiration Date")) { cellValue = "Present"; } summarySheet.Cell(row, column).Value = cellValue; } } row++; } summarySheet.Columns().AdjustToContents(); } public static async Task GetSummaryAsXLWorkbookAsync() { var documenttypes = await api.getDocumentTypesAsync(); var tags = await api.getTagsAsync(); var customFields = await api.getCustomFieldsAsync(); var documents = (await api.getMergedDocumentMetadataAsync(false, true)).Where(x => !x.CustomFields.Any(xx => xx.Name == "Action:Renewed")).OrderBy(x => x.Correspondent).ToList(); var workbook = new XLWorkbook(); PopulateSummaryWorksheet(documenttypes, tags, customFields, documents, workbook, true); PopulateDocumentTypeWorksheets(documenttypes, tags, customFields, documents, workbook, true); return workbook; } } }