Fehlersuche Flask, mit Vue.js

Django, Flask, Bottle, WSGI, CGI…
Sirius3
User
Beiträge: 17993
Registriert: Sonntag 21. Oktober 2012, 17:20

commit ruft man erst am Ende der Funktion auf, wenn alles korrekt geschrieben worden ist, bei einem Fehler ruft man rollback auf.
Und da man das nicht selbst machen möchte, benutzt man einen Kontext-Manager.

Code: Alles auswählen

    @route("/save_input_data", methods=["POST"])
    def save_input_data(self):
        input_data = structure(request.get_json(), Data)
        session = self.database.session
        with session.begin():
            order = Order(
                glykohl_percent=input_data.order.glykohl_percent,
                normvolume_flow=input_data.order.normvolume_flow,
                suction_pressure=input_data.order.suction_pressure,
                temperature_suction_filter=input_data.order.temperature_suction_filter,
            )
            session.add(order)
            session.flush()

            for cooler in input_data.coolers:
                order_cooler_link = OrderCoolerLink(
                    cooler_id=cooler.id,
                    order_id=order.id,
                    cooling_countercurrent=cooler.cooling_countercurrent,
                    gas_humidity_after_cooler=cooler.gas_humidity_after_cooler,
                    gas_humidity_before_cooler=cooler.gas_humidity_before_cooler,
                    gas_pressure=cooler.gas_pressure,
                    gas_temperature_in=cooler.gas_temperature_in,
                    gas_temperature_out=cooler.gas_temperature_out,
                    temperature_cooling_fluid_in=cooler.temperature_cooling_fluid_in,
                    temperature_cooling_fluid_out=cooler.temperature_cooling_fluid_out,
                    number_of_cooler=cooler.number_of_cooler,
                )
                session.add(order_cooler_link)

            for gas in input_data.gases:
                order_gas_link = OrderGasLink(
                    order_id=order.id,
                    gas_id=gas.id,
                    percentage=gas.percent
                )
                session.add(order_gas_link)
Benutzeravatar
Dennis89
User
Beiträge: 1305
Registriert: Freitag 11. Dezember 2020, 15:13

Vielen Dank, funktioniert. 🙂

Ein Fehler ist mir noch aufgefallen. Ich habe die Auftragsnummer gar nirgends gespeichert, nach dieser soll der User aber den Datensatz auswählen, den er wieder sehen will. Habe eine Spalte in die `Order` - Tabelle hinzugefügt und die Eigenschaft UNIQUE mit angegeben. Unsere Auftragsnummer ist ja genau einem Auftrag zugeordnet und für diesen wird berechnet.
Funktioniert auch, wenn ich die gleiche Nummer ein zweites mal speichern will, bekomme ich ein ``sqlalchemy.exc.IntegrityError``.
Die Einschränkung ist aber doch zu hart. Es können sich im Laufe des Projekts Werte ändern, dann sollte man die Möglichkeit haben, den Auftrag zu überschreiben. Am besten nach dem bestätigen eines Hinweises.

Bevor ich jetzt wieder versuche selbst etwas zu programmieren, das es vielleicht schon gibt, wollte ich bei euch kurz nachfragen.

Meine Idee hat auch noch Lücken und Schönheitsfehler, von denen ich nicht weis, ob man das so lässt und das gar nicht stört oder wie man damit umgeht.

Ich wäre es so angegangen:
Als erstes UNIQUE aus der Spalte wieder entfernen.
`save_input_data` wird aufgerufen, ich frage die Tabelle `Order` ab und schaue ob die Auftragsnummer schon drin ist. wenn ja, dann merke ich mir die `id` des Datensatzes und die `input_data`. Dann muss ich an den Browser eine Meldung schicken, dass ein Hinweistext für das Überschreiben der Daten kommt. Wenn er bestätigt wird, dann schreibe ich die Daten und mit der gemerkten `id` lösche ich den alten Datensatz.

Ich sehe dabei das Problem mit dem merken der `id` und der `input_data`. Was heißt Problem, ich muss halt die `id` und die `input_data` mit an den Browser schicken und wieder zurückschicken, damit die nicht verloren gehen.
Der Schönheitsfehler wäre, das ich Lücken in der `id`-Spalte habe. Für die Funktion ist das egal.

Hört sich die Vorgehensweise logisch für euch an?

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

