From 7e084b4af207cb2a7614e33eb0fd634ea8282c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieliski?= Date: Fri, 24 May 2024 20:38:32 +0200 Subject: [PATCH] BaseCalculation Engine --- WebAPI/Calculators/BaseCalc.cs | 108 ++++++++++++++++++ WebAPI/Controllers/LayersController.cs | 4 +- WebAPI/DiunaBI-WebAPI.csproj | 1 + .../t3.MultiSourceSummary.processor.cs | 71 +++++++++++- .../t3.MultiSourceYearSummary.processor.cs | 73 +++++++++++- 5 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 WebAPI/Calculators/BaseCalc.cs diff --git a/WebAPI/Calculators/BaseCalc.cs b/WebAPI/Calculators/BaseCalc.cs new file mode 100644 index 0000000..15d2891 --- /dev/null +++ b/WebAPI/Calculators/BaseCalc.cs @@ -0,0 +1,108 @@ +using System.Globalization; +using DiunaBIWebAPI.dataProcessors; +using WebAPI.Models; +using AngouriMath; +using AngouriMath.Extensions; + +namespace WebAPI.Calculator +{ + public class BaseCalc + { + public string Expresion { get; set; } + + private string ResultCode { get; set; } + private string Formula { get; set; } + + public BaseCalc(string expresion) + { + Expresion = expresion; + Formula = Expresion.Split("=")[1]; + ResultCode = Expresion.Split("=")[0]; + } + + public bool IsFormulaCorrect() + { + // check left side of expression + if (!ResultCode.StartsWith("[") || !ResultCode.EndsWith("]")) + { + return false; + } + if (!ResultCode.Substring(1, ResultCode.Length - 2).All(char.IsDigit)) + { + return false; + } + + ResultCode = ResultCode.Substring(1, ResultCode.Length - 2); + + // check right side of expression + if (!(!string.IsNullOrEmpty(Formula) && + Formula.All(c => char.IsDigit(c) || c == '[' || c == ']' || c == '+'))) + { + return false; + } + + return true; + } + + public Record CalculateT3(List records) + { + Record result = new Record + { + Id = Guid.NewGuid(), + Code = this.ResultCode, + CreatedAt = DateTime.UtcNow, + ModifiedAt = DateTime.UtcNow, + }; + List codes = GetCodes(); + List ingredients = new List(); + foreach (string code in codes) + { + Record? ingredient = records.FirstOrDefault(r => r.Code == code); + if (ingredient == null) + { + throw new Exception($"Record for code {code} not found."); + } + ingredients.Add(ingredient); + } + + for (int i = 1; i <= 32; i++) + { + string formula = Formula; + foreach (Record ingredient in ingredients) + { + formula = formula.Replace($"[{ingredient.Code}]", ProcessHelper.getValue(ingredient, i)?.ToString(CultureInfo.InvariantCulture)); + } + if (formula.Contains("[")) + { + throw new Exception($"Not all placeholders were replaced. Value{i} [{formula}]"); + } + + Entity expr = formula; + double val = (double)expr.EvalNumerical(); + ProcessHelper.setValue(result, i, (double)expr.EvalNumerical()); + } + return result; + } + + private List GetCodes() + { + List codes = new List(); + int endIndex = -1; + + while (true) + { + int startIndex = Formula.IndexOf("[", endIndex + 1, StringComparison.CurrentCulture); + endIndex = Formula.IndexOf("]", startIndex + 1, StringComparison.CurrentCulture); + + if (startIndex == -1 || endIndex == -1) + { + break; + } + + string valueCode = Formula.Substring(startIndex + 1, endIndex - startIndex - 1); + codes.Add(valueCode); + } + return codes; + } + } +} \ No newline at end of file diff --git a/WebAPI/Controllers/LayersController.cs b/WebAPI/Controllers/LayersController.cs index 853aaa6..b6d4a01 100644 --- a/WebAPI/Controllers/LayersController.cs +++ b/WebAPI/Controllers/LayersController.cs @@ -375,7 +375,7 @@ namespace WebAPI.Controllers } if (processType == "T3-MultiSourceYearSummary") { - T3MultiSourceYearSummaryProcessor processor = new T3MultiSourceYearSummaryProcessor(db, googleSheetValues, this); + T3MultiSourceYearSummaryProcessor processor = new T3MultiSourceYearSummaryProcessor(db, googleSheetValues, this, logsController); processor.process(processWorker!); logsController.AddEntry(new LogEntry @@ -433,7 +433,7 @@ namespace WebAPI.Controllers } case "T3-MultiSourceSummary": { - T3MultiSourceSummaryProcessor processor = new T3MultiSourceSummaryProcessor(db, googleSheetValues, this); + T3MultiSourceSummaryProcessor processor = new T3MultiSourceSummaryProcessor(db, googleSheetValues, this, logsController); processor.process(processWorker!); break; } diff --git a/WebAPI/DiunaBI-WebAPI.csproj b/WebAPI/DiunaBI-WebAPI.csproj index d4b03f3..946bf03 100644 --- a/WebAPI/DiunaBI-WebAPI.csproj +++ b/WebAPI/DiunaBI-WebAPI.csproj @@ -8,6 +8,7 @@ + diff --git a/WebAPI/dataProcessors/t3.MultiSourceSummary.processor.cs b/WebAPI/dataProcessors/t3.MultiSourceSummary.processor.cs index 63b56f2..bb346ad 100644 --- a/WebAPI/dataProcessors/t3.MultiSourceSummary.processor.cs +++ b/WebAPI/dataProcessors/t3.MultiSourceSummary.processor.cs @@ -1,6 +1,7 @@ using DiunaBIWebAPI.dataProcessors; using Google.Apis.Sheets.v4; using Microsoft.EntityFrameworkCore; +using WebAPI.Calculator; using WebAPI.Controllers; using WebAPI.Models; @@ -11,15 +12,18 @@ namespace WebAPI.dataProcessors private readonly AppDbContext db; private readonly SpreadsheetsResource.ValuesResource googleSheetValues; private readonly LayersController controller; + private readonly LogsController logsController; public T3MultiSourceSummaryProcessor( AppDbContext _db, SpreadsheetsResource.ValuesResource _googleSheetValues, - LayersController _controller) + LayersController _controller, + LogsController _logsController) { db = _db; googleSheetValues = _googleSheetValues; controller = _controller; + logsController = _logsController; } public void process(Layer processWorker) @@ -105,6 +109,71 @@ namespace WebAPI.dataProcessors } newRecords.Add(processedRecord); } + + // Dynamic Codes + List? dynamicCodes = processWorker.Records?.Where(x => x.Code == "DynamicCode").ToList(); + if (dynamicCodes != null && dynamicCodes.Any()) + { + foreach (Record dynamicCode in dynamicCodes) + { + try + { + if (dynamicCode.Desc1 == null) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Formula in Record {dynamicCode.Id} is missing.", + CreatedAt = DateTime.UtcNow + }); + continue; + } + BaseCalc calc = new BaseCalc(dynamicCode.Desc1); + if (!calc.IsFormulaCorrect()) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Formula {calc.Expresion} in Record {dynamicCode.Id} is not correct", + CreatedAt = DateTime.UtcNow + }); + continue; + } + + try + { + newRecords.Add(calc.CalculateT3(newRecords)); + } + catch (Exception e) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Formula {calc.Expresion} in Record {dynamicCode.Id} error: {e.Message}", + CreatedAt = DateTime.UtcNow + }); + } + } + catch (Exception e) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Calculation error {dynamicCode.Id}: {e.Message} ", + CreatedAt = DateTime.UtcNow + }); + } + } + } + if (isNew) { diff --git a/WebAPI/dataProcessors/t3.MultiSourceYearSummary.processor.cs b/WebAPI/dataProcessors/t3.MultiSourceYearSummary.processor.cs index 0741458..949c799 100644 --- a/WebAPI/dataProcessors/t3.MultiSourceYearSummary.processor.cs +++ b/WebAPI/dataProcessors/t3.MultiSourceYearSummary.processor.cs @@ -1,6 +1,7 @@ using DiunaBIWebAPI.dataProcessors; using Google.Apis.Sheets.v4; using Microsoft.EntityFrameworkCore; +using WebAPI.Calculator; using WebAPI.Controllers; using WebAPI.Models; @@ -11,15 +12,18 @@ namespace WebAPI.dataProcessors private readonly AppDbContext db; private readonly SpreadsheetsResource.ValuesResource googleSheetValues; private readonly LayersController controller; + private readonly LogsController logsController; public T3MultiSourceYearSummaryProcessor( AppDbContext _db, SpreadsheetsResource.ValuesResource _googleSheetValues, - LayersController _controller) + LayersController _controller, + LogsController _logsController) { db = _db; googleSheetValues = _googleSheetValues; controller = _controller; + logsController = _logsController; } public void process(Layer processWorker) @@ -140,8 +144,73 @@ namespace WebAPI.dataProcessors } } newRecords.Add(processedRecord); - } + } + + + + // Dynamic Codes + List? dynamicCodes = processWorker.Records?.Where(x => x.Code == "DynamicCode").ToList(); + if (dynamicCodes != null && dynamicCodes.Any()) + { + foreach (Record dynamicCode in dynamicCodes) + { + try + { + if (dynamicCode.Desc1 == null) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Formula in Record {dynamicCode.Id} is missing.", + CreatedAt = DateTime.UtcNow + }); + continue; + } + BaseCalc calc = new BaseCalc(dynamicCode.Desc1); + if (!calc.IsFormulaCorrect()) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Formula {calc.Expresion} in Record {dynamicCode.Id} is not correct", + CreatedAt = DateTime.UtcNow + }); + continue; + } + try + { + newRecords.Add(calc.CalculateT3(newRecords)); + } + catch (Exception e) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Formula {calc.Expresion} in Record {dynamicCode.Id} error: {e.Message}", + CreatedAt = DateTime.UtcNow + }); + } + } + catch (Exception e) + { + logsController.AddEntry(new LogEntry + { + Title = $"{processWorker!.Name}, {processWorker.Id}", + Type = LogEntryType.warning, + LogType = LogType.process, + Message = $"Calculation error {dynamicCode.Id}: {e.Message} ", + CreatedAt = DateTime.UtcNow + }); + } + } + } if (isNew) { db.Layers.Add(processedLayer);