diff --git a/classquiz/db/models.py b/classquiz/db/models.py index 889ce94..cc395b9 100644 --- a/classquiz/db/models.py +++ b/classquiz/db/models.py @@ -320,7 +320,7 @@ class GameResults(ormar.Model): database = database -class Controllers(ormar.Model): +class Controller(ormar.Model): id: uuid.UUID = ormar.UUID(primary_key=True) user: uuid.UUID | User = ormar.ForeignKey(User) secret_key: str = ormar.String(nullable=False, max_length=24, min_length=24) @@ -329,8 +329,9 @@ class Controllers(ormar.Model): first_seen: datetime | None = ormar.DateTime(nullable=True) name: str = ormar.Text(nullable=False) os_version: str | None = ormar.Text(nullable=True) + wanted_os_version: str = ormar.Text(nullable=True, default=None) class Meta: - tablename = "controllers" + tablename = "controller" metadata = metadata database = database diff --git a/classquiz/routers/box_controller/embedded/__init__.py b/classquiz/routers/box_controller/embedded/__init__.py index b97a314..77a8f97 100644 --- a/classquiz/routers/box_controller/embedded/__init__.py +++ b/classquiz/routers/box_controller/embedded/__init__.py @@ -10,9 +10,10 @@ import uuid from datetime import datetime from fastapi import APIRouter, HTTPException +from fastapi.responses import PlainTextResponse from pydantic import BaseModel from classquiz.config import redis -from classquiz.db.models import PlayGame, Controllers +from classquiz.db.models import PlayGame, Controller from classquiz.routers.box_controller.embedded.socket import router as socket_router router = APIRouter() @@ -31,7 +32,7 @@ class JoinGameResponse(BaseModel): @router.post("/join") async def join_game(data: JoinGameInput) -> JoinGameResponse: - controller = await Controllers.objects.get_or_none(id=data.id, secret_key=data.secret_key) + controller = await Controller.objects.get_or_none(id=data.id, secret_key=data.secret_key) game_pin = await redis.get(f"game:cqc:code:{data.code}") if game_pin is None: raise HTTPException(status_code=404, detail="Game not found") @@ -65,7 +66,7 @@ async def register_with_code(data: RegisterWithCodeInput) -> RegisterWithCodeRes raise HTTPException(status_code=404, detail="Code not found") await redis.delete(f"controller_setup:{data.code}") c_id = uuid.UUID(c_id) - controller = await Controllers.objects.get(id=c_id) + controller = await Controller.objects.get(id=c_id) controller.first_seen = datetime.now() controller.last_seen = datetime.now() await controller.update() @@ -74,9 +75,19 @@ async def register_with_code(data: RegisterWithCodeInput) -> RegisterWithCodeRes @router.get("/ping") async def ping_server(id: uuid.UUID, secret_key: str, version: str): - controller = await Controllers.objects.get_or_none(id=id, secret_key=secret_key) + controller = await Controller.objects.get_or_none(id=id, secret_key=secret_key) if controller is None: raise HTTPException(status_code=404, detail="Key and/or id invalid") controller.last_seen = datetime.now() controller.os_version = version await controller.update() + + +@router.get("/update") +async def get_firmware_version(id: uuid.UUID, secret_key: str) -> PlainTextResponse: + controller = await Controller.objects.get_or_none(id=id, secret_key=secret_key) + if controller is None: + return PlainTextResponse(status_code=404, content="Key and/or id invalid") + if controller.wanted_os_version is None: + return PlainTextResponse(status_code=400, content="No update needed") + return PlainTextResponse(status_code=200, content=controller.wanted_os_version) diff --git a/classquiz/routers/box_controller/embedded/socket.py b/classquiz/routers/box_controller/embedded/socket.py index 5b12d9a..4c2ff03 100644 --- a/classquiz/routers/box_controller/embedded/socket.py +++ b/classquiz/routers/box_controller/embedded/socket.py @@ -62,7 +62,7 @@ async def submit_answer_fn(data_answer: int, game_pin: str, player_id: str, now: await sio.emit("everyone_answered", {}) -button_to_index_map = {"b": 0, "g": 2, "y": 1, "r": 3} +button_to_index_map = {"y": 0, "r": 2, "g": 1, "b": 3} class WebSocketTypes(enum.Enum): @@ -85,7 +85,6 @@ async def websocket_endpoint(ws: WebSocket, game_id: str): await ws.close(code=status.WS_1001_GOING_AWAY) print("Client {} already exists.".format(game_id)) return - print("hI!") await ws.accept() wss_clients[game_id] = ws @@ -119,9 +118,7 @@ async def websocket_endpoint(ws: WebSocket, game_id: str): except (KeyError, AttributeError): await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="InvalidButton").json()) continue - print(data) await submit_answer_fn(answer_index, game_pin, player_id, now) - print("Data from client {}: {}".format(game_id, data)) except WebSocketDisconnect as ex: print("Client {} is disconnected: {}".format(game_id, ex)) diff --git a/classquiz/routers/box_controller/web.py b/classquiz/routers/box_controller/web.py index be641fb..92a0f8a 100644 --- a/classquiz/routers/box_controller/web.py +++ b/classquiz/routers/box_controller/web.py @@ -8,7 +8,7 @@ from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from classquiz.auth import get_current_user -from classquiz.db.models import User, Controllers +from classquiz.db.models import User, Controller from classquiz.config import redis from classquiz.helpers.box_controller import generate_code @@ -32,7 +32,7 @@ async def set_controller_up( code = generate_code(10) if input_data.player_name is None: input_data.player_name = user.username - data = Controllers( + data = Controller( id=uuid.uuid4(), user=user, secret_key=os.urandom(12).hex(), @@ -47,12 +47,12 @@ async def set_controller_up( return SetControllerUpResponse(code=code, id=data.id) -GetControllerResponse = Controllers.get_pydantic(exclude={"secret_key", "user"}) +GetControllerResponse = Controller.get_pydantic(exclude={"secret_key", "user"}) @router.get("/controller") async def get_controller(id: uuid.UUID, user: User = Depends(get_current_user)) -> GetControllerResponse: - controller = await Controllers.objects.get_or_none(id=id, user=user.id) + controller = await Controller.objects.get_or_none(id=id, user=user.id) if controller is None: raise HTTPException(status_code=404, detail="Controller not found") return GetControllerResponse(**controller.dict()) @@ -68,7 +68,7 @@ class ModifyControllerInput(BaseModel): async def modify_controller( data: ModifyControllerInput, user: User = Depends(get_current_user) ) -> GetControllerResponse: - controller = await Controllers.objects.get_or_none(id=data.id, user=user.id) + controller = await Controller.objects.get_or_none(id=data.id, user=user.id) if controller is None: raise HTTPException(status_code=404, detail="Controller not found") controller.player_name = data.player_name @@ -79,10 +79,24 @@ async def modify_controller( @router.get("/list") async def get_all_controllers(user: User = Depends(get_current_user)) -> list[GetControllerResponse]: - controllers = await Controllers.objects.all(user=user.id) + controllers = await Controller.objects.all(user=user.id) if len(controllers) == 0: return [] return_list = [] for controller in controllers: return_list.append(GetControllerResponse(**controller.dict())) return return_list + + +class SetVersionToBeFlashedInput(BaseModel): + id: uuid.UUID + version: str + + +@router.post("/set_update") +async def set_version_to_be_flashed(data: SetVersionToBeFlashedInput, user: User = Depends(get_current_user)): + controller = await Controller.objects.get_or_none(id=data.id, user=user.id) + if controller is None: + raise HTTPException(status_code=404, detail="Controller not found") + controller.wanted_os_version = data.version + await controller.update() diff --git a/frontend/src/lib/i18n/locales/en.json b/frontend/src/lib/i18n/locales/en.json index c6ee4d3..a4a75af 100644 --- a/frontend/src/lib/i18n/locales/en.json +++ b/frontend/src/lib/i18n/locales/en.json @@ -161,7 +161,12 @@ "results": "Results", "note": "Note", "player_plural": "Players", - "score": "Score" + "score": "Score", + "version": "Version", + "never": "Never", + "unknown": "Unknown", + "name": "Name", + "update": "Update" }, "editor": { "time_in_seconds": "Time in seconds", @@ -272,5 +277,16 @@ "correct_answer": "{{count}} correct answer", "correct_answer_plural": "{{count}} correct answers", "time_taken": "Time taken" + }, + "controllers": { + "add_new_controller": "Add new controller", + "player_name": "Player name", + "first_seen": "First seen", + "last_seen": "Last seen", + "controller_name": "Controller Name", + "version_overview": "The newest version available is {{newest_version}} and version {{current_version}} is installed.", + "already_latest_version": "You're already on the latest version", + "cancel_update": "Cancel Update!", + "update_from_to": "Update from {{current_version}} to {{newest_version}}" } } diff --git a/frontend/src/routes/account/controllers/+page.svelte b/frontend/src/routes/account/controllers/+page.svelte index b5eb783..a5898d1 100644 --- a/frontend/src/routes/account/controllers/+page.svelte +++ b/frontend/src/routes/account/controllers/+page.svelte @@ -1,7 +1,13 @@ + + +
+ {$t('controllers.version_overview', { + newest_version, + current_version: controller.os_version + })} +
+ {/await} + {#if newest_version && controller.os_version} +