Fehlersuche Flask, mit Vue.js

Django, Flask, Bottle, WSGI, CGI…
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen und vielen Dank für die Erklärung.

Ich starte jetzt damit eine Testseite mit HTML und CSS zu schreiben, weil auch hier habe ich 0 Routine und muss alles erst recherchieren. Wenn ich das habe, kann ich testen was der Browser daraus macht, wenn ich ihn die PDF drucken lasse.
Wenn mir das nicht gefällt oder die Motivation hoch genug ist, dann kann ich die Testseite als Vorlage für `weasyprint` nehmen und schauen wie ich die Daten mit `Jinja2` da rein bekomme.


Der letzte Wunsch, der für die Webseite neulich geäußert wurde, wird mich vermutlich auch an meine Grenzen bringen. Es wäre schön, das man ältere Berechnungen wieder aufrufen könnte, also nicht die PDF sondern die Eingaben auf der Webseite. Würde die Seite nur von einer Person genutzt werden, würde ich die Eingabedaten "einfach" in eine Datenbank schreiben. Naja ganz einfach nicht, weil ich vom Datenbankdesign eigentlich keine Ahnung habe. Da aber mehrere Benutzer zugreifen können müsste ich Benutzer anlegen, damit jeder seine Berechnungen abspeichern kann. Da kommen einige Probleme/Unwissenheit auf mich zu. Nur mal als kleiner Ausblick, mit was ich mich nach der PDF beschäftigen will.

Schönen Sonntag,
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Ich habe jetzt nicht sonderlich viel CSS eingebaut, aber zumindest mal etwas um testen zu können. Der Browser unterteilt die PDF schon nicht schön auf verschiedene Seiten und auch der URL in den Ecken oben stört mich.
Jetzt habe ich eine Datei erstellt, die gekürzt so aussieht:

Code: Alles auswählen

#!/usr/bin/env python
import jinja2
from weasyprint import HTML
from pathlib import Path

LOGO_PATH = str(Path(__file__).parent / "logo.PNG")

def _make_template(data):
    environment = jinja2.Environment(autoescape=True)
    template = environment.from_string("""
        <style>
          #results {
            font-family: Arial, Helvetica, sans-serif;
            border-collapse: collapse;
            width: 70%;
          }

          #results td, #results th {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
          }

          #results tr:nth-child(even){background-color: #f2f2f2;}
          #results tr:hover {background-color: #ddd;}

          #logo {
            width: 20%;
            height: auto;
            float: inline-end;
          }
        </style>

        <img src={{LOGO_PATH}} alt="logo" id="logo"><br/>
        <h1>Ergebnisse</h1>
          <table id="results">
              {%for result in data.results%}
                <tr><th>Ergebnisse der Stufe {{loop.index}} </th></tr>
                <tr><th>Kondensatausfall</th><td>{{result.condensate_outage}} kg/h</td></tr>
                <tr><th>Leistung</th><td>{{result.cooling_power}} kW</td></tr>
                <br/>
                {%endfor%}
          </table>
    """)
    html_text = template.render(data=data)
    return HTML(string=html_text)


def make_html(data):
    pass


def make_pdf(data):
    html = _make_template(data)
    html.write_pdf("Test.pdf")


def main():
    ...


if __name__ == '__main__':
    main()
Aus meiner Flask-App rufe ich `make_pdf` auf und übergebe die Daten. Es wird auch eine PDF erstellt. Ich weis das ist noch ein relativer Pfad, wird dann noch durch `pathlib.Path().home() / "weiß noch nicht/*.pdf` ersetzt. Sobald ich weis wohin damit

Mein erstes Problem ist, wie bekomme ich denn mein Pfad des Logos in den HTML-Text? `render` nimmt als Argumente **kwargs an, aber auch wenn ich

Code: Alles auswählen

html_text = template.render(data=data, logo=LOGO_PATH)
schreibe und dann im String `{{logo}}` oder nur `logo` schreibe, wird es nicht angezeigt.

Mein anderes Problem ist, das was `_make_template()` jetzt erzeugt, ist auch das was ich an den Browser senden muss. Das wollte ich eigentlich mit `make_html` machen, aber das gibt nur den Rückgabewerte der anderen Funktion dann weiter. Das muss ich noch mal überdenken.
Ich frage mich nur, wie ich dass dann jetzt JavaScript-Seitig einbauen muss. Bis jetzt habe ich da sowas stehen:

