ebird-api-data 0.3.7__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/PKG-INFO +3 -4
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/README.md +1 -1
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/pyproject.toml +3 -4
- ebird_api_data-0.4.0/src/ebird/api/data/__init__.py +1 -0
- ebird_api_data-0.4.0/src/ebird/api/data/factories.py +432 -0
- ebird_api_data-0.4.0/src/ebird/api/data/faker_providers.py +345 -0
- ebird_api_data-0.4.0/src/ebird/api/data/fixtures/README.md +355 -0
- ebird_api_data-0.4.0/src/ebird/api/data/fixtures/generate_portugal_data.py +530 -0
- ebird_api_data-0.4.0/src/ebird/api/data/fixtures/portugal_observations.json +144813 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/loaders.py +86 -40
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/management/commands/add_checklists.py +36 -21
- ebird_api_data-0.4.0/src/ebird/api/data/migrations/0002_expand_filter.py +29 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/filter.py +38 -1
- ebird_api_data-0.4.0/src/ebird/api/data/models/models.md +242 -0
- ebird_api_data-0.3.7/src/ebird/api/data/__init__.py +0 -1
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/.gitignore +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/LICENSE +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/admin.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/apps.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/fields.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/locale/en/LC_MESSAGES/django.mo +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/locale/en/LC_MESSAGES/django.po +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/management/__init__.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/management/commands/__init__.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/migrations/0001_initial.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/migrations/__init__.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/__init__.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/checklist.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/country.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/county.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/location.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/observation.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/observer.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/species.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/models/state.py +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/templates/admin/data/species/change_list_object_tools.html +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/templates/admin/data/species/fetch_species.html +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/templates/data/widgets/translation_field.html +0 -0
- {ebird_api_data-0.3.7 → ebird_api_data-0.4.0}/src/ebird/api/data/widgets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ebird-api-data
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: eBird API Data is a reusable Django app for loading data from eBird into a database.
|
|
5
5
|
Author-email: Stuart MacKay <smackay@flagstonesoftware.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,8 +23,7 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Requires-Dist: beautifulsoup4>=4.13.3
|
|
25
25
|
Requires-Dist: django>=4.2
|
|
26
|
-
Requires-Dist: ebird-api-requests
|
|
27
|
-
Requires-Dist: ebird-codes>=0.0.2
|
|
26
|
+
Requires-Dist: ebird-api-requests==4.0.2
|
|
28
27
|
Requires-Dist: lxml>=6.0.0
|
|
29
28
|
Description-Content-Type: text/markdown
|
|
30
29
|
|
|
@@ -142,7 +141,7 @@ EBIRD_API_KEY=<my api key>
|
|
|
142
141
|
Now, download data from the API:
|
|
143
142
|
|
|
144
143
|
```shell
|
|
145
|
-
python manage.py add_checklists --days 2 US-NY-109
|
|
144
|
+
python manage.py add_checklists --days -2 US-NY-109
|
|
146
145
|
```
|
|
147
146
|
|
|
148
147
|
This loads all the checklists, submitted in the past two days by birders in
|
|
@@ -112,7 +112,7 @@ EBIRD_API_KEY=<my api key>
|
|
|
112
112
|
Now, download data from the API:
|
|
113
113
|
|
|
114
114
|
```shell
|
|
115
|
-
python manage.py add_checklists --days 2 US-NY-109
|
|
115
|
+
python manage.py add_checklists --days -2 US-NY-109
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
This loads all the checklists, submitted in the past two days by birders in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ebird-api-data"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "eBird API Data is a reusable Django app for loading data from eBird into a database."
|
|
5
5
|
keywords = ["checklists", "observations", "species", "Django", "eBird", "models"]
|
|
6
6
|
license = "MIT"
|
|
@@ -13,8 +13,7 @@ requires-python = ">=3.10"
|
|
|
13
13
|
dependencies = [
|
|
14
14
|
"beautifulsoup4>=4.13.3",
|
|
15
15
|
"django>=4.2",
|
|
16
|
-
"ebird-api-requests
|
|
17
|
-
"ebird-codes>=0.0.2",
|
|
16
|
+
"ebird-api-requests==4.0.2",
|
|
18
17
|
"lxml>=6.0.0",
|
|
19
18
|
]
|
|
20
19
|
classifiers = [
|
|
@@ -65,7 +64,7 @@ dev-dependencies = [
|
|
|
65
64
|
]
|
|
66
65
|
|
|
67
66
|
[tool.bumpversion]
|
|
68
|
-
current_version = "0.
|
|
67
|
+
current_version = "0.4.0"
|
|
69
68
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
70
69
|
serialize = ["{major}.{minor}.{patch}"]
|
|
71
70
|
ignore_missing_version = false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0"
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FactoryBoy factories for eBird API data models.
|
|
3
|
+
|
|
4
|
+
These factories provide an easy way to create test data with realistic,
|
|
5
|
+
contextually appropriate values using Faker providers.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from ebird.api.data.factories import ChecklistFactory, ObservationFactory
|
|
9
|
+
|
|
10
|
+
# Create a single checklist with observations
|
|
11
|
+
checklist = ChecklistFactory()
|
|
12
|
+
|
|
13
|
+
# Create multiple checklists
|
|
14
|
+
checklists = ChecklistFactory.create_batch(10)
|
|
15
|
+
|
|
16
|
+
# Create checklist with specific location
|
|
17
|
+
checklist = ChecklistFactory(location__name="Ria de Aveiro")
|
|
18
|
+
|
|
19
|
+
# Create observation with specific species
|
|
20
|
+
observation = ObservationFactory(species__species_code="houspa")
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import factory
|
|
24
|
+
from factory import fuzzy
|
|
25
|
+
from factory.django import DjangoModelFactory
|
|
26
|
+
from faker import Faker
|
|
27
|
+
|
|
28
|
+
from ebird.api.data.models import (
|
|
29
|
+
Country,
|
|
30
|
+
State,
|
|
31
|
+
County,
|
|
32
|
+
Location,
|
|
33
|
+
Species,
|
|
34
|
+
Observer,
|
|
35
|
+
Checklist,
|
|
36
|
+
Observation,
|
|
37
|
+
Filter,
|
|
38
|
+
)
|
|
39
|
+
from ebird.api.data.faker_providers import (
|
|
40
|
+
BirdProvider,
|
|
41
|
+
LocationProvider,
|
|
42
|
+
ObserverProvider,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Initialize Faker with custom providers
|
|
46
|
+
fake = Faker()
|
|
47
|
+
fake.add_provider(BirdProvider)
|
|
48
|
+
fake.add_provider(LocationProvider)
|
|
49
|
+
fake.add_provider(ObserverProvider)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CountryFactory(DjangoModelFactory):
|
|
53
|
+
"""Factory for Country model."""
|
|
54
|
+
|
|
55
|
+
class Meta:
|
|
56
|
+
model = Country
|
|
57
|
+
django_get_or_create = ("code",)
|
|
58
|
+
|
|
59
|
+
code = "PT"
|
|
60
|
+
name = "Portugal"
|
|
61
|
+
place = "PT"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class StateFactory(DjangoModelFactory):
|
|
65
|
+
"""Factory for State model."""
|
|
66
|
+
|
|
67
|
+
class Meta:
|
|
68
|
+
model = State
|
|
69
|
+
django_get_or_create = ("code",)
|
|
70
|
+
|
|
71
|
+
code = factory.LazyFunction(lambda: fake.portuguese_state_code())
|
|
72
|
+
name = factory.LazyAttribute(lambda obj: fake.portuguese_state_name())
|
|
73
|
+
place = factory.LazyAttribute(lambda obj: f"PT, {obj.name}")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class CountyFactory(DjangoModelFactory):
|
|
77
|
+
"""Factory for County model."""
|
|
78
|
+
|
|
79
|
+
class Meta:
|
|
80
|
+
model = County
|
|
81
|
+
|
|
82
|
+
code = factory.Sequence(lambda n: f"PT-{n:02d}-CTY")
|
|
83
|
+
name = factory.Faker("city", locale="pt_PT")
|
|
84
|
+
place = factory.LazyAttribute(lambda obj: f"PT, {obj.name}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class LocationFactory(DjangoModelFactory):
|
|
88
|
+
"""Factory for Location model."""
|
|
89
|
+
|
|
90
|
+
class Meta:
|
|
91
|
+
model = Location
|
|
92
|
+
|
|
93
|
+
identifier = factory.Sequence(lambda n: f"L{n:06d}")
|
|
94
|
+
original = factory.LazyFunction(lambda: fake.location_name())
|
|
95
|
+
name = factory.LazyAttribute(lambda obj: obj.original)
|
|
96
|
+
|
|
97
|
+
country = factory.SubFactory(CountryFactory)
|
|
98
|
+
state = factory.SubFactory(StateFactory)
|
|
99
|
+
county = factory.SubFactory(CountyFactory)
|
|
100
|
+
|
|
101
|
+
latitude = factory.LazyFunction(lambda: fake.portugal_coordinates()[0])
|
|
102
|
+
longitude = factory.LazyFunction(lambda: fake.portugal_coordinates()[1])
|
|
103
|
+
|
|
104
|
+
url = factory.LazyAttribute(
|
|
105
|
+
lambda obj: f"https://ebird.org/hotspot/{obj.identifier}" if obj.hotspot else ""
|
|
106
|
+
)
|
|
107
|
+
hotspot = factory.LazyFunction(lambda: fake.location_is_hotspot())
|
|
108
|
+
|
|
109
|
+
data = factory.LazyFunction(lambda: {"habitat": fake.bird_habitat()})
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SpeciesFactory(DjangoModelFactory):
|
|
113
|
+
"""Factory for Species model."""
|
|
114
|
+
|
|
115
|
+
class Meta:
|
|
116
|
+
model = Species
|
|
117
|
+
django_get_or_create = ("species_code",)
|
|
118
|
+
|
|
119
|
+
species_code = factory.LazyFunction(lambda: fake.bird_species_code())
|
|
120
|
+
taxon_order = factory.Faker("random_int", min=1, max=5000)
|
|
121
|
+
order = "Passeriformes"
|
|
122
|
+
category = "species"
|
|
123
|
+
family_code = factory.Faker("word")
|
|
124
|
+
|
|
125
|
+
common_name = factory.LazyAttribute(
|
|
126
|
+
lambda obj: f'{{"en": "{fake.bird_species_name(obj.species_code)}"}}'
|
|
127
|
+
)
|
|
128
|
+
scientific_name = factory.Faker("sentence", nb_words=2)
|
|
129
|
+
|
|
130
|
+
family_common_name = factory.LazyAttribute(
|
|
131
|
+
lambda obj: f'{{"en": "{obj.family_code.title()}"}}'
|
|
132
|
+
)
|
|
133
|
+
family_scientific_name = ""
|
|
134
|
+
|
|
135
|
+
data = factory.Dict({"habitats": factory.List([factory.LazyFunction(lambda: fake.bird_habitat())])})
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ObserverFactory(DjangoModelFactory):
|
|
139
|
+
"""Factory for Observer model."""
|
|
140
|
+
|
|
141
|
+
class Meta:
|
|
142
|
+
model = Observer
|
|
143
|
+
|
|
144
|
+
identifier = factory.LazyFunction(lambda: fake.observer_identifier())
|
|
145
|
+
name = factory.LazyFunction(lambda: fake.observer_name())
|
|
146
|
+
original = factory.LazyAttribute(lambda obj: obj.name)
|
|
147
|
+
multiple = False
|
|
148
|
+
enabled = True
|
|
149
|
+
|
|
150
|
+
data = factory.LazyFunction(
|
|
151
|
+
lambda: {"experience": fake.observer_experience_level()}
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class ChecklistFactory(DjangoModelFactory):
|
|
156
|
+
"""
|
|
157
|
+
Factory for Checklist model.
|
|
158
|
+
|
|
159
|
+
Creates realistic checklists with appropriate effort metrics based on protocol.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
class Meta:
|
|
163
|
+
model = Checklist
|
|
164
|
+
|
|
165
|
+
identifier = factory.Sequence(lambda n: f"S{n:08d}")
|
|
166
|
+
|
|
167
|
+
# Timestamps
|
|
168
|
+
added = factory.Faker("date_time_this_year", tzinfo=None)
|
|
169
|
+
edited = factory.LazyAttribute(lambda obj: obj.added)
|
|
170
|
+
published = True
|
|
171
|
+
|
|
172
|
+
# Relationships
|
|
173
|
+
country = factory.SubFactory(CountryFactory)
|
|
174
|
+
state = factory.SubFactory(StateFactory)
|
|
175
|
+
county = factory.SubFactory(CountyFactory)
|
|
176
|
+
location = factory.SubFactory(LocationFactory)
|
|
177
|
+
observer = factory.SubFactory(ObserverFactory)
|
|
178
|
+
|
|
179
|
+
# Counts (will be set by post_generation for observations)
|
|
180
|
+
observer_count = 1
|
|
181
|
+
species_count = factory.Faker("random_int", min=3, max=20)
|
|
182
|
+
|
|
183
|
+
# Date/Time
|
|
184
|
+
date = factory.LazyAttribute(lambda obj: obj.started.date() if obj.started else fake.date_this_year())
|
|
185
|
+
time = factory.LazyAttribute(lambda obj: obj.started.time() if obj.started else fake.time())
|
|
186
|
+
started = factory.Faker("date_time_this_year", tzinfo=None)
|
|
187
|
+
|
|
188
|
+
# Protocol and effort
|
|
189
|
+
protocol_code = factory.LazyFunction(lambda: fake.checklist_protocol_code())
|
|
190
|
+
project_code = "EBIRD"
|
|
191
|
+
|
|
192
|
+
duration = factory.LazyAttribute(
|
|
193
|
+
lambda obj: fake.checklist_duration(obj.protocol_code)
|
|
194
|
+
)
|
|
195
|
+
distance = factory.LazyAttribute(
|
|
196
|
+
lambda obj: fake.checklist_distance(obj.protocol_code)
|
|
197
|
+
)
|
|
198
|
+
area = None
|
|
199
|
+
|
|
200
|
+
complete = True
|
|
201
|
+
comments = ""
|
|
202
|
+
|
|
203
|
+
url = factory.LazyAttribute(
|
|
204
|
+
lambda obj: f"https://ebird.org/checklist/{obj.identifier}"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
data = factory.Dict({})
|
|
208
|
+
|
|
209
|
+
@factory.post_generation
|
|
210
|
+
def with_observations(self, create, extracted, **kwargs):
|
|
211
|
+
"""
|
|
212
|
+
Post-generation hook to create observations for the checklist.
|
|
213
|
+
|
|
214
|
+
Usage:
|
|
215
|
+
ChecklistFactory(with_observations=5) # Creates 5 observations
|
|
216
|
+
ChecklistFactory(with_observations=True) # Creates 3-10 observations
|
|
217
|
+
"""
|
|
218
|
+
if not create:
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
if extracted:
|
|
222
|
+
# If boolean True, create random number of observations
|
|
223
|
+
if extracted is True:
|
|
224
|
+
num_observations = fake.random_int(min=3, max=10)
|
|
225
|
+
else:
|
|
226
|
+
num_observations = extracted
|
|
227
|
+
|
|
228
|
+
# Get habitat from location
|
|
229
|
+
habitat = self.location.data.get("habitat", "urban")
|
|
230
|
+
|
|
231
|
+
# Get appropriate species for habitat
|
|
232
|
+
species_codes = fake.bird_species_list_for_habitat(
|
|
233
|
+
habitat, min_species=num_observations, max_species=num_observations
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
for species_code in species_codes:
|
|
237
|
+
species, _ = Species.objects.get_or_create(
|
|
238
|
+
species_code=species_code,
|
|
239
|
+
defaults={
|
|
240
|
+
"common_name": f'{{"en": "{fake.bird_species_name(species_code)}"}}',
|
|
241
|
+
"scientific_name": f"Species {species_code}",
|
|
242
|
+
"taxon_order": fake.random_int(min=1, max=5000),
|
|
243
|
+
"order": "Passeriformes",
|
|
244
|
+
"category": "species",
|
|
245
|
+
"family_code": "family1",
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
ObservationFactory(
|
|
250
|
+
checklist=self,
|
|
251
|
+
species=species,
|
|
252
|
+
observer=self.observer,
|
|
253
|
+
country=self.country,
|
|
254
|
+
state=self.state,
|
|
255
|
+
county=self.county,
|
|
256
|
+
location=self.location,
|
|
257
|
+
date=self.date,
|
|
258
|
+
time=self.time,
|
|
259
|
+
started=self.started,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Update species count
|
|
263
|
+
self.species_count = num_observations
|
|
264
|
+
self.save()
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class ObservationFactory(DjangoModelFactory):
|
|
268
|
+
"""
|
|
269
|
+
Factory for Observation model.
|
|
270
|
+
|
|
271
|
+
Creates realistic observations with appropriate counts and media flags.
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
class Meta:
|
|
275
|
+
model = Observation
|
|
276
|
+
|
|
277
|
+
identifier = factory.Sequence(lambda n: f"OBS{n:09d}")
|
|
278
|
+
|
|
279
|
+
# Timestamps
|
|
280
|
+
edited = factory.Faker("date_time_this_year", tzinfo=None)
|
|
281
|
+
published = True
|
|
282
|
+
|
|
283
|
+
# Relationships
|
|
284
|
+
checklist = factory.SubFactory(ChecklistFactory)
|
|
285
|
+
species = factory.SubFactory(SpeciesFactory)
|
|
286
|
+
observer = factory.SubFactory(ObserverFactory)
|
|
287
|
+
|
|
288
|
+
country = factory.LazyAttribute(lambda obj: obj.checklist.country)
|
|
289
|
+
state = factory.LazyAttribute(lambda obj: obj.checklist.state)
|
|
290
|
+
county = factory.LazyAttribute(lambda obj: obj.checklist.county)
|
|
291
|
+
location = factory.LazyAttribute(lambda obj: obj.checklist.location)
|
|
292
|
+
|
|
293
|
+
# Date/Time (inherit from checklist)
|
|
294
|
+
date = factory.LazyAttribute(lambda obj: obj.checklist.date)
|
|
295
|
+
time = factory.LazyAttribute(lambda obj: obj.checklist.time)
|
|
296
|
+
started = factory.LazyAttribute(lambda obj: obj.checklist.started)
|
|
297
|
+
|
|
298
|
+
# Count based on species
|
|
299
|
+
count = factory.LazyAttribute(
|
|
300
|
+
lambda obj: fake.bird_count(obj.species.species_code)
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Media flags (realistic probabilities)
|
|
304
|
+
audio = factory.LazyFunction(lambda: fake.observation_has_media()["audio"])
|
|
305
|
+
photo = factory.LazyFunction(lambda: fake.observation_has_media()["photo"])
|
|
306
|
+
video = factory.LazyFunction(lambda: fake.observation_has_media()["video"])
|
|
307
|
+
|
|
308
|
+
approved = True
|
|
309
|
+
reason = ""
|
|
310
|
+
comments = ""
|
|
311
|
+
|
|
312
|
+
urn = factory.LazyAttribute(lambda obj: f"urn:observation:{obj.identifier}")
|
|
313
|
+
|
|
314
|
+
data = factory.Dict({})
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class FilterFactory(DjangoModelFactory):
|
|
318
|
+
"""
|
|
319
|
+
Factory for Filter model.
|
|
320
|
+
|
|
321
|
+
Creates filters for bulk-updating species in observations.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
class Meta:
|
|
325
|
+
model = Filter
|
|
326
|
+
|
|
327
|
+
enabled = True
|
|
328
|
+
name = factory.Faker("sentence", nb_words=3)
|
|
329
|
+
|
|
330
|
+
species = factory.SubFactory(SpeciesFactory)
|
|
331
|
+
update_species = factory.SubFactory(SpeciesFactory)
|
|
332
|
+
|
|
333
|
+
# Optional geographic filters
|
|
334
|
+
country = factory.Maybe(
|
|
335
|
+
"with_country",
|
|
336
|
+
yes_declaration=factory.SubFactory(CountryFactory),
|
|
337
|
+
no_declaration=None
|
|
338
|
+
)
|
|
339
|
+
state = factory.Maybe(
|
|
340
|
+
"with_state",
|
|
341
|
+
yes_declaration=factory.SubFactory(StateFactory),
|
|
342
|
+
no_declaration=None
|
|
343
|
+
)
|
|
344
|
+
county = factory.Maybe(
|
|
345
|
+
"with_county",
|
|
346
|
+
yes_declaration=factory.SubFactory(CountyFactory),
|
|
347
|
+
no_declaration=None
|
|
348
|
+
)
|
|
349
|
+
location = factory.Maybe(
|
|
350
|
+
"with_location",
|
|
351
|
+
yes_declaration=factory.SubFactory(LocationFactory),
|
|
352
|
+
no_declaration=None
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
class Params:
|
|
356
|
+
# Parameters to control which geographic filters to include
|
|
357
|
+
with_country = False
|
|
358
|
+
with_state = False
|
|
359
|
+
with_county = False
|
|
360
|
+
with_location = False
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# Convenience functions for common scenarios
|
|
364
|
+
|
|
365
|
+
def create_coastal_checklist(**kwargs):
|
|
366
|
+
"""Create a checklist at a coastal location with appropriate species."""
|
|
367
|
+
location = LocationFactory(data={"habitat": "coastal"})
|
|
368
|
+
checklist = ChecklistFactory(
|
|
369
|
+
location=location,
|
|
370
|
+
with_observations=True,
|
|
371
|
+
**kwargs
|
|
372
|
+
)
|
|
373
|
+
return checklist
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def create_wetland_checklist(**kwargs):
|
|
377
|
+
"""Create a checklist at a wetland location with appropriate species."""
|
|
378
|
+
location = LocationFactory(data={"habitat": "coastal_wetland"})
|
|
379
|
+
checklist = ChecklistFactory(
|
|
380
|
+
location=location,
|
|
381
|
+
with_observations=True,
|
|
382
|
+
**kwargs
|
|
383
|
+
)
|
|
384
|
+
return checklist
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def create_urban_checklist(**kwargs):
|
|
388
|
+
"""Create a checklist at an urban location with appropriate species."""
|
|
389
|
+
location = LocationFactory(data={"habitat": "urban"})
|
|
390
|
+
checklist = ChecklistFactory(
|
|
391
|
+
location=location,
|
|
392
|
+
with_observations=True,
|
|
393
|
+
**kwargs
|
|
394
|
+
)
|
|
395
|
+
return checklist
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def create_grassland_checklist(**kwargs):
|
|
399
|
+
"""Create a checklist at a grassland location with steppe specialists."""
|
|
400
|
+
location = LocationFactory(data={"habitat": "grassland"})
|
|
401
|
+
checklist = ChecklistFactory(
|
|
402
|
+
location=location,
|
|
403
|
+
with_observations=True,
|
|
404
|
+
**kwargs
|
|
405
|
+
)
|
|
406
|
+
return checklist
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def create_expert_observer_checklist(**kwargs):
|
|
410
|
+
"""Create a checklist by an expert observer with many species."""
|
|
411
|
+
observer = ObserverFactory(data={"experience": "expert"})
|
|
412
|
+
checklist = ChecklistFactory(
|
|
413
|
+
observer=observer,
|
|
414
|
+
duration=fuzzy.FuzzyInteger(120, 300).fuzz(),
|
|
415
|
+
species_count=fuzzy.FuzzyInteger(15, 30).fuzz(),
|
|
416
|
+
with_observations=fuzzy.FuzzyInteger(15, 30).fuzz(),
|
|
417
|
+
**kwargs
|
|
418
|
+
)
|
|
419
|
+
return checklist
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def create_beginner_observer_checklist(**kwargs):
|
|
423
|
+
"""Create a checklist by a beginner observer with fewer species."""
|
|
424
|
+
observer = ObserverFactory(data={"experience": "beginner"})
|
|
425
|
+
checklist = ChecklistFactory(
|
|
426
|
+
observer=observer,
|
|
427
|
+
duration=fuzzy.FuzzyInteger(30, 90).fuzz(),
|
|
428
|
+
species_count=fuzzy.FuzzyInteger(3, 8).fuzz(),
|
|
429
|
+
with_observations=fuzzy.FuzzyInteger(3, 8).fuzz(),
|
|
430
|
+
**kwargs
|
|
431
|
+
)
|
|
432
|
+
return checklist
|