import flet as ft import requests import threading import asyncio import websockets from websockets import connect # Ensure this is imported import json from datetime import datetime def get_initials(user_name: str): if user_name: return user_name[0].capitalize() else: return "E" # or any default value you prefer def get_avatar_color(user_name: str): colors_lookup = [ ft.colors.AMBER, ft.colors.BLUE, ft.colors.BROWN, ft.colors.CYAN, ft.colors.GREEN, ft.colors.INDIGO, ft.colors.LIME, ft.colors.ORANGE, ft.colors.PINK, ft.colors.PURPLE, ft.colors.RED, ft.colors.TEAL, ] return colors_lookup[hash(user_name) % len(colors_lookup)] def student_main_page(page: ft.Page): page.clean() page.title = "Room Information" page.theme_mode = ft.ThemeMode.LIGHT page.vertical_alignment = ft.MainAxisAlignment.START # Align all content to the top page.scroll = "adaptive" page.padding = 20 # Container for lessons lessons_container = ft.Column( alignment=ft.MainAxisAlignment.CENTER, spacing=20) def test(e): print(e) page.go("/test") print("test") def ausloggen(e=None): page.go("/login") print(page.session.get("access_token")) def page_dialog_click(e, lesson_id): dialog_leave = ft.AlertDialog( modal=True, title=ft.Text("Bitte bestätige"), content=ft.Text("Möchtest du diesem Raum wirklich verlassen?"), actions=[ ft.TextButton("Ja", on_click=lambda e: (page.close( dialog_leave), leave_current_room(e, lesson_id))), ft.TextButton( "Nein", on_click=lambda e: page.close(dialog_leave)), ], actions_alignment=ft.MainAxisAlignment.END, ) page.dialog = dialog_leave dialog_leave.open = True page.update() def leave_current_room(e=None, unique_id=None): if not unique_id: page.snack_bar = ft.SnackBar( ft.Text("Kein Raum zum verlassen ausgewählt")) page.snack_bar.open = True page.update() return url = f"http://awesom-o.org:8000/student/leave_room/?unique_id={ unique_id}" headers = {"accept": "application/json", "Content-Type": "application/json"} data = {"session_id": page.session.get("access_token")} try: response = requests.post(url, json=data, headers=headers) response.raise_for_status() if response: get_rooms() page.snack_bar = ft.SnackBar( ft.Text("Raum erfolgreich verlassen")) page.snack_bar.open = True page.update() get_rooms() except requests.RequestException as error: # Handle all bad responses error_detail = "Something went wrong." if hasattr(error, "response") and error.response: try: error_detail = error.response.json().get("detail", "Something went wrong.") except json.JSONDecodeError: error_detail = error.response.text or "Etwas ist schiefgelaufen" page.snack_bar = ft.SnackBar(ft.Text(f"Error: {error_detail}")) page.snack_bar.open = True page.update() async def get_rooms(): uri = "ws://localhost:8000/ws/student/my_room" headers = {"session-id": page.session.get("access_token")} try: async with websockets.connect(uri, extra_headers=headers) as websocket: while True: message = await websocket.recv() data = json.loads(message) # Clear previous lessons lessons_container.controls.clear() if "error" in data: # Show "No Rooms for Today" message lessons_container.controls.append( ft.Container( content=ft.Text( "Für heute wurden noch keine Räume ausgewählt.", size=25, text_align="center", ), border=ft.Border( top=ft.BorderSide(1, ft.colors.BLACK), bottom=ft.BorderSide(1, ft.colors.BLACK), left=ft.BorderSide(1, ft.colors.BLACK), right=ft.BorderSide(1, ft.colors.BLACK) ), border_radius=20, padding=10, height=150, bgcolor=ft.colors.WHITE, alignment=ft.alignment.center, ) ) else: rooms = data.get("rooms", []) # Filter and sort lessons for today today = datetime.now().date() today_lessons = [ room for room in rooms if room["lesson_date"] == str(today)] today_lessons.sort(key=lambda x: x["lesson_time"]) # Check if there are rooms on other dates other_date_rooms = [ room for room in rooms if room["lesson_date"] != str(today)] if today_lessons: for lesson in today_lessons: if lesson['current_students'] == lesson['max_students']: BACKROUND_COLOR = "#f10d0c" TEXT_COLOR = ft.colors.WHITE else: BACKROUND_COLOR = "#729fcf" TEXT_COLOR = ft.colors.BLACK # Create lesson card room_card = ft.Card( content=ft.Container( content=ft.Row( [ # Room details ft.Column( [ # Top line: # room_number, info, location ft.Text( f"Heute, { lesson['lesson_time']} Uhr", size=16, color=TEXT_COLOR ), # Second line: first_name and last_name ft.Text( f"Lehrkraft: {lesson['first_name']} { lesson['last_name']} - {lesson['subjects']}", size=16, color=TEXT_COLOR ), # Third line: lesson_time ft.Text( f"Raum {lesson['room_number']} { lesson['info']} | {lesson['location']}", size=16, color=TEXT_COLOR ), ft.Text( f"Belegte Plätze: { lesson['current_students']}/{lesson['max_students']}", size=16, color=TEXT_COLOR ), ], expand=True, alignment=ft.MainAxisAlignment.CENTER, ), # IconButton on the right ft.Column( [ ft.TextButton("Aus Raum abmelden", on_click=lambda e, lesson_id=lesson['unique_id']: page_dialog_click(e, lesson_id), style=ft.ButtonStyle( bgcolor=ft.colors.WHITE, color=ft.colors.BLACK, shape=ft.RoundedRectangleBorder( radius=5), padding=20, ) ), ft.Text("ACHTUNG: Hierdurch wird", size=12), ft.Text( "dein reservierter Platz für", size=12), ft.Text("jemand anderes freigegeben.", size=12), ] ) ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN, ), padding=20, # Add padding inside the card ), color=BACKROUND_COLOR, margin=10, ) lessons_container.controls.append(room_card) else: # Show "No Rooms for Today" message lessons_container.controls.append( ft.Container( content=ft.Text( "Für heute wurden noch keine Räume ausgewählt.", size=25, text_align="center", ), border=ft.Border( top=ft.BorderSide(1, ft.colors.BLACK), bottom=ft.BorderSide( 1, ft.colors.BLACK), left=ft.BorderSide(1, ft.colors.BLACK), right=ft.BorderSide(1, ft.colors.BLACK) ), border_radius=20, padding=10, height=150, bgcolor=ft.colors.WHITE, alignment=ft.alignment.center, ) ) # Update page page.update() except Exception as e: lessons_container.controls.clear() lessons_container.controls.append( ft.Text(f"Error: {e}", size=50, weight="bold", text_align="center")) page.update() def start_get_rooms(): # Ensure a new event loop in the thread loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(get_rooms()) # Start the WebSocket client in a thread import threading threading.Thread(target=start_get_rooms, daemon=True).start() join_more_rooms_button = ft.Container( content=ft.Text("Räume suchen", size=24, color=ft.colors.BLUE_900), on_click=lambda e: page.go("/join"), alignment=ft.alignment.center, width=200, height=60, bgcolor='transparent', ink=True, ) navigation_bar = ft.Row( [ft.Text("Home", size=23, weight=ft.FontWeight.BOLD, width=100), join_more_rooms_button], ) # Add components to page page.add( ft.Column( [ ft.Row( [ ft.Text( "Daltonraum-Buchungssystem der IGS Garbsen", size=30), ft.Column([ ft.PopupMenuButton( items=[ ft.PopupMenuItem(content=ft.Text(f"Eingeloggt als: {page.session.get("username")}", weight=ft.FontWeight.BOLD)), ft.PopupMenuItem(text="Profil anzeigen", on_click=test), ft.PopupMenuItem(text="Ausloggen", on_click=ausloggen), ], content=ft.CircleAvatar( content=ft.Text(get_initials( page.session.get("username"))), color=ft.colors.WHITE, bgcolor=get_avatar_color( page.session.get("username")), ), menu_position=ft.PopupMenuPosition.UNDER, tooltip="", ) ], # Align column content to the end (optional) alignment=ft.MainAxisAlignment.END, ), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN, # Space out the two elements vertical_alignment=ft.CrossAxisAlignment.CENTER, # Align items vertically ), navigation_bar, ft.Row( # Wrap the Text in a Row [ ft.Text( "Du hast dich heute in folgende Daltonräume eingetragen:", size=25) ], alignment=ft.MainAxisAlignment.CENTER # Center the Text horizontally ), ft.Column( [ lessons_container, ], alignment=ft.MainAxisAlignment.CENTER, expand=True, # Make it take available space vertically ), ], expand=True, # This makes the outer column take available space as well alignment=ft.MainAxisAlignment.CENTER, # Center the whole column vertically ) )