Code: Alles auswählen

const response = await fetch('http://localhost:5000/process_calculation');
	      calculated_data.value = await response.json();
        console.log(calculated_data)
        if (!response.ok) {
          console.log("Irgendwas ging schief, aber was?");
        }
        show_result_mode.value = true
Das muss ich ersetzen, weil jetzt kommen ja nicht mehr die Daten, sondern die "fertige" Seite, die nur noch angezeigt werden muss. Und der nächste Knoten kommt dann gleich, weil ich auf der Seite mit den Ergebnissen dann gerne einen Button hätte, der die PDF erstellt.

Könnt ihr mir da bitte noch einmal helfen?

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17947
Registriert: Sonntag 21. Oktober 2012, 17:20

ich verstehe nicht ganz, wo Du jetzt welche Probleme hast. Kannst Du bitte konkret Programm und Problem schildern?
Für Bilder nimmt man ganz normal relative Pfade und gibt bei HTML base_url an.
Und natürlich braucht man css-Styles die die Seite schön zum Drucken machen; dass das automatisch ohne Anpassungen geht, hat ja niemand behauptet.

Wenn Du das PDF erzeugst, speicherst Du das natürlich nicht als Datei, sondern gibst die Daten direkt mit Mime-Type application/pdf zurück, dann weiß der Browser auch, was er damit anfangen kann.
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Antwort.
Für Bilder nimmt man ganz normal relative Pfade und gibt bei HTML base_url an.
Okay, das hat funktioniert.
Und natürlich braucht man css-Styles die die Seite schön zum Drucken machen; dass das automatisch ohne Anpassungen geht, hat ja niemand behauptet.
Achso, ich hatte halt die Seite etwas schön gemacht, damit sie im Browser ein bisschen was her macht und habe das dann als PDF gedruckt. An den Teil, das man für das Drucken auch noch was einbauen muss, habe ich nicht gedacht.

Bevor ich mein Problem bzw. das was ich oben versucht habe auszudrücken, genauers beschreibe, will ich auf deinen letzten Absatz eingehen. Wenn ich das begriffen habe, könnte es sein, dass damit auch mein Problem gelöst ist.
Wenn ich nach `Mime-Type` suche, dann finde ich auf dieser Seite:
https://developer.mozilla.org/en-US/doc ... mmon_types
auch `application/pdf` aber was ich damit machen soll bzw. wie ich damit umgehen soll, weis ich noch nicht.
Ich habe das noch gefunden:
https://stackoverflow.com/questions/280 ... javascript

Weis aber nicht richtig, ob dass das ist was du meinst.

Vielleicht doch noch mal zu dem was ich fragen wollte. Die Python-Datei die ich gepostet habe, erstellt mir jetzt meine Ergebnisseite. So wie sie da steht speichert sie mir die Seite gleich als PDF. Wenn ich `write_pdf` weglasse, dann habe ich eine Seite und weis nicht, wie ich sie im Browser darstellen kann.
Bis jetzt schicke ich serverseitig immer JSON, nehme das mit JavaScript entgegen und baue das "vor Ort" in HTML ein. Jetzt habe ich aber ein HTML-Objekt. Wie gehe ich damit um?
Mehr Code kann ich eigentlich nicht zeigen, weil ich zu diesem Thema nicht mehr habe.
Ich habe diese Test.html Datei:

Code: Alles auswählen

<html lang="en">
<head>
  <meta charset="utf-8">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
  #results {
    font-family: Arial, Helvetica, sans-serif;
    border-collapse: collapse;
    width: 70%;
  }
  
  #results td, #results th {
    border: 1px solid #ddd;
    padding: 8px;
    text-align: left;
  }
  
  #results tr:nth-child(even){background-color: #f2f2f2;}
  
  #results tr:hover {background-color: #ddd;}

  #logo {
    width: 20%;
    height: auto;
    float: inline-end;
    
  }


  </style>
  <title>Kühlerberechnung</title>
</head>
<body>
<div id="app">
<template v-if="show_result_mode">
    <img src="logo.PNG" alt="logo" id="logo"><br/>
    <h1>Ergebnisse</h1>
      <table id="results">
        <template v-for="(result, index) in calculated_data.results">
          <tr><th>Ergebnisse der Stufe {{index + 1}}</th></tr>
          <tr><th>Kondensatausfall</th><td>{{result.condensate_outage}} kg/h</td></tr>
          <br/>
        </template>
      </table>

  
    <div>
    <button @click="show_result_mode = false">Zurück</button>
    </div>
  </template>



