From 53e12e414d008cba135381981940d2ef9a2af36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Tue, 1 Jul 2025 07:53:53 +0200 Subject: [PATCH] R6 GSheet export --- .../Processors/MorskaD6Processor.cs | 236 +++++++++++++++++- .../Controllers/LayersController.cs | 6 - .../admin-monthly/CreateImportWorker.sql | 26 +- .../CreateProcessWorker-T5LastValue.sql | 6 +- 4 files changed, 263 insertions(+), 11 deletions(-) diff --git a/src/Backend/DiunaBI.Plugins.Morska/Processors/MorskaD6Processor.cs b/src/Backend/DiunaBI.Plugins.Morska/Processors/MorskaD6Processor.cs index 79791a6..069d94f 100644 --- a/src/Backend/DiunaBI.Plugins.Morska/Processors/MorskaD6Processor.cs +++ b/src/Backend/DiunaBI.Plugins.Morska/Processors/MorskaD6Processor.cs @@ -122,6 +122,8 @@ public class MorskaD6Processor : MorskaBaseProcessor SaveProcessedLayer(processedLayer, newRecords); + UpdateGoogleSheetReport(processedLayer.Id); + _logger.LogInformation( "{ProcessorType}: Successfully processed {RecordCount} records for layer {LayerName} ({LayerId})", ProcessorType, newRecords.Count, processedLayer.Name, processedLayer.Id); @@ -199,7 +201,7 @@ public class MorskaD6Processor : MorskaBaseProcessor // L8542-D-DEPARTMENTS var dictionary = _db.Layers.Include(x => x.Records).FirstOrDefault(x => x.Number == 8542); - + var departmentLookup = new Dictionary(); if (dictionary?.Records != null) { @@ -374,4 +376,236 @@ public class MorskaD6Processor : MorskaBaseProcessor return string.IsNullOrEmpty(originalDepartment) ? "OTHER" : originalDepartment; } } + + private void UpdateGoogleSheetReport(Guid sourceId) + { + const string googleSheetName = "Raport_R6_DRAFT_2025"; + try + { + const string sheetId = "19AljwrZRg2Rc5hagkfK3u8LIo3x9_GmFNnZEeOJ5g_g"; + + _logger.LogDebug("{ProcessorType}: Updating Google Sheet report {SheetName}", + ProcessorType, googleSheetName); + + // Get processed layer data + var processedLayer = _db.Layers + .Where(x => x.Id == sourceId) + .Include(x => x.Records) + .AsNoTracking() + .FirstOrDefault(); + + if (processedLayer == null) + { + throw new InvalidOperationException($"Processed layer {sourceId} not found"); + } + + // Get codes from sheet header (row 4, columns C to AA) + var codesResponse = _googleSheetValues.Get(sheetId, $"{googleSheetName}!C4:AA4").Execute(); + var codesRow = codesResponse.Values[0]; + + // Update data based on 6-digit codes from processedLayer + UpdateMonthlyDataFromCodes(sheetId, codesRow, processedLayer); + Thread.Sleep(1000); + + // Update yearly summary data (row 20) + UpdateYearlySummaryData(sheetId, codesRow, processedLayer); + Thread.Sleep(1000); + + // Update timestamps + UpdateTimestamps(sheetId, processedLayer); + Thread.Sleep(1000); + + _logger.LogInformation("{ProcessorType}: Successfully updated Google Sheet report {SheetName}", + ProcessorType, googleSheetName); + } + catch (Exception e) + { + _logger.LogError(e, "{ProcessorType}: Failed to update Google Sheet report {SheetName}", + ProcessorType, googleSheetName); + throw; + } + } + + private void UpdateYearlySummaryData(string sheetId, IList codesRow, Layer processedLayer) + { + const string googleSheetName = "Raport_R6_DRAFT_2025"; + + if (processedLayer.Records == null) + { + _logger.LogWarning("{ProcessorType}: No records found in processed layer for yearly summary", ProcessorType); + return; + } + + var summaryValues = new List(); + + foreach (string fourDigitCode in codesRow) + { + // Calculate sum for all months (01-12) for this 4-digit code + double yearlySum = 0; + + for (var month = 1; month <= 12; month++) + { + var sixDigitCode = $"{fourDigitCode}{month:D2}"; + var record = processedLayer.Records.FirstOrDefault(x => x.Code == sixDigitCode); + + if (record?.Value1.HasValue == true) + { + yearlySum += record.Value1.Value; + } + } + + summaryValues.Add(yearlySum.ToString(CultureInfo.GetCultureInfo("pl-PL"))); + + _logger.LogDebug("{ProcessorType}: Calculated yearly sum for code {FourDigitCode}: {YearlySum}", + ProcessorType, fourDigitCode, yearlySum); + } + + // Update row 20 with yearly sums + var valueRange = new ValueRange + { + Values = new List> { summaryValues } + }; + + var columnStart = "C"; + var columnEnd = GetColumnLetter(codesRow.Count + 1); // +1 because we start from C (index 2) + var range = $"{googleSheetName}!{columnStart}20:{columnEnd}20"; + + var update = _googleSheetValues.Update(valueRange, sheetId, range); + update.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.USERENTERED; + update.Execute(); + + _logger.LogInformation("{ProcessorType}: Updated yearly summary data in row 20 with {CodeCount} totals", + ProcessorType, codesRow.Count); + } + + private void UpdateMonthlyDataFromCodes(string sheetId, IList codesRow, Layer processedLayer) + { + const string googleSheetName = "Raport_R6_DRAFT_2025"; + + if (processedLayer.Records == null) + { + _logger.LogWarning("{ProcessorType}: No records found in processed layer", ProcessorType); + return; + } + + // Create a dictionary to store updates for each cell + var cellUpdates = new Dictionary(); + + foreach (var record in processedLayer.Records) + { + if (string.IsNullOrEmpty(record.Code) || record.Code.Length != 6) + { + _logger.LogWarning("{ProcessorType}: Invalid code format in record {RecordId}: {Code}", + ProcessorType, record.Id, record.Code); + continue; + } + + // Extract 4-digit code and month from 6-digit code + var fourDigitCode = record.Code.Substring(0, 4); + var monthStr = record.Code.Substring(4, 2); + + if (!int.TryParse(monthStr, out var month) || month < 1 || month > 12) + { + _logger.LogWarning("{ProcessorType}: Invalid month in code {Code}", ProcessorType, record.Code); + continue; + } + + // Find column index for the 4-digit code + var columnIndex = -1; + for (var i = 0; i < codesRow.Count; i++) + { + if (codesRow[i]?.ToString() == fourDigitCode) + { + columnIndex = i; + break; + } + } + + if (columnIndex == -1) + { + _logger.LogWarning("{ProcessorType}: Code {FourDigitCode} not found in sheet header", + ProcessorType, fourDigitCode); + continue; + } + + // Calculate row (month 01 = row 7, month 02 = row 8, etc.) + var targetRow = 6 + month; // row 7 for month 01, row 8 for month 02, etc. + + // Calculate column letter (C = index 0, D = index 1, etc.) + var targetColumn = GetColumnLetter(columnIndex + 2); // +2 because C is the first column (index 0) + + var cellAddress = $"{targetColumn}{targetRow}"; + var value = record.Value1?.ToString(CultureInfo.GetCultureInfo("pl-PL")) ?? "0"; + + cellUpdates[cellAddress] = value; + + _logger.LogDebug( + "{ProcessorType}: Mapping code {SixDigitCode} (base: {FourDigitCode}, month: {Month}) to cell {CellAddress} with value {Value}", + ProcessorType, record.Code, fourDigitCode, month, cellAddress, value); + } + + // Batch update all cells + if (cellUpdates.Any()) + { + var batchUpdateRequest = new BatchUpdateValuesRequest + { + ValueInputOption = "USER_ENTERED", + Data = cellUpdates.Select(kvp => new ValueRange + { + Range = $"{googleSheetName}!{kvp.Key}", + Values = new List> { new List { kvp.Value } } + }).ToList() + }; + + var batchUpdate = _googleSheetValues.BatchUpdate(batchUpdateRequest, sheetId); + batchUpdate.Execute(); + + _logger.LogInformation("{ProcessorType}: Updated {CellCount} cells in Google Sheet", + ProcessorType, cellUpdates.Count); + } + else + { + _logger.LogWarning("{ProcessorType}: No valid data found to update in Google Sheet", ProcessorType); + } + } + + private string GetColumnLetter(int columnIndex) + { + var columnLetter = string.Empty; + while (columnIndex >= 0) + { + columnLetter = (char)('A' + (columnIndex % 26)) + columnLetter; + columnIndex = columnIndex / 26 - 1; + } + + return columnLetter; + } + + private void UpdateTimestamps(string sheetId, Layer processedLayer) + { + const string googleSheetName = "Raport_R6_DRAFT_2025"; + + var timeUtc = processedLayer.ModifiedAt.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.GetCultureInfo("pl-PL")); + + var warsawTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"); + var warsawTime = TimeZoneInfo.ConvertTimeFromUtc(processedLayer.ModifiedAt.ToUniversalTime(), warsawTimeZone); + var timeWarsaw = warsawTime.ToString("dd.MM.yyyy HH:mm:ss", CultureInfo.GetCultureInfo("pl-PL")); + + var valueRangeTime = new ValueRange + { + Values = new List> + { + new List { timeUtc }, + new List { timeWarsaw } + } + }; + + var updateTime = _googleSheetValues.Update(valueRangeTime, sheetId, $"{googleSheetName}!G1:G2"); + updateTime.ValueInputOption = + SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.USERENTERED; + updateTime.Execute(); + + _logger.LogDebug("{ProcessorType}: Updated timestamps in Google Sheet - UTC: {TimeUtc}, Warsaw: {TimeWarsaw}", + ProcessorType, timeUtc, timeWarsaw); + } } \ No newline at end of file diff --git a/src/Backend/DiunaBI.WebAPI/Controllers/LayersController.cs b/src/Backend/DiunaBI.WebAPI/Controllers/LayersController.cs index 1dd9b01..8fccbbf 100644 --- a/src/Backend/DiunaBI.WebAPI/Controllers/LayersController.cs +++ b/src/Backend/DiunaBI.WebAPI/Controllers/LayersController.cs @@ -70,7 +70,6 @@ public class LayersController : Controller return BadRequest(e.ToString()); } } - [HttpGet] [Route("{id:guid}")] public IActionResult Get(Guid id) @@ -91,7 +90,6 @@ public class LayersController : Controller return BadRequest(e.ToString()); } } - [HttpGet] [Route("getForPowerBI/{apiKey}/{number:int}")] public IActionResult GetByNumber(string apiKey, int number) @@ -140,7 +138,6 @@ public class LayersController : Controller return BadRequest(e.ToString()); } } - [HttpGet] [Route("getConfiguration/{apiKey}/{number:int}")] public IActionResult GetConfigurationByNumber(string apiKey, int number) @@ -202,7 +199,6 @@ public class LayersController : Controller return BadRequest(); } } - [HttpGet] [Route("exportToGoogleSheet/{id:guid}")] public IActionResult ExportToGoogleSheet(Guid id) @@ -237,7 +233,6 @@ public class LayersController : Controller throw; } } - [HttpGet] [Route("ProcessQueue/{apiKey}")] [AllowAnonymous] @@ -253,7 +248,6 @@ public class LayersController : Controller // Queue processing implementation would go here return Ok(); } - [HttpGet] [Route("AutoImport/{apiKey}/{nameFilter}")] [AllowAnonymous] diff --git a/tools/sql-scripts/admin-monthly/CreateImportWorker.sql b/tools/sql-scripts/admin-monthly/CreateImportWorker.sql index b9a276a..924dcab 100644 --- a/tools/sql-scripts/admin-monthly/CreateImportWorker.sql +++ b/tools/sql-scripts/admin-monthly/CreateImportWorker.sql @@ -1,10 +1,10 @@ DECLARE @JustForDebug TINYINT = 0; -- SETUP VARIABLES -DECLARE @Type NVARCHAR(3) = 'K5'; -DECLARE @Month INT = 6; +DECLARE @Type NVARCHAR(3) = 'D1'; +DECLARE @Month INT = 5; DECLARE @Year INT = 2025; -DECLARE @MonthName NVARCHAR(20) = 'Czerwiec_2025'; +DECLARE @MonthName NVARCHAR(20) = 'Maj_2025'; IF @Type NOT IN ('K5', 'PU', 'AK', 'FK', 'D1', 'FK2') BEGIN @@ -50,6 +50,22 @@ BEGIN SELECT 'SheetId is NULL' AS Logger; RETURN; END; +DECLARE @Plugin NVARCHAR(100); +SET @Plugin = + CASE @Type + WHEN 'K5' THEN 'Morska.Process.T3.SingleSource' + WHEN 'PU' THEN 'Morska.Process.T3.SingleSource' + WHEN 'AK' THEN 'Morska.Process.T3.SingleSource' + WHEN 'FK' THEN 'Morska.Process.T3.SingleSource' + WHEN 'D1' THEN 'Morska.Import.D1' + WHEN 'FK2' THEN 'Morska.Import.FK2' + ELSE NULL -- If @Type doesn't match, set it to NULL + END; +IF @SheetId IS NULL + BEGIN + SELECT 'SheetId is NULL' AS Logger; + RETURN; + END; DECLARE @LayerId UNIQUEIDENTIFIER = NEWID(); SELECT @Name AS Name,@Range AS Range, @ImportType AS ImportType, @StartDate AS StartDate, @EndDate AS EndDate; @@ -105,6 +121,10 @@ INSERT INTO [diunabi-morska].[dbo].[Records] ([Id], [Code], [Desc1], [CreatedAt], [ModifiedAt], [CreatedById], [ModifiedById], [IsDeleted], [LayerId]) VALUES ((SELECT NEWID()), 'Type', 'ImportWorker', GETDATE(), GETDATE(), '117be4f0-b5d1-41a1-a962-39dc30cce368', '117be4f0-b5d1-41a1-a962-39dc30cce368', 0, @LayerId); +INSERT INTO [diunabi-morska].[dbo].[Records] +([Id], [Code], [Desc1], [CreatedAt], [ModifiedAt], [CreatedById], [ModifiedById], [IsDeleted], [LayerId]) +VALUES ((SELECT NEWID()), 'Plugin', @Plugin, GETDATE(), GETDATE(), '117be4f0-b5d1-41a1-a962-39dc30cce368', '117be4f0-b5d1-41a1-a962-39dc30cce368', 0, @LayerId); + INSERT INTO [diunabi-morska].[dbo].[Records] ([Id], [Code], [Desc1], [CreatedAt], [ModifiedAt], [CreatedById], [ModifiedById], [IsDeleted], [LayerId]) VALUES ((SELECT NEWID()), 'IsEnabled', 'True', GETDATE(), GETDATE(), '117be4f0-b5d1-41a1-a962-39dc30cce368', '117be4f0-b5d1-41a1-a962-39dc30cce368', 0, @LayerId); diff --git a/tools/sql-scripts/admin-monthly/CreateProcessWorker-T5LastValue.sql b/tools/sql-scripts/admin-monthly/CreateProcessWorker-T5LastValue.sql index 2ee8fa3..da9af1c 100644 --- a/tools/sql-scripts/admin-monthly/CreateProcessWorker-T5LastValue.sql +++ b/tools/sql-scripts/admin-monthly/CreateProcessWorker-T5LastValue.sql @@ -2,7 +2,7 @@ DECLARE @JustForDebug TINYINT = 0; -- SETUP VARIABLES -DECLARE @Month INT = 4; +DECLARE @Month INT = 5; DECLARE @Year INT = 2025; DECLARE @Number INT = (SELECT COUNT(id) + 1 FROM [diunabi-morska].[dbo].[Layers]); @@ -50,6 +50,10 @@ INSERT INTO [diunabi-morska].[dbo].[Records] ([Id], [Code], [Desc1], [CreatedAt], [ModifiedAt], [CreatedById], [ModifiedById], [IsDeleted], [LayerId]) VALUES ((SELECT NEWID()), 'ProcessType', 'T5-LastValues', GETDATE(), GETDATE(), '117be4f0-b5d1-41a1-a962-39dc30cce368', '117be4f0-b5d1-41a1-a962-39dc30cce368', 0, @LayerId); +INSERT INTO [diunabi-morska].[dbo].[Records] +([Id], [Code], [Desc1], [CreatedAt], [ModifiedAt], [CreatedById], [ModifiedById], [IsDeleted], [LayerId]) +VALUES ((SELECT NEWID()), 'Plugin', 'Morska.Process.T5.LastValues', GETDATE(), GETDATE(), '117be4f0-b5d1-41a1-a962-39dc30cce368', '117be4f0-b5d1-41a1-a962-39dc30cce368', 0, @LayerId); + INSERT INTO [diunabi-morska].[dbo].[Records] ([Id], [Code], [Desc1], [CreatedAt], [ModifiedAt], [CreatedById], [ModifiedById], [IsDeleted], [LayerId]) VALUES ((SELECT NEWID()), 'IsEnabled', 'True', GETDATE(), GETDATE(), '117be4f0-b5d1-41a1-a962-39dc30cce368', '117be4f0-b5d1-41a1-a962-39dc30cce368', 0, @LayerId);