From 00a9b9ebdcc45396456bb71efa0a09e533f83af5 Mon Sep 17 00:00:00 2001 From: huesser Date: Mon, 22 Jun 2026 15:45:49 +0200 Subject: [PATCH] app.py PEP 8 konform --- analytics/app.py | 134 +++++++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 64 deletions(-) diff --git a/analytics/app.py b/analytics/app.py index ba141b1..162c64f 100644 --- a/analytics/app.py +++ b/analytics/app.py @@ -3,62 +3,64 @@ import pandas as pd import plotly.express as px import numpy as np -# Umrechnungsfaktor von Terabytes nach Bytes -TB2B = 1024**4 - -# Seitenkonfiguration (gibt uns ein schönes breites Layout) -st.set_page_config(layout="wide") - -# 1. Daten laden (Pfad aus deinem Docker-Setup) +# --- Konstanten --- +TB2B = 1024**4 # TB in Bytes FILE_PATH = "/data/json_cache.json" PRICE_PER_TB = 8.1 # CHF / TB + +# --- Seitenkonfiguration --- +st.set_page_config(layout="wide") + + +# --- Daten laden --- @st.cache_data def load_data(): - # Falls die Datei existiert, laden, sonst leeres Dataframe für den Test + """ + Lädt die Daten aus dem JSON-Cache. + Falls die Datei nicht existiert, werden Dummy-Daten für Testzwecke erstellt. + """ try: return pd.read_json(FILE_PATH) except Exception: - # Dummy-Daten, falls Node-RED noch nichts abgelegt hat return pd.DataFrame([ - {"ownerGroup": "a-123", "department": "4000", "size": 500000}, - {"ownerGroup": "p9999", "department": "6000", "size": 1200000} + {"ownerGroup": "a-123", "department": "4000", "size": 500000, "packedSize": 400000}, + {"ownerGroup": "p9999", "department": "6000", "size": 1200000, "packedSize": 1000000} ]) df = load_data() -# Initialisierung des Session State für die Bereichs-Analyse + +# --- Session State Initialisierung --- if "department" in df.columns: depts = sorted(df["department"].unique().tolist()) if 'gewaehltes_dept' not in st.session_state: st.session_state.gewaehltes_dept = depts[0] if depts else None -# 2. NAVIGATION LINKS (Sidebar) + +# --- Navigation (Sidebar) --- st.sidebar.title("Navigation") st.sidebar.markdown("Wählen Sie eine Ansicht:") - -# Ein Radio-Button verhält sich wie eine klickbare Liste auswahl = st.sidebar.radio( label="Ansichten", options=[ - "Übersicht & Metriken", - "Bereichs-Analyse", - "Nicht zuweisbare Daten", + "Übersicht & Metriken", + "Bereichs-Analyse", + "Nicht zuweisbare Daten", "Rohdaten" ], - label_visibility="collapsed" # Versteckt das Label für eine sauberere Optik + label_visibility="collapsed" ) -# 3. ANZEIGE RECHTS (Hauptbildschirm) -# Hier steuern wir den Inhalt basierend auf der Auswahl links +# --- Hauptansicht --- if auswahl == "Übersicht & Metriken": st.title("Übersicht") - # Beispiel für schnelle Metriken rechts total_vol = df["size"].sum() total_packed_vol = df["packedSize"].sum() + col1, col2 = st.columns(2) col1.metric("Gesamtvolumen (TB)", f'{total_vol/TB2B:.2f}') col1.metric("Gesamtes packetiertes Volumen (TB)", f'{total_packed_vol/TB2B:.2f}') @@ -71,16 +73,14 @@ if auswahl == "Übersicht & Metriken": df_department_overview['Kosten [CHF]'] = ( (df_department_overview['packedSize'] / TB2B) * PRICE_PER_TB ) - df_department_overview['packedSize'] = df_department_overview['packedSize']/TB2B - - # Spalten/Indexnamen nur für die Anzeige anpassen + df_department_overview['packedSize'] /= TB2B + df_department_overview.index.name = "Bereich" df_department_overview.rename( - columns={"packedSize": "Totales packetiertes Volumen [TB]"}, + columns={"packedSize": "Totales packetiertes Volumen [TB]"}, inplace=True ) - # st.table ist besser für kleine, statische Tabellen, die den Inhalt voll anzeigen sollen col2.table( df_department_overview.style.format({ "Totales packetiertes Volumen [TB]": "{:.4f}", @@ -88,32 +88,26 @@ if auswahl == "Übersicht & Metriken": "Kosten [CHF]": "{:.2f}" }) ) -# col2.metric("Abteilungen", f'{df.groupby('department')['packedSize'].sum().to_frame()}', use_container_width=True) - # Hier könnte eine Grafik hin if "department" in df: fig = px.pie( - df, - names="department", - values="packedSize", + df, + names="department", + values="packedSize", title="Verteilung nach Bereichen" ) - # Legende explizit positionieren, um den Abstand zu verringern fig.update_layout(legend=dict(x=0.7, y=0.5)) st.plotly_chart(fig) elif auswahl == "Bereichs-Analyse": st.title("Bereichs-Analyse") - # Ein zusätzlicher Filter, der nur in dieser Ansicht erscheint if "department" in df and depts: - # Finde den Index des gespeicherten Departments try: default_index = depts.index(st.session_state.gewaehltes_dept) except ValueError: default_index = 0 - # Erstelle die Selectbox und speichere die neue Auswahl neue_auswahl = st.selectbox( "Bereich auswählen:", depts, @@ -122,21 +116,22 @@ elif auswahl == "Bereichs-Analyse": st.session_state.gewaehltes_dept = neue_auswahl filtered_df = df[df["department"] == st.session_state.gewaehltes_dept].copy() - - # Werte in der Spalte 'copies' durch Zahlen ersetzen + if "copies" in filtered_df.columns: conditions = [ filtered_df['copies'].str.startswith('one', na=False), filtered_df['copies'].str.startswith('two', na=False) ] choices = [1, 2] - filtered_df['copies'] = np.select(conditions, choices, default=filtered_df['copies']) + filtered_df['copies'] = np.select( + conditions, choices, default=filtered_df['copies'] + ) - # DataFrame nach 'packedSize' absteigend sortieren if "packedSize" in filtered_df.columns: - filtered_df = filtered_df.sort_values(by="packedSize", ascending=False) + filtered_df = filtered_df.sort_values( + by="packedSize", ascending=False + ) - # Spaltennamen für die Anzeige anpassen rename_mapping = { "ownerGroup": "Gruppe", "copies": "Anzahl Kopien", @@ -147,37 +142,51 @@ elif auswahl == "Bereichs-Analyse": } filtered_df.rename(columns=rename_mapping, inplace=True) - # Neue Spalte 'Kosten' hinzufügen if "packetierte Grösse" in filtered_df.columns: - filtered_df["Kosten [CHF]"] = (filtered_df["packetierte Grösse"] / TB2B) * PRICE_PER_TB + filtered_df["Kosten [CHF]"] = \ + (filtered_df["packetierte Grösse"] / TB2B) * PRICE_PER_TB - st.metric(f"Anzahl Gruppen in Department {st.session_state.gewaehltes_dept}", len(filtered_df)) + st.metric( + f"Anzahl Gruppen in Department {st.session_state.gewaehltes_dept}", + len(filtered_df) + ) st.dataframe(filtered_df, use_container_width=True, hide_index=True) elif auswahl == "Nicht zuweisbare Daten": st.title("Nicht zuweisbare Daten") - # Filtere Zeilen, bei denen 'department' nicht in eine Zahl umgewandelt werden kann - no_department_df = df[pd.to_numeric(df['department'], errors='coerce').isna()].copy() + no_department_df = df[ + pd.to_numeric(df['department'], errors='coerce').isna() + ].copy() no_department_vol = no_department_df['packedSize'].sum() - st.write(f'Gesamtvolumen der nicht zuweisbaren Daten: {no_department_vol/TB2B:.2f} TB') - st.write(f'Anzahl der nicht zuweisbaren Gruppen: {len(no_department_df)}') - no_department_df = no_department_df.sort_values(by="packedSize", ascending=False) - no_department_df['unpacketierte Grösse [GB]'] = no_department_df['size'] / (TB2B/1024) - no_department_df['packetierte Grösse [GB]'] = no_department_df['packedSize'] / (TB2B/1024) - no_department_df["Kosten [CHF]"] = (no_department_df["packedSize"] / TB2B) * PRICE_PER_TB - del(no_department_df['size']) - del(no_department_df['packedSize']) - del(no_department_df['beamline']) - # Werte in der Spalte 'copies' durch Zahlen ersetzen + + st.write(f'Gesamtvolumen: {no_department_vol/TB2B:.2f} TB') + st.write(f'Anzahl Gruppen: {len(no_department_df)}') + + no_department_df = no_department_df.sort_values( + by="packedSize", ascending=False + ) + no_department_df['unpacketierte Grösse [GB]'] = \ + no_department_df['size'] / (TB2B / 1024) + no_department_df['packetierte Grösse [GB]'] = \ + no_department_df['packedSize'] / (TB2B / 1024) + no_department_df["Kosten [CHF]"] = \ + (no_department_df["packedSize"] / TB2B) * PRICE_PER_TB + + no_department_df.drop( + columns=['size', 'packedSize', 'beamline'], inplace=True + ) + if "copies" in no_department_df.columns: conditions = [ no_department_df['copies'].str.startswith('one', na=False), no_department_df['copies'].str.startswith('two', na=False) ] choices = [1, 2] - no_department_df['copies'] = np.select(conditions, choices, default=no_department_df['copies']) - # Spaltennamen für die Anzeige anpassen + no_department_df['copies'] = np.select( + conditions, choices, default=no_department_df['copies'] + ) + rename_mapping = { "ownerGroup": "Gruppe", "copies": "Anzahl Kopien", @@ -185,12 +194,9 @@ elif auswahl == "Nicht zuweisbare Daten": } no_department_df.rename(columns=rename_mapping, inplace=True) - # Index zurücksetzen, damit er nicht als Spalte angezeigt wird st.table(no_department_df.reset_index(drop=True)) elif auswahl == "Rohdaten": st.title("Rohdaten") - st.write("Durchsuchen und filtern Sie die vollständige Liste der Gruppen. Grössenangaben in Bytes.") - - # Die Tabelle ohne Index und mit voller Höhe anzeigen - st.dataframe(df, use_container_width=True) #, hide_index=True) + st.write("Durchsuchen und filtern Sie die Gruppen. Grössenangaben in Bytes.") + st.dataframe(df, use_container_width=True) \ No newline at end of file