<template v-else>
  <h2>Test</h2>
    <button @click="start_calculation">Drück mich</button>
</template>
</div>

<script>
  const { createApp, ref } = Vue

  createApp({
    setup() {
      const show_result_mode = ref(false)
      const calculated_data = ref({})

      async function start_calculation() {
        const response = await fetch('http://localhost:5000/process_calculation');
	    calculated_data.value = await response.json();
        show_result_mode.value = true
      };

      return {
        start_calculation, calculated_data, show_result_mode
      }
    }
  }).mount('#app')
</script>
</body>

</html>
Und muss das template mit der Tabelle löschen und dafür muss irgendwie das HTML-Objekt das mit `weasyprint` erstellt wurde angezeigt werden und bei Bedarf als PDF gespeichert werden.
Senden werde ich das vermutlich ähnlich wie hier, nur halt mir `application/pdf`:
https://doc.courtbouillon.org/weasyprin ... l-fetchers
Falls ja, wie gehts dann weiter?

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
grubenfox
User
Beiträge: 497
Registriert: Freitag 2. Dezember 2022, 15:49

hilft das: https://flask.palletsprojects.com/en/3. ... -responses ?
Es ist eigentlich immer dasselbe, ob nun HTML-Text, JavaScript-Text oder PDF-Daten. Nur ein anderer Mimetype im jeweiligen Response-Objekt.
Sirius3
User
Beiträge: 17947
Registriert: Sonntag 21. Oktober 2012, 17:20

Browser sind Ausführungsumgebungen für Javascript, und können interaktive Benutzeroberflächen darstellen, die Auszeichnungssprache dafür ist HTML. Das macht es für den Entwickler einfach und für den Anwender bequem.
HTML kann man auch dafür verwenden, den Aufbau einer Seite zu beschreiben, die klassisch gedruckt wird. Dann hat man aber statisches HTML ohne interaktive Elemente. weasyprint nimmt das als Ausgangspunkt, es fehlt aber die Ausführungsumgebung für Javascript. Ist ja für den Anwendungsfall auch gar nicht nötig. Du kannst also nicht exakt den selben HTML-Code sowohl für eine interaktive Webanwendung als auch für ein statisches PDF benutzen.

Du hast also drei Möglichkeiten: Du benutzt interaktives Javascript+HTML für das Frontend und Python + statisches HTML als Backend. Vorteil, Du brauchst wenig Javascript, mußt aber zwei verschieden HTML-Templates pflegen.
Du benutzt statisches HTML via Jinja sowohl im Front- als auch im Backend. Entbindet dich nicht der Aufgabe, eine schöne Ausgabe für PDF und für den Browser zu entwerfen, macht aber das Frontend weniger bequem für den Anwender.

Du bentuzt sowohl im Backend als auch im Frontend Javascript und vergisst Python komplett.
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Hilfe.
@grubenfox das Problem ist nicht unbedingt das senden, sondern das empfangen bzw darstellen.

@Sirius3
Die 3 scheidet völlig aus. :)
Die erste Möglichkeit habe ich jetzt gerade? Ich habe ein Template dass der Benutzer im Browser angezeigt bekommt und ein zweites, aus dem ich mit Python und `weasyprint` eine PDF erstelle.
Ich würde gerne die zweite Möglichkeit versuchen.

render_pdf.py:

Code: Alles auswählen

#!/usr/bin/env python
import jinja2
from weasyprint import HTML
from pathlib import Path

LOGO_PATH = str(Path(__file__).parent / "logo.PNG")

def make_template(data):
    environment = jinja2.Environment(autoescape=True)
    template = environment.from_string("""
        <style>
          #results {
            font-family: Arial, Helvetica, sans-serif;
            border-collapse: collapse;
            width: 70%;
          }

          #results td, #results th {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
          }

          #results tr:nth-child(even){background-color: #f2f2f2;}
          #results tr:hover {background-color: #ddd;}

          #logo {
            width: 20%;
            height: auto;
            float: inline-end;
          }
        </style>

        <img src={{LOGO_PATH}} alt="logo" id="logo"><br/>
        <h1>Ergebnisse</h1>
          <table id="results">
              {%for result in data.results%}
                <tr><th>Ergebnisse der Stufe {{loop.index}} </th></tr>
                <tr><th>Kondensatausfall</th><td>{{result.condensate_outage}} kg/h</td></tr>
                <tr><th>Leistung</th><td>{{result.cooling_power}} kW</td></tr>
                <br/>
                {%endfor%}
          </table>
    """)
    html_text = template.render(data=data)
    return HTML(string=html_text)


