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 |
-
#--------------------------------------------------------- LEVEL 2 DROPDOWNS
|
| 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
|
|
|
|
|
|
|
|
|