caviri
commited on
Commit
·
f1a6252
1
Parent(s):
869adb6
bug: Expanded Pydantic class to make it compatible with simple and advance mode
Browse files- README.md +3 -6
- app/behavior/behavior_checkbox.py +20 -12
- app/behavior/class_behavior_simple.py +20 -12
- app/circumstances/circumstances_dropdowns.py +92 -65
- app/classes.py +16 -9
- app/physical/physical_checkbox.py +131 -55
- app/validation_submission/submission.py +8 -5
- app/validation_submission/validation.py +119 -75
README.md
CHANGED
@@ -13,7 +13,7 @@ short_description: Digiwild
|
|
13 |
## Docker
|
14 |
|
15 |
``` bash
|
16 |
-
docker build -t ordes/digiwild .
|
17 |
```
|
18 |
|
19 |
|
@@ -29,18 +29,17 @@ python3 main.py
|
|
29 |
### How to develop on docker
|
30 |
|
31 |
``` bash
|
32 |
-
docker run -it -p 7860:7860 -v $(pwd):/home/user/digiwild/ ordes/digiwild
|
33 |
```
|
34 |
|
35 |
## TODO
|
36 |
|
37 |
- [x] Change `wounded` to `wounded / sick`
|
38 |
- [x] Info formatting
|
39 |
-
- [
|
40 |
- [ ] Connection to a database? Maybe an open MongoDB
|
41 |
- [x] GPS Compatibility
|
42 |
- [x] New fields suggested: Number individuals, Species, Comments
|
43 |
-
- [ ] Save new fields values into the JSON. Perform validation too.
|
44 |
- [ ] Add info and placeholder information to the different components.
|
45 |
|
46 |
## Needs
|
@@ -50,5 +49,3 @@ docker run -it -p 7860:7860 -v $(pwd):/home/user/digiwild/ ordes/digiwild
|
|
50 |
- GPS location
|
51 |
- Comments
|
52 |
- Symptomps selection (Dropdown)
|
53 |
-
|
54 |
-
## PR Updates
|
|
|
13 |
## Docker
|
14 |
|
15 |
``` bash
|
16 |
+
docker build -t ordes/digiwild .
|
17 |
```
|
18 |
|
19 |
|
|
|
29 |
### How to develop on docker
|
30 |
|
31 |
``` bash
|
32 |
+
docker run -it -p 7860:7860 -v $(pwd):/home/user/digiwild/ --entrypoint bash ordes/digiwild
|
33 |
```
|
34 |
|
35 |
## TODO
|
36 |
|
37 |
- [x] Change `wounded` to `wounded / sick`
|
38 |
- [x] Info formatting
|
39 |
+
- [x] Use in memory object instead of files to avoid writting / reading problems.
|
40 |
- [ ] Connection to a database? Maybe an open MongoDB
|
41 |
- [x] GPS Compatibility
|
42 |
- [x] New fields suggested: Number individuals, Species, Comments
|
|
|
43 |
- [ ] Add info and placeholder information to the different components.
|
44 |
|
45 |
## Needs
|
|
|
49 |
- GPS location
|
50 |
- Comments
|
51 |
- Symptomps selection (Dropdown)
|
|
|
|
app/behavior/behavior_checkbox.py
CHANGED
@@ -4,33 +4,41 @@ from utils.utils_checkbox import create_checkbox
|
|
4 |
from utils.utils_visible import set_visible
|
5 |
from validation_submission.utils_individual import add_data_to_individual
|
6 |
|
7 |
-
|
|
|
8 |
behavior_checkbox = [behavior.lower() for behavior in behavior_checkbox]
|
9 |
individual = add_data_to_individual("behaviors_type", behavior_checkbox, individual)
|
10 |
return individual
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
17 |
dropdown_config = get_custom_config_dropdowns("config_checkbox_behavior.json")
|
18 |
options = list(dropdown_config.keys())
|
19 |
options = [option.title() for option in options]
|
20 |
-
descriptions =[]
|
21 |
-
for _,subdict in dropdown_config.items():
|
22 |
descriptions.append(subdict["Description"])
|
23 |
return options, descriptions
|
24 |
|
|
|
25 |
def create_behavior_checkbox(section: str, mode: str, visible):
|
26 |
options, descriptions = retrieve_behavior_options_description(mode)
|
27 |
label_checkbox = "Behavior changes observed"
|
28 |
-
checkbox, text = create_checkbox(
|
|
|
|
|
29 |
return checkbox, text
|
30 |
|
31 |
-
|
32 |
-
|
|
|
33 |
visible = set_visible(choice)
|
34 |
checkbox, text = create_behavior_checkbox(section, mode, visible)
|
35 |
individual = add_data_to_individual("behaviors_radio", choice, individual)
|
36 |
-
return checkbox, text, individual
|
|
|
4 |
from utils.utils_visible import set_visible
|
5 |
from validation_submission.utils_individual import add_data_to_individual
|
6 |
|
7 |
+
|
8 |
+
def on_select_behavior(behavior_checkbox, individual):
|
9 |
behavior_checkbox = [behavior.lower() for behavior in behavior_checkbox]
|
10 |
individual = add_data_to_individual("behaviors_type", behavior_checkbox, individual)
|
11 |
return individual
|
12 |
|
13 |
+
|
14 |
+
def retrieve_behavior_options_description(mode: str):
|
15 |
+
# print(f"Retrieve Behavior Option Description: {mode}")
|
16 |
+
if mode == "simple":
|
17 |
+
dropdown_config = get_custom_config_dropdowns(
|
18 |
+
"config_checkbox_behavior_simple.json"
|
19 |
+
)
|
20 |
+
elif mode == "advanced":
|
21 |
dropdown_config = get_custom_config_dropdowns("config_checkbox_behavior.json")
|
22 |
options = list(dropdown_config.keys())
|
23 |
options = [option.title() for option in options]
|
24 |
+
descriptions = []
|
25 |
+
for _, subdict in dropdown_config.items():
|
26 |
descriptions.append(subdict["Description"])
|
27 |
return options, descriptions
|
28 |
|
29 |
+
|
30 |
def create_behavior_checkbox(section: str, mode: str, visible):
|
31 |
options, descriptions = retrieve_behavior_options_description(mode)
|
32 |
label_checkbox = "Behavior changes observed"
|
33 |
+
checkbox, text = create_checkbox(
|
34 |
+
"", section, label_checkbox, visible, options, descriptions
|
35 |
+
)
|
36 |
return checkbox, text
|
37 |
|
38 |
+
|
39 |
+
def show_behavior(choice, section: str, mode: str, individual):
|
40 |
+
# print(f"Show Behavior: {mode}")
|
41 |
visible = set_visible(choice)
|
42 |
checkbox, text = create_behavior_checkbox(section, mode, visible)
|
43 |
individual = add_data_to_individual("behaviors_radio", choice, individual)
|
44 |
+
return checkbox, text, individual
|
app/behavior/class_behavior_simple.py
CHANGED
@@ -1,34 +1,42 @@
|
|
1 |
from pydantic import BaseModel, Field
|
2 |
from typing import Literal, List, Union, Optional
|
3 |
|
|
|
4 |
class BehaviorSimple(BaseModel):
|
5 |
type: str
|
6 |
description: Optional[str] = None # Making the description field optional
|
7 |
|
|
|
8 |
# --- Specific BehaviorSimple classes ---
|
9 |
class GeneralWeakness(BehaviorSimple):
|
10 |
-
type: Literal[
|
11 |
-
description: Optional[
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
class Vomiting(BehaviorSimple):
|
14 |
-
type: Literal[
|
15 |
description: Optional[Literal["Throwing up undigested food, regurgitating"]] = None
|
16 |
|
|
|
17 |
class AtypicalBehavior(BehaviorSimple):
|
18 |
-
type: Literal[
|
19 |
-
description: Optional[
|
|
|
|
|
|
|
20 |
|
21 |
class NoChanges(BehaviorSimple):
|
22 |
-
type: Literal[
|
23 |
description: Optional[Literal["Animal is acting normally"]] = None
|
24 |
|
|
|
25 |
# Union of all possible behaviors
|
26 |
-
BehaviorSimpleType = Union[
|
27 |
-
|
28 |
-
Vomiting,
|
29 |
-
AtypicalBehavior,
|
30 |
-
NoChanges
|
31 |
-
]
|
32 |
|
33 |
# Main class that logs multiple behaviors
|
34 |
class BehaviorsSimple(BaseModel):
|
|
|
1 |
from pydantic import BaseModel, Field
|
2 |
from typing import Literal, List, Union, Optional
|
3 |
|
4 |
+
|
5 |
class BehaviorSimple(BaseModel):
|
6 |
type: str
|
7 |
description: Optional[str] = None # Making the description field optional
|
8 |
|
9 |
+
|
10 |
# --- Specific BehaviorSimple classes ---
|
11 |
class GeneralWeakness(BehaviorSimple):
|
12 |
+
type: Literal["general weakness"]
|
13 |
+
description: Optional[
|
14 |
+
Literal[
|
15 |
+
"Abnormal breathing (dyspnoea), sudden crash, apathy, lethargy, unable to fly but responsive"
|
16 |
+
]
|
17 |
+
] = None
|
18 |
+
|
19 |
|
20 |
class Vomiting(BehaviorSimple):
|
21 |
+
type: Literal["vomiting"]
|
22 |
description: Optional[Literal["Throwing up undigested food, regurgitating"]] = None
|
23 |
|
24 |
+
|
25 |
class AtypicalBehavior(BehaviorSimple):
|
26 |
+
type: Literal["atypical behavior"]
|
27 |
+
description: Optional[
|
28 |
+
Literal["Circling, incoordination, tremors, convulsions"]
|
29 |
+
] = None
|
30 |
+
|
31 |
|
32 |
class NoChanges(BehaviorSimple):
|
33 |
+
type: Literal["no changes"]
|
34 |
description: Optional[Literal["Animal is acting normally"]] = None
|
35 |
|
36 |
+
|
37 |
# Union of all possible behaviors
|
38 |
+
BehaviorSimpleType = Union[GeneralWeakness, Vomiting, AtypicalBehavior, NoChanges]
|
39 |
+
|
|
|
|
|
|
|
|
|
40 |
|
41 |
# Main class that logs multiple behaviors
|
42 |
class BehaviorsSimple(BaseModel):
|
app/circumstances/circumstances_dropdowns.py
CHANGED
@@ -2,114 +2,141 @@ import gradio as gr
|
|
2 |
from utils.utils_config import get_custom_config_dropdowns
|
3 |
from validation_submission.utils_individual import add_data_to_individual
|
4 |
|
5 |
-
|
|
|
6 |
def retrieve_config_options(label, dropdown_config):
|
7 |
options = list(dropdown_config[label].keys())
|
8 |
options = [option.title() for option in options]
|
9 |
return options
|
10 |
|
11 |
-
|
|
|
12 |
dropdown_level2 = gr.Dropdown(choices=[], visible=False)
|
13 |
openfield_level2 = gr.Textbox(visible=False)
|
14 |
dropdown_extra_level2 = gr.Dropdown(choices=[], visible=False)
|
15 |
return dropdown_level2, openfield_level2, dropdown_extra_level2
|
16 |
|
17 |
-
|
|
|
18 |
dropdown_config = get_custom_config_dropdowns("config_dropdown_circumstances.json")
|
19 |
options = retrieve_config_options(label, dropdown_config)
|
20 |
dropdown = gr.Dropdown(choices=options, label=label, interactive=True, visible=True)
|
21 |
dropdown_level2, openfield_level2, dropdown_extra_level2 = reinitialise_level2()
|
22 |
-
return
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
def dropdown_collision(individual):
|
25 |
label = "Collision with a means of transport"
|
26 |
individual = add_data_to_individual("circumstance", label.lower(), individual)
|
27 |
return create_dropdown_level1(label, individual)
|
28 |
|
|
|
29 |
def dropdown_deliberate_destruction(individual):
|
30 |
label = "Destruction / Deliberatly removed"
|
31 |
individual = add_data_to_individual("circumstance", label.lower(), individual)
|
32 |
-
return create_dropdown_level1(label, individual)
|
|
|
33 |
|
34 |
-
def dropdown_indirect_destruction(individual):
|
35 |
label = "Indirect destruction"
|
36 |
individual = add_data_to_individual("circumstance", label.lower(), individual)
|
37 |
-
return create_dropdown_level1(label, individual)
|
38 |
|
39 |
-
|
|
|
40 |
label = "Natural cause"
|
41 |
-
individual = add_data_to_individual(
|
42 |
-
return create_dropdown_level1(label, individual)
|
43 |
|
44 |
|
45 |
-
|
46 |
def get_options(value):
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
def on_select(evt: gr.SelectData, individual): # SelectData is a subclass of EventData
|
74 |
-
options_label, options_dropdown, open_field, extras, extras_label = get_options(
|
|
|
|
|
75 |
individual = add_data_to_individual(
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
82 |
if options_dropdown is not None:
|
83 |
-
dropdown_level2 = gr.Dropdown(
|
84 |
-
|
|
|
|
|
85 |
dropdown_level2 = gr.Dropdown(choices=[], visible=False)
|
86 |
|
87 |
if open_field is not None:
|
88 |
openfield_level2 = gr.Textbox(label=open_field, interactive=True, visible=True)
|
89 |
-
else:
|
90 |
openfield_level2 = gr.Textbox(visible=False)
|
91 |
|
92 |
-
if extras is not None:
|
93 |
-
dropdown_extra_level2 = gr.Dropdown(
|
94 |
-
|
|
|
|
|
95 |
dropdown_extra_level2 = gr.Dropdown(choices=[], visible=False)
|
96 |
return dropdown_level2, openfield_level2, dropdown_extra_level2, individual
|
97 |
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
102 |
return individual
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
|
|
108 |
return individual
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
112 |
individual = add_data_to_individual(
|
113 |
-
|
114 |
-
|
115 |
-
return individual
|
|
|
2 |
from utils.utils_config import get_custom_config_dropdowns
|
3 |
from validation_submission.utils_individual import add_data_to_individual
|
4 |
|
5 |
+
|
6 |
+
# --------------------------------------------------------- LEVEL 1 DROPDOWNS
|
7 |
def retrieve_config_options(label, dropdown_config):
|
8 |
options = list(dropdown_config[label].keys())
|
9 |
options = [option.title() for option in options]
|
10 |
return options
|
11 |
|
12 |
+
|
13 |
+
def reinitialise_level2():
|
14 |
dropdown_level2 = gr.Dropdown(choices=[], visible=False)
|
15 |
openfield_level2 = gr.Textbox(visible=False)
|
16 |
dropdown_extra_level2 = gr.Dropdown(choices=[], visible=False)
|
17 |
return dropdown_level2, openfield_level2, dropdown_extra_level2
|
18 |
|
19 |
+
|
20 |
+
def create_dropdown_level1(label, individual):
|
21 |
dropdown_config = get_custom_config_dropdowns("config_dropdown_circumstances.json")
|
22 |
options = retrieve_config_options(label, dropdown_config)
|
23 |
dropdown = gr.Dropdown(choices=options, label=label, interactive=True, visible=True)
|
24 |
dropdown_level2, openfield_level2, dropdown_extra_level2 = reinitialise_level2()
|
25 |
+
return (
|
26 |
+
dropdown,
|
27 |
+
dropdown_level2,
|
28 |
+
openfield_level2,
|
29 |
+
dropdown_extra_level2,
|
30 |
+
individual,
|
31 |
+
)
|
32 |
+
|
33 |
+
|
34 |
def dropdown_collision(individual):
|
35 |
label = "Collision with a means of transport"
|
36 |
individual = add_data_to_individual("circumstance", label.lower(), individual)
|
37 |
return create_dropdown_level1(label, individual)
|
38 |
|
39 |
+
|
40 |
def dropdown_deliberate_destruction(individual):
|
41 |
label = "Destruction / Deliberatly removed"
|
42 |
individual = add_data_to_individual("circumstance", label.lower(), individual)
|
43 |
+
return create_dropdown_level1(label, individual)
|
44 |
+
|
45 |
|
46 |
+
def dropdown_indirect_destruction(individual):
|
47 |
label = "Indirect destruction"
|
48 |
individual = add_data_to_individual("circumstance", label.lower(), individual)
|
49 |
+
return create_dropdown_level1(label, individual)
|
50 |
|
51 |
+
|
52 |
+
def dropdown_natural_cause(individual):
|
53 |
label = "Natural cause"
|
54 |
+
individual = add_data_to_individual("circumstance", label.lower(), individual)
|
55 |
+
return create_dropdown_level1(label, individual)
|
56 |
|
57 |
|
58 |
+
# --------------------------------------------------------- LEVEL 2 DROPDOWNS
|
59 |
def get_options(value):
|
60 |
+
value = value.lower()
|
61 |
+
options_label = None
|
62 |
+
options_dropdown = None
|
63 |
+
open_field = None
|
64 |
+
extras = None
|
65 |
+
extras_label = None
|
66 |
+
dropdown_config = get_custom_config_dropdowns("config_dropdown_circumstances.json")
|
67 |
+
for _, sub_dict in dropdown_config.items():
|
68 |
+
nested_dict = sub_dict.get(value)
|
69 |
+
if nested_dict is not None:
|
70 |
+
if "Options" in nested_dict.keys():
|
71 |
+
options_dict = nested_dict["Options"]
|
72 |
+
options_label = list(options_dict.keys())[0]
|
73 |
+
options_dropdown = list(options_dict.values())[0]
|
74 |
+
options_dropdown = [option.title() for option in options_dropdown]
|
75 |
+
if "Open" in nested_dict.keys():
|
76 |
+
open_field = nested_dict["Open"]
|
77 |
+
open_field = open_field.title()
|
78 |
+
if "Extra" in nested_dict.keys():
|
79 |
+
for key, val in nested_dict["Extra"].items():
|
80 |
+
extras_label = key
|
81 |
+
extras = val
|
82 |
+
extras = [extra.title() for extra in extras]
|
83 |
+
return options_label, options_dropdown, open_field, extras, extras_label
|
84 |
+
|
85 |
+
|
86 |
def on_select(evt: gr.SelectData, individual): # SelectData is a subclass of EventData
|
87 |
+
options_label, options_dropdown, open_field, extras, extras_label = get_options(
|
88 |
+
evt.value
|
89 |
+
)
|
90 |
individual = add_data_to_individual(
|
91 |
+
"circumstance_type",
|
92 |
+
{
|
93 |
+
"type": (evt.value).lower(),
|
94 |
+
"option_dropdown_label": options_label.lower()
|
95 |
+
if options_label is not None
|
96 |
+
else "NA",
|
97 |
+
"open_field_label": open_field.lower() if open_field is not None else "NA",
|
98 |
+
"extra_label": extras_label.lower() if extras_label is not None else "NA",
|
99 |
+
},
|
100 |
+
individual,
|
101 |
+
)
|
102 |
if options_dropdown is not None:
|
103 |
+
dropdown_level2 = gr.Dropdown(
|
104 |
+
choices=options_dropdown, label=evt.value, interactive=True, visible=True
|
105 |
+
)
|
106 |
+
else:
|
107 |
dropdown_level2 = gr.Dropdown(choices=[], visible=False)
|
108 |
|
109 |
if open_field is not None:
|
110 |
openfield_level2 = gr.Textbox(label=open_field, interactive=True, visible=True)
|
111 |
+
else:
|
112 |
openfield_level2 = gr.Textbox(visible=False)
|
113 |
|
114 |
+
if extras is not None:
|
115 |
+
dropdown_extra_level2 = gr.Dropdown(
|
116 |
+
choices=extras, label=extras_label, interactive=True, visible=True
|
117 |
+
)
|
118 |
+
else:
|
119 |
dropdown_extra_level2 = gr.Dropdown(choices=[], visible=False)
|
120 |
return dropdown_level2, openfield_level2, dropdown_extra_level2, individual
|
121 |
|
122 |
+
|
123 |
+
def on_select_dropdown_level2(evt: gr.SelectData, individual):
|
124 |
+
individual = add_data_to_individual(
|
125 |
+
"circumstance_option_dropdown", evt.value.lower(), individual
|
126 |
+
)
|
127 |
return individual
|
128 |
|
129 |
+
|
130 |
+
def on_select_dropdown_extra_level2(evt: gr.SelectData, individual):
|
131 |
+
individual = add_data_to_individual(
|
132 |
+
"circumstance_extra", evt.value.lower(), individual
|
133 |
+
)
|
134 |
return individual
|
135 |
+
|
136 |
+
|
137 |
+
def on_change_openfield_level2(openfield_level2_dead, individual):
|
138 |
+
# print("Saving open field")
|
139 |
individual = add_data_to_individual(
|
140 |
+
"circumstance_open_field", str(openfield_level2_dead).lower(), individual
|
141 |
+
)
|
142 |
+
return individual
|
app/classes.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
from pydantic import BaseModel, Field
|
2 |
-
from typing import Optional
|
3 |
import numpy as np
|
4 |
from PIL import Image
|
5 |
import io
|
@@ -7,24 +7,30 @@ import base64
|
|
7 |
import uuid
|
8 |
|
9 |
from behavior.class_behavior import Behaviors
|
|
|
10 |
from circumstances.class_circumstance import Circumstances
|
11 |
from physical.class_physical import PhysicalAnomalies
|
|
|
12 |
from follow_up.class_follow_up import FollowUpEvents
|
13 |
from geolocalisation.class_geolocalisation import Geolocalisation
|
14 |
|
|
|
15 |
class Wounded(BaseModel):
|
16 |
circumstances: Circumstances
|
17 |
-
behaviors: Behaviors
|
18 |
-
physical_anomalies: PhysicalAnomalies
|
19 |
follow_up_events: FollowUpEvents
|
20 |
|
|
|
21 |
class Dead(BaseModel):
|
22 |
circumstances: Circumstances
|
23 |
-
physical_anomalies: PhysicalAnomalies
|
24 |
follow_up_events: FollowUpEvents
|
25 |
|
|
|
26 |
class ImageBase64(BaseModel):
|
27 |
-
image: str
|
|
|
28 |
@classmethod
|
29 |
def to_base64(cls, pixel_data: list):
|
30 |
img_array = np.array(pixel_data, dtype=np.uint8)
|
@@ -33,15 +39,16 @@ class ImageBase64(BaseModel):
|
|
33 |
buffer = io.BytesIO()
|
34 |
img.save(buffer, format="PNG")
|
35 |
buffer.seek(0)
|
36 |
-
base64_str = base64.b64encode(buffer.read()).decode(
|
37 |
return cls(image=base64_str)
|
38 |
-
|
|
|
39 |
class Report(BaseModel):
|
40 |
identifier: str
|
41 |
image: ImageBase64
|
42 |
image_md5: str
|
43 |
geolocalisation: Geolocalisation
|
44 |
wounded_state: str
|
45 |
-
wounded: Optional[Wounded] = None
|
46 |
dead_state: str
|
47 |
-
dead: Optional[Dead] = None
|
|
|
1 |
from pydantic import BaseModel, Field
|
2 |
+
from typing import Optional, Union
|
3 |
import numpy as np
|
4 |
from PIL import Image
|
5 |
import io
|
|
|
7 |
import uuid
|
8 |
|
9 |
from behavior.class_behavior import Behaviors
|
10 |
+
from behavior.class_behavior_simple import BehaviorsSimple
|
11 |
from circumstances.class_circumstance import Circumstances
|
12 |
from physical.class_physical import PhysicalAnomalies
|
13 |
+
from physical.class_physical_simple import PhysicalAnomaliesSimple
|
14 |
from follow_up.class_follow_up import FollowUpEvents
|
15 |
from geolocalisation.class_geolocalisation import Geolocalisation
|
16 |
|
17 |
+
|
18 |
class Wounded(BaseModel):
|
19 |
circumstances: Circumstances
|
20 |
+
behaviors: Union[Behaviors, BehaviorsSimple]
|
21 |
+
physical_anomalies: Union[PhysicalAnomalies, PhysicalAnomaliesSimple]
|
22 |
follow_up_events: FollowUpEvents
|
23 |
|
24 |
+
|
25 |
class Dead(BaseModel):
|
26 |
circumstances: Circumstances
|
27 |
+
physical_anomalies: Union[PhysicalAnomalies, PhysicalAnomaliesSimple]
|
28 |
follow_up_events: FollowUpEvents
|
29 |
|
30 |
+
|
31 |
class ImageBase64(BaseModel):
|
32 |
+
image: str
|
33 |
+
|
34 |
@classmethod
|
35 |
def to_base64(cls, pixel_data: list):
|
36 |
img_array = np.array(pixel_data, dtype=np.uint8)
|
|
|
39 |
buffer = io.BytesIO()
|
40 |
img.save(buffer, format="PNG")
|
41 |
buffer.seek(0)
|
42 |
+
base64_str = base64.b64encode(buffer.read()).decode("utf-8")
|
43 |
return cls(image=base64_str)
|
44 |
+
|
45 |
+
|
46 |
class Report(BaseModel):
|
47 |
identifier: str
|
48 |
image: ImageBase64
|
49 |
image_md5: str
|
50 |
geolocalisation: Geolocalisation
|
51 |
wounded_state: str
|
52 |
+
wounded: Optional[Wounded] = None
|
53 |
dead_state: str
|
54 |
+
dead: Optional[Dead] = None
|
app/physical/physical_checkbox.py
CHANGED
@@ -1,92 +1,168 @@
|
|
1 |
import gradio as gr
|
2 |
from utils.utils_config import get_custom_config_dropdowns
|
3 |
from utils.utils_checkbox import create_checkbox
|
4 |
-
from validation_submission.utils_individual import add_data_to_individual
|
5 |
-
|
|
|
|
|
6 |
def get_body_parts(mode):
|
7 |
-
if mode=="simple":
|
8 |
-
dropdown_config = get_custom_config_dropdowns(
|
|
|
|
|
9 |
elif mode == "advanced":
|
10 |
dropdown_config = get_custom_config_dropdowns("config_checkbox_physical.json")
|
11 |
return list(dropdown_config.keys())
|
12 |
|
|
|
13 |
def retrieve_config_options(label, dropdown_config):
|
14 |
options = list(dropdown_config[label].keys())
|
15 |
options = [option.title() for option in options]
|
16 |
return options
|
17 |
|
|
|
18 |
def get_options_description(value, mode):
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
40 |
def create_checkbox_beak(section, mode, label_checkbox, visible):
|
41 |
-
body_part="Beak"
|
42 |
options, descriptions = get_options_description(body_part, mode)
|
43 |
-
return create_checkbox(
|
|
|
|
|
|
|
44 |
|
45 |
def create_checkbox_body(section, mode, label_checkbox, visible):
|
46 |
-
body_part="Body"
|
47 |
options, descriptions = get_options_description(body_part, mode)
|
48 |
-
return create_checkbox(
|
|
|
|
|
|
|
49 |
|
50 |
def create_checkbox_feathers(section, mode, label_checkbox, visible):
|
51 |
-
body_part="Feathers/Wings/Tail"
|
52 |
options, descriptions = get_options_description(body_part, mode)
|
53 |
-
return create_checkbox(
|
|
|
|
|
|
|
54 |
|
55 |
def create_checkbox_head(section, mode, label_checkbox, visible):
|
56 |
-
body_part="Head incl. eyes"
|
57 |
options, descriptions = get_options_description(body_part, mode)
|
58 |
-
return create_checkbox(
|
|
|
|
|
|
|
59 |
|
60 |
def create_checkbox_legs(section, mode, label_checkbox, visible):
|
61 |
-
body_part="Legs"
|
62 |
options, descriptions = get_options_description(body_part, mode)
|
63 |
-
return create_checkbox(
|
|
|
|
|
|
|
64 |
|
65 |
-
|
66 |
def process_body_parts(section, mode, matched_box):
|
67 |
-
#take all except "Common"
|
68 |
body_parts = get_body_parts(mode)
|
69 |
body_parts = body_parts[1:]
|
70 |
label_checkbox = "Physical changes to "
|
71 |
-
visibles = [True if matched_box==body_part else False for body_part in body_parts
|
72 |
-
checkbox_beak, text_beak = create_checkbox_beak(
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
return individual
|
86 |
|
87 |
-
|
|
|
|
|
88 |
|
89 |
def hide_physical(mode):
|
90 |
-
|
91 |
-
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
from utils.utils_config import get_custom_config_dropdowns
|
3 |
from utils.utils_checkbox import create_checkbox
|
4 |
+
from validation_submission.utils_individual import add_data_to_individual
|
5 |
+
|
6 |
+
|
7 |
+
# ---------------------------------------------------------
|
8 |
def get_body_parts(mode):
|
9 |
+
if mode == "simple":
|
10 |
+
dropdown_config = get_custom_config_dropdowns(
|
11 |
+
"config_checkbox_physical_simple.json"
|
12 |
+
)
|
13 |
elif mode == "advanced":
|
14 |
dropdown_config = get_custom_config_dropdowns("config_checkbox_physical.json")
|
15 |
return list(dropdown_config.keys())
|
16 |
|
17 |
+
|
18 |
def retrieve_config_options(label, dropdown_config):
|
19 |
options = list(dropdown_config[label].keys())
|
20 |
options = [option.title() for option in options]
|
21 |
return options
|
22 |
|
23 |
+
|
24 |
def get_options_description(value, mode):
|
25 |
+
# print(f"Get options description: {mode}")
|
26 |
+
if mode == "simple":
|
27 |
+
dropdown_config = get_custom_config_dropdowns(
|
28 |
+
"config_checkbox_physical_simple.json"
|
29 |
+
)
|
30 |
+
elif mode == "advanced":
|
31 |
+
dropdown_config = get_custom_config_dropdowns("config_checkbox_physical.json")
|
32 |
+
# get options
|
33 |
+
options_common = retrieve_config_options("Common", dropdown_config)
|
34 |
+
options_for_value = retrieve_config_options(value, dropdown_config)
|
35 |
+
options_common.extend(options_for_value)
|
36 |
+
options = options_common
|
37 |
+
# get descriptions
|
38 |
+
descriptions = []
|
39 |
+
for key, sub_dict in dropdown_config.items():
|
40 |
+
if key == value or key == "Common":
|
41 |
+
for _, option_dict in sub_dict.items():
|
42 |
+
for description_tag, description in option_dict.items():
|
43 |
+
if "Description" == description_tag:
|
44 |
+
descriptions.append(description)
|
45 |
+
return options, descriptions
|
46 |
+
|
47 |
+
|
48 |
+
# ---------------------------------------------------------
|
49 |
def create_checkbox_beak(section, mode, label_checkbox, visible):
|
50 |
+
body_part = "Beak"
|
51 |
options, descriptions = get_options_description(body_part, mode)
|
52 |
+
return create_checkbox(
|
53 |
+
body_part, section, label_checkbox, visible, options, descriptions
|
54 |
+
)
|
55 |
+
|
56 |
|
57 |
def create_checkbox_body(section, mode, label_checkbox, visible):
|
58 |
+
body_part = "Body"
|
59 |
options, descriptions = get_options_description(body_part, mode)
|
60 |
+
return create_checkbox(
|
61 |
+
body_part, section, label_checkbox, visible, options, descriptions
|
62 |
+
)
|
63 |
+
|
64 |
|
65 |
def create_checkbox_feathers(section, mode, label_checkbox, visible):
|
66 |
+
body_part = "Feathers/Wings/Tail"
|
67 |
options, descriptions = get_options_description(body_part, mode)
|
68 |
+
return create_checkbox(
|
69 |
+
body_part, section, label_checkbox, visible, options, descriptions
|
70 |
+
)
|
71 |
+
|
72 |
|
73 |
def create_checkbox_head(section, mode, label_checkbox, visible):
|
74 |
+
body_part = "Head incl. eyes"
|
75 |
options, descriptions = get_options_description(body_part, mode)
|
76 |
+
return create_checkbox(
|
77 |
+
body_part, section, label_checkbox, visible, options, descriptions
|
78 |
+
)
|
79 |
+
|
80 |
|
81 |
def create_checkbox_legs(section, mode, label_checkbox, visible):
|
82 |
+
body_part = "Legs"
|
83 |
options, descriptions = get_options_description(body_part, mode)
|
84 |
+
return create_checkbox(
|
85 |
+
body_part, section, label_checkbox, visible, options, descriptions
|
86 |
+
)
|
87 |
+
|
88 |
|
89 |
+
# ---------------------------------------------------------
|
90 |
def process_body_parts(section, mode, matched_box):
|
91 |
+
# take all except "Common"
|
92 |
body_parts = get_body_parts(mode)
|
93 |
body_parts = body_parts[1:]
|
94 |
label_checkbox = "Physical changes to "
|
95 |
+
visibles = [True if matched_box == body_part else False for body_part in body_parts]
|
96 |
+
checkbox_beak, text_beak = create_checkbox_beak(
|
97 |
+
section, mode, label_checkbox, visibles[0]
|
98 |
+
)
|
99 |
+
checkbox_body, text_body = create_checkbox_body(
|
100 |
+
section, mode, label_checkbox, visibles[1]
|
101 |
+
)
|
102 |
+
checkbox_feathers, text_feathers = create_checkbox_feathers(
|
103 |
+
section, mode, label_checkbox, visibles[2]
|
104 |
+
)
|
105 |
+
checkbox_head, text_head = create_checkbox_head(
|
106 |
+
section, mode, label_checkbox, visibles[3]
|
107 |
+
)
|
108 |
+
checkbox_legs, text_legs = create_checkbox_legs(
|
109 |
+
section, mode, label_checkbox, visibles[4]
|
110 |
+
)
|
111 |
+
return (
|
112 |
+
checkbox_beak,
|
113 |
+
text_beak,
|
114 |
+
checkbox_body,
|
115 |
+
text_body,
|
116 |
+
checkbox_feathers,
|
117 |
+
text_feathers,
|
118 |
+
checkbox_head,
|
119 |
+
text_head,
|
120 |
+
checkbox_legs,
|
121 |
+
text_legs,
|
122 |
+
)
|
123 |
+
|
124 |
+
|
125 |
+
# ---------------------------------------------------------
|
126 |
+
|
127 |
+
|
128 |
+
def on_select_body_part(body_part_checkbox, body_part, individual):
|
129 |
+
individual = add_data_to_individual(
|
130 |
+
"physical_type_" + body_part.lower(), body_part.lower(), individual
|
131 |
+
)
|
132 |
+
body_part_checkbox = [
|
133 |
+
body_part_check.lower() for body_part_check in body_part_checkbox
|
134 |
+
]
|
135 |
+
individual = add_data_to_individual(
|
136 |
+
"physical_anomaly_" + body_part.lower(), body_part_checkbox, individual
|
137 |
+
)
|
138 |
return individual
|
139 |
|
140 |
+
|
141 |
+
# ---------------------------------------------------------
|
142 |
+
|
143 |
|
144 |
def hide_physical(mode):
|
145 |
+
(
|
146 |
+
checkbox_beak,
|
147 |
+
text_beak,
|
148 |
+
checkbox_body,
|
149 |
+
text_body,
|
150 |
+
checkbox_feathers,
|
151 |
+
text_feathers,
|
152 |
+
checkbox_head,
|
153 |
+
text_head,
|
154 |
+
checkbox_legs,
|
155 |
+
text_legs,
|
156 |
+
) = process_body_parts("wounded", mode, "None")
|
157 |
+
return (
|
158 |
+
checkbox_beak,
|
159 |
+
text_beak,
|
160 |
+
checkbox_body,
|
161 |
+
text_body,
|
162 |
+
checkbox_feathers,
|
163 |
+
text_feathers,
|
164 |
+
checkbox_head,
|
165 |
+
text_head,
|
166 |
+
checkbox_legs,
|
167 |
+
text_legs,
|
168 |
+
)
|
app/validation_submission/submission.py
CHANGED
@@ -1,28 +1,31 @@
|
|
1 |
-
import json
|
2 |
from validation_submission.validation import validate_individual
|
3 |
|
4 |
from huggingface_hub import HfApi
|
5 |
import os
|
6 |
|
|
|
7 |
def validate_save_individual(data, error_box, mode):
|
8 |
individual, error_box = validate_individual(data, error_box, mode)
|
9 |
if individual:
|
10 |
push_to_dataset_hf(individual.model_dump())
|
11 |
return error_box
|
12 |
-
|
|
|
13 |
def push_to_dataset_hf(individual):
|
14 |
token = os.environ.get("HF_TOKEN", None)
|
15 |
api = HfApi(token=token)
|
16 |
import tempfile
|
|
|
17 |
f = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
|
18 |
json.dump(individual, f)
|
19 |
f.flush()
|
20 |
f.close()
|
21 |
-
path_in_repo= f"data/{individual['image_md5']}.json"
|
22 |
-
print(path_in_repo)
|
23 |
# api.upload_file(
|
24 |
# path_or_fileobj=f.name,
|
25 |
# path_in_repo=path_in_repo,
|
26 |
# repo_id="SDSC/digiwild-dataset",
|
27 |
# repo_type="dataset",
|
28 |
-
# )
|
|
|
1 |
+
import json
|
2 |
from validation_submission.validation import validate_individual
|
3 |
|
4 |
from huggingface_hub import HfApi
|
5 |
import os
|
6 |
|
7 |
+
|
8 |
def validate_save_individual(data, error_box, mode):
|
9 |
individual, error_box = validate_individual(data, error_box, mode)
|
10 |
if individual:
|
11 |
push_to_dataset_hf(individual.model_dump())
|
12 |
return error_box
|
13 |
+
|
14 |
+
|
15 |
def push_to_dataset_hf(individual):
|
16 |
token = os.environ.get("HF_TOKEN", None)
|
17 |
api = HfApi(token=token)
|
18 |
import tempfile
|
19 |
+
|
20 |
f = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
|
21 |
json.dump(individual, f)
|
22 |
f.flush()
|
23 |
f.close()
|
24 |
+
path_in_repo = f"data/{individual['image_md5']}.json"
|
25 |
+
# print(path_in_repo)
|
26 |
# api.upload_file(
|
27 |
# path_or_fileobj=f.name,
|
28 |
# path_in_repo=path_in_repo,
|
29 |
# repo_id="SDSC/digiwild-dataset",
|
30 |
# repo_type="dataset",
|
31 |
+
# )
|
app/validation_submission/validation.py
CHANGED
@@ -10,22 +10,29 @@ from physical.class_physical import PhysicalAnomalies
|
|
10 |
from physical.class_physical_simple import PhysicalAnomaliesSimple
|
11 |
from follow_up.class_follow_up import FollowUpEvents
|
12 |
from classes import Report, Wounded, Dead, ImageBase64
|
13 |
-
from validation_submission.processing import
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
def get_fields(data_dict, keyword):
|
16 |
-
extract = {}
|
17 |
for key, val in data_dict.items():
|
18 |
if keyword in key:
|
19 |
extract[key] = val
|
20 |
return extract
|
21 |
|
22 |
-
|
|
|
23 |
error_box = reset_error_box(error_box)
|
24 |
-
#data = get_json_one_individual() # TODO: This should change
|
25 |
data["identifier"] = str(uuid.uuid4())
|
26 |
if "image" in data.keys():
|
27 |
img = ImageBase64.to_base64(data["image"])
|
28 |
-
else:
|
29 |
img = None
|
30 |
if "geolocalisation" in data.keys():
|
31 |
geolocalisation = data["geolocalisation"]
|
@@ -33,72 +40,109 @@ def validate_individual(data, error_box, mode:str):
|
|
33 |
geolocalisation = None
|
34 |
|
35 |
error_behavior = None
|
36 |
-
error_circumstance = None
|
37 |
-
error_followup = None
|
38 |
-
error_physical = None
|
39 |
error_individual = None
|
40 |
if "wounded_state" not in data or "dead_state" not in data:
|
41 |
data["wounded_state"] = "No"
|
42 |
data["dead_state"] = "No"
|
43 |
if (data["wounded_state"] == "Yes") or (data["dead_state"] == "Yes"):
|
44 |
-
data_wounded_dead = data
|
45 |
-
print(data_wounded_dead)
|
46 |
circumstance, error_circumstance = validate_circumstance(data_wounded_dead)
|
47 |
physical, error_physical = validate_physical(data_wounded_dead, mode)
|
48 |
followup, error_followup = validate_follow_up(data_wounded_dead)
|
49 |
|
50 |
-
if data["wounded_state"]=="Yes":
|
51 |
-
print(physical)
|
52 |
behavior, error_behavior = validate_behavior(data_wounded_dead, mode)
|
53 |
-
try
|
54 |
-
individual = Report(
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
64 |
except ValidationError as e:
|
65 |
-
print(
|
|
|
66 |
error_individual = e
|
67 |
|
68 |
-
elif data["dead_state"]=="Yes":
|
69 |
-
try:
|
70 |
-
individual = Report(
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
80 |
except ValidationError as e:
|
81 |
-
print(
|
|
|
82 |
error_individual = e
|
83 |
else:
|
84 |
-
try:
|
85 |
-
individual = Report(
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
|
|
|
|
91 |
except ValidationError as e:
|
92 |
-
print(e)
|
93 |
error_individual = e
|
94 |
-
if
|
95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
individual = None
|
97 |
-
else:
|
98 |
-
error_box= gr.Text(
|
|
|
|
|
|
|
|
|
|
|
99 |
return individual, error_box
|
100 |
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
error_text = ""
|
103 |
if error_circumstance:
|
104 |
error_text += f"Error in circumstance: {error_circumstance}\n"
|
@@ -111,78 +155,78 @@ def show_error(error_box, error_behavior, error_circumstance, error_followup, er
|
|
111 |
if error_individual:
|
112 |
error_text += f"Error in individual: {error_individual}\n"
|
113 |
error_text += "PLEASE CORRECT THESE ERRORS BEFORE SUBMITTING."
|
114 |
-
error_box= gr.Text(
|
|
|
|
|
115 |
return error_box
|
116 |
|
|
|
117 |
def reset_error_box(error_box):
|
118 |
error_box = gr.Text(value=None, visible=False)
|
119 |
return error_box
|
120 |
|
|
|
121 |
#### VALIDATION FUNCTIONS
|
122 |
-
def validate_circumstance(data):
|
123 |
circumstance_raw = get_fields(data, "circumstance")
|
124 |
circumstance_formatted = process_circumstance(circumstance_raw)
|
125 |
-
try:
|
126 |
Circumstances.model_validate(circumstance_formatted)
|
127 |
circumstances = Circumstances(**circumstance_formatted)
|
128 |
error = None
|
129 |
except ValidationError as e:
|
130 |
error = e
|
131 |
-
print(e)
|
132 |
circumstances = None
|
133 |
return circumstances, error
|
134 |
-
|
135 |
|
136 |
-
|
|
|
137 |
behaviors_raw = get_fields(data, "behaviors")
|
138 |
behaviors_formatted = process_behaviors(behaviors_raw)
|
139 |
try:
|
140 |
-
if mode=="simple":
|
141 |
BehaviorsSimple.model_validate(behaviors_formatted)
|
142 |
behavior = BehaviorsSimple(**behaviors_formatted)
|
143 |
-
elif mode=="advanced":
|
144 |
Behaviors.model_validate(behaviors_formatted)
|
145 |
behavior = Behaviors(**behaviors_formatted)
|
146 |
error = None
|
147 |
except ValidationError as e:
|
148 |
-
print(e)
|
149 |
-
print("Validation failed for the behaviors.")
|
150 |
behavior = None
|
151 |
error = e
|
152 |
return behavior, error
|
153 |
-
|
154 |
|
155 |
-
|
|
|
156 |
physical_raw = get_fields(data, "physical")
|
157 |
physical_formatted = process_physical(physical_raw)
|
158 |
-
print(physical_formatted)
|
159 |
-
try:
|
160 |
-
if mode=="simple":
|
161 |
PhysicalAnomaliesSimple.model_validate(physical_formatted)
|
162 |
physical = PhysicalAnomaliesSimple(**physical_formatted)
|
163 |
-
elif mode=="advanced":
|
164 |
PhysicalAnomalies.model_validate(physical_formatted)
|
165 |
physical = PhysicalAnomalies(**physical_formatted)
|
166 |
error = None
|
167 |
except ValidationError as e:
|
168 |
-
print(e)
|
169 |
-
print("Validation failed for the physical anomalies.")
|
170 |
physical = None
|
171 |
error = e
|
172 |
return physical, error
|
173 |
-
|
|
|
174 |
def validate_follow_up(data):
|
175 |
followup_raw = get_fields(data, "followup")
|
176 |
followup_formatted = process_followup(followup_raw)
|
177 |
-
try:
|
178 |
FollowUpEvents.model_validate(followup_formatted)
|
179 |
followup = FollowUpEvents(**followup_formatted)
|
180 |
error = None
|
181 |
except ValidationError as e:
|
182 |
-
print(e)
|
183 |
-
|
184 |
followup = None
|
185 |
return followup, error
|
186 |
-
|
187 |
-
|
188 |
-
|
|
|
10 |
from physical.class_physical_simple import PhysicalAnomaliesSimple
|
11 |
from follow_up.class_follow_up import FollowUpEvents
|
12 |
from classes import Report, Wounded, Dead, ImageBase64
|
13 |
+
from validation_submission.processing import (
|
14 |
+
process_circumstance,
|
15 |
+
process_behaviors,
|
16 |
+
process_physical,
|
17 |
+
process_followup,
|
18 |
+
)
|
19 |
+
|
20 |
|
21 |
def get_fields(data_dict, keyword):
|
22 |
+
extract = {}
|
23 |
for key, val in data_dict.items():
|
24 |
if keyword in key:
|
25 |
extract[key] = val
|
26 |
return extract
|
27 |
|
28 |
+
|
29 |
+
def validate_individual(data, error_box, mode: str):
|
30 |
error_box = reset_error_box(error_box)
|
31 |
+
# data = get_json_one_individual() # TODO: This should change
|
32 |
data["identifier"] = str(uuid.uuid4())
|
33 |
if "image" in data.keys():
|
34 |
img = ImageBase64.to_base64(data["image"])
|
35 |
+
else:
|
36 |
img = None
|
37 |
if "geolocalisation" in data.keys():
|
38 |
geolocalisation = data["geolocalisation"]
|
|
|
40 |
geolocalisation = None
|
41 |
|
42 |
error_behavior = None
|
43 |
+
error_circumstance = None
|
44 |
+
error_followup = None
|
45 |
+
error_physical = None
|
46 |
error_individual = None
|
47 |
if "wounded_state" not in data or "dead_state" not in data:
|
48 |
data["wounded_state"] = "No"
|
49 |
data["dead_state"] = "No"
|
50 |
if (data["wounded_state"] == "Yes") or (data["dead_state"] == "Yes"):
|
51 |
+
data_wounded_dead = data # get_json_tmp("wounded_dead")
|
52 |
+
# print(f"Data Wounded Dead: {data_wounded_dead}")
|
53 |
circumstance, error_circumstance = validate_circumstance(data_wounded_dead)
|
54 |
physical, error_physical = validate_physical(data_wounded_dead, mode)
|
55 |
followup, error_followup = validate_follow_up(data_wounded_dead)
|
56 |
|
57 |
+
if data["wounded_state"] == "Yes":
|
58 |
+
# print(f"Physical: {physical}")
|
59 |
behavior, error_behavior = validate_behavior(data_wounded_dead, mode)
|
60 |
+
try:
|
61 |
+
individual = Report(
|
62 |
+
identifier=data["identifier"],
|
63 |
+
image=img,
|
64 |
+
image_md5=data["image_md5"],
|
65 |
+
geolocalisation=geolocalisation,
|
66 |
+
wounded_state=data["wounded_state"],
|
67 |
+
wounded=Wounded(
|
68 |
+
circumstances=circumstance,
|
69 |
+
behaviors=behavior,
|
70 |
+
physical_anomalies=physical,
|
71 |
+
follow_up_events=followup,
|
72 |
+
),
|
73 |
+
dead_state=data["dead_state"],
|
74 |
+
)
|
75 |
except ValidationError as e:
|
76 |
+
print("Error in wounded_state:")
|
77 |
+
print(e.json())
|
78 |
error_individual = e
|
79 |
|
80 |
+
elif data["dead_state"] == "Yes":
|
81 |
+
try:
|
82 |
+
individual = Report(
|
83 |
+
identifier=data["identifier"],
|
84 |
+
image=img,
|
85 |
+
image_md5=data["image_md5"],
|
86 |
+
geolocalisation=geolocalisation,
|
87 |
+
wounded_state=data["wounded_state"],
|
88 |
+
dead_state=data["dead_state"],
|
89 |
+
dead=Dead(
|
90 |
+
circumstances=circumstance,
|
91 |
+
physical_anomalies=physical,
|
92 |
+
follow_up_events=followup,
|
93 |
+
),
|
94 |
+
)
|
95 |
except ValidationError as e:
|
96 |
+
print("Error in dead_state:")
|
97 |
+
print(e.json())
|
98 |
error_individual = e
|
99 |
else:
|
100 |
+
try:
|
101 |
+
individual = Report(
|
102 |
+
identifier=data["identifier"],
|
103 |
+
image=img,
|
104 |
+
image_md5=data["image_md5"],
|
105 |
+
geolocalisation=geolocalisation,
|
106 |
+
wounded_state=data["wounded_state"],
|
107 |
+
dead_state=data["dead_state"],
|
108 |
+
)
|
109 |
except ValidationError as e:
|
110 |
+
print(f"""Error in individual else: {e}""")
|
111 |
error_individual = e
|
112 |
+
if (
|
113 |
+
error_behavior
|
114 |
+
or error_circumstance
|
115 |
+
or error_followup
|
116 |
+
or error_physical
|
117 |
+
or error_individual
|
118 |
+
):
|
119 |
+
error_box = show_error(
|
120 |
+
error_box,
|
121 |
+
error_behavior,
|
122 |
+
error_circumstance,
|
123 |
+
error_followup,
|
124 |
+
error_physical,
|
125 |
+
error_individual,
|
126 |
+
)
|
127 |
individual = None
|
128 |
+
else:
|
129 |
+
error_box = gr.Text(
|
130 |
+
label="ALL VALID.",
|
131 |
+
value="Record Registered. You can return to the Display.",
|
132 |
+
visible=True,
|
133 |
+
elem_id="valid",
|
134 |
+
)
|
135 |
return individual, error_box
|
136 |
|
137 |
+
|
138 |
+
def show_error(
|
139 |
+
error_box,
|
140 |
+
error_behavior,
|
141 |
+
error_circumstance,
|
142 |
+
error_followup,
|
143 |
+
error_physical,
|
144 |
+
error_individual,
|
145 |
+
):
|
146 |
error_text = ""
|
147 |
if error_circumstance:
|
148 |
error_text += f"Error in circumstance: {error_circumstance}\n"
|
|
|
155 |
if error_individual:
|
156 |
error_text += f"Error in individual: {error_individual}\n"
|
157 |
error_text += "PLEASE CORRECT THESE ERRORS BEFORE SUBMITTING."
|
158 |
+
error_box = gr.Text(
|
159 |
+
label="ERROR DETECTED !", value=error_text, visible=True, elem_id="error"
|
160 |
+
)
|
161 |
return error_box
|
162 |
|
163 |
+
|
164 |
def reset_error_box(error_box):
|
165 |
error_box = gr.Text(value=None, visible=False)
|
166 |
return error_box
|
167 |
|
168 |
+
|
169 |
#### VALIDATION FUNCTIONS
|
170 |
+
def validate_circumstance(data):
|
171 |
circumstance_raw = get_fields(data, "circumstance")
|
172 |
circumstance_formatted = process_circumstance(circumstance_raw)
|
173 |
+
try:
|
174 |
Circumstances.model_validate(circumstance_formatted)
|
175 |
circumstances = Circumstances(**circumstance_formatted)
|
176 |
error = None
|
177 |
except ValidationError as e:
|
178 |
error = e
|
179 |
+
print(f"""Error in Validate_circumstance: {e}""")
|
180 |
circumstances = None
|
181 |
return circumstances, error
|
|
|
182 |
|
183 |
+
|
184 |
+
def validate_behavior(data, mode):
|
185 |
behaviors_raw = get_fields(data, "behaviors")
|
186 |
behaviors_formatted = process_behaviors(behaviors_raw)
|
187 |
try:
|
188 |
+
if mode == "simple":
|
189 |
BehaviorsSimple.model_validate(behaviors_formatted)
|
190 |
behavior = BehaviorsSimple(**behaviors_formatted)
|
191 |
+
elif mode == "advanced":
|
192 |
Behaviors.model_validate(behaviors_formatted)
|
193 |
behavior = Behaviors(**behaviors_formatted)
|
194 |
error = None
|
195 |
except ValidationError as e:
|
196 |
+
print(f"""Error in behaviors validation: {e}""")
|
|
|
197 |
behavior = None
|
198 |
error = e
|
199 |
return behavior, error
|
|
|
200 |
|
201 |
+
|
202 |
+
def validate_physical(data, mode):
|
203 |
physical_raw = get_fields(data, "physical")
|
204 |
physical_formatted = process_physical(physical_raw)
|
205 |
+
# print(physical_formatted)
|
206 |
+
try:
|
207 |
+
if mode == "simple":
|
208 |
PhysicalAnomaliesSimple.model_validate(physical_formatted)
|
209 |
physical = PhysicalAnomaliesSimple(**physical_formatted)
|
210 |
+
elif mode == "advanced":
|
211 |
PhysicalAnomalies.model_validate(physical_formatted)
|
212 |
physical = PhysicalAnomalies(**physical_formatted)
|
213 |
error = None
|
214 |
except ValidationError as e:
|
215 |
+
print(f"""Error in physical_anomalies validation: {e}""")
|
|
|
216 |
physical = None
|
217 |
error = e
|
218 |
return physical, error
|
219 |
+
|
220 |
+
|
221 |
def validate_follow_up(data):
|
222 |
followup_raw = get_fields(data, "followup")
|
223 |
followup_formatted = process_followup(followup_raw)
|
224 |
+
try:
|
225 |
FollowUpEvents.model_validate(followup_formatted)
|
226 |
followup = FollowUpEvents(**followup_formatted)
|
227 |
error = None
|
228 |
except ValidationError as e:
|
229 |
+
print(f"""Error in follow-up events validation: {e}""")
|
230 |
+
|
231 |
followup = None
|
232 |
return followup, error
|
|
|
|
|
|