Also wenn man das ein wenig umsortiert, dann erscheint mir das schon logisch.

UNIQUE bleibt! Man könnte `save_input_data` vielleicht noch in `save_or_update_input_data` umbenennen. Aber ich bleibe hier mal beim Namen `save_input_data`
Ausgangspunkt: die Anwendung hat schon mal die erfassten Daten an den Browser gesendet (der Anwender muss ja auch die Daten sehen um die es geht und die er teilweise ändern darf).

Nun geht es los mit dem Aufruf von `save_input_data`. `save_input_data` vergleicht die übertragenen Daten mit den Daten die für die Auftragsnummer oder für die `id` in der Datenbank gespeichert sind.
Ist der übermittelte Datensatz identisch mit dem gespeicherten Datensatz, dann sind wir raus und fertig.

Gibt es Unterschiede, dann muss ich an den Browser eine Meldung schicken, dass ein Hinweistext für das Überschreiben der Daten kommt. Wenn der bestätigt wird, dann überschreibe ich die gespeicherten Daten mit den veränderten Daten und bin fertig. Wird der Hinweis nicht bestätigt, dann bin ich eh wieder raus und muss nichts machen und bin fertig.
Benutzeravatar
Dennis89
User
Beiträge: 1305
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen und danke für die Antwort.

Stimmt ich muss ja gar nicht löschen, daran hatte ich noch gar nicht gedacht.
Als ich vorhin aufgestanden bin, ist mir noch eingefallen, dass ich die Daten auch nicht hin und her senden muss. Der Browser hat ja schon die ausgelesenen Auftragsnummern. Dann könnte ich per JavaScript schon prüfen und den Hinweis ausgeben und dann nur die Daten schicken die auch gespeichert werden. Dann kommt Python nur zum Einsatz, wenn gespeichert/überschrieben wird.



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

Wobei so ganz allgemein noch der "nette" Fall zu bedenken wäre: bis der Kollege mit seinen Änderungen fertig ist und auf 'speichern' klickt, da haben schon andere Kollegen irgendwelche relevanten Daten geändert. entweder sogar für die gleiche Order oder irgendwelche anderen Daten die auch mit der aktuellen Order in Verbindung stehen. So als Unbeteiligter scheint mir das letzte hier kein Problem zu sein, aber dafür das dieselbe Order von mehreren Personen bearbeitet wird, sehe ich hier im Code erst mal noch nichts. Wird vermutlich extern geregelt, in dem nur ein Mitarbeiter den 'Zettel' mit dem neuen Auftrag in die Finger bekommt. Oder irgendwie so... ist halt die Frage kann/darf das vorkommen und wird passend darauf reagiert...
Benutzeravatar
Dennis89
User
Beiträge: 1305
Registriert: Freitag 11. Dezember 2020, 15:13

Das ist richtig, ich wollte ursprünglich (findet man hier im Thread auch irgendwo) Benutzer anlegen, damit niemand von anderen irgendwas ändern kann. Die Abteilung die dieses Programm nachher hauptsächlich nutzt, wollte das aber nicht. Die sind auch nur zu dritt.

Letztendlich zählt das PDF-Dokument, dass ausgegeben wird. Das wird zu den anderen Dokumenten abgelegt. Wenn jemand ausversehen etwas überspeichert, dann muss man auf das Backup hoffen oder die Werte vom PDF-Dokument noch einmal abschreiben.

Habe mir auch schon überlegt, ob ich eine weitere Spalte anlegen soll, sowas wie `old_data` und wenn ein Auftrag überschrieben wird, wird bei dem alten die Spalte `True` und im Notfall könne ich die Daten dann schnell wieder herstellen. Aber diesen Punkt muss ich noch einmal absprechen, vielleicht reicht ein einfaches regelmäßiges Backup auch aus.

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

Hallo,

ich hatte mich dem Projekt wieder etwas gewidmet und ich glaube ich muss doch noch mal mein TODO aufgreifen, mit dem "etwas generischen schreiben" von @__blackjack__ hier.

Der Stand gerade ist der: Ich kann die Eingabewerte in der Datenbank speichern. Es gibt ein Dropdownmenü, in welchem dem User die gespeicherten Auftragsnummern angezeigt werden. Wählt er eine aus, dann wird die ID dieses Auftrags an den Server gesendet und ich hole mir darüber alle notwendigen Werte aus der Datenbank. Jetzt schreibe ich entweder wieder manuell eine JSON-Struktur und greife auf jedes Attribut der Datenbankklassen zu und sende dass dann dem Browser oder ich muss das irgendwie "schöner" mit was generischem(?) schreiben.

