"""Mock data & business logic for Streamlit Hostel MS.""" import copy import math import random from datetime import date, datetime DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] STATUS_MAP = {0: "Pending", 1: "In Progress", 2: "Resolved"} def seed_data(): random.seed(42) rooms = [] for i in range(15): ac = i % 3 == 0 cap = 2 if i % 2 == 0 else 4 rooms.append({ "id": i + 1, "number": f"R-{101 + i:03d}", "floor": i // 5 + 1, "type": "2-seater" if i % 2 == 0 else "4-seater", "ac": ac, "capacity": cap, "occupied": 0, "rent": 18000 if ac else 12000, "active": True, }) names = [ "Ali Hassan", "Fatima Malik", "Usman Ahmed", "Ayesha Khan", "Bilal Raza", "Hamza Younas", "Abdullah Khan", "Sana Iqbal", "Zain Butt", "Hira Nawaz", ] students = [] for i, name in enumerate(names): students.append({ "id": i + 1, "name": name, "roll": f"UCP-BSAI-{1001 + i}", "phone": f"0300-{1000000 + i:07d}", "emergency": f"Parent {i + 1}", "password": "student123", "room_id": i + 1, "active": True, "rent_due": rooms[i]["rent"], "mess_due": 8500, "utility_due": 1500, "rent_paid": i % 3 != 0, "mess_paid": i % 4 != 0, "last_pay_date": "2026-05-15", "mess_off_days": 0, }) rooms[i]["occupied"] = 1 mess = [] for d in range(7): mess.append({ "day": DAYS[d], "breakfast": "Paratha + Omelette + Tea", "lunch": "Biryani + Raita", "dinner": "Karahi + Naan", }) complaints = [ {"id": 1, "student_idx": 0, "text": "Wi-Fi not working in Room R-101", "status": 0, "created": "2026-06-10"}, {"id": 2, "student_idx": 3, "text": "Ceiling fan making noise", "status": 1, "created": "2026-06-12"}, {"id": 3, "student_idx": 5, "text": "Water heater not heating properly", "status": 0, "created": "2026-06-14"}, ] gate_logs = [ {"id": 1, "student_idx": 0, "type": "OUT", "time": "2026-06-15 08:30"}, {"id": 2, "student_idx": 0, "type": "IN", "time": "2026-06-15 18:45"}, {"id": 3, "student_idx": 5, "type": "OUT", "time": "2026-06-15 09:15"}, ] attendance = [ {"student_idx": i, "date": str(date.today()), "present": i % 5 != 0} for i in range(10) ] announcements = [ {"id": 1, "title": "Mess Timings Updated", "body": "Dinner will be served 7:00–9:00 PM during exam week.", "date": "2026-06-14", "priority": "high"}, {"id": 2, "title": "Fee Deadline Reminder", "body": "Please clear pending dues before June 30 to avoid fine.", "date": "2026-06-13", "priority": "medium"}, {"id": 3, "title": "Guest Policy", "body": "Visitors allowed only in common room until 6 PM.", "date": "2026-06-10", "priority": "low"}, ] payment_history = [ {"student_idx": 1, "type": "Rent", "amount": 18000, "date": "2026-05-01"}, {"student_idx": 1, "type": "Mess", "amount": 8500, "date": "2026-05-01"}, {"student_idx": 4, "type": "Rent", "amount": 12000, "date": "2026-05-03"}, ] return { "rooms": rooms, "students": students, "mess": mess, "complaints": complaints, "gate_logs": gate_logs, "attendance": attendance, "announcements": announcements, "payment_history": payment_history, } def fresh_state(): return copy.deepcopy(seed_data()) def find_student(students, roll): for i, s in enumerate(students): if s["active"] and s["roll"] == roll: return i return -1 def find_room(rooms, number): for i, r in enumerate(rooms): if r["active"] and r["number"].upper() == number.upper(): return i return -1 def room_status(r): if r["occupied"] == 0: return "Vacant" if r["occupied"] < r["capacity"]: return "Partial" return "Full" def pay_label(paid): return "Paid" if paid else "Due" def calc_score(student, work=80000): total = 0.0 for w in range(work): total += math.sin(student["rent_due"] * w * 0.0001) total += math.cos(student["mess_due"] + w * 0.001) if not student["rent_paid"]: total += 5000 if not student["mess_paid"]: total += 3000 return total def match_student(student, key): if not student["active"]: return False k = key.lower() return k in student["name"].lower() or k in student["roll"].lower() def record_payment(db, idx, pay_rent, pay_mess): s = db["students"][idx] today = str(date.today()) if pay_rent and not s["rent_paid"]: s["rent_paid"] = True db["payment_history"].append({ "student_idx": idx, "type": "Rent", "amount": s["rent_due"], "date": today, }) if pay_mess and not s["mess_paid"]: s["mess_paid"] = True db["payment_history"].append({ "student_idx": idx, "type": "Mess", "amount": s["mess_due"], "date": today, }) s["last_pay_date"] = today def add_student(db, name, roll, phone, room_number, password="student123"): if find_student(db["students"], roll) >= 0: return False, "Roll number already exists" ri = find_room(db["rooms"], room_number) if ri < 0: return False, "Room not found" room = db["rooms"][ri] if room["occupied"] >= room["capacity"]: return False, "Room is full" idx = len(db["students"]) db["students"].append({ "id": idx + 1, "name": name, "roll": roll, "phone": phone, "emergency": "Not set", "password": password, "room_id": room["id"], "active": True, "rent_due": room["rent"], "mess_due": 8500, "utility_due": 1500, "rent_paid": False, "mess_paid": False, "last_pay_date": "-", "mess_off_days": 0, }) room["occupied"] += 1 return True, f"Student {name} enrolled in {room['number']}" def assign_room(db, roll, room_number): si = find_student(db["students"], roll) if si < 0: return False, "Student not found" ri = find_room(db["rooms"], room_number) if ri < 0: return False, "Room not found" student = db["students"][si] new_room = db["rooms"][ri] if new_room["occupied"] >= new_room["capacity"]: return False, "Target room is full" if student["room_id"] > 0: for r in db["rooms"]: if r["id"] == student["room_id"] and r["occupied"] > 0: r["occupied"] -= 1 student["room_id"] = new_room["id"] student["rent_due"] = new_room["rent"] new_room["occupied"] += 1 return True, f"Assigned {student['name']} to {new_room['number']}" def add_gate_log(db, student_idx, log_type): db["gate_logs"].append({ "id": len(db["gate_logs"]) + 1, "student_idx": student_idx, "type": log_type, "time": datetime.now().strftime("%Y-%m-%d %H:%M"), }) def mark_attendance(db, student_idx, present): today = str(date.today()) for rec in db["attendance"]: if rec["student_idx"] == student_idx and rec["date"] == today: rec["present"] = present return db["attendance"].append({"student_idx": student_idx, "date": today, "present": present}) def add_announcement(db, title, body, priority="medium"): db["announcements"].insert(0, { "id": len(db["announcements"]) + 1, "title": title, "body": body, "date": str(date.today()), "priority": priority, }) def bulk_mark_attendance(db, present=True): today = str(date.today()) for i, s in enumerate(db["students"]): if not s["active"]: continue mark_attendance(db, i, present) def get_student_gate_logs(db, student_idx, limit=20): return [g for g in db["gate_logs"] if g["student_idx"] == student_idx][-limit:] def get_student_attendance_history(db, student_idx, limit=14): return [a for a in db["attendance"] if a["student_idx"] == student_idx][-limit:] def get_activity_feed(db, limit=8): """Recent events for dashboard activity stream.""" events = [] for g in reversed(db["gate_logs"][-5:]): name = db["students"][g["student_idx"]]["name"] events.append((g["time"], "🚪", f"{name} — Gate {g['type']}", g["time"])) for p in reversed(db["payment_history"][-3:]): name = db["students"][p["student_idx"]]["name"] events.append((p["date"], "💰", f"{name} paid {p['type']}", f"PKR {p['amount']:,}")) for c in reversed(db["complaints"][-3:]): name = db["students"][c["student_idx"]]["name"] events.append((c.get("created", ""), "📝", f"Ticket #{c['id']} — {name}", c["text"][:50])) for a in db["announcements"][:2]: events.append((a["date"], "📢", a["title"], a["body"][:60])) events.sort(key=lambda x: x[0], reverse=True) return [(icon, title, meta) for _, icon, title, meta in events[:limit]] def export_summary(db): stats = get_analytics(db) return { "generated": str(datetime.now()), "analytics": stats, "students": len([s for s in db["students"] if s["active"]]), "rooms": len([r for r in db["rooms"] if r["active"]]), "complaints_open": stats["complaint_counts"].get("Pending", 0), "gate_logs": len(db["gate_logs"]), } def get_analytics(db): rooms = [r for r in db["rooms"] if r["active"]] students = [s for s in db["students"] if s["active"]] vacant = sum(1 for r in rooms if r["occupied"] == 0) partial = sum(1 for r in rooms if 0 < r["occupied"] < r["capacity"]) full = sum(1 for r in rooms if r["occupied"] >= r["capacity"]) rent_clear = sum(1 for s in students if s["rent_paid"]) mess_clear = sum(1 for s in students if s["mess_paid"]) defaulters = sum(1 for s in students if not s["rent_paid"] or not s["mess_paid"]) total_dues = sum( s["rent_due"] + s["mess_due"] + s["utility_due"] for s in students if not s["rent_paid"] or not s["mess_paid"] ) complaint_counts = {STATUS_MAP[k]: 0 for k in STATUS_MAP} for c in db["complaints"]: complaint_counts[STATUS_MAP[c["status"]]] += 1 present_today = sum( 1 for a in db["attendance"] if a["date"] == str(date.today()) and a["present"] ) return { "rooms_total": len(rooms), "students_total": len(students), "vacant": vacant, "partial": partial, "full": full, "rent_clear": rent_clear, "mess_clear": mess_clear, "defaulters": defaulters, "total_dues": total_dues, "complaint_counts": complaint_counts, "present_today": present_today, "occupancy_pct": round( sum(r["occupied"] for r in rooms) / max(sum(r["capacity"] for r in rooms), 1) * 100, 1 ), }