Skip to content

Commit

Permalink
add smaller rules, add support for triage, remove excessive printing
Browse files Browse the repository at this point in the history
  • Loading branch information
claasga committed Feb 10, 2025
1 parent 7d9d51b commit ff2a454
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 89 deletions.
12 changes: 0 additions & 12 deletions backend/dps_training_k/game/channel_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ def _notify_action_event(cls, event_type, channel, applied_action=None):

@classmethod
def create_trainer_log(cls, applied_action, changes, is_updated):
print("Action Instance: creating trainer log")
if changes and "historic_patient_state" in changes:
return
if applied_action == models.ActionInstanceStateNames.PLANNED:
Expand All @@ -204,7 +203,6 @@ def create_trainer_log(cls, applied_action, changes, is_updated):
content = {}
content["name"] = applied_action.name
send_personnel_and_material = False
print(f"The state name is: {applied_action.state_name}")
if applied_action.state_name == models.ActionInstanceStateNames.IN_PROGRESS:
type = models.LogEntry.TYPES.STARTED
send_personnel_and_material = True
Expand Down Expand Up @@ -249,7 +247,6 @@ def create_trainer_log(cls, applied_action, changes, is_updated):
log_entry.materials.add(*material_list)
log_entry.is_dirty = False
log_entry.save()
print("change propagated")
else:
log_entry = models.LogEntry.objects.create(
exercise=applied_action.exercise,
Expand Down Expand Up @@ -364,21 +361,13 @@ def _publish_obj(cls, obj, exercise_frontend_id):
if not obj.is_valid():
return

print(f"Current prozess id: {PROCESS_ID}")
if PROCESS_ID == 1:
print("calling other process for publishing")
publish_to_process(
PARTNER_PROCESS_ID,
{"obj_id": obj.id, "exercise_frontend_id": exercise_frontend_id},
)
print("other process called")
return

print(
f"Publishing obj for frontend_id {exercise_frontend_id} and subscribers: {cls._exercise_subscribers}"
)
if exercise_frontend_id in cls._exercise_subscribers:
print("Exercise in subscribers")
for subscriber in cls._exercise_subscribers[exercise_frontend_id]:
subscriber.receive_log_entry(obj)

Expand All @@ -401,7 +390,6 @@ def get_exercise(cls, log_entry):
@classmethod
def dispatch_event(cls, log_entry, changes, is_updated):
if log_entry.is_valid():
# print(f" While dispatching: {log_entry.type}")
cls._notify_log_update_event(log_entry)

@classmethod
Expand Down
13 changes: 6 additions & 7 deletions backend/dps_training_k/game/consumers/trainer_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,24 +200,23 @@ def handle_create_exercise(self, exercise):
self.subscribe(LogEntryDispatcher.get_group_name(self.exercise))
self.subscribe(ViolationDispatcher.get_group_name(self.exercise.frontend_id))
content = {
"action": "selected_action_id",
"action": "i.V. Zugang",
"timeframe": 2,
"vital_parameters": {
"circulation": "Herzfreq: 83 /min|peripher kräftig tastbar|RR: 143/083"
},
"examination_results": {
"Ultraschall Abdomen": "Abdomen; Normalbefund; keine pathologischen Veränderungen; Thorax: keine Ergüsse sichtbar"
"Ultraschall_Abdomen": "Ultraschall:_Abdomen__Normalbefund__keine_pathologischen_Veraenderungen__Thorax:_keine_Erguesse_sichtbar"
},
}
self.handle_add_rule(
self.exercise, "symptom-combination", "ultraschall_rule", content
)
# content = {"operator": ">=", "personnel_count": 2}
# self.handle_add_rule(self.exercise, "personnel_check", "beq_two_rule", content)
content = {"operator": ">=", "personnel_count": 2}
self.handle_add_rule(self.exercise, "personnel_check", "beq_two_rule", content)

#
# content = {"operator": "<", "personnel_count": 2}
# self.handle_add_rule(self.exercise, "personnel_check", "less_two_rule", content)
content = {"operator": "<", "personnel_count": 2}
self.handle_add_rule(self.exercise, "personnel_check", "less_two_rule", content)

def handle_end_exercise(self, exercise):
exercise.update_state(Exercise.StateTypes.FINISHED)
Expand Down
1 change: 0 additions & 1 deletion backend/dps_training_k/game/models/log_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ def _verbosify_content(self, content):
self.TYPES.TRIAGED: "wurde triagiert",
self.TYPES.UPDATED: "wurde aktualisiert",
}
print(f"Type is: {self.type}")
if self.category == self.CATEGORIES.ACTION:
message += f"{(content['name'])} {type_to_submessage[self.type]}"
message += (
Expand Down
3 changes: 2 additions & 1 deletion backend/dps_training_k/game/templmon/kdps.sig
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
changed_state(string, string, string, string)
unassigned_personnel(string)
assigned_personnel(string, string)
action_finished(string, string)
triage(string, string)
action_canceled(string, string)
action_finished(string, string)
action_started(string, string)
examination_result(string, string, string)
patient_relocated(string, string, string)
Expand Down
10 changes: 8 additions & 2 deletions backend/dps_training_k/game/templmon/log_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class MonpolyLogEntry:
EXAMINATION_RESULT = "examination_result"
ACTION_STARTED = "action_started"
ACTION_CANCELED = "action_canceled"
TRIAGED = "triage"


class LogTransformer:
Expand All @@ -31,10 +32,11 @@ def determine_log_type(cls, log_entry: le.LogEntry):
elif log_entry.category == l_categories.PATIENT:
if log_entry.type == l_types.ARRIVED:
return MonpolyLogEntry.PATIENT_ARRIVED
if log_entry.type == l_types.UPDATED:
elif log_entry.type == l_types.UPDATED:
return MonpolyLogEntry.CHANGED_STATE
elif log_entry.type == l_types.TRIAGED:
return MonpolyLogEntry.TRIAGED
elif log_entry.category == l_categories.ACTION:
print(f"the content is: {log_entry.content}")
if (
log_entry.type == l_types.FINISHED
and "examination_result" in log_entry.content
Expand Down Expand Up @@ -96,6 +98,10 @@ def transform(cls, log_entry: le.LogEntry):
action = log_entry.content.get("name")
result = log_entry.content.get("examination_result")
log_str += ExaminationResult.log(patient_id, action, result)
elif log_type == MonpolyLogEntry.TRIAGED:
patient_id = log_entry.patient_instance.pk
level = log_entry.content.get("level")
log_str += Triaged.log(patient_id, level)
else:
log_str += f"unknown_log_type({log_entry.pk}, {log_entry.type}, {log_entry.category})"

Expand Down
4 changes: 1 addition & 3 deletions backend/dps_training_k/game/templmon/output_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ async def _add_violations(self, timestamp, timepoint, filtered_violations):
)

async def _remove_violations(self, timestamp, timepoint, filtered_violations):
print(f"entered remove violations with {filtered_violations}")
for violation in filtered_violations:
start_stamp, start_point = self.current_unfinished_violations.pop(violation)
await self._violation_listener.dispatch_durational_violation_finished(
Expand Down Expand Up @@ -202,8 +201,7 @@ def get_fullfilling_assignments(assignments_str: str):
if decoded_line[0] != "@":
continue

print("Received output:")
print(decoded_line)
print(f"Received output: {decoded_line}")
decoded_line = decoded_line[1:]
parts = decoded_line.split(self.matches_seperator)
if len(parts) != 3:
Expand Down
168 changes: 126 additions & 42 deletions backend/dps_training_k/game/templmon/rule_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,13 @@ def generate(
SINCE[0,*]
{changed_state_1.mfotl()})
AND
4.0 < {selected_parameter_1}
AND
{selected_parameter_1} < 8.0
{selected_parameter_1} = {sm.LogType._monpolify_string(value_p1)}
AND
((NOT EXISTS {c_changed_state_2.bind([RP.PATIENT.name], False)}. {c_changed_state_2.mfotl()})
SINCE[0,*]
{changed_state_2.mfotl()})
AND
4.0 >= {selected_parameter_2}
AND
{selected_parameter_2} >= 0.0
{selected_parameter_2} = {sm.LogType._monpolify_string(value_p2)}
AND
{changed_state_2.get_variable(RP.DEAD.name)} = "FALSE"
AND
Expand All @@ -218,7 +214,7 @@ def generate(
SINCE[0,*]
{assigned_personnel_1.mfotl()})
AND
personnel_count >= 2)
personnel_count >= {personnel_count_p1})
"""
return cls.create(
rule,
Expand Down Expand Up @@ -480,30 +476,43 @@ def generate(
SingularViolationType(),
name,
),
# cls.create(
# to_late_rule,
# "to_late",
# DurationalViolationType(
# [patient_arrived.get_variable(RP.PATIENT.name)]
# ),
# f"name",
# ),
# cls.create(
# unfinished_rule,
# "unfinished",
# SingularViolationType(),
# name,
# ),
cls.create(
to_late_rule,
"to_late",
DurationalViolationType(
[patient_arrived.get_variable(RP.PATIENT.name)]
),
f"name",
),
cls.create(
unfinished_rule,
"unfinished",
SingularViolationType(),
name,
),
]


class TriageGoalRule(LogRule):
@classmethod
def generate_fullfillment(cls, patient_id, target_time, target_level):

pass
def generate_fullfillment(cls, name, patient_id, target_time, target_level):
rule_str = f"""(ONCE({target_time},*) patient_arrived("{patient_id}", location, start_triage, wound)) #zeitlimit vom nutzer
AND
(NOT PREV(0,*) ONCE({target_time},*) patient_arrived("{patient_id}", location, start_triage, wound)) #siehe oben
AND
(NOT EXISTS level. triage("{patient_id}", level)
SINCE(0,*)
(EXISTS l, w. patient_arrived("{patient_id}", l, "{target_level}", w))
OR
triage("{patient_id}", "{target_level}"))"""
return [
cls.create(
rule_str, "triage_personal_fullfillment", SingularViolationType(), name
)
]

def generate_violation(cls, patient_id, target_time, target_level):
@classmethod
def generate_violation(cls, name, patient_id, target_time, target_level):
rule_str = f"""(ONCE({target_time},*) patient_arrived("{patient_id}", location, start_triage, wound)) #zeitlimit vom nutzer
AND
(NOT PREV(0,*) ONCE({target_time},*) patient_arrived("{patient_id}", location, start_triage, wound)) #siehe oben
Expand All @@ -518,20 +527,102 @@ def generate_violation(cls, patient_id, target_time, target_level):
return [
cls.create(
rule_str,
"triage_personal_violation",
SingularViolationType(),
name,
)
]