An diesem Punkt fehlen mir allerdings die Ideen.

Ich habe ja hier schon "so viel" Code, der die Attribute befüllt:

Code: Alles auswählen

    def save_input_data(self):
        input_data = structure(request.get_json(), Data)
        session = self.database.session
        with session.begin():
            order = Order(
                order_number=input_data.general.order_number,
                glykohl_percent=input_data.general.glykohl_percent,
                normvolume_flow=input_data.general.normvolume_flow,
                suction_pressure=input_data.general.suction_pressure,
                temperature_suction_filter=input_data.general.temperature_suction_filter,
            )
            session.add(order)
            session.flush()
            for cooler in input_data.coolers:
                order_cooler_link = OrderCoolerLink(
                    cooler_id=cooler.id,
                    order_id=order.id,
                    cooling_countercurrent=cooler.cooling_countercurrent,
                    gas_humidity_after_cooler=cooler.gas_humidity_after_cooler,
                    gas_humidity_before_cooler=cooler.gas_humidity_before_cooler,
                    gas_pressure=cooler.gas_pressure,
                    gas_temperature_in=cooler.gas_temperature_in,
                    gas_temperature_out=cooler.gas_temperature_out,
                    temperature_cooling_fluid_in=cooler.temperature_cooling_fluid_in,
                    temperature_cooling_fluid_out=cooler.temperature_cooling_fluid_out,
                    number_of_cooler=cooler.number_of_cooler,
                )
                session.add(order_cooler_link)
            for gas in input_data.gases:
                order_gas_link = OrderGasLink(
                    order_id=order.id, gas_id=gas.id, percentage=gas.percent
                )
                session.add(order_gas_link)
        return Response(status=200)
Und jetzt würde ja mehr oder weniger das Gleiche nur "anders rum" noch mal kommen.
Das ist jetzt für mich irgendwie nicht schön und ich denke, ihr würdet das bestimmt auch anders machen.

Könnt ihr mit bitte zeigen, wie?

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

Die Info hätte ich noch in den anderen Post reinpacken sollen. Die manuell erstellte JSON-Struktur mache ich gerade so:

Code: Alles auswählen

    def get_order_details(self):
        id_ = request.get_json()["id"]
        order = Order.query.filter(Order.id == id_).scalar()
        general = {
            "glykohl_percent": order.glykohl_percent,
            "normvolume_flow": order.normvolume_flow,
            "order_number": order.order_number,
            "suction_pressure": order.suction_pressure,
            "temperature_suction_filter": order.temperature_suction_filter,
        }
        order_gas_link = OrderGasLink.query.filter(OrderGasLink.order_id == id_).all()
        gases = []
        for row in order_gas_link:
            gas = Gas.query.filter(row.gas_id == Gas.id).scalar()
            gases.append(
                {"id": row.gas_id, "name": gas.name, "percent": row.percentage}
            )

        order_cooler_link = OrderCoolerLink.query.filter(
            OrderCoolerLink.order_id == id_
        ).all()
        coolers = []
        for row in order_cooler_link:
            cooler = Cooler.query.filter(row.cooler_id == Cooler.id).scalar()
            coolers.append(
                {
                    "cooling_countercurrent": bool(row.cooling_countercurrent),
                    "gas_humidity_after_cooler": row.gas_humidity_after_cooler,
                    "gas_humidity_before_cooler": row.gas_humidity_before_cooler,
                    "gas_pressure": row.gas_pressure,
                    "gas_temperature_in": row.gas_temperature_in,
                    "gas_temperature_out": row.gas_temperature_out,
                    "id": row.cooler_id,
                    "inside_diameter_cooling_pipe": cooler.inside_diameter_cooling_pipe,
                    "inside_diameter_outer_pipe": cooler.inside_diameter_outer_pipe,
                    "name": cooler.name,
                    "number_of_cooler": row.number_of_cooler,
                    "number_of_pipes": cooler.number_of_pipes,
                    "outside_diameter_cooling_pipe": cooler.outside_diameter_cooling_pipe,
                    "temperature_cooling_fluid_in": row.temperature_cooling_fluid_in,
                    "temperature_cooling_fluid_out": row.temperature_cooling_fluid_out,
                }
            )
        return {"coolers": coolers, "gases": gases, "general": general}
