426 lines
16 KiB
Python
426 lines
16 KiB
Python
import flet as ft
|
|
import asyncio
|
|
import websockets
|
|
import json
|
|
import requests
|
|
import threading
|
|
from datetime import datetime
|
|
|
|
def get_initials(user_name: str):
|
|
if user_name:
|
|
return user_name[0].capitalize()
|
|
else:
|
|
return "E"
|
|
|
|
|
|
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 join_page(page: ft.Page):
|
|
page.clean()
|
|
page.theme_mode = ft.ThemeMode.LIGHT # Set the theme to light
|
|
page.title = "Join Rooms"
|
|
page.vertical_alignment = ft.MainAxisAlignment.START # Align all content to the top
|
|
page.scroll = "adaptive"
|
|
page.padding = 20
|
|
|
|
# Check if the user is logged in
|
|
session = page.session.get("access_token")
|
|
if not session:
|
|
page.go("/login") # Redirect to login if no session
|
|
|
|
def test(e):
|
|
print(e)
|
|
page.go("/test")
|
|
print("test")
|
|
|
|
# Search field
|
|
search_field = ft.TextField(label="Lehrkraft / Raumnummer ", expand=True)
|
|
|
|
# Dropdown for time filter
|
|
time_filter = ft.Dropdown(
|
|
label="Stunde wählen",
|
|
options=[
|
|
ft.dropdown.Option("8:00", "8:00"),
|
|
ft.dropdown.Option("9:20", "9:20"),
|
|
ft.dropdown.Option("10:40", "10:40"),
|
|
ft.dropdown.Option("11:50", "11:50"),
|
|
ft.dropdown.Option("12:50", "12:50"),
|
|
ft.dropdown.Option("13:55", "13:55"),
|
|
ft.dropdown.Option("15:00", "15:00"),
|
|
],
|
|
width=170, # Shorter width for time filter
|
|
)
|
|
|
|
# Cross icon button to clear time filter
|
|
clear_time_filter = ft.IconButton(
|
|
icon=ft.icons.CLOSE,
|
|
on_click=lambda e: clear_filter("time"),
|
|
tooltip="Clear Time Filter",
|
|
)
|
|
|
|
# Container for the room list
|
|
room_container = ft.Column(scroll=ft.ScrollMode.AUTO, expand=True)
|
|
|
|
def ausloggen(e = None):
|
|
page.go("/login")
|
|
print(page.session.set("access_token", None))
|
|
|
|
# Function to clear filters
|
|
def clear_filter(filter_type):
|
|
if filter_type == "time":
|
|
time_filter.value = None
|
|
if filter_type == "lesson":
|
|
lesson_filter.value = None
|
|
|
|
update_room_list(json.loads(page.session.get("rooms_data", "[]")))
|
|
def update_room_list(rooms):
|
|
# Ensure rooms is a list
|
|
if not isinstance(rooms, list):
|
|
rooms = []
|
|
|
|
# Apply search filter
|
|
search_query = search_field.value.lower()
|
|
filtered_rooms = [
|
|
room
|
|
for room in rooms
|
|
if (search_query in str(room.get("room_name", "")).lower() # Search by room_name
|
|
or search_query in room.get("info", "").lower() # Search by room_info
|
|
or search_query in room.get("location", "").lower() # Search by location
|
|
or search_query in room.get("subjects", "").lower() # Search by subjects
|
|
or search_query in room.get("first_name", "").lower() # Search by teacher_first_name
|
|
or search_query in room.get("last_name", "").lower()) # Search by teacher_last_name
|
|
]
|
|
|
|
if time_filter.value: # Only apply the filter if a time is selected
|
|
filtered_rooms = [
|
|
room
|
|
for room in filtered_rooms
|
|
if room.get("lesson_time", "") == time_filter.value
|
|
]
|
|
|
|
if lesson_filter.value: # Only apply the filter if a time is selected
|
|
filtered_rooms = [
|
|
room
|
|
for room in filtered_rooms
|
|
if lesson_filter.value.lower() in room.get("subjects", "").lower().split(", ")
|
|
]
|
|
|
|
if show_full_rooms.value == True:
|
|
filtered_rooms = [
|
|
room
|
|
for room in filtered_rooms
|
|
if room.get("is_open", "") == True
|
|
]
|
|
|
|
# Apply date filter (only if a date is selected)
|
|
room_container.controls.clear()
|
|
|
|
def page_dialog_click(e, unique_id):
|
|
dialog_join = ft.AlertDialog(
|
|
modal=True,
|
|
title=ft.Text("Bitte bestätige"),
|
|
content=ft.Text("Möchtest du diesem Raum wirklich beitreten?"),
|
|
actions=[
|
|
ft.TextButton("Ja", on_click=lambda e: (page.close(dialog_join), room_clicked(e, unique_id))),
|
|
ft.TextButton("Nein", on_click=lambda e: page.close(dialog_join)),
|
|
],
|
|
actions_alignment=ft.MainAxisAlignment.END,
|
|
)
|
|
page.dialog = dialog_join
|
|
dialog_join.open = True
|
|
page.update()
|
|
|
|
def room_clicked(e, unique_id):
|
|
try:
|
|
url = f"http://awesom-o.org:8000/student/join_room/?unique_id={unique_id}"
|
|
headers = {"accept": "application/json", "Content-Type": "application/json"}
|
|
data = {"session_id": page.session.get("access_token")}
|
|
|
|
response = requests.post(url, json=data, headers=headers)
|
|
|
|
if response.status_code == 200:
|
|
try:
|
|
page.snack_bar = ft.SnackBar(ft.Text(f"Raum erfolgreich beigetreten: {unique_id}"))
|
|
page.snack_bar.open = True
|
|
page.update()
|
|
page.go("/")
|
|
except:
|
|
page.snack_bar = ft.SnackBar(ft.Text(f"Ein fehler ist aufgetreten, bitte melde dich bei der Administration"))
|
|
page.snack_bar.open = True
|
|
page.update()
|
|
else:
|
|
dialog_join = ft.AlertDialog(
|
|
modal=True,
|
|
title=ft.Text("Fehler"),
|
|
content=ft.Text(response.json().get("detail")),
|
|
actions=[
|
|
ft.TextButton("Schließen", on_click=lambda e: page.close(dialog_join)),
|
|
],
|
|
actions_alignment=ft.MainAxisAlignment.END,
|
|
)
|
|
page.dialog = dialog_join
|
|
dialog_join.open = True
|
|
page.update()
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
dialog_join = ft.AlertDialog(
|
|
modal=True,
|
|
title=ft.Text("Fehler"),
|
|
content=ft.Text("Ein Fehler ist aufgetreten"),
|
|
actions=[
|
|
ft.TextButton("Schließen", on_click=lambda e: page.close(dialog_join)),
|
|
],
|
|
actions_alignment=ft.MainAxisAlignment.END,
|
|
)
|
|
page.dialog = dialog_join
|
|
dialog_join.open = True
|
|
page.update()
|
|
|
|
|
|
for room in filtered_rooms:
|
|
room_name = room.get("room_name", "Unknown Room")
|
|
room_info = room.get("info", "")
|
|
location = room.get("location", "")
|
|
first_name = room.get("first_name", "")
|
|
last_name = room.get("last_name", "")
|
|
subjects = room.get("subjects", "")
|
|
lesson_time = room.get("lesson_time", "")
|
|
lesson_date = room.get("lesson_date", "")
|
|
max_students = room.get("max_students", 0)
|
|
current_students = room.get("current_students", 0)
|
|
unique_id = room.get("unique_id", "")
|
|
is_open = room.get("is_open", True)
|
|
joined = room.get("joined", False)
|
|
|
|
if current_students == max_students:
|
|
BACKROUND_COLOR = "#f10d0c"
|
|
TEXT_COLOR = ft.colors.WHITE
|
|
user_interface_button_text = ft.Text("Raum bereits voll")
|
|
else:
|
|
BACKROUND_COLOR = "#729fcf"
|
|
TEXT_COLOR = ft.colors.BLACK
|
|
if joined == True:
|
|
user_interface_button_text = ft.Text("Raum bereits gebucht")
|
|
else:
|
|
user_interface_button_text = ft.TextButton(
|
|
"Jetzt Platz reservieren",
|
|
on_click=lambda e, unique_id=unique_id: page_dialog_click(e, unique_id),
|
|
style=ft.ButtonStyle(
|
|
bgcolor=ft.colors.WHITE,
|
|
color=ft.colors.BLACK,
|
|
shape=ft.RoundedRectangleBorder(radius=5),
|
|
padding=20,
|
|
))
|
|
room_card = ft.Card(
|
|
content=ft.Container(
|
|
content=ft.Row(
|
|
[
|
|
# Room details
|
|
ft.Column(
|
|
[
|
|
ft.Text(f"Heute, {lesson_time} Uhr", size=16, color=TEXT_COLOR),
|
|
ft.Text(f"Lehrkraft: {first_name} {last_name} - {subjects}", size=16, color=TEXT_COLOR),
|
|
ft.Text(f"Raum {room_name} {room_info} | {location}", size=16, color=TEXT_COLOR),
|
|
ft.Text(f"Belegte Plätze: {current_students}/{max_students}", size=16, color=TEXT_COLOR),
|
|
],
|
|
expand=True,
|
|
alignment=ft.MainAxisAlignment.CENTER,
|
|
),
|
|
# IconButton on the right
|
|
user_interface_button_text,
|
|
],
|
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
|
),
|
|
padding=20, # Add padding inside the card
|
|
),
|
|
color=BACKROUND_COLOR,
|
|
margin=10,
|
|
)
|
|
room_container.controls.append(room_card)
|
|
|
|
|
|
if not filtered_rooms:
|
|
room_container.controls.append(ft.Text("Keine offenen Räume verfügbar für Heute.", size=18))
|
|
|
|
page.update()
|
|
|
|
# WebSocket listener for real-time updates
|
|
async def listen_for_updates():
|
|
uri = "ws://localhost:8000/ws/open_rooms"
|
|
headers = {"session-id": page.session.get("access_token")}
|
|
|
|
async with websockets.connect(uri, extra_headers=headers) as websocket:
|
|
print("Connected to WebSocket server")
|
|
|
|
try:
|
|
while True:
|
|
# Receive data from the server
|
|
data = await websocket.recv()
|
|
rooms = json.loads(data)
|
|
# Ensure rooms is a list
|
|
if isinstance(rooms, dict):
|
|
rooms = rooms.get("rooms", [])
|
|
elif not isinstance(rooms, list):
|
|
rooms = []
|
|
|
|
# Store rooms data in session for filtering
|
|
page.session.set("rooms_data", json.dumps(rooms))
|
|
|
|
# Extract unique dates and sort them from nearest to longest
|
|
unique_dates = list(set(room.get("lesson_date", "") for room in rooms))
|
|
unique_dates.sort(key=lambda x: datetime.strptime(x, "%Y-%m-%d")) # Sort dates
|
|
|
|
|
|
# Update the room list
|
|
update_room_list(rooms)
|
|
|
|
except websockets.ConnectionClosed:
|
|
print("WebSocket connection closed")
|
|
|
|
# Start the WebSocket listener in a separate thread
|
|
def start_websocket_listener():
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
loop.run_until_complete(listen_for_updates())
|
|
|
|
threading.Thread(target=start_websocket_listener, daemon=True).start()
|
|
|
|
top_bar = ft.Row(
|
|
[
|
|
search_field,
|
|
ft.Row([time_filter, clear_time_filter], spacing=5),
|
|
],
|
|
alignment=ft.MainAxisAlignment.START,
|
|
height=50,
|
|
)
|
|
|
|
lesson_filter = ft.Dropdown(
|
|
label="Fach wählen",
|
|
options=[
|
|
ft.dropdown.Option("De", "De"),
|
|
ft.dropdown.Option("Ma", "Ma"),
|
|
ft.dropdown.Option("NW", "NW"),
|
|
ft.dropdown.Option("En", "En"),
|
|
ft.dropdown.Option("Gs", "Gs"),
|
|
ft.dropdown.Option("Re", "Re"),
|
|
ft.dropdown.Option("WN", "WN"),
|
|
ft.dropdown.Option("Fr", "Fr"),
|
|
ft.dropdown.Option("Sp", "Sp"),
|
|
ft.dropdown.Option("Sn", "Sn"),
|
|
],
|
|
width=170,
|
|
)
|
|
|
|
clear_lesson_filter = ft.IconButton(
|
|
icon=ft.icons.CLOSE,
|
|
on_click=lambda e: clear_filter("lesson"),
|
|
tooltip="Clear Lesson Filter",
|
|
)
|
|
|
|
|
|
show_full_rooms = ft.Checkbox(label="Volle Räume ausblenden ", value=False, label_style=ft.TextStyle(size=20))
|
|
|
|
middle_bar = ft.Row(
|
|
[
|
|
show_full_rooms,
|
|
ft.Row(
|
|
[
|
|
ft.Column(
|
|
[
|
|
ft.Text(
|
|
"Nur Räume anzeigen, in denen\nfolgendes Fach angeboten wird:",
|
|
size=20,
|
|
),
|
|
],
|
|
),
|
|
lesson_filter,
|
|
clear_lesson_filter,
|
|
],
|
|
spacing=5,
|
|
alignment=ft.MainAxisAlignment.END,
|
|
),
|
|
],
|
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
|
height=50,
|
|
)
|
|
|
|
go_home_button = ft.Container(
|
|
content=ft.Text("Home", size=24, color=ft.colors.BLUE_900),
|
|
on_click=lambda e: page.go("/"),
|
|
alignment=ft.alignment.center,
|
|
width=200,
|
|
height=60,
|
|
bgcolor='transparent',
|
|
ink=True,
|
|
)
|
|
|
|
navigation_bar = ft.Row(
|
|
[go_home_button, ft.Text("Räume suchen", size=23, weight=ft.FontWeight.BOLD, width=170, no_wrap=True)],
|
|
)
|
|
|
|
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,
|
|
top_bar,
|
|
middle_bar,
|
|
ft.Column(
|
|
[
|
|
room_container,
|
|
],
|
|
alignment=ft.MainAxisAlignment.CENTER,
|
|
expand=True,
|
|
),
|
|
],
|
|
expand=True,
|
|
alignment=ft.MainAxisAlignment.CENTER,
|
|
)
|
|
)
|