fix: connection leak, Rice index excludes absences, per-party motion count guard

main
Sven Geboers 1 month ago
parent bcf9407957
commit 50f8a06c6d
  1. 135
      explorer.py

@ -258,7 +258,10 @@ def compute_party_discipline(
Rice index per motion per party = fraction of party MPs voting with the party majority.
The per-party score is the average Rice index across all motions in the date range.
Only 'voor' and 'tegen' votes are counted; absent and abstaining MPs are excluded from the
Rice index calculation.
"""
conn = None
try:
conn = duckdb.connect(db_path, read_only=True)
result = conn.execute(
@ -272,7 +275,7 @@ def compute_party_discipline(
WHERE mp_name LIKE '%,%'
AND date >= CAST(? AS DATE)
AND date <= CAST(? AS DATE)
AND vote IN ('voor', 'tegen', 'afwezig', 'onthouden')
AND vote IN ('voor', 'tegen')
),
vote_counts AS (
SELECT
@ -313,11 +316,16 @@ def compute_party_discipline(
""",
[start_date, end_date],
).fetchdf()
conn.close()
return result
except Exception as exc:
logger.warning("compute_party_discipline failed: %s", exc)
return pd.DataFrame(columns=["party", "n_motions", "discipline"])
finally:
if conn is not None:
try:
conn.close()
except Exception:
pass
@st.cache_data(show_spinner="Partijposities op SVD-assen laden…")
@ -955,74 +963,79 @@ def build_compass_tab(db_path: str, window_size: str) -> None:
disc_df = compute_party_discipline(db_path, start_date, end_date)
st.subheader("Stemgedrag cohesie")
if disc_df.empty or disc_df["n_motions"].max() < _MIN_MOTIONS_FOR_DISCIPLINE:
if disc_df.empty:
st.caption(
"Te weinig hoofdelijke stemmingen in dit venster voor een cohesieanalyse."
)
else:
compass_parties = set(df_pos["party"].unique())
disc_df = disc_df[disc_df["party"].isin(compass_parties)].copy()
disc_df = disc_df[disc_df["n_motions"] >= _MIN_MOTIONS_FOR_DISCIPLINE].copy()
if disc_df.empty:
st.caption("Geen overlappende partijen tussen kompas en stemmingsdata.")
else:
disc_df["discipline_pct"] = (disc_df["discipline"] * 100).round(1)
disc_df["party_label"] = disc_df.apply(
lambda r: f"{r['party']} ({int(r['n_motions'])} moties)", axis=1
st.caption(
"Te weinig hoofdelijke stemmingen in dit venster voor een cohesieanalyse."
)
else:
compass_parties = set(df_pos["party"].unique())
disc_df = disc_df[disc_df["party"].isin(compass_parties)].copy()
if disc_df.empty:
st.caption("Geen overlappende partijen tussen kompas en stemmingsdata.")
else:
disc_df["discipline_pct"] = (disc_df["discipline"] * 100).round(1)
disc_df["party_label"] = disc_df.apply(
lambda r: f"{r['party']} ({int(r['n_motions'])} moties)", axis=1
)
bar_fig = px.bar(
disc_df.sort_values("discipline"),
x="discipline_pct",
y="party_label",
orientation="h",
color="discipline_pct",
color_continuous_scale="RdYlGn",
range_color=[80, 100],
labels={"discipline_pct": "Cohesie (%)", "party_label": "Partij"},
title="Cohesie bij hoofdelijke stemmingen",
)
bar_fig.update_layout(
height=max(300, len(disc_df) * 35 + 80),
showlegend=False,
coloraxis_showscale=False,
yaxis_title="",
)
st.plotly_chart(bar_fig, use_container_width=True)
top3 = disc_df.nlargest(3, "discipline")[
["party", "discipline_pct", "n_motions"]
]
bot3 = disc_df.nsmallest(3, "discipline")[
["party", "discipline_pct", "n_motions"]
]
col_a, col_b = st.columns(2)
with col_a:
st.markdown("**Meest eensgezind**")
st.dataframe(
top3.rename(
columns={
"party": "Partij",
"discipline_pct": "Cohesie (%)",
"n_motions": "Moties",
}
),
hide_index=True,
use_container_width=True,
bar_fig = px.bar(
disc_df.sort_values("discipline"),
x="discipline_pct",
y="party_label",
orientation="h",
color="discipline_pct",
color_continuous_scale="RdYlGn",
range_color=[80, 100],
labels={"discipline_pct": "Cohesie (%)", "party_label": "Partij"},
title="Cohesie bij hoofdelijke stemmingen",
)
with col_b:
st.markdown("**Meest verdeeld**")
st.dataframe(
bot3.rename(
columns={
"party": "Partij",
"discipline_pct": "Cohesie (%)",
"n_motions": "Moties",
}
),
hide_index=True,
use_container_width=True,
bar_fig.update_layout(
height=max(300, len(disc_df) * 35 + 80),
showlegend=False,
coloraxis_showscale=False,
yaxis_title="",
)
st.plotly_chart(bar_fig, use_container_width=True)
top3 = disc_df.nlargest(3, "discipline")[
["party", "discipline_pct", "n_motions"]
]
bot3 = disc_df.nsmallest(3, "discipline")[
["party", "discipline_pct", "n_motions"]
]
col_a, col_b = st.columns(2)
with col_a:
st.markdown("**Meest eensgezind**")
st.dataframe(
top3.rename(
columns={
"party": "Partij",
"discipline_pct": "Cohesie (%)",
"n_motions": "Moties",
}
),
hide_index=True,
use_container_width=True,
)
with col_b:
st.markdown("**Meest verdeeld**")
st.dataframe(
bot3.rename(
columns={
"party": "Partij",
"discipline_pct": "Cohesie (%)",
"n_motions": "Moties",
}
),
hide_index=True,
use_container_width=True,
)
# ---------------------------------------------------------------------------

Loading…
Cancel
Save