Irgendwie habe ich das Gefühl, das ich so viel doppelt schreibe. Aber vielleicht muss das bei der Art von Programm auch so sein(?).

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

Guten Abend,

ich habe noch eine weitere, wichtigere, Frage. Vor dem speichern der Daten, schaue ich mit JavaScript jetzt nach, ob die Auftragsnummer schon vorhanden ist. Wenn ja, dann gebe ich eine Meldung aus und wenn diese Bestätigt wird, dann soll der vorhandene Datensatz überschrieben/geupdatet werden.

Ich habe in Python ja für jede Table eine Klasse, die würde ich jetzt auch gerne für das Update benutzen. In der Doku finde ich nichts hilfreiches, allgemein fällt es mir sehr schwer, mich darin zurecht zu finden.
Im Netz finde ich viele Beispiele, wie ich mit SQLAlchemy Updates mache, allerdings wird immer ein Wörterbuch übergeben. Das wäre für mich wieder doppelter Code. Wenn ich einen neuen Datensatz anlege, dann erstelle ich eine Instanz meiner Klasse und beim Update, erstelle ich ein Wörterbuch. Das geht doch sicherlich auch mit der Klasse?

Gespeichert wird aktuell so:

Code: Alles auswählen

    def save_input_data(self):
        input_data = structure(request.get_json(), Data)
        session = self.database.session

        with session.begin():
            order = Order(
                order_number=input_data.general.order_number,
                glykohl_percent=input_data.general.glykohl_percent,
                normvolume_flow=input_data.general.normvolume_flow,
                suction_pressure=input_data.general.suction_pressure / BAR_PA_FACTOR,
                temperature_suction_filter=input_data.general.temperature_suction_filter
                - KELVIN_DIFFERENCE,
            )
            session.add(order)
            session.flush()
Wenn schon ein Datensatz mit der gleichen `order_number` vorhanden ist, gibt es ein `sqlalchemy.exc.IntegrityError` den könnte ich abfangen und dann das Update ausführen oder ich sende vom Brwoser gleich die Info mit, ob neu anlegen oder Update.
Davor müsste ich aber noch rausfinden, wie ich das Update mache.
Hier bei SO stehen ein paar Beispiele, aber dazu muss ich jedes mal die gleichen Informationen, die ich bei der Instanzierung der Klasse geschrieben habe, noch einmal schreiben.

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

Guten Morgen,

ich bin anders vorgegangen. Wenn der Datensatz überschrieben werden soll, dann lösche ich erst alle Daten mit den entsprechenden ID's aus der Datenbank und schreibe dann neu.
Zum einen habe ich eine Funktion, die schreibt nur, eine Funktion die löscht und ich habe nichts doppelt. Da kam ich aber erst drauf, als ich merkte, dass (für mich) das updaten der Reihen zu nicht schönen Code führt, wenn man Beispielsweise einen weiteren Kühler hinzufügt oder einen löscht, das gilt auch für die Gase. Dann muss ich verschiedene Reihen updaten und vielleicht eine davon doch noch löschen und das wurde mir zu chaotisch.

Die Funktionen sehen jetzt so aus:

