Skip to content

Commit

Permalink
wip added observer on log, transformer for log entries, MonPolyRunner…
Browse files Browse the repository at this point in the history
…. But Monpoly isn't available as a command yet
  • Loading branch information
claasga committed Nov 15, 2024
1 parent 48ee2fb commit 4e6cf37
Show file tree
Hide file tree
Showing 19 changed files with 678 additions and 49 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ backend/.vscode/settings.json
.idea/
.vscode/launch.json
backend/dps_training_k/restart_backend.sh
backend/dps_training_k/game/migrations/0008_remove_logentry_message_logentry_content_and_more.py
_build/.filesystem-clock
_build/.lock
_build/log
.gitignore
24 changes: 24 additions & 0 deletions backend/dps_training_k/deployment/django/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,34 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
libpq-dev gcc python3-dev musl-dev \
# Translations dependencies
gettext \
# OCaml and MonPoly dependencies
git opam \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*

# Initialize OPAM and switch to the required OCaml version
RUN opam init -y --disable-sandboxing && \
opam switch create 4.11.1 && \
eval $(opam env) && \
opam install dune -y

# Clone the MonPoly repository
RUN git clone https://bitbucket.org/monpoly/monpoly.git /home/opam/monpoly

# Set the working directory
WORKDIR /home/opam/monpoly

# Install dependencies
RUN eval $(opam env) && opam install --deps-only -y .

# Build and install MonPoly
RUN eval $(opam env) && dune build --release && dune install
ENV PATH="/home/opam/.opam/4.11.1/bin:${PATH}"
# Verify MonPoly installation and PATH
RUN eval $(opam env) &&which monpoly && echo $PATH

WORKDIR ${APP_HOME}
# Install requirements
RUN pip install -r requirements.txt

Expand Down
28 changes: 28 additions & 0 deletions backend/dps_training_k/deployment/monpoly/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Use an official OCaml base image
FROM ocaml/opam:debian-10-ocaml-4.11

# Install necessary packages
RUN sudo apt-get update && sudo apt-get install -y \
git \
opam

# Initialize OPAM and switch to the required OCaml version
RUN opam init -y --disable-sandboxing && \
opam switch create 4.11.1 && \
eval $(opam env)

# Clone the MonPoly repository
RUN git clone https://bitbucket.org/monpoly/monpoly.git

# Set the working directory
WORKDIR /home/opam/monpoly

# Install dependencies
RUN opam install --deps-only -y .

# Build and install MonPoly
RUN dune build --release && \
dune install

# Set the entrypoint to monpoly
ENTRYPOINT ["monpoly"]
157 changes: 111 additions & 46 deletions backend/dps_training_k/game/channel_notifications.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import asyncio

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
Expand Down Expand Up @@ -184,34 +185,39 @@ def create_trainer_log(cls, applied_action, changes, is_updated):
return

message = None
category = models.LogEntry.CATEGORIES.ACTION
type = None
content = {}
content["name"] = applied_action.name
send_personnel_and_material = False
if applied_action.state_name == models.ActionInstanceStateNames.IN_PROGRESS:
message = f'"{applied_action.name}" wurde gestartet'
type = models.LogEntry.TYPES.STARTED
send_personnel_and_material = True
elif applied_action.state_name == models.ActionInstanceStateNames.FINISHED:
message = f'"{applied_action.name}" wurde abgeschlossen'
type = models.LogEntry.TYPES.FINISHED
if applied_action.template.category == template.Action.Category.PRODUCTION:
named_produced_resources = {
material.name: amount
for material, amount in applied_action.template.produced_resources().items()
}
message += f" und hat {str(named_produced_resources)} produziert"
content["produced"] = str(named_produced_resources)
elif (
applied_action.state_name == models.ActionInstanceStateNames.CANCELED
and applied_action.states.filter(
name=models.ActionInstanceStateNames.IN_PROGRESS
).exists()
):
message = f'"{applied_action.name}" wurde abgebrochen'
type = models.LogEntry.TYPES.CANCELED
elif applied_action.state_name == models.ActionInstanceStateNames.IN_EFFECT:
message = f'"{applied_action.name}" beginnt zu wirken'
type = models.LogEntry.TYPES.IN_EFFECT
elif applied_action.state_name == models.ActionInstanceStateNames.EXPIRED:
message = f'"{applied_action.name}" wirkt nicht mehr'

