Files
schoolplanner/frontend/pages/student_main_page.py
2025-01-19 00:10:20 +01:00

344 lines
16 KiB
Python

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
)
)