using Python.Runtime; using System.ComponentModel; 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, /// /// 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; /// /// Debug-Modul für Python-Umgebung /// protected dynamic pyDebug; /// /// 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(SharpPython.PyOptimLevels.O, threaded: true, runtimePath: runtimePath, verbose: false) { base.Initialise(); using (Py.GIL()) { pyModManagement = Py.Import("delta_barth.management"); pyModPipeline = Py.Import("delta_barth.pipelines"); pyDebug = Py.Import("delta_barth._debug"); } } /// /// Gibt Runtime-relevante Pfade in der Konsole aus /// public string DebugCall() { string infos; using (Py.GIL()) { infos = pyDebug.print_infos(); } Console.WriteLine(infos); Console.WriteLine($"PyEngine - PYHOME: {PythonEngine.PythonHome}"); Console.WriteLine($"PyEngine - PYPATH: {PythonEngine.PythonPath}"); return infos; } ///// ///// 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(); // using (Py.GIL()) { // 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(); // string pyJson; // using (Py.GIL()) // { // pyJson = pyModPipeline.pipeline_sales_forecast_dummy(firmaId, analyseBeginn); // } // 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: Liste von Firmen-IDs, 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(List? firmaIds, DateTime? analyseBeginn) //{ // AssertNotDisposed(); // string pyJson; // using (Py.GIL()) // { // pyJson = pyModPipeline.pipeline_sales_forecast(firmaIds, analyseBeginn); // } // 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(); // using (Py.GIL()) // { // pyModManagement.setup(datenPfad, basisApiUrl); // } //} ///// ///// Hole die konfigurierte API-Basis-URL aus der Python-Umgebung ///// ///// konfigurierte Basis-URL //protected string GetBaseApiUrl() //{ // AssertNotDisposed(); // string pyJson; // using (Py.GIL()) // { // pyJson = (string)pyModManagement.get_base_url(); // } // return pyJson; //} ///// ///// Hole den konfigurierten Datenpfad zur Dateiverwaltung aus der Python-Umgebung ///// ///// konfigurierten Datenpfad //protected string GetDataPath() //{ // AssertNotDisposed(); // string pyJson; // using (Py.GIL()) // { // pyJson = (string)pyModManagement.get_data_path(); // } // return pyJson; //} ///// ///// Hole die konfigurierten Nutzerdaten zur API-Interaktion aus der Python-Umgebung ///// ///// konfigurierte Nutzerdaten ///// //protected DataObjects.Credentials GetCredentials() //{ // AssertNotDisposed(); // string pyJson; // using (Py.GIL()) // { // pyJson = (string)pyModManagement.get_credentials(); // } // DataObjects.Credentials? parsed = JsonSerializer.Deserialize(pyJson); // return parsed ?? throw new PythonParsingException("Could not correctly parse object from Python"); //} } }