vancauwe commited on
Commit
a725af0
·
1 Parent(s): 995b526

feat: df for extra info and pydantic validator

Browse files
app/assets/config/config_checkbox_behavior.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "Abnormal breating":
3
  {
4
  "Description": "Problems breathing, breathing sounds"
5
  },
 
1
  {
2
+ "Abnormal breathing":
3
  {
4
  "Description": "Problems breathing, breathing sounds"
5
  },
app/assets/config/config_dropdown_circumstances.json CHANGED
@@ -3,12 +3,10 @@
3
  {
4
  "road vehicle":
5
  {
6
-
7
  "Options": {
8
- "road_type" : ["highway", "main road", "secondary road", "local road/path/trail", "parking lot", "other", "unknown"]
9
  },
10
  "Open": "Infrastructure number"
11
-
12
  },
13
  "train":
14
  {
@@ -16,7 +14,7 @@
16
  },
17
  "aircraft": {},
18
  "boat": {},
19
- "other": {},
20
  "unknown": {}
21
  },
22
  "Destruction / Deliberatly removed" :
@@ -24,27 +22,33 @@
24
  "hunting":
25
  {
26
  "Options": {
27
- "method" : ["shooting", "bow", "falconry", "hounds hunting", "digging up", "other", "unknown"]
 
28
  }
29
  },
30
  "trap":
31
  {
32
  "Options": {
33
- "method": ["killing trap", "pole trap", "trap cage", "corvids nasse", "net", "cage trap", "fall-trap", "glue trap", "insect trap", "other", "unknown"]
 
 
34
  }
35
  },
36
  "poisoning": {},
37
  "removal or direct capture":
38
  {
39
- "Options": {"method": ["gassing", "raptor captured at nest", "brood destruction", "traffic/trade", "capture accident", "scientific sample", "other", "unknown"]
 
 
40
  }
41
  },
42
  "fishing":
43
  {
44
- "Options": {"method" : ["drowned/tangled", "beached with capture indications", "other", "unknown"]
 
45
  }
46
  },
47
- "other": {},
48
  "unknown": {}
49
  },
50
  "Indirect destruction":
@@ -53,46 +57,48 @@
53
  {
54
 
55
  "Options": {
56
- "infrastructure" : ["electric line", "pole/pylon", "other", "unknown"]
57
  },
58
  "Extra":
59
  {
60
- "Cause": ["collision", "electrocution", "unknown"]
61
  }
62
  },
63
  "windfarm": {},
64
  "other collision":
65
  {
66
  "Options": {
67
- "object": ["window", "building", "lighthouse", "cable", "wire fence/barbed wire", "other crash", "unknown"]
68
  }
69
  },
70
  "fall":
71
  {
72
  "Options": {
73
- "location": ["chimney", "empty pole", "hole/well", "other", "unknown"]
74
  }
75
  },
76
  "development work":
77
  {
78
  "Options": {
79
- "work_type" : ["transport infrastructure", "building", "other", "unknown"]
80
  }
81
  },
82
  "pollution / contamination":
83
  {
84
  "Options": {
85
- "pollution_type" : ["oil pollution", "chemical pollution", "heavy metals", "light", "noise", "plastic ingestion", "other", "unknown"]
 
86
  },
87
  "agricultural net protection": {},
88
  "vegetal / forest work":
89
  {
90
  "Options": {
91
- "work_type" : ["clearing/mowing/plowing", "tree felling/pruning", "other", "unknown"]
 
92
  }
93
  }
94
  },
95
- "other": {},
96
  "unknown": {}
97
  },
98
  "Natural cause":
@@ -100,19 +106,20 @@
100
  "predation":
101
  {
102
  "Options": {
103
- "predator" : ["cat", "dog", "rooster/hen", "other domestic animal", "wild birds", "wild mammal", "other", "unknown"]
 
104
  }
105
  },
106
  "weather":
107
  {
108
  "Options": {
109
- "condition" : ["cold wave", "drought", "hail", "lightening", "storm", "other", "unknown"]
110
  }
111
  },
112
  "natural disaster":
113
  {
114
  "Options": {
115
- "disaster" : ["fire", "avalanche", "rock fall", "mudslide", "volcanic eruption/ashes", "other", "unknown"]
116
  }
117
  },
118
  "nest fall": {},
@@ -121,10 +128,10 @@
121
  "accidental drowing":
122
  {
123
  "Options": {
124
- "drowning_location" : ["drinking trough", "pool", "storm pool", "irrigation pool", "natural pool", "flood", "other container", "unknown"]
125
  }
126
  },
127
- "other": {},
128
  "unknown": {}
129
  },
130
  "Unknown": {}
 
3
  {
4
  "road vehicle":
5
  {
 
6
  "Options": {
7
+ "road_type" : ["highway", "main road", "secondary road", "local road/path/trail", "parking lot", "other road", "unknown road"]
8
  },
9
  "Open": "Infrastructure number"
 
10
  },
11
  "train":
12
  {
 
14
  },
15
  "aircraft": {},
16
  "boat": {},
17
+ "other transport collision": {},
18
  "unknown": {}
19
  },
20
  "Destruction / Deliberatly removed" :
 
22
  "hunting":
23
  {
24
  "Options": {
25
+ "method" : ["shooting", "bow", "falconry",
26
+ "hounds hunting", "digging up", "other hunting", "unknow hunting"]
27
  }
28
  },
29
  "trap":
30
  {
31
  "Options": {
32
+ "method": ["killing trap", "pole trap",
33
+ "trap cage", "corvids nasse", "net", "cage trap",
34
+ "fall-trap", "glue trap", "insect trap", "other trap", "unknown trap"]
35
  }
36
  },
37
  "poisoning": {},
38
  "removal or direct capture":
39
  {
40
+ "Options": {"method": ["gassing", "raptor captured at nest",
41
+ "brood destruction", "traffic/trade", "capture accident",
42
+ "scientific sample", "other removal", "unknown removal"]
43
  }
44
  },
45
  "fishing":
46
  {
47
+ "Options": {"method" : ["drowned/tangled", "beached with capture indications",
48
+ "other fishing", "unknown fishing"]
49
  }
50
  },
51
+ "other destruction": {},
52
  "unknown": {}
53
  },
54
  "Indirect destruction":
 
57
  {
58
 
59
  "Options": {
60
+ "infrastructure" : ["electric line", "pole/pylon", "other structure", "unknown structure"]
61
  },
62
  "Extra":
63
  {
64
+ "Cause": ["collision", "electrocution"]
65
  }
66
  },
67
  "windfarm": {},
68
  "other collision":
69
  {
70
  "Options": {
71
+ "object": ["window", "building", "lighthouse", "cable", "wire fence/barbed wire", "other crash", "unknown crash"]
72
  }
73
  },
74
  "fall":
75
  {
76
  "Options": {
77
+ "location": ["chimney", "empty pole", "hole/well", "other fall", "unknown fall"]
78
  }
79
  },
80
  "development work":
81
  {
82
  "Options": {
83
+ "work_type" : ["transport infrastructure", "building", "other work", "unknown work"]
84
  }
85
  },
86
  "pollution / contamination":
87
  {
88
  "Options": {
89
+ "pollution_type" : ["oil pollution", "chemical pollution", "heavy metals",
90
+ "light", "noise", "plastic ingestion", "other pollution", "unknown pollution"]
91
  },
92
  "agricultural net protection": {},
93
  "vegetal / forest work":
94
  {
95
  "Options": {
96
+ "work_type" : ["clearing/mowing/plowing", "tree felling/pruning",
97
+ "other forest work", "unknown forest work"]
98
  }
99
  }
100
  },
101
+ "other indirect destruction": {},
102
  "unknown": {}
103
  },
104
  "Natural cause":
 
106
  "predation":
107
  {
108
  "Options": {
109
+ "predator" : ["cat", "dog", "rooster/hen", "other domestic animal", "wild birds",
110
+ "wild mammal", "other predator", "unknown predator"]
111
  }
112
  },
113
  "weather":
114
  {
115
  "Options": {
116
+ "condition" : ["cold wave", "drought", "hail", "lightening", "storm", "other weather", "unknown weather"]
117
  }
118
  },
119
  "natural disaster":
120
  {
121
  "Options": {
122
+ "disaster" : ["fire", "avalanche", "rock fall", "mudslide", "volcanic eruption/ashes", "other natural disaster", "unknown natural disaster"]
123
  }
124
  },
125
  "nest fall": {},
 
128
  "accidental drowing":
129
  {
130
  "Options": {
131
+ "drowning_location" : ["drinking trough", "pool", "storm pool", "irrigation pool", "natural pool", "flood", "other location", "unknown location"]
132
  }
133
  },
134
+ "other natural cause": {},
135
  "unknown": {}
136
  },
137
  "Unknown": {}
app/assets/config/config_fields.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "fields": [
3
- "latitude",
4
- "longitude",
5
- "wounded",
6
- "dead",
7
- "circumstance",
8
- "circumstance_dropdown_level1",
9
- "circumstance_dropdown_level2",
10
- "circumstance_openfield_level2",
11
- "circumstance_dropdown_extra_level2",
12
- "behavior",
13
- "physical_changes_beak",
14
- "physical_changes_body",
15
- "physical_changes_head",
16
- "physical_changes_feathers",
17
- "physical_changes_legs",
18
- "image"
19
- ]
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/behavior/behavior_checkbox.py CHANGED
@@ -5,6 +5,7 @@ from utils.utils_visible import set_visible
5
  from validation_submission.add_json import add_data_tmp