class AlivenessChecker(LogRule):
@classmethod
def generate_fullfillment(cls, name):
rule_str = f""" NOT EXISTS circulation, breathing. changed_state(patient_id, circulation, breathing, "TRUE")
SINCE[0,*]
EXISTS start_location, start_triage, injury. patient_arrived(patient_id, start_location, start_triage, injury)
"""
return [
cls.create(rule_str, "alive", DurationalViolationType(["patient_id"]), name)
]

@classmethod
def generate_violation(cls, name):
rule_str = 'EXISTS circulation, breathing. changed_state(patient_id, circulation, breathing, "TRUE")\n'
return [cls.create(rule_str, "dead", SingularViolationType(), name)]


class InteractedChecker(LogRule):
@classmethod
def generate_fullfillment(cls, name):
rule_str = """ (EXISTS level. triage(patient_id, level))
OR
(EXISTS personnel_id. assigned_personnel(personnel_id, patient_id))
OR
(EXISTS origin, target. patient_relocated(patient_id, origin, target))
OR
(EXISTS action_id. action_started(patient_id, action_id))
"""
return [cls.create(rule_str, "interacted", SingularViolationType(), name)]

@classmethod
def generate_violation(cls, name):
rule_str = """#Enable -no_rw
NOT
((EXISTS level. triage(patient_id, level))
OR
(EXISTS personnel_id. assigned_personnel(personnel_id, patient_id))
OR
(EXISTS origin, target. patient_relocated(patient_id, origin, target))
OR
(EXISTS action_id. action_started(patient_id, action_id)))
SINCE[0,*)
EXISTS start_location, start_triage, injury. patient_arrived(patient_id, start_location, start_triage, injury)
"""
return [
cls.create(
rule_str, "uninteracted", DurationalViolationType(["patient_id"]), name
)
]


class TriagedChecker(LogRule):
@classmethod
def generate_fullfillment(cls, name):
rule_str = """ NOT (EXISTS level. triage(patient_id, level))
SINCE[0,*)
((EXISTS start_location, start_triage, injury. patient_arrived(patient_id, start_location, start_triage, injury)
AND
NOT start_triage = "Gray")
OR
(EXISTS level. triage(patient_id, level)
AND
NOT level = "Gray"))
"""
return [cls.create(rule_str, "triaged", SingularViolationType(), name)]

@classmethod
def generate_violation(cls, name):
rule_str = """ NOT (EXISTS level. triage(patient_id, level))
SINCE[0,*)
((EXISTS start_location, start_triage, injury. patient_arrived(patient_id, start_location, start_triage, injury)
AND
start_triage = "Gray")
OR
triage(patient_id, "Gray"))
"""
return [
cls.create(
rule_str, "untriaged", DurationalViolationType(["patient_id"]), name
)
]


# from ..channel_notifications import LogEntryDispatcher
#
# test_rule_str = """ (personnel_count <- CNT personnel_id;patient_id
# (NOT unassigned_personnel(personnel_id))
# SINCE[0,*]
# assigned_personnel(personnel_id, patient_id))
# AND
# (personnel_count >= 4)"""
# test_rule = LogRule.create(test_rule_str, "test_rule")
# log_rule_runner = LogRuleRunner(None, LogEntryDispatcher, test_rule)
class BerlinAlgorithm(LogRule):
@classmethod
def generate(cls, name):
pass


if __name__ == "__main__":
RP = sm.RuleProperty
# PersonnelCheckRule.generate(operator="<")
Expand All @@ -544,10 +635,3 @@ def generate_violation(cls, patient_id, target_time, target_level):
{RP.CIRCULATION.name: 10.0},
{"selected_examination_id": "bad"},
)
# String vs int bei 0 in monpoly
# To Late:
# 1. ~~patienten_id wird gebunden in unterem part~~
# 2. ~~string gebundene variablen werden nochmals gebunden~~
# 3. ~~klammerung ums or fehlt~~
# 4. ~~c_version wird nicht verwendet~~
# 5. Examination results werden nicht getestet
3 changes: 0 additions & 3 deletions backend/dps_training_k/game/templmon/rule_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ def _extract_environment_variables(self, output: str):
end = output.find(")", start)
free_variables = output[start:end]
free_variables = free_variables.split(",")
print("Extracted environment variables:")
for free_variable in free_variables:
print(free_variable)
return free_variables

def _environment_infos(self):
Expand Down
9 changes: 9 additions & 0 deletions backend/dps_training_k/game/templmon/signature_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,15 @@ def log(cls, patient, action):
return cls._log([patient, action])


class Triaged(LogType):
_BASE_VARIABLES = [RuleProperty.PATIENT, RuleProperty.TRIAGE]
MONPOLY_NAME = "triage"

@classmethod
def log(cls, patient, triage):
return cls._log([patient, triage])


def generate_monpoly_signature(file_path):
""":file_path: exclude the type of file, e.g. /path/to/file"""

Expand Down
Loading

0 comments on commit ff2a454

Please sign in to comment.