From 7c8b2412358c339e8052fda5e40a8ea2dcb72759 Mon Sep 17 00:00:00 2001 From: foefl Date: Thu, 20 Mar 2025 16:33:36 +0100 Subject: [PATCH] add basic sales forecast pipeline incl. dummy data --- .gitignore | 1 + dopt.DeltaBarth.Tests/JsonStructsTest.cs | 70 ++++++-- dopt.DeltaBarth.Tests/PluginTest.cs | 162 +++++++++++++++++- .../dopt.DeltaBarth.Tests.csproj | 15 +- dopt.DeltaBarth.sln | 22 +-- dopt.DeltaBarth/JsonStructs.cs | 4 +- dopt.DeltaBarth/Plugin.cs | 59 +++++-- dopt.DeltaBarth/dopt.DeltaBarth.csproj | 4 +- 8 files changed, 291 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index 691de33..1aace39 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ ## ## Get latest from `dotnet new gitignore` **/python/ +CREDENTIALS* # dotenv files .env diff --git a/dopt.DeltaBarth.Tests/JsonStructsTest.cs b/dopt.DeltaBarth.Tests/JsonStructsTest.cs index 05ae873..f98c930 100644 --- a/dopt.DeltaBarth.Tests/JsonStructsTest.cs +++ b/dopt.DeltaBarth.Tests/JsonStructsTest.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using Python.Runtime; +using Python.Runtime; +using System.Diagnostics; using System.Text.Json; -using DeltaBarth = dopt.DeltaBarth; -using dopt.DeltaBarth.JsonStructs; namespace dopt.DeltaBarth.Tests { + using System.Collections.Immutable; internal class TestPlugin : DeltaBarth.Plugin { internal dynamic pyModJsonStructs; @@ -33,7 +27,6 @@ namespace dopt.DeltaBarth.Tests }); Console.WriteLine($"Parsed struct is: {prettyJson}"); } - [TestMethod] public void Parse_Error_Status_Test() { @@ -44,6 +37,10 @@ namespace dopt.DeltaBarth.Tests pyJson = (string)plugin.pyModJsonStructs.status_err(); } var parsed = JsonSerializer.Deserialize(pyJson); + Assert.AreEqual(102, parsed.code); + Assert.AreEqual("internal error occurred", parsed.description); + Assert.AreEqual("caused by test", parsed.message); + Assert.IsNull(parsed.apiServerError); PrettyPrint(parsed); plugin.Shutdown(); } @@ -57,6 +54,30 @@ namespace dopt.DeltaBarth.Tests pyJson = (string)plugin.pyModJsonStructs.delta_barth_api_error(); } var parsed = JsonSerializer.Deserialize(pyJson); + Assert.AreEqual(401, parsed.status_code); + Assert.AreEqual("test message", parsed.message); + Assert.AreEqual("test code", parsed.code); + Assert.IsNull(parsed.hints); + Assert.IsNull(parsed.type); + Assert.IsNull(parsed.title); + Assert.IsNull(parsed.traceId); + PrettyPrint(parsed); + plugin.Shutdown(); + } + [TestMethod] + public void Parse_Credentials_Test() + { + var plugin = new TestPlugin(); + string pyJson; + using (Py.GIL()) + { + pyJson = (string)plugin.pyModJsonStructs.api_credentials(); + } + var parsed = JsonSerializer.Deserialize(pyJson); + Assert.AreEqual("user", parsed.username); + Assert.AreEqual("pass", parsed.password); + Assert.AreEqual("test1", parsed.database); + Assert.AreEqual("mandant1", parsed.mandant); PrettyPrint(parsed); plugin.Shutdown(); } @@ -70,6 +91,9 @@ namespace dopt.DeltaBarth.Tests pyJson = (string)plugin.pyModJsonStructs.sales_prognosis_result(); } var parsed = JsonSerializer.Deserialize(pyJson); + Assert.AreEqual(2023, parsed.jahr); + Assert.AreEqual(12, parsed.monat); + Assert.AreEqual(3000.3456m, parsed.vorhersage); PrettyPrint(parsed); plugin.Shutdown(); } @@ -83,6 +107,16 @@ namespace dopt.DeltaBarth.Tests pyJson = (string)plugin.pyModJsonStructs.sales_prognosis_results(); } var parsed = JsonSerializer.Deserialize(pyJson); + Assert.AreEqual(3, parsed.daten.Length); + var e1 = new JsonStructs.UmsatzPrognoseEinzelergebnis { jahr = 2023, monat = 12, vorhersage = 3000.3456m }; + var e2 = new JsonStructs.UmsatzPrognoseEinzelergebnis { jahr = 2024, monat = 1, vorhersage = 3300.685m }; + var e3 = new JsonStructs.UmsatzPrognoseEinzelergebnis { jahr = 2024, monat = 2, vorhersage = 3700.548m }; + ImmutableArray arr = ImmutableArray.Create(e1, e2, e3); + + for (int i = 0; i < parsed.daten.Length; i++) + { + Assert.AreEqual(arr[i], parsed.daten[i]); + } PrettyPrint(parsed); plugin.Shutdown(); } @@ -96,6 +130,22 @@ namespace dopt.DeltaBarth.Tests pyJson = (string)plugin.pyModJsonStructs.sales_prognosis_results_export(); } var parsed = JsonSerializer.Deserialize(pyJson); + // result + var e1 = new JsonStructs.UmsatzPrognoseEinzelergebnis { jahr = 2023, monat = 12, vorhersage = 3000.3456m }; + var e2 = new JsonStructs.UmsatzPrognoseEinzelergebnis { jahr = 2024, monat = 1, vorhersage = 3300.685m }; + var e3 = new JsonStructs.UmsatzPrognoseEinzelergebnis { jahr = 2024, monat = 2, vorhersage = 3700.548m }; + ImmutableArray arr = ImmutableArray.Create(e1, e2, e3); + var data = new JsonStructs.UmsatzPrognoseErgebnisse { daten = arr }; + // check status + Assert.AreEqual(0, parsed.status.code); + Assert.AreEqual("Erfolg", parsed.status.description); + Assert.AreEqual("", parsed.status.message); + Assert.IsNull(parsed.status.apiServerError); + // check result + for (int i = 0; i < parsed.response.daten.Length; i++) + { + Assert.AreEqual(data.daten[i], parsed.response.daten[i]); + } PrettyPrint(parsed); plugin.Shutdown(); } diff --git a/dopt.DeltaBarth.Tests/PluginTest.cs b/dopt.DeltaBarth.Tests/PluginTest.cs index a856bc4..c361ead 100644 --- a/dopt.DeltaBarth.Tests/PluginTest.cs +++ b/dopt.DeltaBarth.Tests/PluginTest.cs @@ -1,29 +1,179 @@ -using dopt.DeltaBarth; +using System.Text.Json; +using dopt.DeltaBarth; +using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext; namespace dopt.DeltaBarth.Tests { + using Microsoft.Extensions.Configuration; + internal class Config + { + public string apiUrl; + public string username; + public string password; + public string database; + public string mandant; + + public Config() { + var config = new ConfigurationBuilder() + .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) + .AddJsonFile("CREDENTIALS.json", optional: false, reloadOnChange: true) + .Build(); + var v = config["delta-barth-server:api:base_url"]; + Assert.IsNotNull(v); + apiUrl = v; + v = config["delta-barth-server:api:user"]; + Assert.IsNotNull(v); + username = v; + v = config["delta-barth-server:api:pwd"]; + Assert.IsNotNull(v); + password = v; + v = config["delta-barth-server:api:db"]; + Assert.IsNotNull(v); + database = v; + v = config["delta-barth-server:api:mandant"]; + Assert.IsNotNull(v); + mandant = v; + } + } + public class TPlugin : DeltaBarth.Plugin + { + public new JsonStructs.Credentials GetCredentials() + { + return base.GetCredentials(); + } + public new string GetBaseApiUrl() + { + return base.GetBaseApiUrl(); + } + } [TestClass] public sealed class PluginTest { + internal Config config = new Config(); + static void PrettyPrint(object toSerialise) + { + string prettyJson = JsonSerializer.Serialize(toSerialise, new JsonSerializerOptions + { + WriteIndented = true + }); + Console.WriteLine($"Parsed struct is: {prettyJson}"); + } [TestMethod] public void ConstructInitialiseFinalise_Test() { - var test = new Plugin(); + var test = new TPlugin(); test.Shutdown(); } [TestMethod] public void Set_and_Obtain_Credentials_Test() { - var test = new Plugin(); + var test = new TPlugin(); string user = "user", password = "password", database = "DB1", mandant = "mandant1"; - test.SetCredentials(user, password, database, mandant); + test.SetzeNutzerdaten(user, password, database, mandant); var creds = test.GetCredentials(); - Console.WriteLine($"Result from Python session was: {creds}"); - Assert.AreEqual(user, creds.user_name); + PrettyPrint(creds); + Assert.AreEqual(user, creds.username); Assert.AreEqual(password, creds.password); Assert.AreEqual(database, creds.database); Assert.AreEqual(mandant, creds.mandant); test.Shutdown(); } + [TestMethod] + public void Set_and_Obtain_BaseApiUrl_Test() + { + var test = new TPlugin(); + string apiUrlSet = "http://10.2.22.21:8080/api/"; + test.SetzeBasisApiUrl(apiUrlSet); + var apiUrlGet = test.GetBaseApiUrl(); + Console.WriteLine($"Result from Python session was: {apiUrlGet}"); + Assert.AreEqual(apiUrlSet, apiUrlGet); + test.Shutdown(); + } + [TestMethod] + public void Startup_Test() + { + var test = new TPlugin(); + string apiUrlSet = "http://10.2.22.21:8080/api/", user = "user", password = "password", database = "DB1", mandant = "mandant1"; + test.Startup(apiUrlSet, user, password, database, mandant); + var apiUrlGet = test.GetBaseApiUrl(); + var creds = test.GetCredentials(); + Console.WriteLine($"Result from Python session was: API-URL={apiUrlGet}, creds={creds}"); + PrettyPrint(creds); + Assert.AreEqual(apiUrlSet, apiUrlGet); + Assert.AreEqual(user, creds.username); + Assert.AreEqual(password, creds.password); + Assert.AreEqual(database, creds.database); + Assert.AreEqual(mandant, creds.mandant); + test.Shutdown(); + } + [TestMethod] + public void UmsatzprognoseDummy_WithoutParams_Test() + { + var test = new TPlugin(); + string apiUrlSet = "http://10.2.22.21:8080/api/", user = "user", password = "password", database = "DB1", mandant = "mandant1"; + test.Startup(apiUrlSet, user, password, database, mandant); + var res = test.UmsatzprognoseDummy(null, null); + PrettyPrint(res); + test.Shutdown(); + } + [TestMethod] + public void UmsatzprognoseDummy_WithCompanyIdWithoutDate_Test() + { + var test = new TPlugin(); + string apiUrlSet = "http://10.2.22.21:8080/api/", user = "user", password = "password", database = "DB1", mandant = "mandant1"; + test.Startup(apiUrlSet, user, password, database, mandant); + var comp_id = 1000; + var res = test.UmsatzprognoseDummy(comp_id, null); + PrettyPrint(res); + test.Shutdown(); + } + [TestMethod] + public void UmsatzprognoseDummy_WithoutCompanyIdWithDate_Test() + { + var test = new TPlugin(); + string apiUrlSet = "http://10.2.22.21:8080/api/", user = "user", password = "password", database = "DB1", mandant = "mandant1"; + test.Startup(apiUrlSet, user, password, database, mandant); + var date = new DateTime(2023, 1, 1, 12, 45, 30); + var res = test.UmsatzprognoseDummy(null, date); + PrettyPrint(res); + test.Shutdown(); + } + + [TestMethod] + [TestCategory("ActiveAPI")] + public void Umsatzprognose_WithCompanyIdWithoutDate_Test() + { + var test = new TPlugin(); + string apiUrlSet = config.apiUrl, user = config.username, password = config.password, database = config.database, mandant = config.mandant; + test.Startup(apiUrlSet, user, password, database, mandant); + var comp_id = 1024; + var res = test.Umsatzprognose(comp_id, null); + PrettyPrint(res); + test.Shutdown(); + } + [TestMethod] + [TestCategory("ActiveAPI")] + public void Umsatzprognose_WithoutCompanyIdWithInvalidDate_Test() + { + var test = new TPlugin(); + string apiUrlSet = config.apiUrl, user = config.username, password = config.password, database = config.database, mandant = config.mandant; + test.Startup(apiUrlSet, user, password, database, mandant); + var date = new DateTime(2030, 1, 1, 12, 45, 30); + var res = test.Umsatzprognose(null, date); + PrettyPrint(res); + test.Shutdown(); + } + [TestMethod] + [TestCategory("ActiveAPI")] + public void Umsatzprognose_WithoutCompanyIdWithValidDate_Test() + { + var test = new TPlugin(); + string apiUrlSet = config.apiUrl, user = config.username, password = config.password, database = config.database, mandant = config.mandant; + test.Startup(apiUrlSet, user, password, database, mandant); + var date = new DateTime(2015, 1, 1, 12, 45, 30); + var res = test.Umsatzprognose(null, date); + PrettyPrint(res); + test.Shutdown(); + } } } diff --git a/dopt.DeltaBarth.Tests/dopt.DeltaBarth.Tests.csproj b/dopt.DeltaBarth.Tests/dopt.DeltaBarth.Tests.csproj index 7ae2207..2270bca 100644 --- a/dopt.DeltaBarth.Tests/dopt.DeltaBarth.Tests.csproj +++ b/dopt.DeltaBarth.Tests/dopt.DeltaBarth.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -14,17 +14,22 @@ --> true x64 + x64 - - - - + + + + + python\%(RecursiveDir)/%(FileName)%(Extension) PreserveNewest + + PreserveNewest + diff --git a/dopt.DeltaBarth.sln b/dopt.DeltaBarth.sln index 3992f49..8cd5faa 100644 --- a/dopt.DeltaBarth.sln +++ b/dopt.DeltaBarth.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.13.35913.81 d17.13 +VisualStudioVersion = 17.13.35913.81 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dopt.DeltaBarth", "dopt.DeltaBarth\dopt.DeltaBarth.csproj", "{F90EB30A-B3B0-4574-9BAC-9091A1CE3599}" EndProject @@ -9,18 +9,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dopt.DeltaBarth.Tests", "do EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Release|Any CPU.Build.0 = Release|Any CPU - {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Release|Any CPU.Build.0 = Release|Any CPU + {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Debug|x64.ActiveCfg = Debug|x64 + {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Debug|x64.Build.0 = Debug|x64 + {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Release|x64.ActiveCfg = Release|x64 + {F90EB30A-B3B0-4574-9BAC-9091A1CE3599}.Release|x64.Build.0 = Release|x64 + {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Debug|x64.ActiveCfg = Debug|x64 + {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Debug|x64.Build.0 = Debug|x64 + {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Release|x64.ActiveCfg = Release|x64 + {C71160B2-0ECF-4764-87C7-72F943E64DD8}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/dopt.DeltaBarth/JsonStructs.cs b/dopt.DeltaBarth/JsonStructs.cs index f47468a..59a41bd 100644 --- a/dopt.DeltaBarth/JsonStructs.cs +++ b/dopt.DeltaBarth/JsonStructs.cs @@ -27,7 +27,7 @@ namespace dopt.DeltaBarth.JsonStructs } public readonly struct Credentials { - public string user_name { get; init; } + public string username { get; init; } public string password { get; init; } public string database { get; init; } public string mandant { get; init; } @@ -36,7 +36,7 @@ namespace dopt.DeltaBarth.JsonStructs { public int jahr { get; init; } public int monat { get; init; } - public float vorhersage { get; init; } + public decimal vorhersage { get; init; } } public readonly struct UmsatzPrognoseErgebnisse { diff --git a/dopt.DeltaBarth/Plugin.cs b/dopt.DeltaBarth/Plugin.cs index 6ea3a4e..20fa8f2 100644 --- a/dopt.DeltaBarth/Plugin.cs +++ b/dopt.DeltaBarth/Plugin.cs @@ -1,6 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; -using dopt; -using Python.Runtime; +using Python.Runtime; using System.Text.Json; namespace dopt.DeltaBarth @@ -9,7 +7,7 @@ namespace dopt.DeltaBarth { protected dynamic pyModManagement; protected dynamic pyModPipeline; - public Plugin() : base() + public Plugin() : base(verbose: false) { base.Initialise(); using (Py.GIL()) @@ -18,25 +16,64 @@ namespace dopt.DeltaBarth pyModPipeline = Py.Import("delta_barth.pipelines"); } } + public void Startup(string basisApiUrl, string nutzername, string passwort, string datenbank, string mandant) + { + SetzeBasisApiUrl(basisApiUrl); + SetzeNutzerdaten(nutzername, passwort, datenbank, mandant); + } public void Shutdown() { base.Finalise(); } - public void SetCredentials(string username, string password, string database, string mandant) + public void SetzeBasisApiUrl(string basisApiUrl) { - using (Py.GIL()) { - pyModManagement.set_credentials(username, password, database, mandant); - } - + pyModManagement.set_base_url(basisApiUrl); } - public JsonStructs.Credentials GetCredentials() + public void SetzeNutzerdaten(string nutzername, string passwort, string datenbank, string mandant) + { + using (Py.GIL()) { + pyModManagement.set_credentials(nutzername, passwort, datenbank, mandant); + } + } + public JsonStructs.UmsatzPrognoseAusgabe UmsatzprognoseDummy(int? firmaId, DateTime? buchungsDatum) + { + string pyJson; + using (Py.GIL()) + { + pyJson = pyModPipeline.pipeline_sales_forecast_dummy(firmaId, buchungsDatum); + } + var parsed = JsonSerializer.Deserialize(pyJson); + + return parsed; + } + public JsonStructs.UmsatzPrognoseAusgabe Umsatzprognose(int? firmaId, DateTime? buchungsDatum) + { + string pyJson; + using (Py.GIL()) + { + pyJson = pyModPipeline.pipeline_sales_forecast(firmaId, buchungsDatum); + } + var parsed = JsonSerializer.Deserialize(pyJson); + + return parsed; + } + protected string GetBaseApiUrl() + { + string pyJson; + using (Py.GIL()) + { + pyJson = (string)pyModManagement.get_base_url(); + } + return pyJson; + } + protected JsonStructs.Credentials GetCredentials() { string pyJson; using (Py.GIL()) { pyJson = (string)pyModManagement.get_credentials(); } - + JsonStructs.Credentials credentials = JsonSerializer.Deserialize(pyJson); return credentials; diff --git a/dopt.DeltaBarth/dopt.DeltaBarth.csproj b/dopt.DeltaBarth/dopt.DeltaBarth.csproj index 7d36c7f..ade9666 100644 --- a/dopt.DeltaBarth/dopt.DeltaBarth.csproj +++ b/dopt.DeltaBarth/dopt.DeltaBarth.csproj @@ -5,10 +5,12 @@ enable enable x64 + x64 + 0.1.0-alpha1 - + python\%(RecursiveDir)/%(FileName)%(Extension) PreserveNewest