|
|
"""Example: Streamlit page patterns - from actual pages/ files."""
|
|
|
|
|
|
import streamlit as st
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
# Example 1: Home page (Home.py)
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
def render_home_page():
|
|
|
"""Simplified version of Home.py."""
|
|
|
|
|
|
st.set_page_config(
|
|
|
page_title="Motief: de stematlas",
|
|
|
page_icon="🗺️",
|
|
|
layout="centered",
|
|
|
initial_sidebar_state="expanded",
|
|
|
)
|
|
|
|
|
|
st.title("🗺️ Motief: de stematlas")
|
|
|
st.markdown(
|
|
|
"**Motief** brengt de Nederlandse Tweede Kamer in kaart op basis van "
|
|
|
"echte stemmingen over moties. Gebruik de Stemwijzer om te ontdekken welke "
|
|
|
"partij het beste bij jouw standpunten past, of verken de politieke ruimte "
|
|
|
"zelf in de Explorer."
|
|
|
)
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
|
|
with col1:
|
|
|
st.subheader("🗳️ Stemwijzer")
|
|
|
st.markdown(
|
|
|
"Stem op echte Tweede Kamer moties en zie welke partij het "
|
|
|
"dichtst bij jouw keuzes staat."
|
|
|
)
|
|
|
st.page_link("pages/1_Stemwijzer.py", label="Open Stemwijzer", icon="🗳️")
|
|
|
|
|
|
with col2:
|
|
|
st.subheader("🔭 Politiek Explorer")
|
|
|
st.markdown(
|
|
|
"Verken het politieke kompas, partijtrajecten door de tijd, "
|
|
|
"en zoek vergelijkbare moties op in het archief."
|
|
|
)
|
|
|
st.page_link("pages/2_Explorer.py", label="Open Explorer", icon="🔭")
|
|
|
|
|
|
st.divider()
|
|
|
st.caption("Data: Tweede Kamer API · Embeddings: QWEN (via OpenRouter)")
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
# Example 2: Thin page wrapper (pages/1_Stemwijzer.py)
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
def render_stemwijzer_page():
|
|
|
"""Pattern: thin page that delegates to module function."""
|
|
|
|
|
|
st.set_page_config(
|
|
|
page_title="Stemwijzer",
|
|
|
page_icon="🗳️",
|
|
|
layout="centered",
|
|
|
)
|
|
|
|
|
|
# Delegate to main module
|
|
|
from explorer import build_mp_quiz_tab
|
|
|
|
|
|
build_mp_quiz_tab("data/motions.db")
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
# Example 3: Session state initialization
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
def init_session_state():
|
|
|
"""Pattern: Initialize all session state at start."""
|
|
|
|
|
|
defaults = {
|
|
|
"session_id": None,
|
|
|
"current_motion_index": 0,
|
|
|
"motions": [],
|
|
|
"show_results": False,
|
|
|
"user_votes": {},
|
|
|
}
|
|
|
|
|
|
for key, default in defaults.items():
|
|
|
if key not in st.session_state:
|
|
|
st.session_state[key] = default
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
# Example 4: Sidebar configuration
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
def render_sidebar():
|
|
|
"""Pattern: Sidebar for configuration."""
|
|
|
|
|
|
with st.sidebar:
|
|
|
st.header("Instellingen")
|
|
|
|
|
|
motion_count = st.slider(
|
|
|
"Aantal moties",
|
|
|
min_value=5,
|
|
|
max_value=25,
|
|
|
value=10,
|
|
|
help="Hoeveel moties wilt u beantwoorden?",
|
|
|
)
|
|
|
|
|
|
policy_area = st.selectbox(
|
|
|
"Beleidsgebied",
|
|
|
[
|
|
|
"Alle",
|
|
|
"Economie",
|
|
|
"Klimaat",
|
|
|
"Immigratie",
|
|
|
"Zorg",
|
|
|
"Onderwijs",
|
|
|
"Defensie",
|
|
|
"Sociale Zaken",
|
|
|
"Algemeen",
|
|
|
],
|
|
|
)
|
|
|
|
|
|
margin_range = st.slider(
|
|
|
"Controversiële moties (%)",
|
|
|
min_value=0,
|
|
|
max_value=100,
|
|
|
value=(0, 100),
|
|
|
help="Filter op hoe omstreden de moties zijn",
|
|
|
)
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
if st.button("Start Nieuwe Sessie", type="primary"):
|
|
|
return {
|
|
|
"motion_count": motion_count,
|
|
|
"policy_area": policy_area,
|
|
|
"margin_range": margin_range,
|
|
|
}
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
# Example 5: Motion voting interface
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
def render_motion_vote(motion: dict, index: int, total: int):
|
|
|
"""Pattern: Display motion and voting buttons."""
|
|
|
|
|
|
st.subheader(f"Motie {index + 1} van {total}")
|
|
|
|
|
|
# Motion content
|
|
|
st.markdown(f"### {motion['title']}")
|
|
|
|
|
|
col1, col2 = st.columns([3, 1])
|
|
|
with col1:
|
|
|
if motion.get("layman_explanation"):
|
|
|
st.info(motion["layman_explanation"])
|
|
|
|
|
|
with st.expander("Meer details"):
|
|
|
st.markdown(f"**Datum:** {motion.get('date', 'Onbekend')}")
|
|
|
st.markdown(f"**Beleidsgebied:** {motion.get('policy_area', 'Onbekend')}")
|
|
|
|
|
|
if motion.get("description"):
|
|
|
st.markdown(f"**Beschrijving:** {motion['description']}")
|
|
|
|
|
|
with col2:
|
|
|
st.metric(
|
|
|
label="Winstmarge",
|
|
|
value=f"{motion.get('winning_margin', 0):.0%}",
|
|
|
delta="Omstreden" if motion.get("controversy_score", 0) > 0.5 else "Helder",
|
|
|
)
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
# Voting buttons
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
|
|
with col1:
|
|
|
st.button(
|
|
|
"👍 **Voor**",
|
|
|
on_click=on_vote,
|
|
|
args=(motion["id"], "Voor"),
|
|
|
use_container_width=True,
|
|
|
)
|
|
|
|
|
|
with col2:
|
|
|
st.button(
|
|
|
"👎 **Tegen**",
|
|
|
on_click=on_vote,
|
|
|
args=(motion["id"], "Tegen"),
|
|
|
use_container_width=True,
|
|
|
)
|
|
|
|
|
|
with col3:
|
|
|
st.button(
|
|
|
"🤔 **Onthouden**",
|
|
|
on_click=on_vote,
|
|
|
args=(motion["id"], "Onthouden"),
|
|
|
use_container_width=True,
|
|
|
)
|
|
|
|
|
|
|
|
|
def on_vote(motion_id: int, vote: str):
|
|
|
"""Callback when user votes."""
|
|
|
|
|
|
# Record vote
|
|
|
from database import db
|
|
|
|
|
|
db.record_vote(
|
|
|
session_id=st.session_state.session_id, motion_id=motion_id, vote=vote
|
|
|
)
|
|
|
|
|
|
# Update session state
|
|
|
st.session_state.user_votes[motion_id] = vote
|
|
|
|
|
|
# Move to next or show results
|
|
|
if st.session_state.current_motion_index < len(st.session_state.motions) - 1:
|
|
|
st.session_state.current_motion_index += 1
|
|
|
else:
|
|
|
st.session_state.show_results = True
|
|
|
|
|
|
st.rerun()
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
# Example 6: Results display
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
def render_results():
|
|
|
"""Pattern: Display voting results."""
|
|
|
|
|
|
from database import db
|
|
|
|
|
|
st.header("📊 Uw Resultaten")
|
|
|
|
|
|
# Get party results
|
|
|
results = db.get_party_results(st.session_state.session_id)
|
|
|
|
|
|
if not results:
|
|
|
st.warning("Geen resultaten beschikbaar")
|
|
|
return
|
|
|
|
|
|
# Sort by agreement
|
|
|
sorted_results = sorted(
|
|
|
results.items(), key=lambda x: x[1].get("agreement_percentage", 0), reverse=True
|
|
|
)
|
|
|
|
|
|
# Display top match
|
|
|
if sorted_results:
|
|
|
top_party, top_data = sorted_results[0]
|
|
|
st.success(
|
|
|
f"**Uw beste match:** {top_party} ({top_data.get('agreement_percentage', 0):.0%} overeenstemming)"
|
|
|
)
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
# Show all parties
|
|
|
for party, data in sorted_results:
|
|
|
agreement = data.get("agreement_percentage", 0)
|
|
|
|
|
|
col1, col2 = st.columns([3, 1])
|
|
|
with col1:
|
|
|
st.markdown(f"**{party}**")
|
|
|
st.progress(agreement, text=f"{agreement:.0%}")
|
|
|
|
|
|
with col2:
|
|
|
st.metric("Overeenstemming", f"{agreement:.0%}")
|
|
|
|
|
|
# Detailed breakdown
|
|
|
with st.expander("Details per motie"):
|
|
|
for motion in st.session_state.motions:
|
|
|
user_vote = st.session_state.user_votes.get(motion["id"], "?")
|
|
|
st.markdown(f"- **{motion['title']}**: U={user_vote}")
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
# Example 7: Tabs layout
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
def render_tabs_example():
|
|
|
"""Pattern: Use tabs for organizing content."""
|
|
|
|
|
|
tab1, tab2, tab3 = st.tabs(["Compass", "Trajectories", "Zoeken"])
|
|
|
|
|
|
with tab1:
|
|
|
st.subheader("Politiek Kompas")
|
|
|
st.write("Visualiseer partijposities in 2D ruimte")
|
|
|
# Add compass chart...
|
|
|
|
|
|
with tab2:
|
|
|
st.subheader("Partij Trajectories")
|
|
|
st.write("Bekijk hoe partijen door de tijd bewegen")
|
|
|
# Add trajectory chart...
|
|
|
|
|
|
with tab3:
|
|
|
st.subheader("Zoek Moties")
|
|
|
|
|
|
query = st.text_input("Zoekterm")
|
|
|
if query:
|
|
|
# Search functionality...
|
|
|
st.write(f"Zoeken naar: {query}")
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
# Demo rendering
|
|
|
init_session_state()
|
|
|
st.write("Streamlit page structure example")
|
|
|
|