diff --git a/backend/dps_training_k/configuration/settings.py b/backend/dps_training_k/configuration/settings.py index c3bf5de5..0758a932 100644 --- a/backend/dps_training_k/configuration/settings.py +++ b/backend/dps_training_k/configuration/settings.py @@ -58,6 +58,7 @@ ] MIDDLEWARE = [ + "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", diff --git a/backend/dps_training_k/game/consumers/abstract_consumer.py b/backend/dps_training_k/game/consumers/abstract_consumer.py index 279aaa08..1f21552e 100644 --- a/backend/dps_training_k/game/consumers/abstract_consumer.py +++ b/backend/dps_training_k/game/consumers/abstract_consumer.py @@ -34,6 +34,7 @@ class ClosureCodes: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.exercise_code = "" + self.exercise = None self.REQUESTS_MAP = {} self.user = None @@ -141,25 +142,24 @@ def disconnect(self, code): # self.user.clear_channel_name() code = self.ClosureCodes.UNKNOWN if not code else code super().disconnect(code) - + def authenticate(self, token): try: token = Token.objects.get(key=token) self.user = token.user self.user.set_channel_name(self.channel_name) - return True + return True, self.user.username except Token.DoesNotExist: self.close(code=self.ClosureCodes.NOT_AUTHENTICATED) - return False + return False, None def _send_exercise(self): - exercise = Exercise.createExercise() patient = Patient.objects.create( - name="Max Mustermann", exercise=exercise, patientId=123456 + name="Max Mustermann", exercise=self.exercise, patientId=123456 ) exercise_object = { "exercise": { - "exerciseId": exercise.exerciseId, + "exerciseId": self.exercise.exerciseId, "areas": [ { "areaName": "X", @@ -167,25 +167,14 @@ def _send_exercise(self): { "patientId": patient.patientId, "patientName": patient.name, - "patientCode": 0 - } - ], - "personnel": [ - { - "personnelId": 0, - "personnelName": "X" + "patientCode": 0, + "triage": patient.triage, } ], - "material": [ - { - "materialId": 0, - "materialName": "X" - } - ] + "personnel": [{"personnelId": 0, "personnelName": "X"}], + "material": [{"materialId": 0, "materialName": "X"}], } - ] + ], } } - self.send_event( - self.OutgoingMessageTypes.EXERCISE, exercise=exercise_object - ) \ No newline at end of file + self.send_event(self.OutgoingMessageTypes.EXERCISE, exercise=exercise_object) diff --git a/backend/dps_training_k/game/consumers/patient_consumer.py b/backend/dps_training_k/game/consumers/patient_consumer.py index 67a26b29..7cc07f0a 100644 --- a/backend/dps_training_k/game/consumers/patient_consumer.py +++ b/backend/dps_training_k/game/consumers/patient_consumer.py @@ -1,11 +1,13 @@ from .abstract_consumer import AbstractConsumer from urllib.parse import parse_qs +from game.models import Patient class PatientConsumer(AbstractConsumer): class PatientIncomingMessageTypes: EXAMPLE = "example" TEST_PASSTHROUGH = "test-passthrough" + TRIAGE = "triage" class PatientOutgoingMessageTypes: RESPONSE = "response" @@ -14,7 +16,8 @@ class PatientOutgoingMessageTypes: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.patient_code = "" + self.patientId = "" + self.patient = None self.REQUESTS_MAP = { self.PatientIncomingMessageTypes.EXAMPLE: ( self.handle_example, @@ -24,21 +27,29 @@ def __init__(self, *args, **kwargs): self.PatientIncomingMessageTypes.TEST_PASSTHROUGH: ( self.handle_test_passthrough, ), + self.PatientIncomingMessageTypes.TRIAGE: ( + self.handle_triage, + "triage", + ), } def connect(self): query_string = parse_qs(self.scope["query_string"].decode()) token = query_string.get("token", [None])[0] - if self.authenticate(token): + success, patientId = self.authenticate(token) + if success: + self.patient = Patient.objects.get(patientId=patientId) + self.patientId = patientId + self.exercise = self.patient.exercise self.accept() self._send_exercise() def handle_example(self, exercise_code, patient_code): self.exercise_code = exercise_code - self.patient_code = patient_code + self.patientId = patient_code self.send_event( self.PatientOutgoingMessageTypes.RESPONSE, - content=f"exercise_code {self.exercise_code} & patient_code {self.patient_code}", + content=f"exercise_code {self.exercise_code} & patient_code {self.patientId}", ) def handle_test_passthrough(self): @@ -46,3 +57,7 @@ def handle_test_passthrough(self): self.PatientOutgoingMessageTypes.TEST_PASSTHROUGH, message="received test event", ) + + def handle_triage(self, triage): + self.patient.triage = triage + self._send_exercise() diff --git a/backend/dps_training_k/game/consumers/trainer_consumer.py b/backend/dps_training_k/game/consumers/trainer_consumer.py index 8c165f10..5adc76e1 100644 --- a/backend/dps_training_k/game/consumers/trainer_consumer.py +++ b/backend/dps_training_k/game/consumers/trainer_consumer.py @@ -1,4 +1,5 @@ from .abstract_consumer import AbstractConsumer +from game.models import Exercise class TrainerConsumer(AbstractConsumer): @@ -59,6 +60,7 @@ def handle_example(self, exercise_code): ) def handle_create_exercise(self): + self.exercise = Exercise.createExercise() self._send_exercise() def handle_test_passthrough(self): diff --git a/backend/dps_training_k/game/migrations/0006_patient_triage.py b/backend/dps_training_k/game/migrations/0006_patient_triage.py new file mode 100644 index 00000000..aa95ab95 --- /dev/null +++ b/backend/dps_training_k/game/migrations/0006_patient_triage.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.1 on 2024-03-25 14:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('game', '0005_rename_invitation_code_exercise_exerciseid_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='patient', + name='triage', + field=models.CharField(choices=[('-', 'undefined'), ('R', 'red'), ('Y', 'yellow'), ('G', 'green'), ('A', 'airway'), ('B', 'breathing'), ('C', 'circulation'), ('D', 'disability'), ('E', 'exposure')], default='-'), + ), + ] diff --git a/backend/dps_training_k/game/models/patient.py b/backend/dps_training_k/game/models/patient.py index b676d0c1..d3764544 100644 --- a/backend/dps_training_k/game/models/patient.py +++ b/backend/dps_training_k/game/models/patient.py @@ -8,6 +8,17 @@ class Patient(Eventable, Transitionable, UpdateSignals, models.Model): + class Triage(models.TextChoices): + UNDEFINED = "-", "undefined" + RED = "R", "red" + YELLOW = "Y", "yellow" + GREEN = "G", "green" + Airway = "A", "airway" + BREATHING = "B", "breathing" + CIRCULATION = "C", "circulation" + DISABILITY = "D", "disability" + EXPOSURE = "E", "exposure" + name = models.CharField( max_length=100, default="Max Mustermann" ) # technically patientData but kept here for simplicity for now @@ -24,6 +35,10 @@ class Patient(Eventable, Transitionable, UpdateSignals, models.Model): patientId = models.IntegerField( help_text="patientId used to log into patient - therefore part of authentication" ) + triage = models.CharField( + choices=Triage.choices, + default=Triage.UNDEFINED, + ) def __str__(self): return f"Patient #{self.id} called {self.name} with ID {self.patientId}" diff --git a/backend/dps_training_k/game/tests/factories/exercise_factory.py b/backend/dps_training_k/game/tests/factories/exercise_factory.py index f674521e..6a7bf037 100644 --- a/backend/dps_training_k/game/tests/factories/exercise_factory.py +++ b/backend/dps_training_k/game/tests/factories/exercise_factory.py @@ -16,8 +16,8 @@ class Meta: class ExerciseFactory(factory.django.DjangoModelFactory): class Meta: model = Exercise - django_get_or_create = ("config", "invitation_code", "state") + django_get_or_create = ("config", "exerciseId", "state") config = factory.SubFactory(SavedExerciseFactory) - invitation_code = "a" * settings.INVITATION_LOGIC.code_length + exerciseId = "a" * settings.INVITATION_LOGIC.code_length state = Exercise.ExerciseStateTypes.CONFIGURATION diff --git a/backend/dps_training_k/game/tests/factories/patient_factory.py b/backend/dps_training_k/game/tests/factories/patient_factory.py index 68b348d1..68548250 100644 --- a/backend/dps_training_k/game/tests/factories/patient_factory.py +++ b/backend/dps_training_k/game/tests/factories/patient_factory.py @@ -6,8 +6,8 @@ class PatientFactory(factory.django.DjangoModelFactory): class Meta: model = Patient - django_get_or_create = ("name", "exercise", "patientCode") + django_get_or_create = ("name", "exercise", "patientId") name = "Max Mustermann" exercise = factory.SubFactory(ExerciseFactory) - patientCode = 123456 + patientId = 123456 diff --git a/backend/dps_training_k/game/tests/test_websocket_authentication.py b/backend/dps_training_k/game/tests/test_websocket_authentication.py index e158e425..df9e6089 100644 --- a/backend/dps_training_k/game/tests/test_websocket_authentication.py +++ b/backend/dps_training_k/game/tests/test_websocket_authentication.py @@ -1,5 +1,6 @@ from django.test import TransactionTestCase from game.models import User +from .factories import PatientFactory from rest_framework.authtoken.models import Token from configuration.asgi import application from channels.testing import WebsocketCommunicator @@ -9,8 +10,9 @@ class PatientWebSocketTest(TransactionTestCase): def setUp(self): super().setUp() # Create a user and token for testing - self.user = User.objects.create_user(username="testpatient123", password="test") + self.user = User.objects.create_user(username="123456", password="test") self.token, _ = Token.objects.get_or_create(user=self.user) + self.patient = PatientFactory() async def test_authenticated_websocket_connection(self): # Connect to the WebSocket diff --git a/backend/dps_training_k/game/views.py b/backend/dps_training_k/game/views.py index 5c8335d7..bd587844 100644 --- a/backend/dps_training_k/game/views.py +++ b/backend/dps_training_k/game/views.py @@ -3,18 +3,26 @@ from rest_framework.response import Response from django.contrib.auth import authenticate from rest_framework.authtoken.models import Token +from game.models import User class PatientAccessView(APIView): def post(self, request, *args, **kwargs): + user, created = User.objects.get_or_create( + username="123456" + ) # Ensure the username is a string + if created: + user.set_password("2") # Properly hash the password + user.save() + if not (request.data.get("exerciseId") and request.data.get("patientId")): return Response( status=status.HTTP_400_BAD_REQUEST, data="Some required fields are missing", ) - exercise_code = request.data.get("exerciseId") - patient_code = request.data.get("patientId") - user = authenticate(username=exercise_code, password=patient_code) + exercise_id = str(request.data.get("exerciseId")) + patient_id = str(request.data.get("patientId")) + user = authenticate(username=exercise_id, password=patient_id) if user: token, created = Token.objects.get_or_create(user=user) return Response({"token": token.key}, status=status.HTTP_200_OK) diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index 3219db32..c2eb69c1 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -45,3 +45,63 @@ button:active { background-color: var(--green); } +.popup-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + text-align: center; + z-index: 1; +} + +.list { + margin-top: 30px; + margin-left: 30px; + margin-right: 30px; +} + +.listItem { + position: relative; + background-color: #FFFFFF; + border: 1px solid rgb(209, 213, 219); + display: flex; + align-items: center; + text-align: left; + margin-top: -1px; +} + +.listItemButton { + position: relative; + background-color: #FFFFFF; + border: none; + display: flex; + align-items: center; + font-size: 1.25rem; + padding: .75rem 1rem; + padding-left: 0; + text-align: left; + height: 50px; + width: 100%; +} + +.listItemAddButton { + text-align: center; + position: relative; + background-color: #FFFFFF; + border: 1px solid rgb(209, 213, 219); + box-sizing: border-box; + width: 100%; + font-size: 1.25rem; + line-height: 1.25rem; + padding: .75rem 1rem; + margin-top: -1px; +} + +.listItemId, .listItemName { + padding: .75rem 1rem; +} \ No newline at end of file diff --git a/frontend/src/components/screensTrainer/ScreenExerciseCreation.vue b/frontend/src/components/screensTrainer/ScreenExerciseCreation.vue index 9fbe19f8..5e4ae8f8 100644 --- a/frontend/src/components/screensTrainer/ScreenExerciseCreation.vue +++ b/frontend/src/components/screensTrainer/ScreenExerciseCreation.vue @@ -6,7 +6,7 @@ import TopBarTrainer from "@/components/widgets/TopBarTrainer.vue" import {svg} from "@/assets/Svg" import {setArea} from "@/components/screensTrainer/ScreenResourceCreation.vue" - import AreaPopup from '../widgets/AreaPopup.vue' + import DeleteItemPopup from '../widgets/DeleteItemPopup.vue' const exerciseStore = useExerciseStore() @@ -32,21 +32,27 @@ socketTrainer.areaAdd() } + function deleteArea(){ + socketTrainer.areaDelete(currentArea.value) + } + const showPopup = ref(false) \ No newline at end of file diff --git a/frontend/src/components/screensTrainer/pagesResourceCreation/PageMaterial.vue b/frontend/src/components/screensTrainer/pagesResourceCreation/PageMaterial.vue index cdcaac3c..5381795c 100644 --- a/frontend/src/components/screensTrainer/pagesResourceCreation/PageMaterial.vue +++ b/frontend/src/components/screensTrainer/pagesResourceCreation/PageMaterial.vue @@ -1,6 +1,10 @@ \ No newline at end of file diff --git a/frontend/src/components/widgets/AddPatientPopup.vue b/frontend/src/components/widgets/AddPatientPopup.vue index dcc7d45e..2b265e82 100644 --- a/frontend/src/components/widgets/AddPatientPopup.vue +++ b/frontend/src/components/widgets/AddPatientPopup.vue @@ -82,20 +82,6 @@ \ No newline at end of file diff --git a/frontend/src/components/widgets/ButtonMainAction.vue b/frontend/src/components/widgets/ButtonMainAction.vue index 3915d95c..c8201626 100644 --- a/frontend/src/components/widgets/ButtonMainAction.vue +++ b/frontend/src/components/widgets/ButtonMainAction.vue @@ -1,6 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/src/components/widgets/EditPatientPopup.vue b/frontend/src/components/widgets/EditPatientPopup.vue index 15a4db03..ff966d00 100644 --- a/frontend/src/components/widgets/EditPatientPopup.vue +++ b/frontend/src/components/widgets/EditPatientPopup.vue @@ -83,20 +83,6 @@ \ No newline at end of file diff --git a/frontend/src/components/widgets/ToggleSwitchForListItems.vue b/frontend/src/components/widgets/ToggleSwitchForListItems.vue index bd3f2b14..ffe63214 100644 --- a/frontend/src/components/widgets/ToggleSwitchForListItems.vue +++ b/frontend/src/components/widgets/ToggleSwitchForListItems.vue @@ -61,6 +61,6 @@ input:checked + .slider { } input:checked + .slider:before { - transform: translateX(22px); + transform: translateX(23px); } \ No newline at end of file diff --git a/frontend/src/components/widgets/TriagePopup.vue b/frontend/src/components/widgets/TriagePopup.vue index 6a5b44a0..692ec024 100644 --- a/frontend/src/components/widgets/TriagePopup.vue +++ b/frontend/src/components/widgets/TriagePopup.vue @@ -45,20 +45,6 @@