using System.Globalization; using DiunaBIWebAPI.dataImporters; using Google.Apis.Sheets.v4; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using WebAPI.dataProcessors; using WebAPI.Exports; using WebAPI.Models; namespace WebAPI.Controllers { [ApiController] [Route("api/[controller]")] public class LayersController : Controller { private readonly AppDbContext db; private SpreadsheetsResource.ValuesResource? googleSheetValues; private GoogleDriveHelper googleDriveHelper; private GoogleSheetsHelper googleSheetsHelper; private readonly IConfiguration configuration; private readonly LogsController logsController; public LayersController( AppDbContext _db, GoogleSheetsHelper _googleSheetsHelper, GoogleDriveHelper _googleDriveHelper, IConfiguration _configuration) { db = _db; if (_googleSheetsHelper.Service is not null) { googleSheetValues = _googleSheetsHelper.Service.Spreadsheets.Values; } googleSheetsHelper = _googleSheetsHelper; googleDriveHelper = _googleDriveHelper; configuration = _configuration; logsController = new LogsController(googleSheetsHelper, googleDriveHelper, configuration); } [HttpGet] public IActionResult GetAll(int start, int limit, string? name, LayerType? type) { try { IQueryable response = db.Layers.Where(x => !x.IsDeleted); if (name != null) { response = response.Where(x => x.Name != null && x.Name.Contains(name)); } if (type != null) { response = response.Where(x => x.Type == type); } return Ok(response .OrderByDescending(x => x.Number) .Skip(start).Take(limit).ToList()); } catch (Exception e) { return BadRequest(e.ToString()); } } [HttpPost] public IActionResult Save(Layer input) { try { //Request.Headers.TryGetValue("userId", out var value); //Guid currentUserId = new Guid(value!); return Ok(); } catch (Exception e) { return BadRequest(e.ToString()); } } [HttpGet] [Route("{id}")] public IActionResult Get(Guid id) { try { return Ok(db.Layers .Include(x => x.CreatedBy) .Include(x => x.Records) .Where(x => x.Id == id && !x.IsDeleted).First()); } catch (Exception e) { return BadRequest(e.ToString()); } } [HttpGet] [Route("getByNumber/{apiKey}/{number}")] public IActionResult GetByNumber(string apiKey, int number) { if (apiKey != configuration["apiKey"]) { return Unauthorized(); } try { return Ok(db.Layers .Include(x => x.CreatedBy) .Include(x => x.Records) .Where(x => x.Number == number && !x.IsDeleted).First()); } catch (Exception e) { return BadRequest(e.ToString()); } } [HttpGet] [Route("exportToGoogleSheet/{id}")] public IActionResult ExportToGoogleSheet(Guid id) { if (googleSheetValues is null) { throw new Exception("Google Sheets API not initialized"); } Layer layer = db.Layers .Include(x => x.Records!.OrderByDescending(x => x.Code)) .Where(x => x.Id == id && !x.IsDeleted).First(); var export = new googleSheetExport(googleDriveHelper, googleSheetValues, configuration); export.export(layer); return Ok(true); } [HttpGet] [Route("AutoImport/{apiKey}")] [AllowAnonymous] public IActionResult AutoImport(string apiKey) { if (Request.Host.Value != configuration["apiLocalUrl"] || apiKey != configuration["apiKey"]) { return Unauthorized(); } if (googleSheetValues is null) { throw new Exception("Google Sheets API not initialized"); } try { List importWorkerLayers; importWorkerLayers = db.Layers .Include(x => x.Records) .Where(x => x.Records!.Any(x => x.Code == "Type" && x.Desc1 == "ImportWorker") && x.Records!.Any(x => x.Code == "IsEnabled" && x.Desc1 == "True") ) .OrderBy(x => x.CreatedAt) .ToList(); if (importWorkerLayers.Count() == 0) { logsController.AddEntry(new LogEntry { Title = "No Layers to import.", Type = LogEntryType.info, LogType = LogType.import, CreatedAt = DateTime.UtcNow, }); return Ok(); } foreach (Layer importWorker in importWorkerLayers) { try { string? type = importWorker.Records!.FirstOrDefault(x => x.Code == "ImportType")?.Desc1; if (type == null) { type = "Standard"; } if (type == "FK2") { MorskaFk2Importer importer = new MorskaFk2Importer(db, googleSheetValues, this); importer.import(importWorker); logsController.AddEntry(new LogEntry { Title = $"{importWorker!.Name}, {importWorker.Id}", Type = LogEntryType.info, LogType = LogType.import, Message = "Success", CreatedAt = DateTime.UtcNow }); return Ok(); } string? startDate = importWorker.Records!.FirstOrDefault(x => x.Code == "StartDate")?.Desc1; if (startDate == null) { throw new Exception("StartDate record nod found"); } string? endDate = importWorker.Records!.Where(x => x.Code == "EndDate").First().Desc1; if (endDate == null) { throw new Exception("EndDate record nod found"); } var startDateParsed = DateTime.ParseExact(startDate!, "yyyy.MM.dd", null); var endDateParsed = DateTime.ParseExact(endDate!, "yyyy.MM.dd", null); if (startDateParsed.Date <= DateTime.UtcNow.Date && endDateParsed.Date >= DateTime.UtcNow.Date) { MorskaImporter importer = new MorskaImporter(db, googleSheetValues, this); importer.import(importWorker); Thread.Sleep(5000); // be aware of GSheet API quota logsController.AddEntry(new LogEntry { Title = $"{importWorker!.Name}, {importWorker.Id}", Type = LogEntryType.info, LogType = LogType.import, Message = "Success", CreatedAt = DateTime.UtcNow }); } else if (IsImportedLayerUpToDate(importWorker) == false) { MorskaImporter importer = new MorskaImporter(db, googleSheetValues, this); importer.import(importWorker); Thread.Sleep(5000); // be aware of GSheet API quota logsController.AddEntry(new LogEntry { Title = $"{importWorker!.Name}, {importWorker.Id}", Type = LogEntryType.warning, LogType = LogType.import, Message = "Success (reimported)", CreatedAt = DateTime.UtcNow }); } else { logsController.AddEntry(new LogEntry { Title = $"{importWorker!.Name}, {importWorker.Id}", Type = LogEntryType.warning, LogType = LogType.import, Message = "importLayer records are up of date. Not processed.", CreatedAt = DateTime.UtcNow }); } } catch (Exception e) { logsController.AddEntry(new LogEntry { Title = $"{importWorker!.Name}, {importWorker.Id}", Type = LogEntryType.error, LogType = LogType.import, Message = e.ToString(), CreatedAt = DateTime.UtcNow }); } } return Ok(); } catch (Exception e) { logsController.AddEntry(new LogEntry { Title = "Process error", Type = LogEntryType.error, LogType = LogType.import, Message = e.ToString(), CreatedAt = DateTime.UtcNow }); return BadRequest(e.ToString()); } } [HttpGet] [Route("AutoProcess/{apiKey}")] [AllowAnonymous] public IActionResult AutoProcess(string apiKey) { if (Request.Host.Value != configuration["apiLocalUrl"] || apiKey != configuration["apiKey"]) { return Unauthorized(); } if (googleSheetValues is null) { throw new Exception("Google Sheets API not initialized"); } string[] processTypes = new string[] { "T3-SingleSource", "T3-SourceYearSummary", "T3-MultiSourceSummary", // AA "T3-MultiSourceYearSummary", // AA/13 "T3-R1" }; foreach (string type in processTypes) { try { logsController.AddEntry(new LogEntry { Title = $"Processing: {type}", Type = LogEntryType.info, LogType = LogType.process, CreatedAt = DateTime.UtcNow, }); List processWorkerLayers = db.Layers .Include(x => x.Records) .Where(x => x.Records!.Any(x => x.Code == "Type" && x.Desc1 == "ProcessWorker") && x.Records!.Any(x => x.Code == "IsEnabled" && x.Desc1 == "True") && x.Records!.Any(x => x.Code == "ProcessType" && x.Desc1 == type) ) .OrderBy(x => x.CreatedAt) .ToList(); if (processWorkerLayers.Count() == 0) { logsController.AddEntry(new LogEntry { Title = "No Layers to process.", Type = LogEntryType.info, LogType = LogType.process, CreatedAt = DateTime.UtcNow, }); } foreach (Layer processWorker in processWorkerLayers) { try { ProcessLayer(processWorker); } catch (Exception e) { logsController.AddEntry(new LogEntry { Title = $"{processWorker!.Name}, {processWorker.Id}", Type = LogEntryType.error, LogType = LogType.process, Message = e.ToString(), CreatedAt = DateTime.UtcNow }); } } } catch (Exception e) { logsController.AddEntry(new LogEntry { Title = "Process error", Type = LogEntryType.error, LogType = LogType.process, Message = e.ToString(), CreatedAt = DateTime.UtcNow }); } } return Ok(); } internal void ProcessLayer(Layer processWorker) { if (googleSheetValues == null) { throw new Exception("Google Sheets API not initialized"); } string? name = processWorker.Name; string? year = processWorker?.Records?.SingleOrDefault(x => x.Code == "Year")?.Desc1; if (year == null) { throw new Exception("Year record nod found"); } string? processType = processWorker?.Records?.SingleOrDefault(x => x.Code == "ProcessType")?.Desc1; if (processType == null) { throw new Exception("ProcessType record not found"); } if (processType == "T3-SourceYearSummary") { T3SourceYearSummaryProcessor processor = new T3SourceYearSummaryProcessor(db, googleSheetValues, this); processor.process(processWorker!); logsController.AddEntry(new LogEntry { Title = $"{processWorker!.Name}, {processWorker.Id}", Type = LogEntryType.info, LogType = LogType.process, Message = "Success", CreatedAt = DateTime.UtcNow }); return; } if (processType == "T3-MultiSourceYearSummary") { T3MultiSourceYearSummaryProcessor processor = new T3MultiSourceYearSummaryProcessor(db, googleSheetValues, this, logsController); processor.process(processWorker!); logsController.AddEntry(new LogEntry { Title = $"{processWorker!.Name}, {processWorker.Id}", Type = LogEntryType.info, LogType = LogType.process, Message = "Success", CreatedAt = DateTime.UtcNow }); return; } if (processType == "T3-MultiSourceCopySelectedCodesYearSummary") { T3MultiSourceCopySelectedCodesYearSummaryProcessor processor = new T3MultiSourceCopySelectedCodesYearSummaryProcessor(db, googleSheetValues, this); processor.process(processWorker!); logsController.AddEntry(new LogEntry { Title = $"{processWorker!.Name}, {processWorker.Id}", Type = LogEntryType.info, LogType = LogType.process, Message = "Success", CreatedAt = DateTime.UtcNow }); return; } if (processType == "T3-R1") { T3R1Processor processor = new T3R1Processor(db, googleSheetValues, this, logsController); processor.process(processWorker!); logsController.AddEntry(new LogEntry { Title = $"{processWorker!.Name}, {processWorker.Id}", Type = LogEntryType.info, LogType = LogType.process, Message = "Success", CreatedAt = DateTime.UtcNow }); return; } string? month = processWorker?.Records?.SingleOrDefault(x => x.Code == "Month")?.Desc1; if (month == null) { throw new Exception("Month record not found"); } switch (processType!) { case "T3-SingleSource": { T3SingleSourceProcessor processor = new T3SingleSourceProcessor(db, googleSheetValues, this); processor.process(processWorker!); break; } case "T3-MultiSourceSummary": { T3MultiSourceSummaryProcessor processor = new T3MultiSourceSummaryProcessor(db, googleSheetValues, this, logsController); processor.process(processWorker!); break; } case "T3-MultiSourceCopySelectedCodes": { T3MultiSourceCopySelectedCodesProcessor processor = new T3MultiSourceCopySelectedCodesProcessor(db, googleSheetValues, this); processor.process(processWorker!); break; } } logsController.AddEntry(new LogEntry { Title = $"{processWorker!.Name}, {processWorker.Id}", Type = LogEntryType.info, LogType = LogType.process, Message = "Success", CreatedAt = DateTime.UtcNow }); } internal void SaveRecords(Guid id, ICollection records, Guid currentUserId) { try { List toDelete = db.Records.Where(x => x.LayerId == id).ToList(); if (toDelete.Count > 0) { db.Records.RemoveRange(toDelete); } foreach (Record record in records) { record.CreatedById = currentUserId; record.CreatedAt = DateTime.UtcNow; record.ModifiedById = currentUserId; record.ModifiedAt = DateTime.UtcNow; record.LayerId = id; db.Records.Add(record); } } catch (Exception) { throw; } } internal void WriteToConsole(params string[] messages) { foreach (string message in messages) { Console.WriteLine($"DiunaLog: {message}"); } } private bool IsImportedLayerUpToDate(Layer importWorker) { if (googleSheetValues is null) { throw new Exception("Google Sheets API not initialized"); } Layer? newestLayer = db.Layers .Include(x => x.Records) .Where(x => x.ParentId == importWorker.Id) .OrderByDescending(x => x.CreatedAt) .FirstOrDefault(); if (newestLayer is null) { return true; // importWorker is not active yet, no check needed } string? sheetId = importWorker.Records!.Where(x => x.Code == "SheetId").FirstOrDefault()?.Desc1; if (sheetId == null) { throw new Exception($"SheetId not found, {importWorker.Name}"); } string? sheetTabName = importWorker.Records!.Where(x => x.Code == "SheetTabName").FirstOrDefault()?.Desc1; if (sheetTabName == null) { throw new Exception($"SheetTabName not found, {importWorker.Name}"); } string? dataRange = importWorker.Records!.Where(x => x.Code == "DataRange").FirstOrDefault()?.Desc1; if (dataRange == null) { throw new Exception($"DataRange not found, {importWorker.Name}"); } var dataRangeResponse = googleSheetValues.Get(sheetId, $"{sheetTabName}!{dataRange}").Execute(); var data = dataRangeResponse.Values; bool isUpToDate = true; for (int i = 0; i < data[1].Count; i++) { double value; if (data[0][i].ToString() != "") { Record? record = newestLayer.Records!.Where(x => x.Code == data[0][i].ToString()).FirstOrDefault(); if (record == null) { WriteToConsole("Code not found in DiunaBI", data[0][i].ToString()!); isUpToDate = false; continue; } else if ( double.TryParse(data[1][i].ToString(), CultureInfo.GetCultureInfo("pl-PL"), out value) && record.Value1 != value) { WriteToConsole($"Code: {data[0][i]}. DiunaBI: {string.Format("{0:N2}", record.Value1)}. GoogleSheet: {data[1][i]}"); isUpToDate = false; } } } foreach (Record record in newestLayer.Records!) { if (data[0].Contains(record.Code)) { continue; } WriteToConsole($"Code not found in GoogleSheet: {record.Code}"); isUpToDate = false; } return isUpToDate; } } }