type = models.LogEntry.TYPES.EXPIRED
if message and send_personnel_and_material:
log_entry = models.LogEntry.objects.create(
exercise=applied_action.exercise,
message=message,
category=category,
type=type,
content=content,
patient_instance=applied_action.patient_instance,
area=applied_action.destination_area,
is_dirty=True,
Expand All @@ -229,7 +235,9 @@ def create_trainer_log(cls, applied_action, changes, is_updated):
elif message:
log_entry = models.LogEntry.objects.create(
exercise=applied_action.exercise,
message=message,
category=category,
type=type,
content=content,
patient_instance=applied_action.patient_instance,
area=applied_action.destination_area,
)
Expand Down Expand Up @@ -276,15 +284,16 @@ def dispatch_event(cls, obj, changes, is_updated):
def create_trainer_log(cls, exercise, changes, is_updated):
if not is_updated:
return
message = ""
if (
changes
and "state" in changes
and exercise.state == models.Exercise.StateTypes.RUNNING
):
message = "Übung gestartet"
if message:
models.LogEntry.objects.create(exercise=exercise, message=message)
models.LogEntry.objects.create(
exercise=exercise,
category=models.LogEntry.CATEGORIES.EXERCISE,
type=models.LogEntry.TYPES.STARTED,
)

@classmethod
def get_exercise(cls, exercise):
Expand All @@ -306,7 +315,39 @@ def _notify_exercise_end_event(cls, exercise):
cls._notify_group(channel, event)


class LogEntryDispatcher(ChannelNotifier):
class Observable:
_exercise_subscribers = {}

@classmethod
def subscribe_to_exercise(cls, exercise, subscriber, send_past_logs=False):
if exercise in cls._exercise_subscribers:
cls._exercise_subscribers[exercise].add(subscriber)
else:
cls._exercise_subscribers[exercise] = {subscriber}
if send_past_logs:
past_logs = models.LogEntry.objects.filter(exercise=exercise)
for log_entry in past_logs:
subscriber.receive_log_entry(log_entry)

@classmethod
def unsubscribe_from_exercise(cls, exercise, subscriber):
if exercise in cls._exercise_subscribers:
cls._exercise_subscribers[exercise].remove(subscriber)

@classmethod
def _puplish_obj(cls, obj, exercise):
if exercise in cls._exercise_subscribers:
for subscriber in cls._exercise_subscribers[exercise]:
asyncio.run(subscriber.receive_log_entry(obj))


class LogEntryDispatcher(Observable, ChannelNotifier):

@classmethod
def save_and_notify(cls, obj, changes, save_origin, *args, **kwargs):
super().save_and_notify(obj, changes, save_origin, *args, **kwargs)
cls._puplish_obj(obj, obj.exercise)

@classmethod
def get_group_name(cls, exercise):
return f"{exercise.__class__.__name__}_{exercise.id}_log"
Expand Down Expand Up @@ -349,47 +390,58 @@ def create_trainer_log(cls, material, changes, is_updated):
changes_set = set(changes) if changes else set()
assignment_changes = {"patient_instance", "area", "lab"}

category = models.LogEntry.CATEGORIES.MATERIAL
type = None
content = {"name": material.name}
if not is_updated:
message = f"{material.name} ist erschienen"
type = models.LogEntry.TYPES.ARRIVED
if material.area:
message += f" in {material.area}"
content["location"] = str(material.area)
if material.lab:
message += f" in {material.lab}"
content["location"] = str(material.lab)
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(material),
message=message,
category=category,
type=type,
content=content,
is_dirty=True,
)
log_entry.materials.add(material)
log_entry.set_dirty(False)
return

if changes_set & assignment_changes:
message = f"{material.name} wurde zugewiesen"
type = models.LogEntry.TYPES.ASSIGNED
current_location = material.attached_instance()
content["location_type"] = current_location.frontend_model_name()
content["location_name"] = current_location.name
log_entry = None

if isinstance(current_location, models.PatientInstance):
message += f" zu {current_location.frontend_model_name()} {current_location.name}"
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(material),
message=message,
category=category,
type=type,
content=content,
patient_instance=current_location,
is_dirty=True,
)
if isinstance(current_location, models.Area):
message += f" zu {current_location.frontend_model_name()} {current_location.name}"
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(material),
message=message,
category=category,
type=type,
content=content,
area=current_location,
is_dirty=True,
)
if isinstance(current_location, models.Lab):
message += f" zu {current_location.frontend_model_name()} {current_location.name}"
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(material),
message=message,
category=category,
type=type,
content=content,
is_dirty=True,
)
if log_entry:
Expand Down Expand Up @@ -426,38 +478,44 @@ def dispatch_event(cls, patient_instance, changes, is_updated):
@classmethod
def create_trainer_log(cls, patient_instance, changes, is_updated):
changes_set = set(changes) if changes else set()
category = models.LogEntry.CATEGORIES.PATIENT
type = None
message = None
content = {"name": patient_instance.name, "code": str(patient_instance.code)}