Code: Alles auswählen

    @route("/save_input_data", methods=["POST"])
    def save_input_data(self):
        input_data = structure(request.get_json(), Data)
        if input_data.general.overwrite:
            self.delete_existing_rows(input_data)
        self.save_order(input_data)
        return Response(status=200)

    def save_order(self, input_data):
        session = self.database.session
        with session.begin():
            order = Order(
                order_number=input_data.general.order_number,
                glykohl_percent=input_data.general.glykohl_percent,
                normvolume_flow=input_data.general.normvolume_flow,
                suction_pressure=input_data.general.suction_pressure / BAR_PA_FACTOR,
                temperature_suction_filter=input_data.general.temperature_suction_filter
                - KELVIN_DIFFERENCE,
            )
            session.add(order)
            session.flush()
            for cooler in input_data.coolers:
                order_cooler_link = OrderCoolerLink(
                    cooler_id=cooler.id,
                    order_id=order.id,
                    cooling_countercurrent=cooler.cooling_countercurrent,
                    gas_humidity_after_cooler=cooler.gas_humidity_after_cooler,
                    gas_humidity_before_cooler=cooler.gas_humidity_before_cooler,
                    gas_pressure=cooler.gas_pressure / BAR_PA_FACTOR,
                    gas_temperature_in=cooler.gas_temperature_in - KELVIN_DIFFERENCE,
                    gas_temperature_out=cooler.gas_temperature_out - KELVIN_DIFFERENCE,
                    temperature_cooling_fluid_in=cooler.temperature_cooling_fluid_in
                    - KELVIN_DIFFERENCE,
                    temperature_cooling_fluid_out=cooler.temperature_cooling_fluid_out
                    - KELVIN_DIFFERENCE,
                    number_of_cooler=cooler.number_of_cooler,
                )
                session.add(order_cooler_link)
            for gas in input_data.gases:
                order_gas_link = OrderGasLink(
                    order_id=order.id, gas_id=gas.id, percentage=gas.percent
                )
                session.add(order_gas_link)

    def delete_existing_rows(self, input_data):
        session = self.database.session
        with session.begin():
            order_id = (
                Order.query.filter(
                    Order.order_number == input_data.general.order_number
                )
                .scalar()
                .id
            )
            session.query(Order).filter(Order.id == order_id).delete()
            session.query(OrderCoolerLink).filter(
                OrderCoolerLink.order_id == order_id
            ).delete()
            session.query(OrderGasLink).filter(
                OrderGasLink.order_id == order_id
            ).delete()
            session.flush()
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1305
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ich arbeite hier gerade weiter und mir ist etwas aufgefallen und ich brauche schon wieder eure Meinung, welche von meinen zwei Ideen ich umsetzen soll oder ob es eine bessere gibt.
Zum Problem: Bevor ich die Datenbank in diesem Projekt hatte, habe ich technische Daten von Kühler und Gase in jeweils einer *.json - Datei gehabt. Die Dateien habe ich dann durch die Datenbank ersetzt, allerdings nicht aus dem Projekt gelöscht und deswegen fiel mir das Problem damals nicht auf.

Ich habe unter anderem diese Struktur (habe mal alle Funktionen und Klassen gelöscht, die nichts mit dem Problem zu tun haben):

Code: Alles auswählen

#!/usr/bin/env python
from json import loads
from math import isclose
from pathlib import Path

from attrs import define, field
from attrs.validators import and_
from attrs.validators import gt as is_greater
from attrs.validators import in_
from attrs.validators import le as is_less_or_equal
from flask import Flask
from flask_classful import FlaskView
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase

TECHNICAL_DATA_PATH = Path(__file__).parent / "TechnicalData"
VALID_GASES_FILE = TECHNICAL_DATA_PATH / "gases.json"
COOLER_DATA_FILE = TECHNICAL_DATA_PATH / "CoolerData.json"


class Base(DeclarativeBase):
    pass


database = SQLAlchemy(model_class=Base)


@define
class GasData:
    id: int = field()
    name: str = field(
        validator=in_(loads(VALID_GASES_FILE.read_bytes())["valid_gases"])
    )
    percent: float = field(validator=and_(is_greater(0), is_less_or_equal(100)))


@define
class Data:
    gases: list[GasData] = field()

    @gases.validator
    def _validate_gases(self, _attribute, value):
        total = sum(gas.percent for gas in value)
        if not isclose(100, total):
            raise ValueError(f"total percent must be 100, not {total}")


@define
class App(FlaskView):
    database = field()


def main():
    app = Flask(__name__)
    app.config.from_object(__name__)
    app.config["SQLALCHEMY_DATABASE_URI"] = (
        "mysql+pymysql://dennis:Dennis89@127.0.0.1:3306/CoolerCalculation"
    )
    CORS(app, resources={r"/*": {"origins": "*"}})
    database.init_app(app)
    App.register(
        app,
        route_base="/",
        init_argument=database,
    )
    app.run()


if __name__ == "__main__":
    main()
Es geht um den `validator` von `GasData` ich lese die Daten noch aus der Datei und das gleiche mache ich für den Kühler auch. Muss/soll ich hierfür irgendwie eine zweite Verbindung zur Datenbank "in" der Klasse aufbauen? (falls das überhaupt geht, ich meine mich zu erinnern, dass das so gar nicht möglich ist)
Ich musste laut Doku ja `database` schon global verfügbar machen, muss/soll ich in dem Fall "alles" was die Datenbank betrifft global verfügbar machen?

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