def main():
    ...


if __name__ == '__main__':
    main()
Meine app.py auf das wesentliche reduziert. An sich geht es ja nur um `process_calculation` das wird aufgerufen und `data` ist mein Objekt dass die Ergebnisse und Eingaben enthält.

Code: Alles auswählen

#!/usr/bin/env python
from json import loads
from flask_weasyprint import render_pdf

from attrs import define, field

from flask import Flask
from flask_classful import FlaskView, request, route
from flask_cors import CORS
from render_pdf import make_pdf


@define
class App(FlaskView):
    cooler_data = field()
    user_input = None
    results = None


    @route("/process_calculation", methods=["POST", "GET"])
    def process_calculation(self):
        self.process_input(DATA)
        if request.method == "GET":
            data = self.merge_result_and_input()
            html = make_pdf(data)
            return render_pdf(html)


def main():
    app = Flask(__name__)
    app.config.from_object(__name__)
    CORS(app, resources={r"/*": {"origins": "*"}})
    App.register(
        app, route_base="/", init_argument=loads(COOLER_DATA_FILE.read_bytes())
    )
    app.run()


if __name__ == "__main__":
    main()
Was mache ich jetzt mit dem was Python mir zurück schickt? So wie das jetzt ist, passiert gar nichts. Ich muss irgendwo doch reinschreiben, dass da was vom Server kommt und man das im Browser darstellen soll? Irgendwie mit dem `response`-Objekt hier arbeiten?

Code: Alles auswählen

<html lang="en">
<head>
  <meta charset="utf-8">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

  <title>Kühlerberechnung</title>
</head>
<body>
<div id="app">
<template v-if="show_result_mode">
    <h1>Ergebnisse</h1>
    <form  action="/process_calculation" method="POST"> 
    <div>
    <button @click="show_result_mode = false">Zurück</button>
    </div>
  </template>



<template v-else>
  <h2>Test</h2>
    <button @click="start_calculation">Drück mich</button>
</template>
</div>

<script>
  const { createApp, ref } = Vue

  createApp({
    setup() {
      const show_result_mode = ref(false)
      const calculated_data = ref({})

      async function start_calculation() {
        const response = await fetch('http://localhost:5000/process_calculation');
        show_result_mode.value = true
      };

      return {
        start_calculation, calculated_data, show_result_mode
      }
    }
  }).mount('#app')
</script>
</body>

</html>
Danke und Grüße
Dennis

Edit: Ach wenn ich http://127.0.0.1:5000/process_calculation aufrufe, dann kommt meine PDF-Ansicht :)
Das muss ich nur irgendwie mit der anderen Seite verbinden.

Edit: Okay mit `window.open` in JavaScript kann ich die Seite öffnen. Das ist mal ein großer Schritt vorwärts. Danke an der Stelle noch mal für eure Hilfe.

Geht das auch, das man die Ergebnisse, wie vorher auch, als HTML anzeigt und nur bei Bedarf dann extra als PDF ohne zwei Tampletes pflegen zu müssen?
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
grubenfox
User
Beiträge: 497
Registriert: Freitag 2. Dezember 2022, 15:49

Dennis89 hat geschrieben: Sonntag 9. Juni 2024, 21:16 Geht das auch, das man die Ergebnisse, wie vorher auch, als HTML anzeigt und nur bei Bedarf dann extra als PDF ohne zwei Tampletes pflegen zu müssen?
Ja, zwei unterschiedliche URLs definieren. Eine, die man z.B. als http://127.0.0.1:5000/process_calculati ... df-zurueck aufrufen kann und die dann deine PDF-Ansicht zurückliefert und eine andere http://127.0.0.1:5000/process_calculati ... ml-zurueck die dann eben HTML zurückliefert. Die erste steht ja schon oben bei der Methode `process_calculation`, fehlt nur noch eine Methode die sich um die neue zweite Flask-Route kümmert.