6
 
7
  def on_select_behavior(behavior_checkbox):
 
8
  add_data_tmp("wounded_dead", "behaviors_type", behavior_checkbox)
9
 
10
  def retrieve_behavior_options_description():
 
5
  from validation_submission.add_json import add_data_tmp
6
 
7
  def on_select_behavior(behavior_checkbox):
8
+ behavior_checkbox = [behavior.lower() for behavior in behavior_checkbox]
9
  add_data_tmp("wounded_dead", "behaviors_type", behavior_checkbox)
10
 
11
  def retrieve_behavior_options_description():
app/behavior/class_behavior.py CHANGED
@@ -1,51 +1,50 @@
1
  from pydantic import BaseModel, Field
2
- from typing import Literal, List, Union
3
-
4
 
5
  class Behavior(BaseModel):
6
  type: str
7
- description: str
8
 
9
  # --- Specific Behavior classes ---
10
  class AbnormalBreathing(Behavior):
11
  type: Literal['abnormal breathing']
12
- description: Literal["Problems breathing, breathing sounds"]
13
 
14
  class CrashFalling(Behavior):
15
  type: Literal['crash, falling from the sky']
16
- description: Literal["Suddenly falling from the sky"]
17
 
18
  class Diarrhea(Behavior):
19
  type: Literal['diarrhea']
20
- description: Literal["Observed diarrhea"]
21
 
22
  class Lameness(Behavior):
23
  type: Literal['lameness']
24
- description: Literal["Apparent limping or not able to walk properly"]
25
 
26
  class Neurological(Behavior):
27
  type: Literal['neurological']
28
- description: Literal["Circling, incoordination, tremors, convulsions, fast eye movements"]
29
 
30
  class OtherAbnormalBehavior(Behavior):
31
  type: Literal['other abnormal behavior']
32
- description: Literal["Other than weakness, other than neurologic"]
33
 
34
  class UnableToFly(Behavior):
35
  type: Literal['unable to fly']
36
- description: Literal["Animal alert and tries to fly but can not take off"]
37
 
38
  class Vomiting(Behavior):
39
  type: Literal['vomiting']
40
- description: Literal["Throwing up undigested food, regurgitating"]
41
 
42
  class Weakness(Behavior):
43
  type: Literal['weakness']
44
- description: Literal["Non responsive, does not fly away when approached, lethargy"]
45
 
46
  class NoChanges(Behavior):
47
  type: Literal['no changes']
48
- description: Literal["Animal is acting normally"]
49
 
50
  # Union of all possible behaviors
