Hamzavelous's picture
Enhanced UI/UX, new features, C PDC benchmarks
86369aa verified
Raw
History Blame Contribute Delete
31.6 kB
"""
Hostel Management System β€” Streamlit Web App
PDC Lab Project | UCP BSAI
Ch Hamza Younas (L1F23BAI0089) & Muhammad Abdullah (L1F23BSAI0063)
"""
import json
import time
from datetime import date
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st
from hostel_data import (
STATUS_MAP,
add_announcement,
add_gate_log,
add_student,
assign_room,
bulk_mark_attendance,
export_summary,
find_room,
find_student,
fresh_state,
get_activity_feed,
get_analytics,
get_student_attendance_history,
get_student_gate_logs,
mark_attendance,
match_student,
pay_label,
record_payment,
room_status,
)
from pdc_benchmark import CHART_CATEGORIES, run_all_benchmarks
from theme import (
activity_feed,
feature_grid,
hero,
inject_theme,
metrics_row,
page_footer,
room_grid,
sidebar_brand,
)
st.set_page_config(
page_title="Hostel MS | PDC Lab",
page_icon="🏠",
layout="wide",
initial_sidebar_state="expanded",
)
inject_theme()
def init_session():
defaults = {
"db": fresh_state(),
"user": None,
"role": None,
"student_idx": -1,
}
for key, val in defaults.items():
if key not in st.session_state:
st.session_state[key] = val
def logout():
st.session_state.user = None
st.session_state.role = None
st.session_state.student_idx = -1
def active_students(db):
return [s for s in db["students"] if s["active"]]
def active_rooms(db):
return [r for r in db["rooms"] if r["active"]]
def student_name(db, idx):
return db["students"][idx]["name"]
def room_number(db, room_id):
for r in db["rooms"]:
if r["id"] == room_id:
return r["number"]
return "None"
# ── LOGIN ─────────────────────────────────────────────────────────────────────
def page_login():
hero("🏠 Hostel Management System", "Smart hostel operations · PDC parallel computing demo")
feature_grid([
("πŸ“Š", "Live Dashboard", "Occupancy, fees, complaints & analytics at a glance"),
("πŸ”", "Role Portals", "Separate admin & student workspaces with secure login"),
("⚑", "PDC Benchmarks", "Real OpenMP & POSIX Threads speedup tests in C"),
("🍽️", "Full Operations", "Rooms, mess, gate logs, attendance & announcements"),
])
st.markdown("<br>", unsafe_allow_html=True)
c1, c2, c3 = st.columns([1, 2, 1])
with c2:
st.markdown('<div class="login-shell">', unsafe_allow_html=True)
st.markdown(
"""
<div class="login-header">
<div class="icon">🏠</div>
<h2>Welcome Back</h2>
<p>Sign in to your hostel portal</p>
</div>
""",
unsafe_allow_html=True,
)
tab_a, tab_s = st.tabs(["πŸ” Admin / Warden", "πŸŽ“ Student"])
with tab_a:
user = st.text_input("Username", value="admin", key="adm_user")
pwd = st.text_input("Password", type="password", value="admin123", key="adm_pwd")
if st.button("Login as Admin", type="primary", use_container_width=True):
if user == "admin" and pwd == "admin123":
st.session_state.user = "Warden"
st.session_state.role = "admin"
st.success("Welcome, Warden!")
time.sleep(0.3)
st.rerun()
else:
st.error("Invalid admin credentials")
with tab_s:
roll = st.text_input("Roll No", value="UCP-BSAI-1001", key="stu_roll")
pwd2 = st.text_input("Password", type="password", value="student123", key="stu_pwd")
if st.button("Login as Student", type="primary", use_container_width=True):
idx = find_student(st.session_state.db["students"], roll)
if idx >= 0 and st.session_state.db["students"][idx]["password"] == pwd2:
st.session_state.user = st.session_state.db["students"][idx]["name"]
st.session_state.role = "student"
st.session_state.student_idx = idx
st.success(f"Welcome, {st.session_state.user}!")
time.sleep(0.3)
st.rerun()
else:
st.error("Invalid roll number or password")
st.caption("Demo Β· admin/admin123 Β· UCP-BSAI-1001/student123")
st.markdown("</div>", unsafe_allow_html=True)
page_footer()
# ── ADMIN ─────────────────────────────────────────────────────────────────────
def admin_dashboard():
db = st.session_state.db
stats = get_analytics(db)
hero("Admin Control Center", f"Welcome back, {st.session_state.user}", badge="Live")
metrics_row([
(stats["students_total"], "Active Students", f"{stats['present_today']} present today"),
(f"{stats['occupancy_pct']}%", "Occupancy", f"{stats['vacant']} vacant rooms"),
(stats["defaulters"], "Fee Defaulters", f"PKR {stats['total_dues']:,.0f} pending"),
(len(db["complaints"]), "Open Tickets", "Track in Mess & Complaints"),
])
c1, c2, c3 = st.columns(3)
with c1:
fig = px.pie(
names=["Vacant", "Partial", "Full"],
values=[stats["vacant"], stats["partial"], stats["full"]],
title="Room Availability",
color_discrete_sequence=["#10B981", "#F59E0B", "#6366F1"],
hole=0.45,
)
fig.update_layout(template="plotly_dark", height=320, margin=dict(t=40, b=20))
st.plotly_chart(fig, use_container_width=True)
with c2:
fig = go.Figure(go.Bar(
x=["Rent Clear", "Mess Clear", "Defaulters"],
y=[stats["rent_clear"], stats["mess_clear"], stats["defaulters"]],
marker_color=["#6366F1", "#A855F7", "#EF4444"],
))
fig.update_layout(template="plotly_dark", title="Fee Status", height=320, margin=dict(t=40, b=20))
st.plotly_chart(fig, use_container_width=True)
with c3:
cc = stats["complaint_counts"]
fig = px.bar(
x=list(cc.keys()), y=list(cc.values()),
title="Complaint Pipeline", color=list(cc.values()),
color_continuous_scale="Viridis",
)
fig.update_layout(template="plotly_dark", height=320, showlegend=False, margin=dict(t=40, b=20))
st.plotly_chart(fig, use_container_width=True)
st.markdown("#### πŸ“’ Latest Announcements")
for a in db["announcements"][:3]:
pri = a["priority"].upper()
st.info(f"**{a['title']}** ({pri}) β€” {a['body']}")
c1, c2 = st.columns([1, 1])
with c1:
st.markdown('<p class="panel-title">πŸ• Live Activity Feed</p>', unsafe_allow_html=True)
feed = get_activity_feed(db)
if feed:
activity_feed(feed)
else:
st.caption("No recent activity")
with c2:
st.markdown('<p class="panel-title">πŸ›οΈ Room Occupancy Grid</p>', unsafe_allow_html=True)
tiles = [{
"number": r["number"], "occupied": r["occupied"],
"capacity": r["capacity"], "status": room_status(r),
} for r in active_rooms(db)[:10]]
room_grid(tiles)
page_footer()
def admin_rooms():
hero("Room Management", "Inventory, capacity & rent configuration")
db = st.session_state.db
tab1, tab2, tab3 = st.tabs(["πŸ“‹ All Rooms", "βž• Add Room", "πŸ—‘οΈ Delete"])
with tab1:
rows = [{
"Room": r["number"], "Floor": r["floor"], "Type": r["type"],
"AC": "Yes" if r["ac"] else "No", "Occ/Cap": f"{r['occupied']}/{r['capacity']}",
"Status": room_status(r), "Rent (PKR)": r["rent"],
} for r in active_rooms(db)]
st.dataframe(pd.DataFrame(rows), use_container_width=True, hide_index=True)
st.download_button("⬇️ Export CSV", pd.DataFrame(rows).to_csv(index=False),
"rooms.csv", "text/csv", use_container_width=True)
with tab2:
with st.form("add_room"):
num = st.text_input("Room Number", placeholder="R-116")
floor = st.number_input("Floor", 1, 10, 1)
rtype = st.selectbox("Type", ["2-seater", "4-seater"])
ac = st.checkbox("AC Room")
cap = st.number_input("Capacity", 1, 8, 2)
rent = st.number_input("Monthly Rent (PKR)", 5000, 50000, 12000)
if st.form_submit_button("Add Room", type="primary"):
if find_room(db["rooms"], num) >= 0:
st.error(f"Room {num} already exists")
else:
db["rooms"].append({
"id": len(db["rooms"]) + 1, "number": num, "floor": floor,
"type": rtype, "ac": ac, "capacity": cap, "occupied": 0,
"rent": rent, "active": True,
})
st.success(f"Room {num} added")
st.rerun()
with tab3:
num = st.text_input("Room to delete")
if st.button("Delete Room", type="primary"):
ri = find_room(db["rooms"], num)
if ri < 0:
st.error("Room not found")
elif any(s["active"] and s["room_id"] == db["rooms"][ri]["id"] for s in db["students"]):
st.error("Cannot delete β€” students assigned")
else:
db["rooms"][ri]["active"] = False
st.success(f"Room {num} removed")
st.rerun()
def admin_students():
hero("Student Management", "Enrollment, search, room assignment")
db = st.session_state.db
tab1, tab2, tab3, tab4 = st.tabs(["πŸ“‹ Roster", "βž• Enroll", "πŸ” Search", "🏠 Assign Room"])
with tab1:
rows = []
for s in active_students(db):
status = "CLEAR" if s["rent_paid"] and s["mess_paid"] else "DUE"
rows.append({
"Name": s["name"], "Roll": s["roll"], "Room": room_number(db, s["room_id"]),
"Phone": s["phone"], "Rent": pay_label(s["rent_paid"]),
"Mess": pay_label(s["mess_paid"]), "Status": status,
})
st.dataframe(pd.DataFrame(rows), use_container_width=True, hide_index=True)
st.download_button("⬇️ Export CSV", pd.DataFrame(rows).to_csv(index=False),
"students.csv", "text/csv")
with tab2:
with st.form("enroll"):
name = st.text_input("Full Name")
roll = st.text_input("Roll Number", placeholder="UCP-BSAI-1011")
phone = st.text_input("Phone", placeholder="0300-1234567")
room = st.text_input("Room Number", placeholder="R-116")
if st.form_submit_button("Enroll Student", type="primary"):
ok, msg = add_student(db, name, roll, phone, room)
st.success(msg) if ok else st.error(msg)
if ok:
st.rerun()
with tab3:
key = st.text_input("Search by name or roll")
if key:
t0 = time.perf_counter()
results = [s for s in db["students"] if match_student(s, key)]
elapsed = time.perf_counter() - t0
st.dataframe(pd.DataFrame([{
"Name": s["name"], "Roll": s["roll"], "Room": room_number(db, s["room_id"]),
"Rent": pay_label(s["rent_paid"]), "Mess": pay_label(s["mess_paid"]),
} for s in results]), use_container_width=True, hide_index=True)
st.caption(f"Found {len(results)} in {elapsed:.6f}s")
with tab4:
roll = st.text_input("Student Roll", key="assign_roll")
room = st.text_input("Target Room", key="assign_room")
if st.button("Assign Room", type="primary"):
ok, msg = assign_room(db, roll, room)
st.success(msg) if ok else st.error(msg)
if ok:
st.rerun()
def admin_fees():
hero("Fee & Finance", "Challans, payments & defaulter tracking")
db = st.session_state.db
tab1, tab2, tab3, tab4 = st.tabs(["πŸ“„ Challans", "πŸ’³ Record Payment", "⚠️ Defaulters", "πŸ“œ History"])
with tab1:
rows, total = [], 0
for s in active_students(db):
t = s["rent_due"] + s["mess_due"] + s["utility_due"]
total += t
rows.append({"Name": s["name"], "Rent": s["rent_due"], "Mess": s["mess_due"],
"Utility": s["utility_due"], "Total": t})
st.dataframe(pd.DataFrame(rows), use_container_width=True, hide_index=True)
st.metric("Grand Total Monthly Dues", f"PKR {total:,.0f}")
with tab2:
roll = st.text_input("Student Roll No")
idx = find_student(db["students"], roll) if roll else -1
if idx >= 0:
s = db["students"][idx]
st.info(f"**{s['name']}** Β· Rent: {pay_label(s['rent_paid'])} Β· Mess: {pay_label(s['mess_paid'])}")
if s["rent_paid"] and s["mess_paid"]:
st.success("All payments clear")
else:
opts = []
if not s["rent_paid"]:
opts.append("Rent")
if not s["mess_paid"]:
opts.append("Mess")
choice = st.multiselect("Record payment for", opts)
if st.button("Save Payment", type="primary"):
record_payment(db, idx, "Rent" in choice, "Mess" in choice)
st.success("Payment recorded")
st.rerun()
with tab3:
rows = [{"Name": s["name"], "Roll": s["roll"],
"Rent": pay_label(s["rent_paid"]), "Mess": pay_label(s["mess_paid"])}
for s in active_students(db) if not s["rent_paid"] or not s["mess_paid"]]
st.dataframe(pd.DataFrame(rows) if rows else pd.DataFrame([{"Status": "No defaulters"}]),
use_container_width=True, hide_index=True)
with tab4:
hist = []
for p in db["payment_history"]:
hist.append({
"Student": student_name(db, p["student_idx"]),
"Type": p["type"], "Amount": p["amount"], "Date": p["date"],
})
st.dataframe(pd.DataFrame(hist), use_container_width=True, hide_index=True)
def admin_mess_complaints():
hero("Mess & Complaints", "Weekly menu & maintenance tickets")
db = st.session_state.db
tab1, tab2, tab3 = st.tabs(["🍽️ Mess Menu", "πŸ“‹ Complaints", "πŸ“’ Announcements"])
with tab1:
cols = st.columns(2)
for i, m in enumerate(db["mess"]):
with cols[i % 2]:
with st.expander(f"πŸ“… {m['day']}", expanded=i < 2):
st.markdown(f"πŸŒ… **Breakfast:** {m['breakfast']}")
st.markdown(f"β˜€οΈ **Lunch:** {m['lunch']}")
st.markdown(f"πŸŒ™ **Dinner:** {m['dinner']}")
with tab2:
filter_st = st.selectbox("Filter by status", ["All"] + list(STATUS_MAP.values()))
for c in db["complaints"]:
if filter_st != "All" and STATUS_MAP[c["status"]] != filter_st:
continue
name = student_name(db, c["student_idx"])
st.markdown(f"**#{c['id']}** Β· {name} Β· `{STATUS_MAP[c['status']]}` Β· _{c.get('created', '')}_")
st.write(c["text"])
new_st = st.selectbox("Status", [0, 1, 2], format_func=lambda x: STATUS_MAP[x],
key=f"st_{c['id']}")
if st.button("Update", key=f"btn_{c['id']}"):
c["status"] = new_st
st.success("Updated")
st.rerun()
with tab3:
with st.form("ann"):
title = st.text_input("Title")
body = st.text_area("Message")
pri = st.selectbox("Priority", ["low", "medium", "high"])
if st.form_submit_button("Publish", type="primary") and title and body:
add_announcement(db, title, body, pri)
st.success("Announcement published")
st.rerun()
for a in db["announcements"]:
st.info(f"**{a['title']}** ({a['priority']}) β€” {a['date']}\n\n{a['body']}")
def admin_gate_attendance():
hero("Gate & Attendance", "Entry/exit logs & daily roll call")
db = st.session_state.db
tab1, tab2 = st.tabs(["πŸšͺ Gate Logs", "βœ… Attendance"])
with tab1:
c1, c2 = st.columns(2)
with c1:
rolls = {s["roll"]: i for s in active_students(db)}
roll = st.selectbox("Student", list(rolls.keys()))
log_type = st.selectbox("Action", ["IN", "OUT"])
if st.button("Log Entry", type="primary"):
add_gate_log(db, rolls[roll], log_type)
st.success(f"{log_type} logged for {student_name(db, rolls[roll])}")
st.rerun()
with c2:
logs = [{
"Student": student_name(db, g["student_idx"]),
"Type": g["type"], "Time": g["time"],
} for g in reversed(db["gate_logs"][-15:])]
st.dataframe(pd.DataFrame(logs), use_container_width=True, hide_index=True)
with tab2:
today = str(date.today())
bc1, bc2, bc3 = st.columns(3)
if bc1.button("βœ… Mark All Present", use_container_width=True):
bulk_mark_attendance(db, True)
st.success("All students marked present")
st.rerun()
if bc2.button("❌ Mark All Absent", use_container_width=True):
bulk_mark_attendance(db, False)
st.warning("All students marked absent")
st.rerun()
if bc3.button("πŸ“Š Attendance Summary", use_container_width=True):
present = sum(1 for a in db["attendance"]
if a["date"] == today and a["present"])
st.info(f"Present today: **{present}** / {len(active_students(db))}")
st.divider()
for i, s in enumerate(active_students(db)):
rec = next((a for a in db["attendance"]
if a["student_idx"] == i and a["date"] == today), None)
present = rec["present"] if rec else False
c1, c2, c3 = st.columns([3, 1, 1])
c1.write(f"**{s['name']}** ({s['roll']})")
if c2.button("Present", key=f"p_{i}"):
mark_attendance(db, i, True)
st.rerun()
if c3.button("Absent", key=f"a_{i}"):
mark_attendance(db, i, False)
st.rerun()
st.caption("Present" if present else "Absent")
def admin_analytics():
hero("Analytics Hub", "Deep insights across hostel operations")
db = st.session_state.db
stats = get_analytics(db)
c1, c2 = st.columns(2)
with c1:
students = active_students(db)
df = pd.DataFrame({
"Category": ["Paid", "Due"],
"Count": [
sum(1 for s in students if s["rent_paid"]),
sum(1 for s in students if not s["rent_paid"]),
],
})
fig = px.pie(df, names="Category", values="Count", title="Rent Collection",
color="Category", color_discrete_map={"Paid": "#6366F1", "Due": "#EF4444"}, hole=0.4)
fig.update_layout(template="plotly_dark", height=350)
st.plotly_chart(fig, use_container_width=True)
with c2:
floors = {}
for r in active_rooms(db):
floors[r["floor"]] = floors.get(r["floor"], 0) + r["occupied"]
fig = px.bar(x=list(floors.keys()), y=list(floors.values()), title="Occupancy by Floor",
labels={"x": "Floor", "y": "Students"}, color_discrete_sequence=["#A855F7"])
fig.update_layout(template="plotly_dark", height=350)
st.plotly_chart(fig, use_container_width=True)
st.markdown("#### Summary")
st.json({
"occupancy_rate": f"{stats['occupancy_pct']}%",
"defaulters": stats["defaulters"],
"pending_dues_pkr": stats["total_dues"],
"complaints": stats["complaint_counts"],
"gate_logs_today": len(db["gate_logs"]),
})
page_footer()
def admin_reports():
hero("Reports & Export", "Download operational snapshots", badge="Export")
db = st.session_state.db
summary = export_summary(db)
c1, c2 = st.columns(2)
with c1:
st.markdown("#### πŸ“¦ Summary Report")
st.json(summary)
st.download_button(
"⬇️ Download JSON Report",
json.dumps(summary, indent=2),
"hostel_report.json",
"application/json",
use_container_width=True,
)
with c2:
students = active_students(db)
df = pd.DataFrame([{
"Name": s["name"], "Roll": s["roll"],
"Room": room_number(db, s["room_id"]),
"Rent": pay_label(s["rent_paid"]), "Mess": pay_label(s["mess_paid"]),
} for s in students])
st.download_button(
"⬇️ Download Student Roster CSV",
df.to_csv(index=False),
"roster.csv",
"text/csv",
use_container_width=True,
)
rooms_df = pd.DataFrame([{
"Room": r["number"], "Floor": r["floor"], "Status": room_status(r),
"Occupied": r["occupied"], "Capacity": r["capacity"],
} for r in active_rooms(db)])
st.download_button(
"⬇️ Download Room Inventory CSV",
rooms_df.to_csv(index=False),
"rooms.csv",
"text/csv",
use_container_width=True,
)
if st.button("πŸ”„ Reset Demo Data", type="secondary"):
st.session_state.db = fresh_state()
st.success("Demo data restored")
st.rerun()
page_footer()
def page_help():
hero("Help & About", "Project documentation & credentials")
st.markdown("""
### About
**Hostel Management System** β€” PDC Lab Project for UCP BSAI.
Built by **Ch Hamza Younas** & **Muhammad Abdullah** under **Ma'am Khadija**.
### Demo Logins
| Role | Username / Roll | Password |
|------|-----------------|----------|
| Admin | `admin` | `admin123` |
| Student | `UCP-BSAI-1001` | `student123` |
### PDC Benchmark
Runs compiled **search_bench** with **Sequential**, **OpenMP**, and **POSIX Threads** in C.
### Links
- [Hugging Face Space](https://huggingface.co/spaces/Hamzavelous/Hostel-Management_System)
- [GitHub Repository](https://github.com/Hamzavelous/Hostel-Management_System)
""")
page_footer()
def page_pdc():
hero("⚑ PDC Performance Test", "Sequential · OpenMP · POSIX Threads", badge="C Backend")
st.markdown(
"Runs the compiled **search_bench** binary with real OpenMP and pthread parallelism β€” "
"same engine as the console C application."
)
keyword = st.text_input("Search keyword", value="Ali")
if st.button("Run Benchmark", type="primary", use_container_width=True):
try:
with st.spinner("Executing C parallel benchmarks..."):
rows, kw = run_all_benchmarks(None, keyword)
except FileNotFoundError as exc:
st.error(str(exc))
return
except RuntimeError as exc:
st.error(f"Benchmark failed: {exc}")
return
df = pd.DataFrame(rows)
df["Time (ms)"] = (df["Time (s)"] * 1000).round(2)
st.dataframe(
df[["Method", "Workers", "Fee (s)", "Search (s)", "Time (s)", "Time (ms)", "Found", "Speedup"]],
use_container_width=True, hide_index=True,
)
chart_df = df.set_index("Label").reindex(CHART_CATEGORIES).reset_index()
fig = px.bar(chart_df, x="Label", y="Speedup", color="Method", text="Speedup",
title="Speedup vs Sequential Baseline",
color_discrete_map={
"Sequential": "#6366F1", "OpenMP": "#A855F7", "POSIX Threads": "#10B981",
})
fig.update_traces(texttemplate="%{text:.2f}x", textposition="outside")
fig.update_layout(template="plotly_dark", height=420, xaxis_title="")
st.plotly_chart(fig, use_container_width=True)
c1, c2 = st.columns(2)
with c1:
fig2 = px.line(chart_df, x="Label", y="Time (ms)", markers=True, title="Execution Time (ms)")
fig2.update_layout(template="plotly_dark", height=300)
st.plotly_chart(fig2, use_container_width=True)
with c2:
st.info(
f"**Workload:** Fee calculation + student search\n\n"
f"**Keyword:** \"{kw}\" matched **{rows[0]['Found']}** student(s)\n\n"
f"**Best speedup:** {max(r['Speedup'] for r in rows):.2f}x"
)
# ── STUDENT ───────────────────────────────────────────────────────────────────
def student_profile():
s = st.session_state.db["students"][st.session_state.student_idx]
db = st.session_state.db
hero("My Profile", s["name"])
c1, c2 = st.columns(2)
with c1:
st.markdown("#### Personal")
st.write(f"**Roll:** {s['roll']}")
st.write(f"**Phone:** {s['phone']}")
st.write(f"**Emergency:** {s['emergency']}")
st.write(f"**Last Payment:** {s['last_pay_date']}")
with c2:
st.markdown("#### Room")
if s["room_id"] > 0:
r = next(x for x in db["rooms"] if x["id"] == s["room_id"])
st.write(f"**Room:** {r['number']} Β· Floor {r['floor']}")
st.write(f"**Type:** {r['type']} Β· AC: {'Yes' if r['ac'] else 'No'}")
st.write(f"**Monthly Rent:** PKR {r['rent']:,.0f}")
else:
st.warning("No room assigned")
def student_fees_page():
s = st.session_state.db["students"][st.session_state.student_idx]
total = s["rent_due"] + s["mess_due"] + s["utility_due"]
hero("My Fees", "Payment status")
metrics_row([
(f"PKR {s['rent_due']:,.0f}", "Rent", pay_label(s["rent_paid"])),
(f"PKR {s['mess_due']:,.0f}", "Mess", pay_label(s["mess_paid"])),
(f"PKR {s['utility_due']:,.0f}", "Utility", "Fixed"),
(f"PKR {total:,.0f}", "Total Due", "Monthly"),
])
if s["rent_paid"] and s["mess_paid"]:
st.success("βœ… All payments clear β€” thank you!")
else:
st.warning("⚠️ Pending dues β€” please visit the warden office")
def student_complaint_page():
idx = st.session_state.student_idx
db = st.session_state.db
hero("Complaint Box", "Report maintenance issues")
text = st.text_area("Describe your issue", placeholder="Wi-Fi, plumbing, electricity...")
if st.button("Submit Ticket", type="primary") and text.strip():
cid = len(db["complaints"]) + 1
db["complaints"].append({
"id": cid, "student_idx": idx, "text": text.strip(),
"status": 0, "created": str(date.today()),
})
st.success(f"Ticket #{cid} submitted")
st.rerun()
st.markdown("#### My Tickets")
for c in db["complaints"]:
if c["student_idx"] == idx:
st.info(f"#{c['id']}: {c['text']} β€” **{STATUS_MAP[c['status']]}**")
def student_mess_page():
hero("Mess Menu", "Weekly dining schedule")
for m in st.session_state.db["mess"]:
with st.expander(f"πŸ“… {m['day']}"):
st.write(f"πŸŒ… **Breakfast:** {m['breakfast']}")
st.write(f"β˜€οΈ **Lunch:** {m['lunch']}")
st.write(f"πŸŒ™ **Dinner:** {m['dinner']}")
def student_announcements():
hero("Announcements", "Notices from hostel administration")
for a in st.session_state.db["announcements"]:
icon = "πŸ”΄" if a["priority"] == "high" else "🟑" if a["priority"] == "medium" else "🟒"
st.markdown(f"{icon} **{a['title']}** Β· _{a['date']}_")
st.write(a["body"])
st.divider()
page_footer()
def student_activity():
idx = st.session_state.student_idx
db = st.session_state.db
hero("My Activity", "Gate logs & attendance history")
tab1, tab2 = st.tabs(["πŸšͺ Gate Logs", "βœ… Attendance"])
with tab1:
logs = get_student_gate_logs(db, idx)
if logs:
st.dataframe(pd.DataFrame([{
"Action": g["type"], "Time": g["time"],
} for g in reversed(logs)]), use_container_width=True, hide_index=True)
else:
st.info("No gate logs yet")
with tab2:
hist = get_student_attendance_history(db, idx)
if hist:
st.dataframe(pd.DataFrame([{
"Date": a["date"],
"Status": "Present" if a["present"] else "Absent",
} for a in reversed(hist)]), use_container_width=True, hide_index=True)
else:
st.info("No attendance records yet")
page_footer()
# ── ROUTER ────────────────────────────────────────────────────────────────────
ADMIN_PAGES = {
"🏠 Dashboard": admin_dashboard,
"πŸ›οΈ Rooms": admin_rooms,
"πŸŽ“ Students": admin_students,
"πŸ’° Fees": admin_fees,
"🍽️ Mess & Complaints": admin_mess_complaints,
"πŸšͺ Gate & Attendance": admin_gate_attendance,
"πŸ“Š Analytics": admin_analytics,
"πŸ“ Reports": admin_reports,
"⚑ PDC Performance": page_pdc,
"❓ Help": page_help,
}
STUDENT_PAGES = {
"πŸ‘€ My Profile": student_profile,
"πŸ’° My Fees": student_fees_page,
"πŸ“ Complaints": student_complaint_page,
"🍽️ Mess Menu": student_mess_page,
"πŸ“’ Announcements": student_announcements,
"πŸ• My Activity": student_activity,
"⚑ PDC Performance": page_pdc,
"❓ Help": page_help,
}
def main():
init_session()
if st.session_state.role is None:
page_login()
return
with st.sidebar:
sidebar_brand()
st.markdown(f"### πŸ‘€ {st.session_state.user}")
st.caption(f"Role Β· **{st.session_state.role.title()}**")
if st.session_state.role == "student":
pending = sum(
1 for a in st.session_state.db["announcements"] if a["priority"] == "high"
)
st.caption(f"πŸ“’ {pending} high-priority notices")
elif st.session_state.role == "admin":
stats = get_analytics(st.session_state.db)
st.caption(f"⚠️ {stats['defaulters']} fee defaulters · {stats['vacant']} vacant rooms")
st.divider()
pages = ADMIN_PAGES if st.session_state.role == "admin" else STUDENT_PAGES
choice = st.radio("Navigate", list(pages.keys()), label_visibility="collapsed")
st.divider()
if st.button("πŸšͺ Logout", use_container_width=True, type="secondary"):
logout()
st.rerun()
st.divider()
st.caption("PDC Lab Β· UCP BSAI")
st.caption("Hamza Younas & Abdullah")
st.caption("[HF Space](https://huggingface.co/spaces/Hamzavelous/Hostel-Management_System)")
pages[choice]()
if __name__ == "__main__":
main()