Wobei das Template (wenn es für HTML- und PDF-Ansicht genutzt wird) dann ja einen Link enthält der dann bei anklicken das PDF zurückliefert. Da macht es wohl Sinn (nur) im Print-CSS diesen Link unsichtbar zu machen...
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Hi und danke für die Antwort.

Ich teste das heute Abend mal und melde mich dann wieder.


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Abend zusammen,

hat etwas länger gedauert. Ich habe die Variante getestet, bin aktuell aber für den weiteren Versuch dabei geblieben, das direkt die PDF-Darstellung geöffnet wird. Einfach um es noch etwas einfach zu halten. Die Möglichkeiten kenne ich jetzt und wenn es an das Desgin und den Ablauf geht, kann ich das bei Bedarf einbauen oder jemand anders.

Ich hatte schon angedeutet, dass ein speichern und wieder abrufen der Ergebnisse bzw. Eingaben gewünscht ist. Dazu hätte ich grundlegende Fragen.
Ich würde es für sinnvoll halten, dass man dann Benutzer anlegt und jeder Benutzer seine Berechnungen wieder abrufen kann.
Ich habe von Datenbankdesign keine Ahnung. Ich brauche zumindest mal einen Namen und ein Passwort, so das mein Programm in die Datenbank schauen kann und nach dem Namen zu suchen und dem passenden Passwort. Passwörter werden nicht in Klartext abgelegt, sondern gehasht?
Und nun muss jeder Benutzer eine "eigene" Tabelle haben in der die ganzen Eingaben für die Berechnung mit einer ID hinterlegt sind. Ist hier das Stichwort "relationale Datenbank" richtig, nach dem ich suchen soll?
Könnt ihr mir sonst noch irgendwelche wichtigen Informationen mit auf den Weg geben, die ich beachten sollte oder falls ich eine falsche Vorstellung habe, diese bitte verbessern?

Ich würde mich die nächsten Tage gerne mit dem Thema beschäftigen.

Viele Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
grubenfox
User
Beiträge: 497
Registriert: Freitag 2. Dezember 2022, 15:49

Die, wie auch immer geartete, Datenstruktur in der die benutzerspezifischen Parameter für die verschiedenen Benutzer(-namen) hinterlegt sind, soll persistent werden? Vermutlich wird die, wie auch immer geartete, Datenstruktur wohl ein Dictionary mit den Benutzernamen als 'Key' sein.
Reicht da nicht ein

Code: Alles auswählen

import shelve
(plus den zusätzlichen Programmzeilen zum lesen/schreiben der Daten)?
Benutzeravatar
__blackjack__
User
Beiträge: 13369
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@grubenfox: Ist ein `shelve` denn sicher gegen die Verwendung aus mehreren Threads oder gar Prozessen?
“It is easier to optimize correct code than to correct optimized code.” — Bill Harlan
Benutzeravatar
sparrow
User
Beiträge: 4279
Registriert: Freitag 17. April 2009, 10:28

@Dennis89: Wenn du eh mit einer Datenbank arbeitest, bietet sich die auch für die Speicherung der Benutzer an.
Ich bin mir fast sicher, dass es da auch schon etwas gibt, dass man in Flask Stöpsel kann, damit man sich um das Hasen des Passworts und die Session nicht per Hand kümmern muss.

In einer relationalen Datenbank gibt es nicht eine Tabelle je Benutzer sondern eine Tabelle für alle Messungen. Und es gibt darin einen Fremdschlüssel auf den Benutzer. Und jede Selektion von Daten aus der Tabelle schränkst du auf die Datensätze des aktuellen Benutzers ein.
Benutzeravatar
grubenfox
User
Beiträge: 497
Registriert: Freitag 2. Dezember 2022, 15:49

__blackjack__ hat geschrieben: Samstag 15. Juni 2024, 01:22 @grubenfox: Ist ein `shelve` denn sicher gegen die Verwendung aus mehreren Threads oder gar Prozessen?
auf der einen Seite: wenn man selbst das nachprogrammiert, was bei einer Datenbank andere Leute schon fertig programmiert haben, dann 'vermutlich'. ;)
eine andere Seite: auch bei einer Datenbank hat man das Thema "Concurrency Control" um das man sich kümmern muss. Je nach gewähltem "Transaction Isolation Level" lautet auch bei einer Datenbank die Antwort auf die Frage 'Nein'.
Dann wäre da noch die Frage ob mehrere Threads oder gar Prozesse eigentlich relevant sind. Gestern Abend war das für mich ein Programm/Webanwendung das einmal beim Programmstart die Konfiguration seiner Benutzer (dort steht nur welche Parameter der Benutzer grubenfox zuletzt nutzte, welche der Benutzer Dennis89, ...) einliest und irgendwann, vielleicht beim Runterfahren, die Konfigurationen wieder auf die Platte schreibt. Wobei im Zweifel paralleles Lesen der Konfig sollte hoffentlich auch bei Shelve kein Problem sein. Das Schreiben der Daten ist da kritischer.