51
  BehaviorType = Union[
@@ -64,4 +63,4 @@ BehaviorType = Union[
64
  # Main class that logs multiple behaviors
65
  class Behaviors(BaseModel):
66
  behaviors_radio: str # e.g., "Yes"
67
- behaviors_type: List[BehaviorType] = Field(..., discriminator='type')
 
1
  from pydantic import BaseModel, Field
2
+ from typing import Literal, List, Union, Optional
 
3
 
4
  class Behavior(BaseModel):
5
  type: str
6
+ description: Optional[str] = None # Making the description field optional
7
 
8
  # --- Specific Behavior classes ---
9
  class AbnormalBreathing(Behavior):
10
  type: Literal['abnormal breathing']
11
+ description: Optional[Literal["Problems breathing, breathing sounds"]] = None
12
 
13
  class CrashFalling(Behavior):
14
  type: Literal['crash, falling from the sky']
15
+ description: Optional[Literal["Suddenly falling from the sky"]] = None
16
 
17
  class Diarrhea(Behavior):
18
  type: Literal['diarrhea']
19
+ description: Optional[Literal["Observed diarrhea"]] = None
20
 
21
  class Lameness(Behavior):
22
  type: Literal['lameness']
23
+ description: Optional[Literal["Apparent limping or not able to walk properly"]] = None
24
 
25
  class Neurological(Behavior):
26
  type: Literal['neurological']
27
+ description: Optional[Literal["Circling, incoordination, tremors, convulsions, fast eye movements"]] = None
28
 
29
  class OtherAbnormalBehavior(Behavior):
30
  type: Literal['other abnormal behavior']
31
+ description: Optional[Literal["Other than weakness, other than neurologic"]] = None
32
 
33
  class UnableToFly(Behavior):
34
  type: Literal['unable to fly']
35
+ description: Optional[Literal["Animal alert and tries to fly but can not take off"]] = None
36
 
37
  class Vomiting(Behavior):
38
  type: Literal['vomiting']
39
+ description: Optional[Literal["Throwing up undigested food, regurgitating"]] = None
40
 
41
  class Weakness(Behavior):
42
  type: Literal['weakness']
43
+ description: Optional[Literal["Non responsive, does not fly away when approached, lethargy"]] = None
44
 
45
  class NoChanges(Behavior):
46
  type: Literal['no changes']
47
+ description: Optional[Literal["Animal is acting normally"]] = None
48
 
49
  # Union of all possible behaviors
50
  BehaviorType = Union[
 
63
  # Main class that logs multiple behaviors
64
  class Behaviors(BaseModel):
65
  behaviors_radio: str # e.g., "Yes"
66
+ behaviors_type: Optional[List[BehaviorType]] = None
app/circumstances/circumstances_dropdowns.py CHANGED
@@ -73,7 +73,7 @@ def get_options(value):
73
  def on_select(evt: gr.SelectData): # SelectData is a subclass of EventData
74
  options_label, options_dropdown, open_field, extras, extras_label = get_options(evt.value)
75
  add_data_tmp("wounded_dead",
76
- "cirumstance_type",
77
  {"type": (evt.value).lower(),
78
  "option_dropdown_label" : options_label.lower() if options_label is not None else 'NA',
79
  "open_field_label" : open_field.lower() if open_field is not None else 'NA',
@@ -98,14 +98,15 @@ def on_select(evt: gr.SelectData): # SelectData is a subclass of EventData
98
  def on_select_dropdown_level2(evt: gr.SelectData):
99
  add_data_tmp("wounded_dead",
100
  "circumstance_option_dropdown",
101
- evt.value)
102
 
103
- def on_select_openfield_level2(evt: gr.SelectData):
 
104
  add_data_tmp("wounded_dead",
105
  "circumstance_open_field",
106
- evt.value)
107
 
108
  def on_select_dropdown_extra_level2(evt: gr.SelectData):
109
  add_data_tmp("wounded_dead",
110
  "circumstance_extra",
111
- evt.value)
 
73
  def on_select(evt: gr.SelectData): # SelectData is a subclass of EventData
74
  options_label, options_dropdown, open_field, extras, extras_label = get_options(evt.value)
75
  add_data_tmp("wounded_dead",
76
+ "circumstance_type",
77
  {"type": (evt.value).lower(),
78
  "option_dropdown_label" : options_label.lower() if options_label is not None else 'NA',
79
  "open_field_label" : open_field.lower() if open_field is not None else 'NA',
 
98
  def on_select_dropdown_level2(evt: gr.SelectData):
99
  add_data_tmp("wounded_dead",
100
  "circumstance_option_dropdown",
101
+ evt.value.lower())
102
 
103
+ def on_change_openfield_level2(evt: gr.EventData):
104
+ print("Saving open field")
105
  add_data_tmp("wounded_dead",
106
  "circumstance_open_field",
107
+ evt.value.lower())
108
 
109
  def on_select_dropdown_extra_level2(evt: gr.SelectData):
110
  add_data_tmp("wounded_dead",
111
  "circumstance_extra",
112
+ evt.value.lower())
app/circumstances/class_circumstance.py CHANGED
@@ -9,7 +9,9 @@ class CircumstanceTypeBase(BaseModel):
9
  class RoadVehicleCollision(CircumstanceTypeBase):
10
  type: Literal['road_vehicle']
11
  infrastructure_number: Optional[str] = None
12
- road_type: Literal['highway', 'main road', 'secondary road', 'local road/path/trail', 'parking lot', 'other', 'unknown']
 
 
13
 
14
  class TrainCollision(CircumstanceTypeBase):
15
  type: Literal['train']
@@ -30,22 +32,28 @@ class UnknownTransportCollision(CircumstanceTypeBase):
30
  # Destruction / Deliberately removed
31
  class HuntingDestruction(CircumstanceTypeBase):
32
  type: Literal['hunting']
33
- method: Literal['shooting', 'bow', 'falconry', 'hounds hunting', 'digging up', 'other', 'unknown']
 
 
34
 
35
  class TrapDestruction(CircumstanceTypeBase):
36
  type: Literal['trap']
37
- method: Literal['killing trap', 'pole trap', 'trap cage', 'corvids nasse', 'net', 'cage trap', 'fall-trap', 'glue trap', 'insect trap', 'other', 'unknown']
 
 
38
 
39
  class PoisoningDestruction(CircumstanceTypeBase):
40
  type: Literal['poisoning']
41
 
42
  class RemovalDestruction(CircumstanceTypeBase):
43
  type: Literal['removal or direct capture']
44
- method: Literal['gassing', 'raptor captured at nest', 'brood destruction', 'traffic/trade', 'capture accident', 'scientific sample', 'other', 'unknown']
 
 
45
 
46
  class FishingDestruction(CircumstanceTypeBase):
47
  type: Literal['fishing']
48
- method: Literal['drowned/tangled', 'beached with capture indications', 'other', 'unknown']
49
 
50
  class OtherDestruction(CircumstanceTypeBase):
51
  type: Literal['other destruction']
@@ -56,34 +64,38 @@ class UnknownDestruction(CircumstanceTypeBase):
56
  # Indirect destruction
57
  class PylonElectricGridDestruction(CircumstanceTypeBase):
58
  type: Literal['pylone and electric grid']
59
- infrastructure: Literal['electric line', 'pole/pylon', 'other', 'unknown']
60
- cause: Literal['collision', 'electrocution', 'unknown']
61
 
62
  class WindfarmDestruction(CircumstanceTypeBase):
63
  type: Literal['windfarm']
64
 
65
  class OtherCollisionDestruction(CircumstanceTypeBase):
66
  type: Literal['other collision']
67
- object: Literal['window', 'building', 'lighthouse', 'cable', 'wire fence/barbed wire', 'other crash', 'unknown']
 
68
 
69
  class FallDestruction(CircumstanceTypeBase):
70
  type: Literal['fall']
71
- location: Literal['chimney', 'empty pole', 'hole/well', 'other', 'unknown']
72
 
73
  class DevelopmentWorkDestruction(CircumstanceTypeBase):
74
  type: Literal['development work']
75
- work_type: Literal['transport infrastructure', 'building', 'other', 'unknown']
76
 
77
  class PollutionContaminationDestruction(CircumstanceTypeBase):
78
  type: Literal['pollution / contamination']
79
- pollution_type: Literal['oil pollution', 'chemical pollution', 'heavy metals', 'light', 'noise', 'plastic ingestion', 'other', 'unknown']
 
 
80
 
81
  class AgriculturalNetProtectionDestruction(CircumstanceTypeBase):
82
  type: Literal['agricultural net protection']
83
 
84
  class VegetalForestWorkDestruction(CircumstanceTypeBase):
85
  type: Literal['vegetal / forest work']
86
- work_type: Literal['clearing/mowing/plowing', 'tree felling/pruning', 'other', 'unknown']
 
87
 
88
  class OtherIndirectDestruction(CircumstanceTypeBase):
89
  type: Literal['other indirect desctruction']
@@ -94,15 +106,21 @@ class UnknownIndirectDestruction(CircumstanceTypeBase):
94
  # Natural cause
95
  class Predation(CircumstanceTypeBase):
96
  type: Literal['predation']
97
- predator: Literal['cat', 'dog', 'rooster/hen', 'other domestic animal', 'wild birds', 'wild mammal', 'other', 'unknown']
 
 
98
 
99
  class Weather(CircumstanceTypeBase):
100
  type: Literal['weather']
101
- condition: Literal['cold wave', 'drought', 'hail', 'lightening', 'storm', 'other', 'unknown']
 
 
102
 
103
  class NaturalDisaster(CircumstanceTypeBase):
104
  type: Literal['natural disaster']
105
- disaster: Literal['fire', 'avalanche', 'rock fall', 'mudslide', 'volcanic eruption/ashes', 'other', 'unknown']
 
 
106
 
107
  class NestFall(CircumstanceTypeBase):
108
  type: Literal['nest fall']
@@ -115,7 +133,10 @@ class DiseaseParasite(CircumstanceTypeBase):
115
 
116
  class AccidentalDrowning(CircumstanceTypeBase):
117
  type: Literal['accidental drowning']
118
- drowning_location: Literal['drinking trough', 'pool', 'storm pool', 'irrigation pool', 'natural pool', 'flood', 'other container', 'unknown']
 
 
 
119
 
120
  class OtherNaturalCause(CircumstanceTypeBase):
121
  type: Literal['other natural cause']
@@ -166,9 +187,9 @@ CircumstanceType = Union[
166
 
167
  # Main Circumstance class
168
  class Circumstances(BaseModel):
169
- circumstance: str # e.g., "COLLISION"
170
  circumstance_radio: str # e.g., "Yes"
171
- circumstance_type: CircumstanceType = Field(..., discriminator='type')
 
172
 
173
 
174
  # Example usage
 
9
  class RoadVehicleCollision(CircumstanceTypeBase):
10
  type: Literal['road_vehicle']
11
  infrastructure_number: Optional[str] = None
12
+ road_type: Literal['highway', 'main road',
13
+ 'secondary road', 'local road/path/trail',
14
+ 'parking lot']
15
 
16
  class TrainCollision(CircumstanceTypeBase):
17
  type: Literal['train']
 
32
  # Destruction / Deliberately removed
33
  class HuntingDestruction(CircumstanceTypeBase):
34
  type: Literal['hunting']
35
+ method: Literal['shooting', 'bow', 'falconry',
36
+ 'hounds hunting', 'digging up',
37
+ "other hunting", "unknow hunting"]
38
 
39
  class TrapDestruction(CircumstanceTypeBase):
40
  type: Literal['trap']
41
+ method: Literal['killing trap', 'pole trap', 'trap cage', 'corvids nasse',
42
+ 'net', 'cage trap', 'fall-trap', 'glue trap', 'insect trap',
43
+ "other trap", "unknown trap"]
44
 
45
  class PoisoningDestruction(CircumstanceTypeBase):
46
  type: Literal['poisoning']
47
 
48
  class RemovalDestruction(CircumstanceTypeBase):
49
  type: Literal['removal or direct capture']
50
+ method: Literal['gassing', 'raptor captured at nest', 'brood destruction',
51
+ 'traffic/trade', 'capture accident', 'scientific sample',
52
+ "other removal", "unknown removal"]
53
 
54
  class FishingDestruction(CircumstanceTypeBase):
55
  type: Literal['fishing']
56
+ method: Literal['drowned/tangled', 'beached with capture indications', "other fishing", "unknown fishing"]
57
 
58
  class OtherDestruction(CircumstanceTypeBase):
59
  type: Literal['other destruction']
 
64
  # Indirect destruction
65
  class PylonElectricGridDestruction(CircumstanceTypeBase):
66
  type: Literal['pylone and electric grid']
67
+ infrastructure: Literal['electric line', 'pole/pylon', "other structure", "unknown structure"]
68
+ cause: Literal['collision', 'electrocution']
69
 
70
  class WindfarmDestruction(CircumstanceTypeBase):
71
  type: Literal['windfarm']
72
 
73
  class OtherCollisionDestruction(CircumstanceTypeBase):
74
  type: Literal['other collision']
75
+ object: Literal['window', 'building', 'lighthouse',
76
+ 'cable', 'wire fence/barbed wire', 'other crash', 'unknown crash']
77
 
78
  class FallDestruction(CircumstanceTypeBase):
79
  type: Literal['fall']
80
+ location: Literal['chimney', 'empty pole', 'hole/well', 'other fall', 'unknown fall']
81
 
82
  class DevelopmentWorkDestruction(CircumstanceTypeBase):
83
  type: Literal['development work']
84
+ work_type: Literal['transport infrastructure', 'building', 'other work', 'unknown work']
85
 
86
  class PollutionContaminationDestruction(CircumstanceTypeBase):
87
  type: Literal['pollution / contamination']
88
+ pollution_type: Literal['oil pollution', 'chemical pollution', 'heavy metals',
89
+ 'light', 'noise',
90
+ 'plastic ingestion', 'other pollution', 'unknown pollution']
91
 
92
  class AgriculturalNetProtectionDestruction(CircumstanceTypeBase):
93
  type: Literal['agricultural net protection']
94
 
95
  class VegetalForestWorkDestruction(CircumstanceTypeBase):
96
  type: Literal['vegetal / forest work']
97
+ work_type: Literal['clearing/mowing/plowing', 'tree felling/pruning',
98
+ 'other forest work', 'unknown forest work']
99
 
100
  class OtherIndirectDestruction(CircumstanceTypeBase):
101
  type: Literal['other indirect desctruction']
 
106
  # Natural cause
107
  class Predation(CircumstanceTypeBase):
108
  type: Literal['predation']
109
+ predator: Literal['cat', 'dog', 'rooster/hen',
110
+ 'other domestic animal', 'wild birds',
111
+ 'wild mammal', 'other predator', 'unknown predator']
112
 
113
  class Weather(CircumstanceTypeBase):
114
  type: Literal['weather']
115
+ condition: Literal['cold wave', 'drought', 'hail',
116
+ 'lightening', 'storm',
117
+ 'other weather', 'unknown weather']
118
 
119
  class NaturalDisaster(CircumstanceTypeBase):
120
  type: Literal['natural disaster']
121
+ disaster: Literal['fire', 'avalanche', 'rock fall',
122
+ 'mudslide', 'volcanic eruption/ashes',
123
+ 'other natural disaster', 'unknown natural disaster']
124
 
125
  class NestFall(CircumstanceTypeBase):
126
  type: Literal['nest fall']
 
133
 
134
  class AccidentalDrowning(CircumstanceTypeBase):
135
  type: Literal['accidental drowning']
136
+ drowning_location: Literal['drinking trough', 'pool',
137
+ 'storm pool', 'irrigation pool',
138
+ 'natural pool', 'flood',
139
+ 'other location', 'unknown location']
140
 
141
  class OtherNaturalCause(CircumstanceTypeBase):
142
  type: Literal['other natural cause']
 
187
 
188
  # Main Circumstance class
189
  class Circumstances(BaseModel):
 
190
  circumstance_radio: str # e.g., "Yes"
191
+ circumstance: Optional[str] = None # e.g., "COLLISION"
192
+ circumstance_type: Optional[CircumstanceType] = Field(None, discriminator='type')
193
 
194
 
195
  # Example usage
app/classes.py CHANGED
@@ -1,5 +1,10 @@
1
  from pydantic import BaseModel, Field
2
- from typing import Literal, List, Union, Optional
 
 
 
 
 
3
 
4
  from behavior.class_behavior import Behaviors
5
  from circumstances.class_circumstance import Circumstances
@@ -9,22 +14,33 @@ from geolocalisation.class_geolocalisation import Geolocalisation
9
 
10
  class Wounded(BaseModel):
11
  circumstances: Circumstances
12
- behaviors: List[Behaviors]
13
- physical_anomalies: List[PhysicalAnomalies]
14
- follow_up_events: List[FollowUpEvents]
15
 
16
  class Dead(BaseModel):
17
- circumstances: List[Circumstances]
18
- physical_anomalies: List[PhysicalAnomalies]
19
- follow_up_events: List[FollowUpEvents]
20
-
21
- class Image(BaseModel):
22
- image: List[float]
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  class Report(BaseModel):
25
- image: Image
 
26
  geolocalisation: Geolocalisation
27
- wounded_state: bool
28
  wounded: Optional[Wounded] = None
29
- dead_state: bool
30
  dead: Optional[Dead] = None
 
1
  from pydantic import BaseModel, Field
2
+ from typing import Optional
3
+ import numpy as np
4
+ from PIL import Image
5
+ import io
6
+ import base64
7
+ import uuid
8
 
9
  from behavior.class_behavior import Behaviors
10
  from circumstances.class_circumstance import Circumstances
 
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)
31
+ img = Image.fromarray(img_array)
32
+ # Save the image to a bytes buffer
33
+ buffer = io.BytesIO()
34
+ img.save(buffer, format="PNG")
35
+ buffer.seek(0)
36
+ base64_str = base64.b64encode(buffer.read()).decode('utf-8')
37
+ return cls(image=base64_str)
38
+
39
  class Report(BaseModel):
40
+ identifier: str
41
+ image: ImageBase64
42
  geolocalisation: Geolocalisation
43
+ wounded_state: str
44
  wounded: Optional[Wounded] = None
45
+ dead_state: str
46
  dead: Optional[Dead] = None
app/dead.py CHANGED
@@ -1,12 +1,12 @@
1
  import gradio as gr
2
  from circumstances.circumstances import create_circumstances
3
  from follow_up.followup_events import create_followup_dropdowns, create_followup_open
4
- from validation_submission.add_json import add_data_to_individual
5
 
6
  def show_section_dead(visible):
7
  if visible==True:
8
- add_data_to_individual("dead_bool", "True")
9
- add_data_to_individual("wounded_bool", "False")
10
 
11
  with gr.Column(visible=visible, elem_id="dead") as section_dead:
12
  gr.Markdown("# Dead Animal")
 
1
  import gradio as gr
2
  from circumstances.circumstances import create_circumstances
3
  from follow_up.followup_events import create_followup_dropdowns, create_followup_open
4
+ from validation_submission.add_json import add_data_to_individual
5
 
6
  def show_section_dead(visible):
7
  if visible==True:
8
+ add_data_to_individual("wounded_state", "No")
9
+ add_data_to_individual("dead_state", "Yes")
10
 
11
  with gr.Column(visible=visible, elem_id="dead") as section_dead:
12
  gr.Markdown("# Dead Animal")
app/follow_up/class_follow_up.py CHANGED
@@ -3,45 +3,40 @@ from typing import Literal, Union, Optional, List
3
 
4
  # --- Event follow-up classes ---
5
 
6
- # Animal collected event
7
  class AnimalCollectedEvent(BaseModel):
8
  type: Literal['animal collected']
9
- collected: Literal['Yes', 'No']
10
 
11
- # Recipient event
12
  class RecipientEvent(BaseModel):
13
  type: Literal['recipient']
14
- recipient: Literal['Veterinary', 'Care center', 'Local Museum', 'National Museum', 'Other']
15
-
16
- # Radiography event
 
17
  class RadiographyEvent(BaseModel):
18
  type: Literal['radiography']
19
- radiography: Literal['Yes', 'No', 'Unknown']
20
 
21
- # Given answer event
22
  class GivenAnswerEvent(BaseModel):
23
  type: Literal['given answer']
24
  answer: Literal[
25
- 'Nothing',
26
- 'Complaint against X',
27
- 'Complaint',
28
- 'Police call',
29
- 'Discussion with the speaker',
30
- 'Press release',
31
- 'Unknown'
32
  ]
33
 
34
- # Name of recipient/museum (open text field)
35
  class NameOfRecipientEvent(BaseModel):
36
  type: Literal['recipient name']
37
- name: str # Open text field for entering the name
38
 
39
- # Collection reference (open text field)
40
  class CollectionReferenceEvent(BaseModel):
41
  type: Literal['collection reference']
42
- reference: str # Open text field for entering the reference
43
 
44
- # Union of all possible follow-up event types
45
  FollowUpEventType = Union[
46
  AnimalCollectedEvent,
47
  RecipientEvent,
@@ -51,7 +46,5 @@ FollowUpEventType = Union[
51
  CollectionReferenceEvent
52
  ]
53
 
54
- # Main class that logs multiple follow-up events
55
  class FollowUpEvents(BaseModel):
56
- follow_up_events: List[FollowUpEventType]
57
-
 
3
 
4
  # --- Event follow-up classes ---
5
 
 
6
  class AnimalCollectedEvent(BaseModel):
7
  type: Literal['animal collected']
8
+ collected: Literal['yes', 'no']
9
 
 
10
  class RecipientEvent(BaseModel):
11
  type: Literal['recipient']
12
+ recipient: Literal['veterinary', 'care center',
13
+ 'local museum', 'national museum',
14
+ 'other']
15
+
16
  class RadiographyEvent(BaseModel):
17
  type: Literal['radiography']
18
+ radiography: Literal['yes', 'no', 'unknown']
19
 
 
20
  class GivenAnswerEvent(BaseModel):
21
  type: Literal['given answer']
22
  answer: Literal[
23
+ 'nothing',
24
+ 'complaint against x',
25
+ 'complaint',
26
+ 'police call',
27
+ 'discussion with the speaker',
28
+ 'press release',
29
+ 'unknown'
30
  ]
31
 
 
32
  class NameOfRecipientEvent(BaseModel):
33
  type: Literal['recipient name']
34
+ name: str
35
 
 
36
  class CollectionReferenceEvent(BaseModel):
37
  type: Literal['collection reference']
38
+ reference: str
39
 
 
40
  FollowUpEventType = Union[
41
  AnimalCollectedEvent,
42
  RecipientEvent,
 
46
  CollectionReferenceEvent
47
  ]
48
 
 
49
  class FollowUpEvents(BaseModel):
50
+ follow_up_events: Optional[List[FollowUpEventType]] = None
 
app/gallery.py CHANGED
@@ -1,84 +1,18 @@
1
  import gradio as gr
2
  import pandas as pd
3
- import numpy as np
4
- from dotenv import load_dotenv
5
- import os
6
 
7
- from utils.utils_config import load_config
8
- from validation_submission.get_json import get_json_one_individual
9
- from validation_submission.submission import save_to_all_individuals
10
- from validation_submission.validation import validate_individual
11
 
 
12
 
13
- load_dotenv()
14
- PATH = os.getcwd() + "/"
15
- PATH_ASSETS = os.getenv('PATH_ASSETS')
16
- PATH_CONFIG = PATH + PATH_ASSETS + "config/"
17
 
18
- def get_headers():
19
- headers_config = load_config(PATH_CONFIG + "config_headers.json")
20
- headers = headers_config["headers"]
21
- return headers
22
-
23
- def get_fields():
24
- fields_config = load_config(PATH_CONFIG + "config_fields.json")
25
- fields = fields_config["fields"]
26
- return fields
27
-
28
- def match_data_to_fields(fields, one_individual):
29
- new_row = {}
30
- for key in fields:
31
- if key in one_individual:
32
- if key=="image":
33
- new_row[key] = one_individual[key]
34
- elif type(one_individual[key])==list:
35
- new_row[key] = ' , '.join(one_individual[key])
36
- else:
37
- new_row[key] = one_individual[key]
38
- else:
39
- new_row[key] = "NA"
40
- return new_row
41
-
42
-
43
-
44
- def process_animals(all_animals):
45
- processed_animals = []
46
- for _, animal in all_animals.items():
47
- image = np.array(animal["image"])
48
- caption = []
49
- for key, val in animal.items():
50
- if key!="image":
51
- if key=="latitude":
52
- caption.extend([
53
- " | Latitude: " + str(animal["latitude"])])
54
- elif key=="longitude":
55
- caption.extend([
56
- " | Longitude: " + str(animal["longitude"])])
57
- elif key=="wounded" and val=="True":
58
- caption.extend([" | Wounded: " + animal["wounded"]])
59
- elif key=="dead" and val=="True":
60
- caption.extend([" | Dead: " + animal["dead"]])
61
- # elif key=="circumstance":
62
- # caption.extend([" | Circumstances: " ,
63
- # animal["circumstance"],
64
- # animal["circumstance_dropdown_level1"],
65
- # animal["circumstance_dropdown_level2"],
66
- # animal["circumstance_openfield_level2"],
67
- # animal["circumstance_dropdown_extra_level2"]])
68
- # elif key=="behavior":
69
- # caption.extend([" | Behavior: ", animal[key]])
70
- # elif "physical_changes" in key:
71
- # if not(" | Physical Changes: " in caption) :
72
- # caption.extend([" | Physical Changes: ",
73
- # "Beak: " + animal["physical_changes_beak"],
74
- # "Body: " + animal["physical_changes_body"],
75
- # "Head: " + animal["physical_changes_head"],
76
- # "Feathers: " + animal["physical_changes_feathers"],
77
- # "Legs: " + animal["physical_changes_legs"]])
78
- caption_str = " ".join(caption)
79
- animal = (image, caption_str)
80
- processed_animals.append(animal)
81
- return processed_animals
82
 
83
  def set_gallery_size(len_animals):
84
  if len_animals < 10:
@@ -89,62 +23,92 @@ def set_gallery_size(len_animals):
89
  num_rows = len_animals/(num_cols)
90
  return num_cols, num_rows
91
 
92
- def save_individual_to_gallery(gallery):
93
- validate_individual()
94
- one_individual = get_json_one_individual()
95
- fields = get_fields()
96
- one_individual_matched = match_data_to_fields(fields, one_individual)
97
- all_animals = save_to_all_individuals(one_individual_matched)
98
- num_cols, num_rows = set_gallery_size(len(all_animals))
99
- processed_animals = process_animals(all_animals)
 
 
 
 
 
 
 
 
 
 
 
 
100
  gallery = gr.Gallery(
101
  label="Gallery of Records", elem_id="gallery",
102
  columns=[num_cols], rows=[num_rows],
103
- value=processed_animals,
104
  object_fit="contain", height="auto", interactive=False)
105
  return gallery
106
-
107
- # def save_individual_to_df(df):
108
- # fields = get_fields()
109
- # one_individual = get_json_one_individual()
110
- # headers = get_headers()
111
- # new_row = match_data_to_fields(fields, one_individual)
112
- # new_row = format_row(new_row, headers)
113
- # new_row_df = pd.DataFrame([new_row], columns=headers)
114
- # df_new = pd.concat([df, new_row_df], ignore_index=True)
115
- # df_gr = gr.DataFrame(visible=True,
116
- # value=df_new,
117
- # headers=headers)
118
- # return df_gr
119
 
120
- # def save_and_rest_df(df):
121
- # save_all_animals(df)
122
- # df = gr.Dataframe(fields=get_fields(),
123
- # visible=False)
124
- # return df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
- # def format_row(new_row, headers):
127
- # formatted_row = {}
128
- # #formatted_row["image"] = new_row["image"]
129
- # for header in headers:
130
- # if header=="location":
131
- # formatted_row[header] = " | ".join(["Latitude: " + str(new_row["latitude"]),
132
- # "Longitude: " + str(new_row["longitude"])])
133
- # elif header=="state":
134
- # formatted_row[header] = " | ".join(["Wounded: " + new_row["wounded"],
135
- # "Dead: " + new_row["dead"]])
136
- # elif header=="circumstance":
137
- # formatted_row[header] = " | ".join([new_row["circumstance"],
138
- # new_row["circumstance_dropdown_level1"],
139
- # new_row["circumstance_dropdown_level2"],
140
- # new_row["circumstance_openfield_level2"],
141
- # new_row["circumstance_dropdown_extra_level2"]])
142
- # elif header=="behavior":
143
- # formatted_row[header] = new_row[header]
144
- # elif header=="physical_changes":
145
- # formatted_row[header] = " | ".join([new_row["physical_changes_beak"],
146
- # new_row["physical_changes_body"],
147
- # new_row["physical_changes_head"],
148
- # new_row["physical_changes_feathers"],
149
- # new_row["physical_changes_legs"]])
150
- # return list(formatted_row.values())
 
1
  import gradio as gr
2
  import pandas as pd
 
 
 
3
 
4
+ from validation_submission.submission import validate_save_individual
5
+ from validation_submission.get_json import get_json_all_individuals
 
 
6
 
7
+ HEADERS = ["Identifier", "Location", "Wounded", "Dead"]
8
 
 
 
 
 
9
 
10
+ from PIL import Image
11
+ from io import BytesIO
12
+ import base64
13
+ def convert_image(image_base64_str):
14
+ im = Image.open(BytesIO(base64.b64decode(image_base64_str)))
15
+ return im
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  def set_gallery_size(len_animals):
18
  if len_animals < 10:
 
23
  num_rows = len_animals/(num_cols)
24
  return num_cols, num_rows
25
 
26
+ def save_individual_to_gallery(gallery, df):
27
+ validate_save_individual()
28
+ all_animals = get_json_all_individuals()
29
+ gallery_animals = process_animals_for_gallery(all_animals)
30
+ gallery = make_gallery(gallery_animals)
31
+ df_animals = process_animals_for_df(all_animals)
32
+ df = make_df(df_animals)
33
+ return gallery, df
34
+
35
+ def process_animals_for_gallery(all_animals):
36
+ gallery_animals = []
37
+ for _, animal in all_animals.items():
38
+ image = convert_image(animal["image"]["image"])
39
+ caption = animal["identifier"]
40
+ animal = (image, caption)
41
+ gallery_animals.append(animal)
42
+ return gallery_animals
43
+
44
+ def make_gallery(gallery_animals):
45
+ num_cols, num_rows = set_gallery_size(len(gallery_animals))
46
  gallery = gr.Gallery(
47
  label="Gallery of Records", elem_id="gallery",
48
  columns=[num_cols], rows=[num_rows],
49
+ value=gallery_animals,
50
  object_fit="contain", height="auto", interactive=False)
51
  return gallery
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ def keep_only_values(dict_to_filter):
54
+ info_text = ""
55
+ values_to_ignore = ["Yes", "No", "NA"]
56
+ if dict_to_filter:
57
+ for key, val in dict_to_filter.items():
58
+ if type(val) is dict:
59
+ subset_text = keep_only_values(val)
60
+ info_text += f"{subset_text}"
61
+ elif type(val) is list:
62
+ for item in val:
63
+ if type(item) is dict:
64
+ subset_text = keep_only_values(item)
65
+ info_text += f"{subset_text}"
66
+ elif (val is not None) and (type(val) is not bool) and (val not in values_to_ignore):
67
+ info_text += f" {key} : {val} |"
68
+ else:
69
+ print("Ignoring value: ", val)
70
+ print("Associated key: ", key)
71
+ else:
72
+ info_text = "NaN"
73
+ return info_text
74
+
75
+
76
+ def process_animals_for_df(all_animals):
77
+ df_animals = {}
78
+ identifiers =[]
79
+ geo =[]
80
+ wounded =[]
81
+ dead =[]
82
+ for _, animal in all_animals.items():
83
+ identifier_value = animal["identifier"]
84
+ identifiers.append(identifier_value)
85
+ geo_dict = animal["geolocalisation"]
86
+ geo_values = keep_only_values(geo_dict)
87
+ geo.append(geo_values)
88
+ wounded_dict = animal["wounded"]
89
+ wounded_values = keep_only_values(wounded_dict)
90
+ wounded.append(wounded_values)
91
+ dead_dict = animal["dead"]
92
+ dead_values = keep_only_values(dead_dict)
93
+ dead.append(dead_values)
94
+ df_animals["Identifier"] = identifiers
95
+ df_animals["Location"] = geo
96
+ df_animals["Wounded"] = wounded
97
+ df_animals["Dead"] = dead
98
+ return df_animals
99
+
100
+
101
+ def make_df(df_animals):
102
+ df = pd.DataFrame.from_dict(df_animals)
103
+ styled_df = df.style.set_properties(**{
104
+ 'max-width': '100px', # Adjust width as needed
105
+ 'white-space': 'normal', # Allows text to wrap to the next line
106
+ 'word-wrap': 'break-word' # Breaks long words to fit within the width
107
+ })
108
+ df_gradio = gr.DataFrame(visible=True,
109
+ value=df,
110
+ headers=HEADERS, interactive=False)
111
+ # df = gr.DataFrame(visible=True,
112
+ # headers=HEADERS)
113
+ return df_gradio
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/main_multianimal.py CHANGED
@@ -29,8 +29,7 @@ with gr.Blocks(theme=theme, css=css) as demo:
29
  with gr.Row():
30
  show_modal = gr.Button("Add an Animal", scale=3)
31
  submit_button = gr.Button("Submit All Animals", scale=1)
32
- # df = gr.Dataframe(headers=get_headers(),
33
- # visible=False)
34
  gallery = gr.Gallery(
35
  label="Gallery of Records", elem_id="gallery",
36
  columns=[1], rows=[1],
@@ -126,6 +125,7 @@ with gr.Blocks(theme=theme, css=css) as demo:
126
  fe_collection_dropdown_wounded, fe_recepient_dropdown_wounded, fe_radio_dropdown_wounded, fe_answer_dropdown_wounded, \
127
  fe_name_recipient_wounded, fe_collection_ref_wounded \
128
  ])
 
129
  # ---------------------------------------------------------
130
  # Wounded Button Logic
131
  partial_show_section_wounded = partial(show_section_wounded, True)
@@ -149,6 +149,9 @@ with gr.Blocks(theme=theme, css=css) as demo:
149
  fe_collection_dropdown_dead, fe_recepient_dropdown_dead, fe_radio_dropdown_dead, fe_answer_dropdown_dead, \
150
  fe_name_recipient_dead, fe_collection_ref_dead \
151
  ])
 
 
 
152
  # ---------------------------------------------------------
153
  # Dropdowns Dead
154
  button_collision_dead.click(dropdown_collision,
@@ -159,7 +162,7 @@ with gr.Blocks(theme=theme, css=css) as demo:
159
 
160
  dropdown_dead.select(on_select, None, [dropdown_level2_dead, openfield_level2_dead, dropdown_extra_level2_dead])
161
  dropdown_level2_dead.select(on_select_dropdown_level2)
162
- openfield_level2_dead.select(on_select_openfield_level2)
163
  dropdown_extra_level2_dead.select(on_select_dropdown_extra_level2)
164
  # ---------------------------------------------------------
165
  # Radio Cause Wounded
@@ -178,7 +181,7 @@ with gr.Blocks(theme=theme, css=css) as demo:
178
 
179
  dropdown_wounded.select(on_select, None, [dropdown_level2_wounded, openfield_level2_wounded, dropdown_extra_level2_wounded])
180
  dropdown_level2_wounded.select(on_select_dropdown_level2)
181
- openfield_level2_wounded.select(on_select_openfield_level2)
182
  dropdown_extra_level2_wounded.select(on_select_dropdown_extra_level2)
183
  # ---------------------------------------------------------
184
  # Radio Behavior Wounded
@@ -255,8 +258,8 @@ with gr.Blocks(theme=theme, css=css) as demo:
255
  # inputs=[df],
256
  # outputs=[df])
257
  button_df.click(save_individual_to_gallery,
258
- inputs=[gallery],
259
- outputs=[gallery]
260
  )
261
  button_df.click(lambda: Modal(visible=False), None, modal)
262
 
@@ -266,7 +269,6 @@ with gr.Blocks(theme=theme, css=css) as demo:
266
  show_modal.click(create_json_one_individual)
267
  show_modal.click(create_tmp)
268
  #submit_button.click(save_and_rest_df, inputs=[df], outputs=[df])
269
- submit_button.click(save_individual_to_gallery)
270
 
271
 
272
 
 
29
  with gr.Row():
30
  show_modal = gr.Button("Add an Animal", scale=3)
31
  submit_button = gr.Button("Submit All Animals", scale=1)
32
+ df = gr.Dataframe(headers=["Identifier", "Location", "Wounded", "Dead"], visible=False, interactive=False)
 
33
  gallery = gr.Gallery(
34
  label="Gallery of Records", elem_id="gallery",
35
  columns=[1], rows=[1],
 
125
  fe_collection_dropdown_wounded, fe_recepient_dropdown_wounded, fe_radio_dropdown_wounded, fe_answer_dropdown_wounded, \
126
  fe_name_recipient_wounded, fe_collection_ref_wounded \
127
  ])
128
+
129
  # ---------------------------------------------------------
130
  # Wounded Button Logic
131
  partial_show_section_wounded = partial(show_section_wounded, True)
 
149
  fe_collection_dropdown_dead, fe_recepient_dropdown_dead, fe_radio_dropdown_dead, fe_answer_dropdown_dead, \
150
  fe_name_recipient_dead, fe_collection_ref_dead \
151
  ])
152
+ def save_wounded_state():
153
+ add_data_tmp("wounded_dead", "wounded", "Yes")
154
+ butt_wounded.click(save_wounded_state())
155
  # ---------------------------------------------------------
156
  # Dropdowns Dead
157
  button_collision_dead.click(dropdown_collision,
 
162
 
163
  dropdown_dead.select(on_select, None, [dropdown_level2_dead, openfield_level2_dead, dropdown_extra_level2_dead])
164
  dropdown_level2_dead.select(on_select_dropdown_level2)
165
+ openfield_level2_dead.input(on_change_openfield_level2)
166
  dropdown_extra_level2_dead.select(on_select_dropdown_extra_level2)
167
  # ---------------------------------------------------------
168
  # Radio Cause Wounded
 
181
 
182
  dropdown_wounded.select(on_select, None, [dropdown_level2_wounded, openfield_level2_wounded, dropdown_extra_level2_wounded])
183
  dropdown_level2_wounded.select(on_select_dropdown_level2)
184
+ openfield_level2_wounded.select(on_change_openfield_level2)
185
  dropdown_extra_level2_wounded.select(on_select_dropdown_extra_level2)
186
  # ---------------------------------------------------------
187
  # Radio Behavior Wounded
 
258
  # inputs=[df],
259
  # outputs=[df])
260
  button_df.click(save_individual_to_gallery,
261
+ inputs=[gallery, df],
262
+ outputs=[gallery, df]
263
  )
264
  button_df.click(lambda: Modal(visible=False), None, modal)
265
 
 
269
  show_modal.click(create_json_one_individual)
270
  show_modal.click(create_tmp)
271
  #submit_button.click(save_and_rest_df, inputs=[df], outputs=[df])
 
272
 
273
 
274
 
app/physical/class_physical.py CHANGED
@@ -16,52 +16,52 @@ CommonAnomalies = Literal[
16
  # --- Beak-related Anomalies ---
17
  class BeakAnomaly(BaseModel):
18
  type: Literal['beak']
19
- anomaly_type: Literal[
20
  'adhesion',
21
  'deformation',
22
  CommonAnomalies
23
- ]
24
 
25
  # --- Body-related Anomalies ---
26
  class BodyAnomaly(BaseModel):
27
  type: Literal['body']
28
- anomaly_type: Literal[
29
  'emaciation',
30
  'fluffed up',
31
  'stained feathers',
32
  CommonAnomalies
33
- ]
34
 
35
  # --- Legs-related Anomalies ---
36
  class LegAnomaly(BaseModel):
37
  type: Literal['legs']
38
- anomaly_type: Literal[
39
  'missing limb',
40
  'deformation',
41
  CommonAnomalies
42
- ]
43
 
44
  # --- Feathers/Wings/Tail-related Anomalies ---
45
  class FeathersWingsTailAnomaly(BaseModel):
46
  type: Literal['feathers/wings/tail']
47
- anomaly_type: Literal[
48
  'fluffed up',
49
  'feather abnormalities',
50
  'stained feathers',
51
  'abnormal wing posture',
52
  'missing limb',
53
  CommonAnomalies
54
- ]
55
 
56
  # --- Head-related Anomalies (including eyes) ---
57
  class HeadAnomaly(BaseModel):
58
- type: Literal['head']
59
- anomaly_type: Literal[
60
  'ear changes',
61
  'eye changes',
62
  'tilted head',
63
  CommonAnomalies
64
- ]
65
 
66
 
67
  # Union of all possible anomaly types for specific body parts
@@ -76,4 +76,4 @@ AnomalyType = Union[
76
  # Main PhysicalAnomaly class that logs anomalies across different body parts
77
  class PhysicalAnomalies(BaseModel):
78
  physical_radio: str
79
- physical_anomalies_type: List[AnomalyType] = Field(..., discriminator='type')
 
16
  # --- Beak-related Anomalies ---
17
  class BeakAnomaly(BaseModel):
18
  type: Literal['beak']
19
+ anomaly_type: List[Literal[
20
  'adhesion',
21
  'deformation',
22
  CommonAnomalies
23
+ ]]
24
 
25
  # --- Body-related Anomalies ---
26
  class BodyAnomaly(BaseModel):
27
  type: Literal['body']
28
+ anomaly_type: List[Literal[
29
  'emaciation',
30
  'fluffed up',
31
  'stained feathers',
32
  CommonAnomalies
33
+ ]]
34
 
35
  # --- Legs-related Anomalies ---
36
  class LegAnomaly(BaseModel):
37
  type: Literal['legs']
38
+ anomaly_type: List[Literal[
39
  'missing limb',
40
  'deformation',
41
  CommonAnomalies
42
+ ]]
43
 
44
  # --- Feathers/Wings/Tail-related Anomalies ---
45
  class FeathersWingsTailAnomaly(BaseModel):
46
  type: Literal['feathers/wings/tail']
47
+ anomaly_type: List[Literal[
48
  'fluffed up',
49
  'feather abnormalities',
50
  'stained feathers',
51
  'abnormal wing posture',
52
  'missing limb',
53
  CommonAnomalies
54
+ ]]
55
 
56
  # --- Head-related Anomalies (including eyes) ---
57
  class HeadAnomaly(BaseModel):
58
+ type: Literal['head incl. eyes']
59
+ anomaly_type: List[Literal[
60
  'ear changes',
61
  'eye changes',
62
  'tilted head',
63
  CommonAnomalies
64
+ ]]
65
 
66
 
67
  # Union of all possible anomaly types for specific body parts
 
76
  # Main PhysicalAnomaly class that logs anomalies across different body parts
77
  class PhysicalAnomalies(BaseModel):
78
  physical_radio: str
79
+ physical_anomalies_type: Optional[List[AnomalyType]] = None
app/physical/physical_checkbox.py CHANGED
@@ -72,8 +72,9 @@ def process_body_parts(section, matched_box):
72
  #---------------------------------------------------------
73
 
74
  def on_select_body_part(body_part_checkbox, body_part):
75
- add_data_tmp("wounded_dead", "physical_type_"+body_part, body_part)
76
- add_data_tmp("wounded_dead", "physical_anomaly_type_"+body_part, body_part_checkbox)
 
77
 
78
  #---------------------------------------------------------
79
 
 
72
  #---------------------------------------------------------
73
 
74
  def on_select_body_part(body_part_checkbox, body_part):
75
+ add_data_tmp("wounded_dead", "physical_type_"+body_part.lower(), body_part.lower())
76
+ body_part_checkbox = [body_part_check.lower() for body_part_check in body_part_checkbox]
77
+ add_data_tmp("wounded_dead", "physical_anomaly_"+body_part.lower(), body_part_checkbox)
78
 
79
  #---------------------------------------------------------
80
 
app/validation_submission/add_json.py CHANGED
@@ -8,7 +8,7 @@ def add_data_to_individual(key, value):
8
  create_json_one_individual(one_individual)
9
 
10
  def add_data_tmp(tmp_name, key, value):
11
- with open(f"app/assets/json_tmp/tmp_{tmp_name}.json", 'r') as openfile:
12
  tmp = json.load(openfile)
13
  tmp[key] = value
14
  create_tmp(tmp_name, tmp)
 
8
  create_json_one_individual(one_individual)
9
 
10
  def add_data_tmp(tmp_name, key, value):
11
+ with open(f"app/assets/tmp_json/tmp_{tmp_name}.json", 'r') as openfile:
12
  tmp = json.load(openfile)
13
  tmp[key] = value
14
  create_tmp(tmp_name, tmp)
app/validation_submission/create_json.py CHANGED
@@ -12,5 +12,5 @@ def create_json_all_individuals(all_individuals={}):
12
 
13
  def create_tmp(tmp_name="wounded_dead", tmp={}):
14
  tmp = json.dumps(tmp)
15
- with open(f"app/assets/json_tmp/tmp_{tmp_name}.json", "w") as outfile:
16
  outfile.write(tmp)
 
12
 
13
  def create_tmp(tmp_name="wounded_dead", tmp={}):
14
  tmp = json.dumps(tmp)
15
+ with open(f"app/assets/tmp_json/tmp_{tmp_name}.json", "w") as outfile:
16
  outfile.write(tmp)
app/validation_submission/get_json.py CHANGED
@@ -11,6 +11,6 @@ def get_json_all_individuals():
11
  return all_individuals
12
 
13
  def get_json_tmp(tmp_name):
14
- with open(f"app/assets/json_tmp/tmp_{tmp_name}.json", "r") as openfile:
15
  tmp_json = json.load(openfile)
16
  return tmp_json
 
11
  return all_individuals
12
 
13
  def get_json_tmp(tmp_name):
14
+ with open(f"app/assets/tmp_json/tmp_{tmp_name}.json", "r") as openfile:
15
  tmp_json = json.load(openfile)
16
  return tmp_json
app/validation_submission/processing.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #### PROCESS FUNCTIONS
2
+
3
+ def process_circumstance(data):
4
+ fields_to_check = ["option_dropdown", "open_field", "extra"]
5
+ if data["circumstance_radio"] == "Yes":
6
+ for field in fields_to_check:
7
+ if data["circumstance_type"][field+"_label"] == "NA":
8
+ data["circumstance_type"].pop(field+"_label")
9
+ else :
10
+ val = data[f"circumstance_{field}"]
11
+ key = data["circumstance_type"][field+"_label"]
12
+ data["circumstance_type"][key] = val
13
+ data["circumstance_type"].pop(field+"_label")
14
+ # {"circumstance_radio": true,
15
+ # "circumstance": "destruction / deliberatly removed",
16
+ # "cirumstance_type": {"type": "removal or direct capture",
17
+ # "option_dropdown_label": "method",
18
+ # "open_field_label": "NA",
19
+ # "extra_label": "NA"},
20
+ # "circumstance_option_dropdown": "Traffic/Trade"}
21
+ return data
22
+
23
+ def process_behaviors(data):
24
+ # INPUT :
25
+ #"behaviors_radio": true,
26
+ # "behaviors_type": ["Crash, Falling From The Sky", "Neurological"]
27
+ #OUTPUT:
28
+ # "behaviors_radio": "Yes",
29
+ # "behaviors_type": [
30
+ # {
31
+ # "type": "abnormal breathing",
32
+ # "description": "Problems breathing, breathing sounds"
33
+ # }
34
+ behaviors =[]
35
+ if data["behaviors_radio"] == "Yes":
36
+ for type in data["behaviors_type"]:
37
+ new_behavior = {}
38
+ new_behavior["type"] = type
39
+ behaviors.append(new_behavior)
40
+ data["behaviors_type"] = behaviors
41
+ return data
42
+
43
+ def process_physical(data):
44
+ # INPUT
45
+ # "physical_type_feathers": "feathers",
46
+ # "physical_anomaly_type_feathers": ["Blood", "Swelling"]}
47
+
48
+ # OUTPUT
49
+ # "physical_radio": "Yes",
50
+ # "physical_anomalies_type": [
51
+ # {
52
+ # "type": "beak",
53
+ # "anomaly_type": "deformation"
54
+ # },
55
+ # {
56
+ # "type": "body",
57
+ # "anomaly_type": "fluffed up"
58
+ # },
59
+ body_parts= ["beak", "body", "legs", "feathers/wings/tail", "head incl. eyes"]
60
+ anomalies=[]
61
+ reformatted = {}
62
+ reformatted["physical_radio"] = data["physical_radio"]
63
+ if data["physical_radio"] == "Yes":
64
+ for body_part in body_parts:
65
+ anomaly = {}
66
+ for key, val in data.items():
67
+ if "type_"+ body_part in key:
68
+ anomaly["type"] = body_part
69
+ elif "anomaly_"+ body_part in key:
70
+ anomaly["anomaly_type"] = val
71
+ if anomaly:
72
+ anomalies.append(anomaly)
73
+ reformatted["physical_anomalies_type"] = anomalies
74
+ return reformatted
75
+
76
+ def process_followup(data):
77
+ # "follow_up_events": [
78
+ # {
79
+ # "type": "animal collected",
80
+ # "option": "Yes"
81
+ # },
82
+ # {
83
+ # "type": "recipient",
84
+ # "option": "Veterinary",
85
+ # "name_recipient": "Dr. Jane Smith"
86
+ # },
87
+ # {
88
+ # "type": "radiography",
89
+ # "option": "Unknown"
90
+ # },
91
+ # {
92
+ # "type": "given answer",
93
+ # "option": "Discussion with the speaker"
94
+ # },
95
+ # {
96
+ # "type": "collection reference",
97
+ # "reference": "Specimen ID: 12345, Collected on 2023-09-15"
98
+ # }
99
+ # ]
100
+ followup_events = []
101
+ for key, val in data.items():
102
+ followup_event={}
103
+ type = key.split("followup")[-1]
104
+ option = type.split(" ")[-1]
105
+ followup_event["type"] = type.strip()
106
+ followup_event[option.strip()] = val
107
+ followup_events.append(followup_event)
108
+ reformatted ={}
109
+ reformatted["follow_up_events"] = followup_events
110
+ return reformatted
app/validation_submission/submission.py CHANGED
@@ -1,5 +1,11 @@
1
  import json
2
  from validation_submission.get_json import get_json_all_individuals
 
 
 
 
 
 
3
 
4
  def save_to_all_individuals(one_individual):
5
  all_individuals = get_json_all_individuals()
 
1
  import json
2
  from validation_submission.get_json import get_json_all_individuals
3
+ from validation_submission.validation import validate_individual
4
+
5
+ def validate_save_individual():
6
+ individual = validate_individual()
7
+ save_to_all_individuals(individual.model_dump())
8
+ return individual
9
 
10
  def save_to_all_individuals(one_individual):
11
  all_individuals = get_json_all_individuals()
app/validation_submission/validation.py CHANGED
@@ -1,9 +1,12 @@
 
 
1
  from validation_submission.get_json import get_json_tmp, get_json_one_individual
2
- from classes import Report
3
  from circumstances.class_circumstance import Circumstances
4
  from behavior.class_behavior import Behaviors
5
  from physical.class_physical import PhysicalAnomalies
6
  from follow_up.class_follow_up import FollowUpEvents
 
 
7
 
8
  def get_fields(data_dict, keyword):
9
  extract = {}
@@ -13,154 +16,84 @@ def get_fields(data_dict, keyword):
13
  return extract
14
 
15
  def validate_individual():
16
- data = get_json_tmp("wounded_dead")
17
- circumstance = validate_circumstance(data)
18
- behaviors = validate_behavior(data)
19
- validate_physical(data)
20
- validate_follow_up(data)
21
- validate_individual()
22
- pass
23
-
24
- #### PROCESS FUNCTIONS
25
-
26
- def process_circumstance(data):
27
- fields_to_check = ["option_dropdown", "open_field", "extra"]
28
- for field in fields_to_check:
29
- if data["circumstance_type"][field+"_label"] == "NA":
30
- data["circumstance_type"].pop([field+"_label"])
31
- else :
32
- key = data[f"circumstance_{field}"]
33
- val = data["circumstance_type"][field+"_label"]
34
- data["circumstance_type"][key] = val
35
- data["circumstance_type"].pop([field+"_label"])
36
- # {"circumstance_radio": true,
37
- # "circumstance": "destruction / deliberatly removed",
38
- # "cirumstance_type": {"type": "removal or direct capture",
39
- # "option_dropdown_label": "method",
40
- # "open_field_label": "NA",
41
- # "extra_label": "NA"},
42
- # "circumstance_option_dropdown": "Traffic/Trade"}
43
- return data
44
-
45
- def process_behaviors(data):
46
- # INPUT :
47
- #"behaviors_radio": true,
48
- # "behaviors_type": ["Crash, Falling From The Sky", "Neurological"]
49
- #OUTPUT:
50
- # "behaviors_radio": "Yes",
51
- # "behaviors_type": [
52
- # {
53
- # "type": "abnormal breathing",
54
- # "description": "Problems breathing, breathing sounds"
55
- # }
56
- behaviors =[]
57
- for type in data["behaviors_type"]:
58
- new_behavior = {}
59
- new_behavior["type"] = type
60
- behaviors.append(new_behavior)
61
- data["behaviors_type"] = behaviors
62
- return data
63
 
64
- def process_physical(data):
65
- # INPUT
66
- # "physical_type_feathers": "feathers",
67
- # "physical_anomaly_type_feathers": ["Blood", "Swelling"]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
- # OUTPUT
70
- # "physical_radio": "Yes",
71
- # "physical_anomalies_type": [
72
- # {
73
- # "type": "beak",
74
- # "anomaly_type": "deformation"
75
- # },
76
- # {
77
- # "type": "body",
78
- # "anomaly_type": "fluffed up"
79
- # },
80
- body_parts= ["beak", "body", "legs", "feathers", "head"]
81
- anomalies=[]
82
- reformatted = {}
83
- reformatted["physical_radio"] = data["physical_radio"]
84
- for body_part in body_parts:
85
- anomaly = {}
86
- for key, val in data.items():
87
- if "type_"+ body_part in key:
88
- anomaly["type"] = val
89
- elif "anomaly_type_"+ body_part in key:
90
- anomaly["anomaly_type"] = val
91
- anomalies.append(anomaly)
92
- reformatted["physical_anomalies_type"] = anomalies
93
- return reformatted
94
 
95
- def process_followup(data):
96
- # "follow_up_events": [
97
- # {
98
- # "type": "animal collected",
99
- # "option": "Yes"
100
- # },
101
- # {
102
- # "type": "recipient",
103
- # "option": "Veterinary",
104
- # "name_recipient": "Dr. Jane Smith"
105
- # },
106
- # {
107
- # "type": "radiography",
108
- # "option": "Unknown"
109
- # },
110
- # {
111
- # "type": "given answer",
112
- # "option": "Discussion with the speaker"
113
- # },
114
- # {
115
- # "type": "collection reference",
116
- # "reference": "Specimen ID: 12345, Collected on 2023-09-15"
117
- # }
118
- # ]
119
- followup_events = []
120
- for key, val in data.items():
121
- followup_event={}
122
- type = key.split("followup")[-1]
123
- option = type.split(" ")[-1]
124
- followup_event["type"] = type
125
- followup_event[option] = val
126
- followup_events.append(followup_event)
127
- return followup_events
128
 
129
  #### VALIDATION FUNCTIONS
130
  def validate_circumstance(data):
131
  circumstance_raw = get_fields(data, "circumstance")
132
  circumstance_formatted = process_circumstance(circumstance_raw)
133
- if not Circumstances(**circumstance_formatted).validate():
134
  print("Validation failed for the circumstance.")
135
  else:
136
  return Circumstances(**circumstance_formatted)
137
 
138
  def validate_behavior(data):
139
- behaviors_raw = get_fields(data, "behaviours")
140
  behaviors_formatted = process_behaviors(behaviors_raw)
141
- if not Behaviors(**behaviors_formatted).validate():
142
- print("Validation failed for the behaviours.")
143
- else:
144
  return Behaviors(**behaviors_formatted)
 
 
 
145
 
146
  def validate_physical(data):
147
  physical_raw = get_fields(data, "physical")
148
  physical_formatted = process_physical(physical_raw)
149
- if not PhysicalAnomalies(**physical_formatted).validate():
150
- print("Validation failed for the physical anomalies.")
151
- else:
152
  return PhysicalAnomalies(**physical_formatted)
153
-
 
 
154
  def validate_follow_up(data):
155
  followup_raw = get_fields(data, "followup")
156
  followup_formatted = process_followup(followup_raw)
157
- if not FollowUpEvents(**followup_formatted).validate():
158
- print("Validation failed for the follow-up events.")
159
- else:
160
  return FollowUpEvents(**followup_formatted)
161
-
162
- def validate_individual():
163
- individual = get_json_one_individual()
164
- if not Report(individual).validate():
165
- print("Validation failed for creating the individual report.")
166
- pass
 
1
+ import uuid
2
+
3
  from validation_submission.get_json import get_json_tmp, get_json_one_individual
 
4
  from circumstances.class_circumstance import Circumstances
5
  from behavior.class_behavior import Behaviors
6
  from physical.class_physical import PhysicalAnomalies
7
  from follow_up.class_follow_up import FollowUpEvents
8
+ from classes import Report, Wounded, Dead, ImageBase64
9
+ from validation_submission.processing import process_circumstance, process_behaviors, process_physical, process_followup
10
 
11
  def get_fields(data_dict, keyword):
12
  extract = {}
 
16
  return extract
17
 
18
  def validate_individual():
19
+ data = get_json_one_individual()
20
+ data["identifier"] = str(uuid.uuid4())
21
+ if "wounded_state" not in data or "dead_state" not in data:
22
+ data["wounded_state"] = "No"
23
+ data["dead_state"] = "No"
24
+ if (data["wounded_state"] == "Yes") or (data["dead_state"] == "Yes"):
25
+ data_wounded_dead = get_json_tmp("wounded_dead")
26
+ circumstance = validate_circumstance(data_wounded_dead)
27
+ physical = validate_physical(data_wounded_dead)
28
+ followup = validate_follow_up(data_wounded_dead)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ if data["wounded_state"]=="Yes":
31
+ behavior = validate_behavior(data_wounded_dead)
32
+ individual = Report(identifier = data["identifier"],
33
+ image = ImageBase64.to_base64(data["image"]),
34
+ geolocalisation = data["geolocalisation"],
35
+ wounded_state = data["wounded_state"],
36
+ wounded = Wounded(circumstances = circumstance,
37
+ behaviors = behavior,
38
+ physical_anomalies = physical,
39
+ follow_up_events = followup),
40
+ dead_state = data["dead_state"])
41
+ elif data["dead_state"]=="Yes":
42
+ individual = Report(identifier = data["identifier"],
43
+ image = ImageBase64.to_base64(data["image"]),
44
+ geolocalisation = data["geolocalisation"],
45
+ wounded_state = data["wounded_state"],
46
+ dead_state = data["dead_state"],
47
+ dead = Dead(circumstances = circumstance,
48
+ physical_anomalies = physical,
49
+ follow_up_events = followup)
50
+ )
51
+ else:
52
+ data["image"] = ImageBase64.to_base64(data["image"])
53
+ if not Report(**data).validate():
54
+ print("Validation failed for creating the individual report.")
55
+ else:
56
+ individual = Report(**data)
57
+ return individual
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  #### VALIDATION FUNCTIONS
62
  def validate_circumstance(data):
63
  circumstance_raw = get_fields(data, "circumstance")
64
  circumstance_formatted = process_circumstance(circumstance_raw)
65
+ if not Circumstances.model_validate(circumstance_formatted):
66
  print("Validation failed for the circumstance.")
67
  else:
68
  return Circumstances(**circumstance_formatted)
69
 
70
  def validate_behavior(data):
71
+ behaviors_raw = get_fields(data, "behaviors")
72
  behaviors_formatted = process_behaviors(behaviors_raw)
73
+ try:
74
+ Behaviors.model_validate(behaviors_formatted)
 
75
  return Behaviors(**behaviors_formatted)
76
+ except:
77
+ print("Validation failed for the behaviors.")
78
+
79
 
80
  def validate_physical(data):
81
  physical_raw = get_fields(data, "physical")
82
  physical_formatted = process_physical(physical_raw)
83
+ try:
84
+ PhysicalAnomalies.model_validate(physical_formatted)
 
85
  return PhysicalAnomalies(**physical_formatted)
86
+ except:
87
+ print("Validation failed for the physical anomalies.")
88
+
89
  def validate_follow_up(data):
90
  followup_raw = get_fields(data, "followup")
91
  followup_formatted = process_followup(followup_raw)
92
+ try:
93
+ FollowUpEvents.model_validate(followup_formatted)
 
94
  return FollowUpEvents(**followup_formatted)
95
+ except:
96
+ print("Validation failed for the follow-up events.")
97
+
98
+
99
+
 
app/wounded.py CHANGED
@@ -8,8 +8,8 @@ from validation_submission.add_json import add_data_to_individual
8
 
9
  def show_section_wounded(visible):
10
  if visible==True:
11
- add_data_to_individual("wounded_bool", "True")
12
- add_data_to_individual("dead_bool", "False")
13
 
14
  with gr.Column(visible=visible, elem_id="wounded") as wounded_section:
15
  gr.Markdown("# Wounded Animal")
 
8
 
9
  def show_section_wounded(visible):
10
  if visible==True:
11
+ add_data_to_individual("wounded_state", "Yes")
12
+ add_data_to_individual("dead_state", "No")
13
 
14
  with gr.Column(visible=visible, elem_id="wounded") as wounded_section:
15
  gr.Markdown("# Wounded Animal")