Änderung: Filterfunktionen costobjects.py implementiert

This commit is contained in:
knedlik
2026-02-26 07:58:25 +01:00
parent 3f7e405824
commit 20cd0b8547
2 changed files with 134 additions and 23 deletions

View File

@@ -6,7 +6,7 @@ select
KOSTENOBJEKT as obj,
BEZEICHNUNG,
VERANTWORTLICHER,
FELD_2_X30 as VORGESETZER,
FELD_2_X30 as vorgesetzter,
ZUORDNUNGSGRUPPE_1 as zgrp1,
ZUORDNUNGSGRUPPE_2 as zgrp2,
ZUORDNUNGSGRUPPE_3 as zgrp3,
@@ -14,6 +14,9 @@ select
ZUORDNUNGSGRUPPE_5 as zgrp5,
ZUORDNUNGSGRUPPE_6 as zgrp6,
FELD_1_X30 as fertigung,
OBJEKTGRUPPE as objgrp
OBJEKTGRUPPE as objgrp,
sysdate
from
pkos
where
objekttyp in ('01', '02')

View File

@@ -5,41 +5,149 @@ from data.db import load_data
from auth_runtime import require_login
from ui.sidebar import build_sidebar, hide_sidebar_if_logged_out
from auth import get_fullname_for_user
import numpy as np
# hide_sidebar_if_logged_out()
st.set_page_config(page_title="Co-App Home", page_icon="🏠", layout="wide")
authenticator = require_login()
st.session_state["authenticator"] = authenticator
@st.cache_data
def cache_data():
return load_data("ora_kostenobjekte","oracle")
def render_report():
@st.cache_data
def cache_data() -> pd.DataFrame:
"""
Load and cache the base dataset for this page.
Streamlit reruns the script on every interaction; caching avoids
repeated I/O and makes filtering feel instant.
"""
try:
df = load_data("ora_kostenobjekte", "oracle")
return df
except:
st.warning("Fehler beim Laden der Daten", icon="⚠️")
def sidebar_filters(df) -> dict:
"""
Render the sidebar UI and return the current filter selections.
This function should contain UI concerns only (widgets, layout),
and not data filtering logic, to keep the code maintainable.
"""
st.sidebar.header("Filter")
if st.sidebar.button("Refresh (Global)"):
cache_data.clear()
st.rerun()
year = st.sidebar.selectbox(label="Jahr", options=(2025, 2026), index=1)
filter_text = st.sidebar.text_input(label="Textsuche", placeholder="Suche Objekt, Text, Verantwortlicher")
col_s1, col_s2 = st.sidebar.columns(2)
with col_s1:
typ = st.sidebar.selectbox(label="Typ", options=sorted(df["typ"].dropna().unique()), index=1)
with col_s2:
obj = st.sidebar.multiselect("obj", sorted(df["obj"].dropna().unique()))
zgrp1 = st.sidebar.multiselect("ZGrp1", sorted(df["zgrp1"].dropna().unique()))
zgrp2 = st.sidebar.multiselect("ZGrp2", sorted(df["zgrp2"].dropna().unique()))
zgrp3 = st.sidebar.multiselect("ZGrp3", sorted(df["zgrp3"].dropna().unique()))
return {"year": year, "filter_text": filter_text, "typ": typ, "obj": obj, "zgrp1": zgrp1, "zgrp2": zgrp2, "zgrp3": zgrp3}
def build_mask(df: pd.DataFrame, sidebar_filter: dict) -> np.ndarray:
"""
Build a boolean mask based on filter selections.
The mask approach keeps the logic readable and makes it easy
to add more conditions later.
"""
mask = np.ones(len(df), dtype=bool)
filter_text = (sidebar_filter.get("filter_text") or "").strip()
if filter_text:
m1 = df["bezeichnung"].astype("string").str.contains(filter_text, case=False, na=False, regex=False)
m2 = df["verantwortlicher"].astype("string").str.contains(filter_text, case=False, na=False, regex=False)
m3 = df["vorgesetzter"].astype("string").str.contains(filter_text, case=False, na=False, regex=False)
mask &= (m1 | m2 | m3)
if sidebar_filter["year"]:
mask &= df["jahr"].eq(sidebar_filter["year"])
if sidebar_filter["typ"]:
mask &= df["typ"].eq(sidebar_filter["typ"])
if sidebar_filter["obj"]:
mask &= df["obj"].isin(sidebar_filter["obj"])
if sidebar_filter["zgrp1"]:
mask &= df["zgrp1"].isin(sidebar_filter["zgrp1"])
if sidebar_filter["zgrp2"]:
mask &= df["zgrp2"].isin(sidebar_filter["zgrp2"])
if sidebar_filter["zgrp3"]:
mask &= df["zgrp3"].isin(sidebar_filter["zgrp3"])
return mask
def render_table(df):
"""
Render the result table.
Keep this function presentation-only: it should not modify data.
"""
st.dataframe(df, hide_index=True, width="stretch", height="stretch")
def page():
"""
Page entry point: orchestrates data loading, UI, filtering, and rendering.
"""
# -----------------------------
# Data loading (cached)
# -----------------------------
df = cache_data()
zgrp1_options = st.multiselect(
"Zuordnungsgruppe1",
["SPI", "KULA", "AT"]
)
year = 2026
# -----------------------------
# UI (Sidebar)
# -----------------------------
sidebar_filter = sidebar_filters(df)
# -----------------------------
# Business logic (filtering)
# -----------------------------
mask = build_mask(df, sidebar_filter)
# -----------------------------
# Presentation
# -----------------------------
df = df.sort_values(by="obj", key=lambda s: pd.to_numeric(s, errors="coerce"))
df_clean = df[df.columns[:-1]] # letzte Spalten entfernen (sysdate)
df_display = df_clean.loc[mask]
render_table(df_display)
col1, col2 = st.columns(2)
data_as_of = df["sysdate"].max()
row_count = len(df_display)
with col1:
st.text(f"Datenstand: {data_as_of}")
st.text("Datenquelle: Oracle (PENTA)")
with col2:
st.markdown(f"<div style='text-align: right;'>Anzahl Zeilen: {row_count}</div>", unsafe_allow_html=True)
df_actual_year = df[df["jahr"] == year]
df_actual_year = df_actual_year.sort_values(
by="obj",
key=lambda s: pd.to_numeric(s, errors="coerce")
)
if zgrp1_options:
df_view = df_actual_year[df_actual_year["zgrp1"].isin(zgrp1_options)]
else:
df_view = df_actual_year
st.dataframe(df_view, height=600, hide_index=True, width="stretch")
if __name__ == "__main__":
df = render_report()
df = page()