app.py PEP 8 konform
This commit is contained in:
+61
-55
@@ -3,42 +3,45 @@ import pandas as pd
|
|||||||
import plotly.express as px
|
import plotly.express as px
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
# Umrechnungsfaktor von Terabytes nach Bytes
|
# --- Konstanten ---
|
||||||
TB2B = 1024**4
|
TB2B = 1024**4 # TB in Bytes
|
||||||
|
|
||||||
# Seitenkonfiguration (gibt uns ein schönes breites Layout)
|
|
||||||
st.set_page_config(layout="wide")
|
|
||||||
|
|
||||||
# 1. Daten laden (Pfad aus deinem Docker-Setup)
|
|
||||||
FILE_PATH = "/data/json_cache.json"
|
FILE_PATH = "/data/json_cache.json"
|
||||||
PRICE_PER_TB = 8.1 # CHF / TB
|
PRICE_PER_TB = 8.1 # CHF / TB
|
||||||
|
|
||||||
|
|
||||||
|
# --- Seitenkonfiguration ---
|
||||||
|
st.set_page_config(layout="wide")
|
||||||
|
|
||||||
|
|
||||||
|
# --- Daten laden ---
|
||||||
@st.cache_data
|
@st.cache_data
|
||||||
def load_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:
|
try:
|
||||||
return pd.read_json(FILE_PATH)
|
return pd.read_json(FILE_PATH)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Dummy-Daten, falls Node-RED noch nichts abgelegt hat
|
|
||||||
return pd.DataFrame([
|
return pd.DataFrame([
|
||||||
{"ownerGroup": "a-123", "department": "4000", "size": 500000},
|
{"ownerGroup": "a-123", "department": "4000", "size": 500000, "packedSize": 400000},
|
||||||
{"ownerGroup": "p9999", "department": "6000", "size": 1200000}
|
{"ownerGroup": "p9999", "department": "6000", "size": 1200000, "packedSize": 1000000}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
df = load_data()
|
df = load_data()
|
||||||
|
|
||||||
# Initialisierung des Session State für die Bereichs-Analyse
|
|
||||||
|
# --- Session State Initialisierung ---
|
||||||
if "department" in df.columns:
|
if "department" in df.columns:
|
||||||
depts = sorted(df["department"].unique().tolist())
|
depts = sorted(df["department"].unique().tolist())
|
||||||
if 'gewaehltes_dept' not in st.session_state:
|
if 'gewaehltes_dept' not in st.session_state:
|
||||||
st.session_state.gewaehltes_dept = depts[0] if depts else None
|
st.session_state.gewaehltes_dept = depts[0] if depts else None
|
||||||
|
|
||||||
# 2. NAVIGATION LINKS (Sidebar)
|
|
||||||
|
# --- Navigation (Sidebar) ---
|
||||||
st.sidebar.title("Navigation")
|
st.sidebar.title("Navigation")
|
||||||
st.sidebar.markdown("Wählen Sie eine Ansicht:")
|
st.sidebar.markdown("Wählen Sie eine Ansicht:")
|
||||||
|
|
||||||
# Ein Radio-Button verhält sich wie eine klickbare Liste
|
|
||||||
auswahl = st.sidebar.radio(
|
auswahl = st.sidebar.radio(
|
||||||
label="Ansichten",
|
label="Ansichten",
|
||||||
options=[
|
options=[
|
||||||
@@ -47,18 +50,17 @@ auswahl = st.sidebar.radio(
|
|||||||
"Nicht zuweisbare Daten",
|
"Nicht zuweisbare Daten",
|
||||||
"Rohdaten"
|
"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":
|
if auswahl == "Übersicht & Metriken":
|
||||||
st.title("Übersicht")
|
st.title("Übersicht")
|
||||||
|
|
||||||
# Beispiel für schnelle Metriken rechts
|
|
||||||
total_vol = df["size"].sum()
|
total_vol = df["size"].sum()
|
||||||
total_packed_vol = df["packedSize"].sum()
|
total_packed_vol = df["packedSize"].sum()
|
||||||
|
|
||||||
col1, col2 = st.columns(2)
|
col1, col2 = st.columns(2)
|
||||||
col1.metric("Gesamtvolumen (TB)", f'{total_vol/TB2B:.2f}')
|
col1.metric("Gesamtvolumen (TB)", f'{total_vol/TB2B:.2f}')
|
||||||
col1.metric("Gesamtes packetiertes Volumen (TB)", f'{total_packed_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['Kosten [CHF]'] = (
|
||||||
(df_department_overview['packedSize'] / TB2B) * PRICE_PER_TB
|
(df_department_overview['packedSize'] / TB2B) * PRICE_PER_TB
|
||||||
)
|
)
|
||||||
df_department_overview['packedSize'] = df_department_overview['packedSize']/TB2B
|
df_department_overview['packedSize'] /= TB2B
|
||||||
|
|
||||||
# Spalten/Indexnamen nur für die Anzeige anpassen
|
|
||||||
df_department_overview.index.name = "Bereich"
|
df_department_overview.index.name = "Bereich"
|
||||||
df_department_overview.rename(
|
df_department_overview.rename(
|
||||||
columns={"packedSize": "Totales packetiertes Volumen [TB]"},
|
columns={"packedSize": "Totales packetiertes Volumen [TB]"},
|
||||||
inplace=True
|
inplace=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# st.table ist besser für kleine, statische Tabellen, die den Inhalt voll anzeigen sollen
|
|
||||||
col2.table(
|
col2.table(
|
||||||
df_department_overview.style.format({
|
df_department_overview.style.format({
|
||||||
"Totales packetiertes Volumen [TB]": "{:.4f}",
|
"Totales packetiertes Volumen [TB]": "{:.4f}",
|
||||||
@@ -88,9 +88,7 @@ if auswahl == "Übersicht & Metriken":
|
|||||||
"Kosten [CHF]": "{:.2f}"
|
"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:
|
if "department" in df:
|
||||||
fig = px.pie(
|
fig = px.pie(
|
||||||
df,
|
df,
|
||||||
@@ -98,22 +96,18 @@ if auswahl == "Übersicht & Metriken":
|
|||||||
values="packedSize",
|
values="packedSize",
|
||||||
title="Verteilung nach Bereichen"
|
title="Verteilung nach Bereichen"
|
||||||
)
|
)
|
||||||
# Legende explizit positionieren, um den Abstand zu verringern
|
|
||||||
fig.update_layout(legend=dict(x=0.7, y=0.5))
|
fig.update_layout(legend=dict(x=0.7, y=0.5))
|
||||||
st.plotly_chart(fig)
|
st.plotly_chart(fig)
|
||||||
|
|
||||||
elif auswahl == "Bereichs-Analyse":
|
elif auswahl == "Bereichs-Analyse":
|
||||||
st.title("Bereichs-Analyse")
|
st.title("Bereichs-Analyse")
|
||||||
|
|
||||||
# Ein zusätzlicher Filter, der nur in dieser Ansicht erscheint
|
|
||||||
if "department" in df and depts:
|
if "department" in df and depts:
|
||||||
# Finde den Index des gespeicherten Departments
|
|
||||||
try:
|
try:
|
||||||
default_index = depts.index(st.session_state.gewaehltes_dept)
|
default_index = depts.index(st.session_state.gewaehltes_dept)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
default_index = 0
|
default_index = 0
|
||||||
|
|
||||||
# Erstelle die Selectbox und speichere die neue Auswahl
|
|
||||||
neue_auswahl = st.selectbox(
|
neue_auswahl = st.selectbox(
|
||||||
"Bereich auswählen:",
|
"Bereich auswählen:",
|
||||||
depts,
|
depts,
|
||||||
@@ -123,20 +117,21 @@ elif auswahl == "Bereichs-Analyse":
|
|||||||
|
|
||||||
filtered_df = df[df["department"] == st.session_state.gewaehltes_dept].copy()
|
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:
|
if "copies" in filtered_df.columns:
|
||||||
conditions = [
|
conditions = [
|
||||||
filtered_df['copies'].str.startswith('one', na=False),
|
filtered_df['copies'].str.startswith('one', na=False),
|
||||||
filtered_df['copies'].str.startswith('two', na=False)
|
filtered_df['copies'].str.startswith('two', na=False)
|
||||||
]
|
]
|
||||||
choices = [1, 2]
|
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:
|
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 = {
|
rename_mapping = {
|
||||||
"ownerGroup": "Gruppe",
|
"ownerGroup": "Gruppe",
|
||||||
"copies": "Anzahl Kopien",
|
"copies": "Anzahl Kopien",
|
||||||
@@ -147,37 +142,51 @@ elif auswahl == "Bereichs-Analyse":
|
|||||||
}
|
}
|
||||||
filtered_df.rename(columns=rename_mapping, inplace=True)
|
filtered_df.rename(columns=rename_mapping, inplace=True)
|
||||||
|
|
||||||
# Neue Spalte 'Kosten' hinzufügen
|
|
||||||
if "packetierte Grösse" in filtered_df.columns:
|
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)
|
st.dataframe(filtered_df, use_container_width=True, hide_index=True)
|
||||||
|
|
||||||
elif auswahl == "Nicht zuweisbare Daten":
|
elif auswahl == "Nicht zuweisbare Daten":
|
||||||
st.title("Nicht zuweisbare Daten")
|
st.title("Nicht zuweisbare Daten")
|
||||||
|
|
||||||
# Filtere Zeilen, bei denen 'department' nicht in eine Zahl umgewandelt werden kann
|
no_department_df = df[
|
||||||
no_department_df = df[pd.to_numeric(df['department'], errors='coerce').isna()].copy()
|
pd.to_numeric(df['department'], errors='coerce').isna()
|
||||||
|
].copy()
|
||||||
no_department_vol = no_department_df['packedSize'].sum()
|
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)}')
|
st.write(f'Gesamtvolumen: {no_department_vol/TB2B:.2f} TB')
|
||||||
no_department_df = no_department_df.sort_values(by="packedSize", ascending=False)
|
st.write(f'Anzahl Gruppen: {len(no_department_df)}')
|
||||||
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 = no_department_df.sort_values(
|
||||||
no_department_df["Kosten [CHF]"] = (no_department_df["packedSize"] / TB2B) * PRICE_PER_TB
|
by="packedSize", ascending=False
|
||||||
del(no_department_df['size'])
|
)
|
||||||
del(no_department_df['packedSize'])
|
no_department_df['unpacketierte Grösse [GB]'] = \
|
||||||
del(no_department_df['beamline'])
|
no_department_df['size'] / (TB2B / 1024)
|
||||||
# Werte in der Spalte 'copies' durch Zahlen ersetzen
|
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:
|
if "copies" in no_department_df.columns:
|
||||||
conditions = [
|
conditions = [
|
||||||
no_department_df['copies'].str.startswith('one', na=False),
|
no_department_df['copies'].str.startswith('one', na=False),
|
||||||
no_department_df['copies'].str.startswith('two', na=False)
|
no_department_df['copies'].str.startswith('two', na=False)
|
||||||
]
|
]
|
||||||
choices = [1, 2]
|
choices = [1, 2]
|
||||||
no_department_df['copies'] = np.select(conditions, choices, default=no_department_df['copies'])
|
no_department_df['copies'] = np.select(
|
||||||
# Spaltennamen für die Anzeige anpassen
|
conditions, choices, default=no_department_df['copies']
|
||||||
|
)
|
||||||
|
|
||||||
rename_mapping = {
|
rename_mapping = {
|
||||||
"ownerGroup": "Gruppe",
|
"ownerGroup": "Gruppe",
|
||||||
"copies": "Anzahl Kopien",
|
"copies": "Anzahl Kopien",
|
||||||
@@ -185,12 +194,9 @@ elif auswahl == "Nicht zuweisbare Daten":
|
|||||||
}
|
}
|
||||||
no_department_df.rename(columns=rename_mapping, inplace=True)
|
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))
|
st.table(no_department_df.reset_index(drop=True))
|
||||||
|
|
||||||
elif auswahl == "Rohdaten":
|
elif auswahl == "Rohdaten":
|
||||||
st.title("Rohdaten")
|
st.title("Rohdaten")
|
||||||
st.write("Durchsuchen und filtern Sie die vollständige Liste der Gruppen. Grössenangaben in Bytes.")
|
st.write("Durchsuchen und filtern Sie die Gruppen. Grössenangaben in Bytes.")
|
||||||
|
st.dataframe(df, use_container_width=True)
|
||||||
# Die Tabelle ohne Index und mit voller Höhe anzeigen
|
|
||||||
st.dataframe(df, use_container_width=True) #, hide_index=True)
|
|
||||||
Reference in New Issue
Block a user