diff --git a/dopt.DeltaBarth/DataObjects.cs b/dopt.DeltaBarth/DataObjects.cs index ec8dc0c..a0f368e 100644 --- a/dopt.DeltaBarth/DataObjects.cs +++ b/dopt.DeltaBarth/DataObjects.cs @@ -7,44 +7,130 @@ using System.Collections.Immutable; namespace dopt.DeltaBarth.DataObjects { + /// + /// Eine verpackte Fehlermeldung, die vom API-Server zur Verfügung gestellt wurde. + /// public record class ApiServerError { + /// + /// Statuscode der HTTP-Anfrage + /// public required int status_code { get; init; } + /// + /// Fehlerbezeichnung + /// public required string message { get; init; } + /// + /// optional: spezifischer Code als Text, der vom Server ausgegeben wurde + /// public string? code { get; init; } + /// + /// optional: spezifische Hinweise als Text, der vom Server ausgegeben wurde + /// public string? hints { get; init; } + /// + /// optional: spezifischer Typus als Text, der vom Server ausgegeben wurde + /// public string? type { get; init; } + /// + /// optional: spezifischer Titel als Text, der vom Server ausgegeben wurde + /// public string? title { get; init; } + /// + /// optional: spezifischer ID (vermutlich zur Nachverfolgung) als Text, der vom Server ausgegeben wurde + /// public string? traceId { get; init; } } + /// + /// Status-Objekt: Gibt Aufschluss über Erfolg/Misserfolg einer Routine und beschreibt aufgetretene Fehler + /// mit zusätzlichen Informationen + /// public record class Status { + /// + /// bibliotheksinterner Fehlercode + /// public required int code { get; init; } + /// + /// Fehlerbeschreibung oder Python-Exception-Name + /// public required string description { get; init; } + /// + /// genauere Beschreibung oder Fehler-Inhalt + /// public required string message { get; init; } + /// + /// optional: eventuell aufgetretener API-Server-Fehler + /// public ApiServerError? apiServerError { get; init; } } + /// + /// Nutzerdaten zur API-Interaktion + /// public record class Credentials { + /// + /// Nutzername + /// public required string username { get; init; } + /// + /// Passwort + /// public required string password { get; init; } + /// + /// Datenbank + /// public required string database { get; init; } + /// + /// Mandant + /// public required string mandant { get; init; } } + /// + /// Einzelergebnis der Umsatzprognose, 1 Eintrag von potenziell mehreren + /// public record class UmsatzPrognoseEinzelergebnis { + /// + /// Jahr des Eintrags + /// public required int jahr { get; init; } + /// + /// Monat des Eintrags + /// public required int monat { get; init; } + /// + /// Prognosewert für den Umsatz + /// public required decimal vorhersage { get; init; } } + /// + /// Sammlung von unterschiedlichen Einzelergebnissen + /// + /// public record class UmsatzPrognoseErgebnisse { + /// + /// unveränderliche Sammlung von Einzelergebnissen in Form eines Array + /// public required ImmutableArray daten { get; init; } } + /// + /// Ausgabe der Prognose-Pipeline: + /// enthält das Ergebnis als auch einen dazugehörigen Status + /// public record class UmsatzPrognoseAusgabe { + /// + /// Sammlung von Prognosewerten + /// + /// public required UmsatzPrognoseErgebnisse response { get; init; } + /// + /// Status über den Erfolg/Misserfolg der Routine + /// + /// public required Status status { get; init; } } } diff --git a/dopt.DeltaBarth/Plugin.cs b/dopt.DeltaBarth/Plugin.cs index 13a87cb..2ff618a 100644 --- a/dopt.DeltaBarth/Plugin.cs +++ b/dopt.DeltaBarth/Plugin.cs @@ -4,32 +4,95 @@ using System.Text.Json; namespace dopt.DeltaBarth { + /// + /// Spiegelung der internen Fehlertypen innerhalb der Python-Bibliothek + /// public enum StatusCodes { + /// + /// erfolgreiche, fehlerfreie Durchführung der Routine + /// [Description("Keine Fehler aufgetreten")] Erfolg = 0, + /// + /// Bei der API-Abfrage wurde der Timeout getriggert. + /// [Description("Bei der Verbindung zum API-Server kam es zum Timeout")] VerbindungTimeout = 1, + /// + /// Bei der API-Abfrage ist ein unerwarteter Fehler aufgetreten, der unmittelbar die HTTP-Anfrage betrifft. + /// [Description("Bei der Verbindung zum API-Server ist ein Fehler aufgetreten")] VerbindungFehler = 2, + /// + /// Der ausgewählte Datensatz enthält nicht genügend Datenpunkte. + /// [Description("Der bereitgestellte Datensatz enthält in Summe zu wenige Einzeleinträge")] DatensatzZuWenigeDatenpunkte = 3, + /// + /// Der ausgewählte Datensatz enthält nach Aggregation pro Monat nicht genügend Datenpunkte. + /// [Description("Der bereitgestellte Datensatz enthält nach Aggregation zu Monaten zu wenig Einträge")] DatensatzZuWenigeMonatsdatenpunkte = 4, - [Description("Die Prognosequalität des Modells erfüllt nicht ide Mindestanforderungen")] + /// + /// Die Prognosequalität des Modells ist nicht zufriedenstellend. Eine verlässliche Prognose ist nicht möglich. + /// + [Description("Die Prognosequalität des Modells erfüllt nicht die Mindestanforderungen")] KeineVerlaesslichePrognose = 5, + /// + /// Es ist intern ein Fehler aufgetreten aufgetreten + /// + [Description("Interne Fehler, die während der Routine aufgetreten sind")] + InternerFehler = 100, + /// + /// Es ist ein Fehler beim Schreiben der programminternen Datenbank aufgetreten. + /// + [Description("Interne Fehler, die während der Datenbankinteraktion aufgetreten sind")] + InternerDbFehler = 150, + /// + /// Es ist ein Fehler auf dem API-Server aufgetreten. + /// Das dazugehörige Status-Objekt sollte diesen Fehler zur Verfügung stellen können. + /// + /// + /// [Description("Vom API-Server wurde eine Fehlermeldung zurückgegeben")] ApiServerFehler = 400, } + /// + /// Eine Exception, die genutzt wird, um anzuzeigen, dass beim Parsen der Python-Objekte + /// ein Fehler aufgetreten ist. + /// public class PythonParsingException : Exception { + /// + /// Konstruktor ohne Inhalt + /// public PythonParsingException() { } + /// + /// Konstruktor mit Nachricht + /// + /// public PythonParsingException(string message) : base(message) { } } + /// + /// Plugin-Klasse, mit der die Interaktion der zugrundeliegenden Python-Runtime erfolgt + /// public class Plugin : SharpPython.BasePlugin { + /// + /// Python-interne Zustandsverwaltung + /// protected dynamic pyModManagement; + /// + /// Python-interne Routinen und Pipelines + /// protected dynamic pyModPipeline; + /// + /// Konstruktor der Plugin-Klasse. + /// Kann mit beliebigem Pfad zu einer Python-Runtime initialisiert werden. + /// + /// Der Pfad zur Python-Runtime. Dieser muss zu dem Ordner zeigen, + /// in welchem die Runtime in Form eines Ordners mit dem Namen "python" abliegt. public Plugin(string runtimePath) : base(runtimePath: runtimePath, verbose: false) { base.Initialise(); @@ -39,12 +102,30 @@ namespace dopt.DeltaBarth pyModPipeline = Py.Import("delta_barth.pipelines"); } } + /// + /// Initialisiert das Plugin mit allen relevanten Paramtern für die weitere Nutzung. + /// Diese Methode sollte nur einmal je Instanz genutzt werden. + /// + /// Pfad zu einem Ordner, in dem Programmdaten ohne Bedenken dauerhaft abgelegt werden können. + /// Basis-URL zum Zugriff auf die API. Dies muss eine vollständige URL sein inkl. der Route "/api". + /// Nutzername für die Datenbankanmeldung. + /// Passwort für die Datenbankanmeldung. + /// Name der Datenbank, bei der die Anmeldung erfolgen soll. + /// Mandant für die Datenbankanmeldung. public void Startup(string datenPfad, string basisApiUrl, string nutzername, string passwort, string datenbank, string mandant) { AssertNotDisposed(); Setup(datenPfad, basisApiUrl); SetzeNutzerdaten(nutzername, passwort, datenbank, mandant); } + /// + /// Diese Methode erlaubt es, die relevanten Nutzerdaten zur Laufzeit des Plugins zu ändern. + /// Dies beinhaltet: Nutzername, Passwort, Datenbankname, Mandant + /// + /// Nutzername für die Datenbankanmeldung. + /// Passwort für die Datenbankanmeldung. + /// Name der Datenbank, bei der die Anmeldung erfolgen soll. + /// Mandant für die Datenbankanmeldung. public void SetzeNutzerdaten(string nutzername, string passwort, string datenbank, string mandant) { AssertNotDisposed(); @@ -52,6 +133,17 @@ namespace dopt.DeltaBarth pyModManagement.set_credentials(nutzername, passwort, datenbank, mandant); } } + /// + /// Ausführung der Umsatzprognose-Pipeline mit Dummy-Daten. + /// Es werden keine API-Abrufe durchgeführt und somit auch keine Live-Daten genutzt. + /// + /// optional: Firmen-ID, für die die Pipeline ausgeführt werden soll. + /// Wird der Parameter nicht zur Verfügung gestellt, werden alle Firmen bzw. Kunden abgerufen + /// optional: Start-Datum, ab dem die Daten für die Erstellung des Prognosemodells genutzt werden. + /// Daten, die weiter in der Vergangenheit liegen, werden nicht berücksichtigt. + /// Wird der Parameter nicht zur Verfügung gestellt, wird die gesamte Historie genutzt. + /// Umsatzprognose inkl. Status-Objekt zur Nachvollziehbarkeit etwaig aufgetretener Fehler. + /// public DataObjects.UmsatzPrognoseAusgabe UmsatzprognoseDummy(int? firmaId, DateTime? analyseBeginn) { AssertNotDisposed(); @@ -63,6 +155,18 @@ namespace dopt.DeltaBarth var parsed = JsonSerializer.Deserialize(pyJson) ?? throw new PythonParsingException("Could not correctly parse object from Python"); return parsed; } + /// + /// Ausführung der Umsatzprognose-Pipeline mit Live-Daten. + /// Es werden API-Abrufe durchgeführt und somit auch Live-Daten genutzt. + /// Hierfür muss sichergestellt sein, dass der API-Server erreichbar und abrufbereit ist. + /// + /// optional: Firmen-ID, für die die Pipeline ausgeführt werden soll. + /// Wird der Parameter nicht zur Verfügung gestellt, werden alle Firmen bzw. Kunden abgerufen + /// optional: Start-Datum, ab dem die Daten für die Erstellung des Prognosemodells genutzt werden. + /// Daten, die weiter in der Vergangenheit liegen, werden nicht berücksichtigt. + /// Wird der Parameter nicht zur Verfügung gestellt, wird die gesamte Historie genutzt. + /// Umsatzprognose inkl. Status-Objekt zur Nachvollziehbarkeit etwaig aufgetretener Fehler. + /// public DataObjects.UmsatzPrognoseAusgabe Umsatzprognose(int? firmaId, DateTime? analyseBeginn) { AssertNotDisposed(); @@ -74,6 +178,11 @@ namespace dopt.DeltaBarth var parsed = JsonSerializer.Deserialize(pyJson) ?? throw new PythonParsingException("Could not correctly parse object from Python"); return parsed; } + /// + /// Setup der Python-internen Umgebung + /// + /// Pfad zu einem Ordner, in dem Programmdaten ohne Bedenken dauerhaft abgelegt werden können. + /// Basis-URL zum Zugriff auf die API. Dies muss eine vollständige URL sein inkl. der Route "/api". protected void Setup(string datenPfad, string basisApiUrl) { AssertNotDisposed(); @@ -82,6 +191,10 @@ namespace dopt.DeltaBarth pyModManagement.setup(datenPfad, basisApiUrl); } } + /// + /// Hole die konfigurierte API-Basis-URL aus der Python-Umgebung + /// + /// konfigurierte Basis-URL protected string GetBaseApiUrl() { AssertNotDisposed(); @@ -92,6 +205,10 @@ namespace dopt.DeltaBarth } return pyJson; } + /// + /// Hole den konfigurierten Datenpfad zur Dateiverwaltung aus der Python-Umgebung + /// + /// konfigurierten Datenpfad protected string GetDataPath() { AssertNotDisposed(); @@ -102,6 +219,11 @@ namespace dopt.DeltaBarth } return pyJson; } + /// + /// Hole die konfigurierten Nutzerdaten zur API-Interaktion aus der Python-Umgebung + /// + /// konfigurierte Nutzerdaten + /// protected DataObjects.Credentials GetCredentials() { AssertNotDisposed(); diff --git a/dopt.DeltaBarth/dopt.DeltaBarth.csproj b/dopt.DeltaBarth/dopt.DeltaBarth.csproj index a520fe0..90063bd 100644 --- a/dopt.DeltaBarth/dopt.DeltaBarth.csproj +++ b/dopt.DeltaBarth/dopt.DeltaBarth.csproj @@ -7,6 +7,7 @@ x64 x64 0.3.3-dev1 + True