if not is_updated:
message = f"Patient*in {patient_instance.name}({patient_instance.code}) wurde eingeliefert."
type = models.LogEntry.TYPES.ARRIVED
if (
patient_instance.static_information
and patient_instance.static_information.injury
):
message += f" Patient*in hat folgende Verletzungen: {patient_instance.static_information.injury}"
content["injuries"] = str(patient_instance.static_information.injury)
elif changes and "triage" in changes:
# get_triage_display gets the long version of a ChoiceField
message = f"Patient*in {patient_instance.name} wurde triagiert auf {patient_instance.get_triage_display()}"
content["level"] = str(patient_instance.get_triage_display())
type = models.LogEntry.TYPES.TRIAGED
elif changes and changes_set & cls.location_changes:
message = f"Patient*in {patient_instance.name} wurde verlegt"
type = models.LogEntry.TYPES.MOVED
current_location = patient_instance.attached_instance()
content["location_type"] = current_location.frontend_model_name()
content["location_name"] = str(current_location.name)

if isinstance(current_location, models.Area):
message += f" nach {current_location.frontend_model_name()} {current_location.name}"
if isinstance(current_location, models.Lab):
message += f" nach {current_location.frontend_model_name()} {current_location.name}"
if message:
if patient_instance.area:
models.LogEntry.objects.create(
exercise=cls.get_exercise(patient_instance),
message=message,
content=content,
category=category,
type=type,
patient_instance=patient_instance,
area=patient_instance.area,
)
else:
models.LogEntry.objects.create(
exercise=cls.get_exercise(patient_instance),
message=message,
category=category,
type=type,
content=content,
patient_instance=patient_instance,
)

Expand Down Expand Up @@ -507,48 +565,55 @@ def dispatch_event(cls, personnel, changes, is_updated):
@classmethod
def create_trainer_log(cls, personnel, changes, is_updated):
changes_set = set(changes) if changes else set()

content = {"name": personnel.name}
if not is_updated:
message = f"{personnel.name} ist eingetroffen"
if personnel.area:
message += f" in {personnel.area}"
content["location_type"] = personnel.area.frontend_model_name()
content["location_name"] = personnel.area.name
if personnel.lab:
message += f" in {personnel.lab}"
content["location_type"] = personnel.lab.frontend_model_name()
content["location_name"] = personnel.lab.name
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(personnel),
message=message,
category=models.LogEntry.CATEGORIES.PERSONNEL,
type=models.LogEntry.TYPES.ARRIVED,
content=content,
is_dirty=True,
)
log_entry.personnel.add(personnel)
log_entry.set_dirty(False)
return

if changes_set & cls.assignment_changes:
message = f"{personnel.name} wurde zugewiesen"
current_location = personnel.attached_instance()
content["location_type"] = current_location.frontend_model_name()
content["location_name"] = current_location.name
log_entry = None

if isinstance(current_location, models.PatientInstance):
message += f" zu {current_location.frontend_model_name()} {current_location.name}"
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(personnel),
message=message,
category=models.LogEntry.CATEGORIES.PERSONNEL,
type=models.LogEntry.TYPES.ASSIGNED,
content=content,
patient_instance=current_location,
is_dirty=True,
)
if isinstance(current_location, models.Area):
message += f" zu {current_location.frontend_model_name()} {current_location.name}"
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(personnel),
message=message,
category=models.LogEntry.CATEGORIES.PERSONNEL,
type=models.LogEntry.TYPES.UNASSIGNED,
content=content,
area=current_location,
is_dirty=True,
)
if isinstance(current_location, models.Lab):
message += f" zu {current_location.frontend_model_name()} {current_location.name}"
log_entry = models.LogEntry.objects.create(
exercise=cls.get_exercise(personnel),
message=message,
category=models.LogEntry.CATEGORIES.PERSONNEL,
type=models.LogEntry.TYPES.ASSIGNED,
content=content,
is_dirty=True,
)
if log_entry:
Expand Down
Loading

0 comments on commit 4e6cf37

Please sign in to comment.