An das speichern der ganzen verschiedenen Messungen in der DB hatte ich gestern nicht gedacht.
Dennis89 hat geschrieben: Freitag 14. Juni 2024, 21:33 Könnt ihr mir sonst noch irgendwelche wichtigen Informationen mit auf den Weg geben, die ich beachten sollte

Ich würde mich die nächsten Tage gerne mit dem Thema beschäftigen.
Hiermit getan... "Concurrency Control" und "Transaction Isolation Level" wären zwei Stichpunkte mit denen man sich auseinander setzten sollte... und wann irgendwelche Daten irgendwo persistent hingeschrieben werden. Ob sich da was gegenseitig stören könnte...
Sirius3
User
Beiträge: 17947
Registriert: Sonntag 21. Oktober 2012, 17:20

@Denis89: Es gibt sowas wie flask-login und flask-sqlalchemy. Du musst dir also nicht alles selbst überlegen.
Vielleicht ist es auch eine Alternative auf django umzusteigen, das bringt das, was Du brauchst, schon mit und hat gute Tutorials.
Benutzeravatar
sparrow
User
Beiträge: 4279
Registriert: Freitag 17. April 2009, 10:28

@grubenfox: Naja, die Art der Anwendung bedingt ja geradezu, dass eine Art von Parralelität vorhanden sein muss. Selbst bei einem asynchronen Ansatz startet man mehr als einen Prozess. Und dein Ansatz würde nur funktionieren, wenn die Benutzerdaten statisch wären. Ansonsten müsste man ein komplexes Konstrukt bauen, wie sie während der Laufzeit durch die Prozesse synchronisiert werden. Und auch dann ist es ratsam die sofort auf die Platte zu schreiben und nicht erst beim Abschalten. Sonst sind sie weg, wenn auf dem Weg dorthin etwas schief geht.
Alles Probleme, die gerade ACID Datenbanken seit Jahrzehnten gelöst haben.
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die ganzen Information.

Bis jetzt arbeite ich noch mit keiner Datenbank, die wird jetzt erst dazu kommen. Es gibt noch keine Benutzer und keine Speicherung der Eingaben.

Ich melde mich wieder, wenn ich mich eingelesen habe und eventuell schon etwas Code habe.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Abend,

das Projekt gibt es noch und es geht jetzt auch wieder weiter. Die Speicherung der Daten ist noch aktuell, allerdings soll es keine Profile geben, sondern jeder kann ohne Anmeldung das Programm nutzen und seine Werte speichern. Es wird ein weiteres Feld "Auftragsnummer" geben, dass ist dann in der Datenbank der `primary key`. Also zumindest nach meiner Vorstellung. Wenn ein Benutzer gespeicherte Werte sucht, soll er die nach der Auftragsnummer suchen können.

Mir wurde ja zu Django geraten, wenn es um Benutzer-Profile geht. Damit habe ich mich auch etwas beschäftigt und die Möglichkeiten sind echt cool. Da das jetzt nicht gebraucht wird, dachte ich mir das ich bei Flask bleiben kann. Ich habe mir jetzt ehrlich gesagt rein in Bezug auf die Datenbank nicht weiter angeschaut, was Django da bietet und auch `flask-sqlalchemy` habe ich mir noch nicht angeschaut.
Aber als Vorbereitung habe ich mich endlich aufgerafft und eine Datenbank mit MariaDB erstellt und das erste mal `SQLAlchemy` verwendet. Das gefällt mir auch sehr gut.

Mein Plan ist jetzt erst mal eine Datenbank für mein Programm aufzubauen und ehrlich gesagt habe ich keine Ahnung, wie man die sinnvoll aufbaut.
Für Tests habe ich Beispieleingabedaten, die sehen so aus:

Code: Alles auswählen