Du benutzt ja attrs, statt Datenbankmodellen.
Sobald Du auf Datenbankmodelle umsteigst, sollte also die Validierung die Datenbank übernehmen.
`database` ist nicht wirklich global, sondern sorgt dafür, dass für jeden Request eine Verbindung zur Datenbank existiert und Du die darum nicht auch noch kümmern mußt.
Und deshalb sind alle Datenbankzugriffe auch nur innerhalb eines Requests.
›Verbindung zur Datenbank‹ in der Klasse geht nicht und braucht es auch nicht.
Benutzeravatar
Dennis89
User
Beiträge: 1305
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für deine Antwort. Stimmt, dass das nicht wirklich global ist, hatte __blackjack__ auch schon erklärt, da habe ich mich falsch ausgedrückt. Hätte eher fragen sollen, ob da noch mehr auf Modulebene gehört.

Wenn ich deine Antwort lese, habe ich das Gefühlt, das ich zu viel Code entfernt habe und bin mir jetzt schon fast sicher, das ich zu viel Code habe. Die Klassen, die ich gezeigt habe, dienen zur Validierung und um anschließend ein `structure`-Objekt zu erstellen.
Für die Datenbank habe ich noch einmal extra Klassen und das ist auch der Grund wieso ich `GasData` und `Gas` habe. Als ich die Datenbank eingeführt habe, sah ich keinen anderen weg, als noch einmal Klassen zu erstellen.

Code: Alles auswählen

#!/usr/bin/env python
from json import loads
from math import isclose
from pathlib import Path

from attrs import define, field
from attrs.validators import and_
from attrs.validators import ge as is_greater_or_equal
from attrs.validators import gt as is_greater
from attrs.validators import in_
from attrs.validators import le as is_less_or_equal
from attrs.validators import min_len
from cattrs import structure, unstructure
from cooler_calculation import calculate_cooler
from flask import Flask, Response, jsonify
from flask_classful import FlaskView, request, route
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_weasyprint import render_pdf
from icecream import ic
from make_template import make_template
from sqlalchemy import text, update
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

TECHNICAL_DATA_PATH = Path(__file__).parent / "TechnicalData"
COOLER_DATA_FILE = TECHNICAL_DATA_PATH / "CoolerData.json"
VALID_GASES_FILE = TECHNICAL_DATA_PATH / "gases.json"


class Base(DeclarativeBase):
    pass


database = SQLAlchemy(model_class=Base)


class Gas(database.Model):
    __tablename__ = "Gas"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(unique=True)


class OrderGasLink(database.Model):
    __tablename__ = "OrderGasLink"
    id: Mapped[int] = mapped_column(primary_key=True)
    order_id: Mapped[int] = mapped_column(unique=True)
    gas_id: Mapped[int] = mapped_column()


class Order(database.Model):
    __tablename__ = "Order"
    id: Mapped[int] = mapped_column(primary_key=True)
    order_number: Mapped[int] = mapped_column(unique=True)
    glykohl_percent: Mapped[float] = mapped_column()
    normvolume_flow: Mapped[float] = mapped_column()
    suction_pressure: Mapped[float] = mapped_column()
    temperature_suction_filter: Mapped[float] = mapped_column()


@define
class GasData:
    id: int = field()
    name: str = field(
        validator=in_(loads(VALID_GASES_FILE.read_bytes())["valid_gases"])
    )
    percent: float = field(validator=and_(is_greater(0), is_less_or_equal(100)))


@define
class Data:
    gases: list[GasData] = field()

    @gases.validator
    def _validate_gases(self, _attribute, value):
        total = sum(gas.percent for gas in value)
        if not isclose(100, total):
            raise ValueError(f"total percent must be 100, not {total}")


@define
class App(FlaskView):
    database = field()


def main():
    app = Flask(__name__)
    app.config.from_object(__name__)
    app.config["SQLALCHEMY_DATABASE_URI"] = (
        "mysql+pymysql://dennis:Dennis89@127.0.0.1:3306/CoolerCalculation"
    )
    CORS(app, resources={r"/*": {"origins": "*"}})
    database.init_app(app)
    App.register(
        app,
        route_base="/",
        init_argument=database,
    )
    app.run()


if __name__ == "__main__":
    main()
Den Kühler habe ich weggelassen, da er analog zu `Gas` bzw `GasData` ist und es hier übersichtlicher ist.

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