Skip to content

Commit

Permalink
#30 backend: wip of phase change
Browse files Browse the repository at this point in the history
  • Loading branch information
claasga committed Mar 27, 2024
1 parent 27253f9 commit 408044b
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 9 deletions.
34 changes: 34 additions & 0 deletions backend/dps_training_k/game/channel_notifications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.db.models.signals import post_save
from django.dispatch import receiver
from helpers.signals import post_update
from game.models import Patient
from template.serializer.state_serialize import StateSerializer


class ChannelEventTypes:
ACTION_CONFIRMATION = "action-confirmation"
ACTION_DECLINATION = "action-declination"
ACTION_RESULT = "action-result"
PHASE_UPDATE = "state"


def _notify_group(group_channel_name, event):
async_to_sync(get_channel_layer().group_send)(group_channel_name, event)


def _notify_instance(instance_channel_name, event):
async_to_sync(get_channel_layer().send)(instance_channel_name, event)


@receiver(post_update, patient=Patient)
def notify_patient_phase_change(patient):
state = patient.stateID.get()
channel = patient.channel_name
serializer = StateSerializer(state)
event = {
"type": ChannelEventTypes.PHASE_UPDATE,
**serializer.data,
}
_notify_group(channel, event)
19 changes: 13 additions & 6 deletions backend/dps_training_k/game/models/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@
from django.conf import settings
from helpers.eventable import Eventable
from helpers.transitionable import Transitionable
from helpers.signals import UpdateSignals
from .scheduled_event import ScheduledEvent
from template.models.patient_state import PatientState


class Patient(Eventable, models.Model):
class Patient(Eventable, Transitionable, UpdateSignals, models.Model):
name = models.CharField(
max_length=100, default="Max Mustermann"
) # technically patientData but kept here for simplicity for now
# patientCode = models.ForeignKey() # currently called "SensenID"
exercise = models.ForeignKey("Exercise", on_delete=models.CASCADE)
stateID = models.OneToOneField(
# might want to add a StateInstance model and refernce that later on
state = models.OneToOneField(
"template.PatientState",
on_delete=models.SET_NULL,
null=True,
default=settings.DEFAULT_STATE_ID,
# default=PatientState.objects.get(pk=settings.DEFAULT_STATE_ID),
)
# measureID = models.ManyToManyField()
patientId = models.IntegerField(
Expand Down Expand Up @@ -48,6 +50,11 @@ def schedule_state_transition(self):
)

def transition_state(self):
next_state = self.determine_next_state()
self.state = next_state
return True
if not self.execute_state_change():
return
self.schedule_state_transition()

def is_dead(self):
if self.stateID.is_dead:
return True
return False
2 changes: 1 addition & 1 deletion backend/dps_training_k/game/models/scheduled_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def create_event(cls, exercise, t_sim_delta, method_name, patient=None, area=Non
method_name=method_name,
)
scheduled_event.save()
owner = Owner.create_owner(scheduled_event, patient=patient, area=area)
Owner.create_owner(scheduled_event, patient=patient, area=area)

@classmethod
def calculate_finish_time(cls, t_sim_delta, exercise):
Expand Down
1 change: 1 addition & 0 deletions backend/dps_training_k/game/tests/test_channels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ToDo: implement
1 change: 1 addition & 0 deletions backend/dps_training_k/game/tests/test_signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ToDo: Test update signal working
1 change: 1 addition & 0 deletions backend/dps_training_k/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from .exercise_serializer import *
from .invitation_logic import *
from .one_field_not_null import *
from .transitionable import *
3 changes: 3 additions & 0 deletions backend/dps_training_k/helpers/eventable.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def remove_events(self):


class Eventable(AbstractEventable):
"""
This mixin provides the functionality of removing and scheduling all current events
"""

def remove_events(self):
for event in self.owned_events.all():
Expand Down
28 changes: 28 additions & 0 deletions backend/dps_training_k/helpers/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ToDo: Adapt online code, import in Channels
from django.dispatch import Signal
from django.db import models

post_update = Signal()


class SignalingQuerySet(models.query.QuerySet):
def update(self, kwargs):
super().update(kwargs)
post_update.send(sender=self.model)


class UpdateSignalManager(models.Manager):
def getqueryset(self):
return SignalingQuerySet(self.model, using=self._db)


class UpdateSignals(models.Model):
"""
When using this class signals will be send when calling the Foo.objects.update() method.
To decrease coupling we encourage to always use the update method when changing fields
"""

objects = UpdateSignalManager()

class Meta:
abstract = True
27 changes: 27 additions & 0 deletions backend/dps_training_k/helpers/transitionable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Transitionable:
"""
This mixin provides table-based state transition logic.
This class assumes that we are using the PatientState directly and not an InstanceClass
"""

def execute_state_change(self):
if self.is_dead():
return False
# ToDo: Add actual logic, remove stub
next_state_id = self.determine_next_state(self.state.id)
if not next_state_id:
return False
self.state = self.states.get(pk=next_state_id)
return True

def stub_determine_next_state(self, current_state):
while not self.states.get(pk=current_state + 1):
if current_state % 10:
return None
current_state += 1
return current_state + 1


# ToDo: Implement Stub for now
# ToDo: Imoprt old componenent from old dps
# ToDo: Update to amount based logic
5 changes: 3 additions & 2 deletions backend/dps_training_k/template/models/patient_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ class PatientState(models.Model):
stateID = models.IntegerField(
help_text="state number as it is used in original data set"
)
patientID = models.ForeignKey(
"game.Patient", on_delete=models.CASCADE, related_name="state"
patient = models.ForeignKey(
"game.Patient", on_delete=models.CASCADE, related_name="states"
)
data = models.JSONField(help_text="data for patient in current phase")
phase = models.IntegerField(help_text="current phase, e.g. 3")
is_dead = models.BooleanField(default=False)

class Meta:
unique_together = (
Expand Down

0 comments on commit 408044b

Please sign in to comment.