DATA = {
    "gases": [{"name": "CO2", "percent": "100"}],
    "general": {
        "glykohl_percent": "50",
        "normvolume_flow": "500",
        "suction_pressure": "1",
        "temperature_suction_filter": "25",
    },
    "coolers": [
        {
            "name": "DN 40",
            "cooling_countercurrent": True,
            "gas_humidity_after_cooler": "100",
            "gas_humidity_before_cooler": "100",
            "gas_pressure": "4.2",
            "gas_temperature_in": "170.9",
            "gas_temperature_out": "40",
            "inside_diameter_cooling_pipe": "1",
            "inside_diameter_outer_pipe": "100",
            "number_of_pipes": "2",
            "outside_diameter_cooling_pipe": "2",
            "temperature_cooling_fluid_in": "25",
            "temperature_cooling_fluid_out": "38",
            "number_of_cooler": "1",
        },
        {
            "name": "DN 40",
            "cooling_countercurrent": True,
            "gas_humidity_after_cooler": "100",
            "gas_humidity_before_cooler": "100",
            "gas_pressure": "19",
            "gas_temperature_in": "165.3",
            "gas_temperature_out": "40",
            "inside_diameter_cooling_pipe": "1",
            "inside_diameter_outer_pipe": "100",
            "number_of_pipes": "2",
            "outside_diameter_cooling_pipe": "2",
            "temperature_cooling_fluid_in": "25",
            "temperature_cooling_fluid_out": "38",
            "number_of_cooler": "1",
        },
    ],
}
`coolers` und `gases` sind Listen, wie kann ich das in der Datenbank abbilden und würdet ihr 3 tables machen, die sich alle auf die Auftragsnummer beziehen bzw. mit einem Fremdschlüsse "Auftragsnummer"? Oder eine Tabelle mit "Auftragsnummer" als `primary key` und dann alle Eingabewerte runter schreiben? (Falls das mit den Listen irgendwie geht)

Ich bin hier gerade sehr planlos. Ich denke mir, bevor ich nicht weis, wie die Datenbank bzw das Datenbankdesign aussieht, brauche ich auch nicht zu überlegen oder Flask oder nicht, weil ich dann erst mal ohne das Web-Zeugs mit SQLAlchemy Code geschrieben hätte um mit der Datenbank richtig um zugehen. Meine einzige Berührung mit Datenbanken war, zwei Temperaturwerte rein zu schreiben und auszulesen. Daher ist das für mich noch komplett neu und ich würde mich sehr freuen, wenn ihr mit helfen würdet. Sehr gerne auch mit alternative Vorgehensweisen.

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17947
Registriert: Sonntag 21. Oktober 2012, 17:20

Bei Datenbanktabellen möchte man möglichst keine Information doppelt speichern.
Du hast also Gase mit all ihren generischen Eigenschaften, und Cooler, mit generischen Eigenschaften.
Dann hast Du Aufträge, mit konkret verwendeten Gasen und Coolern.
Damit zähle ich bereits 5 Tabellen.
1. gas_id, name, ...
2. cooler_id, name, ...
3. order_id, glycol_precentage, normvolume_flow, ...
Und die Verknüpfung der Tabellen als eigene Tabelle:
4. order_id, gas_id, percentage
5. oder_id, cooler_id, ...
Benutzeravatar
Dennis89
User
Beiträge: 1259
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Antwort.
Ich versuche mir das in "richtigen" Tabellen vorzustellen und visuell würde das nach meinem Verständnis dann so aussehen:

Code: Alles auswählen

gas_id | name
-------------
1      | CO2
2      | H2


cooler_id | name | inside_diamter | usw.
----------------------------------------
1         | DN40 | 1              |


oder_id | glycol_percentage | normvolume_flow
---------------------------------------------
12345   | 30                | 500


order_id | gas_id | percentage
------------------------------
12345    | 1      | 50
12345    | 2      | 50


order_id | cooler_id
--------------------
12345    | 1
Ich glaube, dass da noch ein Fehler drin ist, weil ich könnte doch in der 4. Tabelle anstat `gas_id` gleich den Gasname reinschreiben? Zu Gasen gibt es ja nicht mehr als Name und Anteil. Und eigentlich geht das doch so gar nicht, ich kann nicht in einer Tabelle öfters die gleiche ID haben (order_id)?

Das war doch sicherlich anders gemeint?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten