feat: validation for follow up events
Browse files- .gitignore +1 -0
- app/assets/json_tmp/tmp_wounded_dead.json +0 -1
- app/classes.py +3 -3
- app/follow_up/class_follow_up.py +6 -6
- app/follow_up/followup_events.py +1 -1
- app/main_multianimal.py +12 -12
- app/validation_submission/validation.py +81 -26
.gitignore
CHANGED
@@ -8,6 +8,7 @@ __pycache__/
|
|
8 |
#Data
|
9 |
test/data/**
|
10 |
data/**
|
|
|
11 |
|
12 |
# C extensions
|
13 |
*.so
|
|
|
8 |
#Data
|
9 |
test/data/**
|
10 |
data/**
|
11 |
+
app/assets/tmp_json/**
|
12 |
|
13 |
# C extensions
|
14 |
*.so
|
app/assets/json_tmp/tmp_wounded_dead.json
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
{"circumstance_radio": true, "circumstance": "collision with a means of transport", "cirumstance_type": {"type": "boat", "option_dropdown_label": "NA", "open_field_label": "NA", "extra_label": "NA"}, "behavior_radio": "Yes", "behaviors_observed": ["Crash, Falling From The Sky", "Diarrhea"], "physical_radio": "Yes", "physical_type_head": "head", "physical_anomaly_type_head": ["Injury", "Parasites"], "physical_type_legs": "legs", "physical_anomaly_type_legs": ["Missing Limb"], "fe_fe_collection": "no", "fe_fe_recipient": "local museum", "fe_fe_name_recipient": "reserve", "fe_fe_radio": "unknown", "fe_fe_answer": "police call", "fe_fe_collection_ref": "ref"}
|
|
|
|
app/classes.py
CHANGED
@@ -3,19 +3,19 @@ from typing import Literal, List, Union, Optional
|
|
3 |
|
4 |
from behavior.class_behavior import Behaviors
|
5 |
from circumstances.class_circumstance import Circumstances
|
6 |
-
from physical.class_physical import
|
7 |
from follow_up.class_follow_up import FollowUpEvents
|
8 |
from geolocalisation.class_geolocalisation import Geolocalisation
|
9 |
|
10 |
class Wounded(BaseModel):
|
11 |
circumstances: Circumstances
|
12 |
behaviors: List[Behaviors]
|
13 |
-
physical_anomalies: List[
|
14 |
follow_up_events: List[FollowUpEvents]
|
15 |
|
16 |
class Dead(BaseModel):
|
17 |
circumstances: List[Circumstances]
|
18 |
-
physical_anomalies: List[
|
19 |
follow_up_events: List[FollowUpEvents]
|
20 |
|
21 |
class Image(BaseModel):
|
|
|
3 |
|
4 |
from behavior.class_behavior import Behaviors
|
5 |
from circumstances.class_circumstance import Circumstances
|
6 |
+
from physical.class_physical import PhysicalAnomalies
|
7 |
from follow_up.class_follow_up import FollowUpEvents
|
8 |
from geolocalisation.class_geolocalisation import Geolocalisation
|
9 |
|
10 |
class Wounded(BaseModel):
|
11 |
circumstances: Circumstances
|
12 |
behaviors: List[Behaviors]
|
13 |
+
physical_anomalies: List[PhysicalAnomalies]
|
14 |
follow_up_events: List[FollowUpEvents]
|
15 |
|
16 |
class Dead(BaseModel):
|
17 |
circumstances: List[Circumstances]
|
18 |
+
physical_anomalies: List[PhysicalAnomalies]
|
19 |
follow_up_events: List[FollowUpEvents]
|
20 |
|
21 |
class Image(BaseModel):
|
app/follow_up/class_follow_up.py
CHANGED
@@ -6,22 +6,22 @@ from typing import Literal, Union, Optional, List
|
|
6 |
# Animal collected event
|
7 |
class AnimalCollectedEvent(BaseModel):
|
8 |
type: Literal['animal collected']
|
9 |
-
|
10 |
|
11 |
# Recipient event
|
12 |
class RecipientEvent(BaseModel):
|
13 |
type: Literal['recipient']
|
14 |
-
|
15 |
-
|
16 |
# Radiography event
|
17 |
class RadiographyEvent(BaseModel):
|
18 |
type: Literal['radiography']
|
19 |
-
|
20 |
|
21 |
# Given answer event
|
22 |
class GivenAnswerEvent(BaseModel):
|
23 |
type: Literal['given answer']
|
24 |
-
|
25 |
'Nothing',
|
26 |
'Complaint against X',
|
27 |
'Complaint',
|
@@ -33,7 +33,7 @@ class GivenAnswerEvent(BaseModel):
|
|
33 |
|
34 |
# Name of recipient/museum (open text field)
|
35 |
class NameOfRecipientEvent(BaseModel):
|
36 |
-
type: Literal['
|
37 |
name: str # Open text field for entering the name
|
38 |
|
39 |
# Collection reference (open text field)
|
|
|
6 |
# Animal collected event
|
7 |
class AnimalCollectedEvent(BaseModel):
|
8 |
type: Literal['animal collected']
|
9 |
+
collected: Literal['Yes', 'No']
|
10 |
|
11 |
# Recipient event
|
12 |
class RecipientEvent(BaseModel):
|
13 |
type: Literal['recipient']
|
14 |
+
recipient: Literal['Veterinary', 'Care center', 'Local Museum', 'National Museum', 'Other']
|
15 |
+
|
16 |
# Radiography event
|
17 |
class RadiographyEvent(BaseModel):
|
18 |
type: Literal['radiography']
|
19 |
+
radiography: Literal['Yes', 'No', 'Unknown']
|
20 |
|
21 |
# Given answer event
|
22 |
class GivenAnswerEvent(BaseModel):
|
23 |
type: Literal['given answer']
|
24 |
+
answer: Literal[
|
25 |
'Nothing',
|
26 |
'Complaint against X',
|
27 |
'Complaint',
|
|
|
33 |
|
34 |
# Name of recipient/museum (open text field)
|
35 |
class NameOfRecipientEvent(BaseModel):
|
36 |
+
type: Literal['recipient name']
|
37 |
name: str # Open text field for entering the name
|
38 |
|
39 |
# Collection reference (open text field)
|
app/follow_up/followup_events.py
CHANGED
@@ -40,7 +40,7 @@ def create_fe_answer_dropdown(followup_config, visible, elem_id):
|
|
40 |
return fe_answer_dropdown
|
41 |
|
42 |
def save_fe(value, key):
|
43 |
-
add_data_tmp("wounded_dead", key.lower(), value.lower())
|
44 |
|
45 |
|
46 |
|
|
|
40 |
return fe_answer_dropdown
|
41 |
|
42 |
def save_fe(value, key):
|
43 |
+
add_data_tmp("wounded_dead", "followup " + key.lower(), value.lower())
|
44 |
|
45 |
|
46 |
|
app/main_multianimal.py
CHANGED
@@ -210,21 +210,21 @@ with gr.Blocks(theme=theme, css=css) as demo:
|
|
210 |
|
211 |
# ---------------------------------------------------------
|
212 |
# Follow Up Events Wounded
|
213 |
-
fe_collection_dropdown_wounded.select(save_fe, inputs=[fe_collection_dropdown_wounded, gr.Textbox("
|
214 |
-
fe_recepient_dropdown_wounded.select(save_fe, inputs=[fe_recepient_dropdown_wounded, gr.Textbox("
|
215 |
-
fe_radio_dropdown_wounded.select(save_fe, inputs=[fe_radio_dropdown_wounded, gr.Textbox("
|
216 |
-
fe_answer_dropdown_wounded.select(save_fe, inputs=[fe_answer_dropdown_wounded, gr.Textbox("
|
217 |
-
fe_name_recipient_wounded.input(save_fe, inputs=[fe_name_recipient_wounded, gr.Textbox("
|
218 |
-
fe_collection_ref_wounded.input(save_fe, inputs=[fe_collection_ref_wounded, gr.Textbox("
|
219 |
|
220 |
# ---------------------------------------------------------
|
221 |
# Follow Up Events Dead
|
222 |
-
fe_collection_dropdown_dead.select(save_fe, inputs=[fe_collection_dropdown_dead, gr.Textbox("
|
223 |
-
fe_recepient_dropdown_dead.select(save_fe, inputs=[fe_recepient_dropdown_dead, gr.Textbox("
|
224 |
-
fe_radio_dropdown_dead.select(save_fe, inputs=[fe_radio_dropdown_dead, gr.Textbox("
|
225 |
-
fe_answer_dropdown_dead.select(save_fe, inputs=[fe_answer_dropdown_dead, gr.Textbox("
|
226 |
-
fe_name_recipient_dead.input(save_fe, inputs=[fe_name_recipient_dead, gr.Textbox("
|
227 |
-
fe_collection_ref_dead.input(save_fe, inputs=[fe_collection_ref_dead, gr.Textbox("
|
228 |
|
229 |
# ---------------------------------------------------------
|
230 |
# Add One Individual's Data to the Dataframe
|
|
|
210 |
|
211 |
# ---------------------------------------------------------
|
212 |
# Follow Up Events Wounded
|
213 |
+
fe_collection_dropdown_wounded.select(save_fe, inputs=[fe_collection_dropdown_wounded, gr.Textbox("animal collected", visible=False)])
|
214 |
+
fe_recepient_dropdown_wounded.select(save_fe, inputs=[fe_recepient_dropdown_wounded, gr.Textbox("recipient", visible=False)])
|
215 |
+
fe_radio_dropdown_wounded.select(save_fe, inputs=[fe_radio_dropdown_wounded, gr.Textbox("radiography", visible=False)])
|
216 |
+
fe_answer_dropdown_wounded.select(save_fe, inputs=[fe_answer_dropdown_wounded, gr.Textbox("given answer", visible=False)])
|
217 |
+
fe_name_recipient_wounded.input(save_fe, inputs=[fe_name_recipient_wounded, gr.Textbox("recipient name", visible=False)])
|
218 |
+
fe_collection_ref_wounded.input(save_fe, inputs=[fe_collection_ref_wounded, gr.Textbox("collection reference", visible=False)])
|
219 |
|
220 |
# ---------------------------------------------------------
|
221 |
# Follow Up Events Dead
|
222 |
+
fe_collection_dropdown_dead.select(save_fe, inputs=[fe_collection_dropdown_dead, gr.Textbox("animal collected", visible=False)])
|
223 |
+
fe_recepient_dropdown_dead.select(save_fe, inputs=[fe_recepient_dropdown_dead, gr.Textbox("recipient", visible=False)])
|
224 |
+
fe_radio_dropdown_dead.select(save_fe, inputs=[fe_radio_dropdown_dead, gr.Textbox("radiography", visible=False)])
|
225 |
+
fe_answer_dropdown_dead.select(save_fe, inputs=[fe_answer_dropdown_dead, gr.Textbox("given answer", visible=False)])
|
226 |
+
fe_name_recipient_dead.input(save_fe, inputs=[fe_name_recipient_dead, gr.Textbox("recipient name", visible=False)])
|
227 |
+
fe_collection_ref_dead.input(save_fe, inputs=[fe_collection_ref_dead, gr.Textbox("collection reference", visible=False)])
|
228 |
|
229 |
# ---------------------------------------------------------
|
230 |
# Add One Individual's Data to the Dataframe
|
app/validation_submission/validation.py
CHANGED
@@ -3,6 +3,7 @@ from classes import Report
|
|
3 |
from circumstances.class_circumstance import Circumstances
|
4 |
from behavior.class_behavior import Behaviors
|
5 |
from physical.class_physical import PhysicalAnomalies
|
|
|
6 |
|
7 |
def get_fields(data_dict, keyword):
|
8 |
extract = {}
|
@@ -20,14 +21,7 @@ def validate_individual():
|
|
20 |
validate_individual()
|
21 |
pass
|
22 |
|
23 |
-
|
24 |
-
def validate_circumstance(data):
|
25 |
-
circumstance_raw = get_fields(data, "circumstance")
|
26 |
-
circumstance_formatted = process_circumstance(circumstance_raw)
|
27 |
-
if not Circumstances(**circumstance_formatted).validate():
|
28 |
-
print("Validation failed for the circumstance.")
|
29 |
-
else:
|
30 |
-
return Circumstances(**circumstance_formatted)
|
31 |
|
32 |
def process_circumstance(data):
|
33 |
fields_to_check = ["option_dropdown", "open_field", "extra"]
|
@@ -48,14 +42,6 @@ def process_circumstance(data):
|
|
48 |
# "circumstance_option_dropdown": "Traffic/Trade"}
|
49 |
return data
|
50 |
|
51 |
-
def validate_behavior(data):
|
52 |
-
behaviors_raw = get_fields(data, "behaviours")
|
53 |
-
behaviors_formatted = process_behaviors(behaviors_raw)
|
54 |
-
if not Behaviors(**behaviors_formatted).validate():
|
55 |
-
print("Validation failed for the behaviours.")
|
56 |
-
else:
|
57 |
-
return Behaviors(**behaviors_formatted)
|
58 |
-
|
59 |
def process_behaviors(data):
|
60 |
# INPUT :
|
61 |
#"behaviors_radio": true,
|
@@ -75,18 +61,11 @@ def process_behaviors(data):
|
|
75 |
data["behaviors_type"] = behaviors
|
76 |
return data
|
77 |
|
78 |
-
def validate_physical(data):
|
79 |
-
physical_raw = get_fields(data, "physical")
|
80 |
-
physical_formatted = process_physical(physical_raw)
|
81 |
-
if not PhysicalAnomalies(**physical_formatted).validate():
|
82 |
-
print("Validation failed for the physical anomalies.")
|
83 |
-
else:
|
84 |
-
return PhysicalAnomalies(**physical_formatted)
|
85 |
-
|
86 |
def process_physical(data):
|
87 |
# INPUT
|
88 |
# "physical_type_feathers": "feathers",
|
89 |
# "physical_anomaly_type_feathers": ["Blood", "Swelling"]}
|
|
|
90 |
# OUTPUT
|
91 |
# "physical_radio": "Yes",
|
92 |
# "physical_anomalies_type": [
|
@@ -98,11 +77,87 @@ def process_physical(data):
|
|
98 |
# "type": "body",
|
99 |
# "anomaly_type": "fluffed up"
|
100 |
# },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
|
104 |
def validate_follow_up(data):
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
def validate_individual():
|
108 |
individual = get_json_one_individual()
|
|
|
3 |
from circumstances.class_circumstance import Circumstances
|
4 |
from behavior.class_behavior import Behaviors
|
5 |
from physical.class_physical import PhysicalAnomalies
|
6 |
+
from follow_up.class_follow_up import FollowUpEvents
|
7 |
|
8 |
def get_fields(data_dict, keyword):
|
9 |
extract = {}
|
|
|
21 |
validate_individual()
|
22 |
pass
|
23 |
|
24 |
+
#### PROCESS FUNCTIONS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
def process_circumstance(data):
|
27 |
fields_to_check = ["option_dropdown", "open_field", "extra"]
|
|
|
42 |
# "circumstance_option_dropdown": "Traffic/Trade"}
|
43 |
return data
|
44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
def process_behaviors(data):
|
46 |
# INPUT :
|
47 |
#"behaviors_radio": true,
|
|
|
61 |
data["behaviors_type"] = behaviors
|
62 |
return data
|
63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
def process_physical(data):
|
65 |
# INPUT
|
66 |
# "physical_type_feathers": "feathers",
|
67 |
# "physical_anomaly_type_feathers": ["Blood", "Swelling"]}
|
68 |
+
|
69 |
# OUTPUT
|
70 |
# "physical_radio": "Yes",
|
71 |
# "physical_anomalies_type": [
|
|
|
77 |
# "type": "body",
|
78 |
# "anomaly_type": "fluffed up"
|
79 |
# },
|
80 |
+
body_parts= ["beak", "body", "legs", "feathers", "head"]
|
81 |
+
anomalies=[]
|
82 |
+
reformatted = {}
|
83 |
+
reformatted["physical_radio"] = data["physical_radio"]
|
84 |
+
for body_part in body_parts:
|
85 |
+
anomaly = {}
|
86 |
+
for key, val in data.items():
|
87 |
+
if "type_"+ body_part in key:
|
88 |
+
anomaly["type"] = val
|
89 |
+
elif "anomaly_type_"+ body_part in key:
|
90 |
+
anomaly["anomaly_type"] = val
|
91 |
+
anomalies.append(anomaly)
|
92 |
+
reformatted["physical_anomalies_type"] = anomalies
|
93 |
+
return reformatted
|
94 |
|
95 |
+
def process_followup(data):
|
96 |
+
# "follow_up_events": [
|
97 |
+
# {
|
98 |
+
# "type": "animal collected",
|
99 |
+
# "option": "Yes"
|
100 |
+
# },
|
101 |
+
# {
|
102 |
+
# "type": "recipient",
|
103 |
+
# "option": "Veterinary",
|
104 |
+
# "name_recipient": "Dr. Jane Smith"
|
105 |
+
# },
|
106 |
+
# {
|
107 |
+
# "type": "radiography",
|
108 |
+
# "option": "Unknown"
|
109 |
+
# },
|
110 |
+
# {
|
111 |
+
# "type": "given answer",
|
112 |
+
# "option": "Discussion with the speaker"
|
113 |
+
# },
|
114 |
+
# {
|
115 |
+
# "type": "collection reference",
|
116 |
+
# "reference": "Specimen ID: 12345, Collected on 2023-09-15"
|
117 |
+
# }
|
118 |
+
# ]
|
119 |
+
followup_events = []
|
120 |
+
for key, val in data.items():
|
121 |
+
followup_event={}
|
122 |
+
type = key.split("followup")[-1]
|
123 |
+
option = type.split(" ")[-1]
|
124 |
+
followup_event["type"] = type
|
125 |
+
followup_event[option] = val
|
126 |
+
followup_events.append(followup_event)
|
127 |
+
return followup_events
|
128 |
+
|
129 |
+
#### VALIDATION FUNCTIONS
|
130 |
+
def validate_circumstance(data):
|
131 |
+
circumstance_raw = get_fields(data, "circumstance")
|
132 |
+
circumstance_formatted = process_circumstance(circumstance_raw)
|
133 |
+
if not Circumstances(**circumstance_formatted).validate():
|
134 |
+
print("Validation failed for the circumstance.")
|
135 |
+
else:
|
136 |
+
return Circumstances(**circumstance_formatted)
|
137 |
+
|
138 |
+
def validate_behavior(data):
|
139 |
+
behaviors_raw = get_fields(data, "behaviours")
|
140 |
+
behaviors_formatted = process_behaviors(behaviors_raw)
|
141 |
+
if not Behaviors(**behaviors_formatted).validate():
|
142 |
+
print("Validation failed for the behaviours.")
|
143 |
+
else:
|
144 |
+
return Behaviors(**behaviors_formatted)
|
145 |
+
|
146 |
+
def validate_physical(data):
|
147 |
+
physical_raw = get_fields(data, "physical")
|
148 |
+
physical_formatted = process_physical(physical_raw)
|
149 |
+
if not PhysicalAnomalies(**physical_formatted).validate():
|
150 |
+
print("Validation failed for the physical anomalies.")
|
151 |
+
else:
|
152 |
+
return PhysicalAnomalies(**physical_formatted)
|
153 |
|
154 |
def validate_follow_up(data):
|
155 |
+
followup_raw = get_fields(data, "followup")
|
156 |
+
followup_formatted = process_followup(followup_raw)
|
157 |
+
if not FollowUpEvents(**followup_formatted).validate():
|
158 |
+
print("Validation failed for the follow-up events.")
|
159 |
+
else:
|
160 |
+
return FollowUpEvents(**followup_formatted)
|
161 |
|
162 |
def validate_individual():
|
163 |
individual = get_json_one_individual()
|