From 068af86fcb08ca1bffe43ed1ef8147ab2ee1d1f0 Mon Sep 17 00:00:00 2001 From: "peter.fong" Date: Sun, 23 Mar 2025 21:37:54 +0000 Subject: [PATCH] ppo dedup and smooth flow --- delfland/ppo-insight/data.db | Bin 106496 -> 106496 bytes delfland/ppo-insight/ppo_insight.py | 145 ++++++++++-------- delfland/ppo-insight/templates/resultaat.html | 34 +++- 3 files changed, 112 insertions(+), 67 deletions(-) diff --git a/delfland/ppo-insight/data.db b/delfland/ppo-insight/data.db index c2b293668f71fdff5d9abe126b0ba51b62802c47..0bbf89f798a0cda22ae65042ef9a1f79c2b7cb26 100644 GIT binary patch delta 329 zcmZoTz}9epZGtqfA_D`16cDolvG_y{b4JCD2@Cl7c!6AIJ{Jc59KIWTE}I<%%=lOu z1!fjcF7OX#H`6sR1fuDVOpNBt0yB%if~Kf~g+RgN>0V5Xx|0G}HnS-F=4br8Sy16V z|HKB3NHM6Tl0aGlWHAtf#6*Eu1c-%!SO|y(fmi^D`GJ@Zh(WI60b*_-<^p0)Am#vK Yb|7X0VivFsn-vr;@Y93X*szfq0CA{G6aWAK literal 106496 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 diff --git a/delfland/ppo-insight/ppo_insight.py b/delfland/ppo-insight/ppo_insight.py index 3221882..88b8728 100644 --- a/delfland/ppo-insight/ppo_insight.py +++ b/delfland/ppo-insight/ppo_insight.py @@ -48,7 +48,7 @@ def init_db(): PO_schema_Niet_gebruikt TEXT, Cluster TEXT, Locatie TEXT, - Locatie_omschrijving TEXT, + Locatie_omschrijving TEXT, Klasse_object TEXT, Categorie TEXT ) @@ -60,38 +60,45 @@ def allowed_file(filename): return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS -@app.route("/download", methods=["GET"]) -def download_excel(): - x_str = request.args.get("x") - if not x_str: - flash("Geen datum opgegeven voor download.", "danger") - return redirect(url_for("upload_file")) +def fetch_extrapolation_results(request): + x_str = request.args.get("x") or request.form.get("selected_date") + # print(request.form.get("keuze_dagen_omrekenen")) + keuze_dagen_omrekenen = request.args.get( + "keuze_dagen_omrekenen") or request.form.get("keuze_dagen_omrekenen") or "D" - try: - x = datetime.strptime(x_str, "%Y-%m-%d") - except ValueError: - flash("Ongeldige datum opgegeven.", "danger") - return redirect(url_for("upload_file")) + today = datetime.today().strftime('%Y-%m-%d') + error = None + print( + f"selected date {x_str} keuze_dagen_omrekenen {keuze_dagen_omrekenen}") - result = get_extrapolation(x) + result, warningmessage = get_extrapolation(x_str, keuze_dagen_omrekenen) if not result: - flash("Geen resultaten beschikbaar voor de geselecteerde datum.", "warning") - return redirect(url_for("upload_file")) + return None, "Geen resultaten beschikbaar voor de geselecteerde datum.", today, keuze_dagen_omrekenen, x_str + return result, error, today, keuze_dagen_omrekenen, x_str, warningmessage + + +@app.route("/download", methods=["GET", "POST"]) +def download_excel(): + result, error, today, keuze_dagen_omrekenen, selected_date, warningmessage = fetch_extrapolation_results( + request) + + if error: + flash(error, "danger") + # return redirect(url_for("upload_file")) df = pd.DataFrame(result) - # Verwijder de Vervaldatum kolom if 'Vervaldatum' in df.columns: df = df.drop(columns=['Vervaldatum']) - # df.rename(columns={"Begindatum": "Vervaldatum"}, inplace=True) + output = io.BytesIO() with pd.ExcelWriter(output, engine="openpyxl") as writer: df.to_excel(writer, index=False, - sheet_name=f"Extrapolatie_{x.strftime('%Y-%m-%d')}") + sheet_name=f"Extrapolatie_{today}") output.seek(0) - filename = f"extrapolatie_{x.strftime('%Y-%m-%d')}.xlsx" + filename = f"extrapolatie_{today}.xlsx" return Response( output, mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", @@ -99,6 +106,22 @@ def download_excel(): ) +@app.route("/extrapolate", methods=["GET", "POST"]) +def show_result(): + + result, error, today, keuze_dagen_omrekenen, selected_date, warningmessage = fetch_extrapolation_results( + request) + if error: + flash(error, "danger") + # return redirect(url_for("upload_file")) + + if warningmessage: + flash(warningmessage, "warning") + + return render_template("resultaat.html", result=result, today=today, selected_date=selected_date, + keuze_dagen_omrekenen=keuze_dagen_omrekenen) + + @app.route("/", methods=["GET", "POST"]) def upload_file(): if request.method == "POST": @@ -118,7 +141,8 @@ def upload_file(): process_excel(filepath) flash("Bestand geüpload en verwerkt! Bekijk de resultaten", "success") - return redirect(url_for("upload_file")) + # Hier omleiden naar de 'resultaat' pagina + return redirect(url_for("show_result")) return render_template("index.html") @@ -159,55 +183,25 @@ def process_excel(filepath): print(f"Aantal records in de database: {count}") -@app.route("/extrapolate", methods=["GET", "POST"]) -def show_result(): - today = datetime.today().strftime('%Y-%m-%d') +def get_extrapolation(x, keuze_dagen_omrekenen): - x_str = request.form.get("selected_date") - keuze_dagen_omrekenen = request.form.get( - 'keuze_dagen_omrekenen', 'D') # Default is "D" (Dagen) - - if x_str: - try: - x = datetime.strptime(x_str, "%Y-%m-%d") - except ValueError: - flash("Ongeldige datum ingevoerd.", "danger") - return redirect(url_for("upload_file")) - - # **Met datum -> gebruik extrapolatie** - result = get_extrapolation(x, keuze_dagen_omrekenen) - else: - # **Zonder datum -> toon ruwe databasegegevens** - with sqlite3.connect(DATABASE) as conn: - cursor = conn.cursor() - cursor.execute("SELECT * FROM onderhoud") - columns = [desc[0] for desc in cursor.description] - rows = cursor.fetchall() - - result = [dict(zip(columns, row)) - for row in rows] # Zet om naar dicts voor weergave - - return render_template("resultaat.html", result=result, selected_date=x_str, today=today, - keuze_dagen_omrekenen=keuze_dagen_omrekenen) + raw_data = get_raw_data() + print(f"keuze_dagen_omrekenen: {keuze_dagen_omrekenen}") + if not x and not keuze_dagen_omrekenen: + warning_msg = "Waarschuwing: Geen datum of keuze_dagen_omrekenen opgegeven, ruwe data wordt getoond." + print(warning_msg) + return raw_data, warning_msg + if not x: + warning_msg = "Waarschuwing: Geen datum opgegeven, ruwe data wordt getoond." + print(warning_msg) + return raw_data, warning_msg -def get_extrapolation(x, keuze_dagen_omrekenen): - if x is None: - return [] - print(f"keuze {keuze_dagen_omrekenen}") today = datetime.today() - with sqlite3.connect(DATABASE) as conn: - cursor = conn.cursor() - cursor.execute("SELECT * FROM onderhoud") - columns = [desc[0] for desc in cursor.description] - rows = cursor.fetchall() - - raw_data = [dict(zip(columns, row)) for row in rows] result = raw_data.copy() - for row in raw_data: - vervaldatum = row.get("Vervaldatum") + vervaldatum = row.get("Begindatum") uom = row.get("UOM") frequentie = row.get("Frequentie", 0) @@ -216,7 +210,7 @@ def get_extrapolation(x, keuze_dagen_omrekenen): if vervaldatum: extrapolated_dates = extrapolate_vervaldatum( vervaldatum, uom, frequentie, x, keuze_dagen_omrekenen) - + print(extrapolated_dates) if not extrapolated_dates: print( f"Geen extrapolatie-data voor vervaldatum: {vervaldatum}") @@ -230,14 +224,31 @@ def get_extrapolation(x, keuze_dagen_omrekenen): ) <= today.date() else "Nee" new_row["Begindatum"] = date - print(f"Added row: {date}") + # print(f"Added row: {date}") result.append(new_row) + return remove_duplicates(result), "" # Dubbele records verwijderen + + +def get_raw_data(): + with sqlite3.connect(DATABASE) as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM onderhoud") + columns = [desc[0] for desc in cursor.description] + rows = cursor.fetchall() + + if 'Vervaldatum' in columns: + columns[columns.index('Vervaldatum')] = 'Begindatum' + + raw_data = [dict(zip(columns, row)) for row in rows] - return remove_duplicates(result) # Dubbele records verwijderen + for record in raw_data: + if 'Begindatum' in record: + record['Begindatum'] = str( + pd.to_datetime(record['Begindatum']).date()) + return raw_data def remove_duplicates(data, unique_keys=None): - print("removing duplicates") if not data: return data @@ -265,6 +276,8 @@ def remove_duplicates(data, unique_keys=None): def extrapolate_vervaldatum(vervaldatum, uom, frequentie, x, keuze_dagen_omrekenen="D"): print(f"keuze {keuze_dagen_omrekenen}") vervaldatum = pd.to_datetime(str(vervaldatum), errors="coerce") + print(x) + x = pd.to_datetime(x) if pd.isna(vervaldatum): return [] diff --git a/delfland/ppo-insight/templates/resultaat.html b/delfland/ppo-insight/templates/resultaat.html index 172b233..9788679 100644 --- a/delfland/ppo-insight/templates/resultaat.html +++ b/delfland/ppo-insight/templates/resultaat.html @@ -16,12 +16,28 @@
+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + + {% endif %} + {% endwith %}

Resultaten van de query

Terug naar upload
+

@@ -43,7 +59,11 @@
- Download als Excel ({{ selected_date }}) +
+ + +
+ @@ -148,6 +168,18 @@ $('#resetTable').on('click', function () { window.location.href = "{{ url_for('show_result') }}"; }); + + + + // Wanneer de download-knop wordt ingedrukt, stel dan de geselecteerde datum in + $(".btn.btn-success").on('click', function (e) { + var selectedDate = $("#datepicker").val(); // Haal de geselecteerde datum op + if (selectedDate) { + $("#selected_date_hidden").val(selectedDate); // Zet de waarde van het verborgen veld + } + // Verstuur het formulier + $("#dateForm").submit(); + }); });