ebird-api-data 0.1.0__tar.gz → 0.2.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.
Files changed (45) hide show
  1. ebird_api_data-0.2.0/.gitignore +31 -0
  2. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/PKG-INFO +6 -27
  3. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/pyproject.toml +13 -10
  4. ebird_api_data-0.2.0/src/ebird/api/data/__init__.py +1 -0
  5. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/admin.py +4 -12
  6. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/loaders.py +24 -10
  7. ebird_api_data-0.2.0/src/ebird/api/data/locale/en/LC_MESSAGES/django.mo +0 -0
  8. ebird_api_data-0.2.0/src/ebird/api/data/locale/en/LC_MESSAGES/django.po +676 -0
  9. ebird_api_data-0.2.0/src/ebird/api/data/migrations/0002_convert_foreign_keys_to_integers.py +103 -0
  10. ebird_api_data-0.2.0/src/ebird/api/data/migrations/0003_make_codes_and_identifiers_primary_keys.py +93 -0
  11. ebird_api_data-0.2.0/src/ebird/api/data/migrations/0004_rename_foreign_keys.py +103 -0
  12. ebird_api_data-0.2.0/src/ebird/api/data/migrations/0005_add_new_foreign_keys.py +104 -0
  13. ebird_api_data-0.2.0/src/ebird/api/data/migrations/0006_set_new_foreign_keys.py +266 -0
  14. ebird_api_data-0.2.0/src/ebird/api/data/migrations/0007_remove_old_fields_and_ids.py +188 -0
  15. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/checklist.py +3 -2
  16. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/country.py +1 -1
  17. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/county.py +1 -1
  18. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/location.py +3 -2
  19. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/observation.py +3 -2
  20. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/observer.py +3 -1
  21. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/species.py +3 -2
  22. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/state.py +2 -2
  23. ebird_api_data-0.2.0/src/ebird/api/data/templates/admin/data/species/change_list_object_tools.html +13 -0
  24. ebird_api_data-0.2.0/src/ebird/api/data/templates/admin/data/species/fetch_species.html +52 -0
  25. ebird_api_data-0.2.0/src/ebird/api/data/templates/data/widgets/translation_field.html +10 -0
  26. ebird_api_data-0.1.0/setup.cfg +0 -4
  27. ebird_api_data-0.1.0/src/ebird/api/data/__init__.py +0 -1
  28. ebird_api_data-0.1.0/src/ebird_api_data.egg-info/PKG-INFO +0 -206
  29. ebird_api_data-0.1.0/src/ebird_api_data.egg-info/SOURCES.txt +0 -30
  30. ebird_api_data-0.1.0/src/ebird_api_data.egg-info/dependency_links.txt +0 -1
  31. ebird_api_data-0.1.0/src/ebird_api_data.egg-info/requires.txt +0 -5
  32. ebird_api_data-0.1.0/src/ebird_api_data.egg-info/top_level.txt +0 -1
  33. ebird_api_data-0.1.0/tests/test_loader.py +0 -26
  34. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/LICENSE +0 -0
  35. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/README.rst +0 -0
  36. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/apps.py +0 -0
  37. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/fields.py +0 -0
  38. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/management/__init__.py +0 -0
  39. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/management/commands/__init__.py +0 -0
  40. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/management/commands/add_checklists.py +0 -0
  41. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/migrations/0001_initial.py +0 -0
  42. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/migrations/__init__.py +0 -0
  43. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/__init__.py +0 -0
  44. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/models/filter.py +0 -0
  45. {ebird_api_data-0.1.0 → ebird_api_data-0.2.0}/src/ebird/api/data/widgets.py +0 -0
@@ -0,0 +1,31 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[co]
4
+ *.egg*
5
+
6
+ # Editor temp files
7
+ *~
8
+
9
+ # Ignore dot files except those required for the project
10
+ .*
11
+ !.editorconfig
12
+ !.env.example
13
+ !.envrc
14
+ !.gitignore
15
+ !.gitkeep
16
+ !.pre-commit-config.yaml
17
+ !.readthedocs.yaml
18
+
19
+ # data
20
+ *.sqlite3
21
+ *.csv
22
+
23
+ !ebird_basic_dataset_sample.csv
24
+ !MyEBirdData.csv
25
+
26
+ #docs
27
+ _build/
28
+
29
+ # directories
30
+ build/
31
+ logs/
@@ -1,39 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ebird-api-data
3
- Version: 0.1.0
3
+ Version: 0.2.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
- License: The MIT License (MIT)
7
-
8
- Copyright (c) 2025 Stuart MacKay
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in all
18
- copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- SOFTWARE.
27
-
28
- Keywords: checklists,observations,species,Django,eBird,models
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: Django,checklists,eBird,models,observations,species
29
9
  Classifier: Development Status :: 5 - Production/Stable
30
- Classifier: Intended Audience :: Science/Research
31
10
  Classifier: Framework :: Django
32
11
  Classifier: Framework :: Django :: 4.2
33
12
  Classifier: Framework :: Django :: 5.0
34
13
  Classifier: Framework :: Django :: 5.1
35
14
  Classifier: Framework :: Django :: 5.2
36
- Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Intended Audience :: Science/Research
37
16
  Classifier: Natural Language :: English
38
17
  Classifier: Programming Language :: Python :: 3.10
39
18
  Classifier: Programming Language :: Python :: 3.11
@@ -42,12 +21,12 @@ Classifier: Programming Language :: Python :: 3.13
42
21
  Classifier: Topic :: Scientific/Engineering
43
22
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
44
23
  Requires-Python: >=3.10
45
- Description-Content-Type: text/x-rst
46
24
  Requires-Dist: beautifulsoup4>=4.13.3
47
25
  Requires-Dist: django>=4.2
48
26
  Requires-Dist: ebird-api-requests>=4.0.1
49
27
  Requires-Dist: ebird-codes>=0.0.2
50
28
  Requires-Dist: lxml>=6.0.0
29
+ Description-Content-Type: text/x-rst
51
30
 
52
31
  eBird API Data
53
32
  ==============
@@ -1,13 +1,10 @@
1
- [build-system]
2
- build-backend = "setuptools.build_meta"
3
- requires = ["setuptools >= 61.0"]
4
-
5
1
  [project]
6
2
  name = "ebird-api-data"
7
- version = "0.1.0"
3
+ version = "0.2.0"
8
4
  description = "eBird API Data is a reusable Django app for loading data from eBird into a database."
9
5
  keywords = ["checklists", "observations", "species", "Django", "eBird", "models"]
10
- license = { file = "LICENSE" }
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
11
8
  authors = [
12
9
  {name = "Stuart MacKay", email = "smackay@flagstonesoftware.com"},
13
10
  ]
@@ -28,7 +25,6 @@ classifiers = [
28
25
  "Framework :: Django :: 5.0",
29
26
  "Framework :: Django :: 5.1",
30
27
  "Framework :: Django :: 5.2",
31
- "License :: OSI Approved :: MIT License",
32
28
  "Natural Language :: English",
33
29
  "Programming Language :: Python :: 3.10",
34
30
  "Programming Language :: Python :: 3.11",
@@ -38,8 +34,15 @@ classifiers = [
38
34
  "Topic :: Scientific/Engineering :: Information Analysis",
39
35
  ]
40
36
 
41
- [tool.setuptools]
42
- license-files = []
37
+ [build-system]
38
+ requires = ["hatchling"]
39
+ build-backend = "hatchling.build"
40
+
41
+ [tool.hatch.build.targets.sdist]
42
+ only-include=["src"]
43
+
44
+ [tool.hatch.build.targets.wheel]
45
+ packages=["src/ebird"]
43
46
 
44
47
  [tool.uv]
45
48
  dev-dependencies = [
@@ -61,7 +64,7 @@ dev-dependencies = [
61
64
  ]
62
65
 
63
66
  [tool.bumpversion]
64
- current_version = "0.1.0"
67
+ current_version = "0.2.0"
65
68
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
66
69
  serialize = ["{major}.{minor}.{patch}"]
67
70
  ignore_missing_version = false
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -142,7 +142,7 @@ class CountyAdmin(admin.ModelAdmin):
142
142
 
143
143
  @admin.register(models.Location)
144
144
  class LocationAdmin(admin.ModelAdmin):
145
- list_display = ("identifier", "names", "county", "state", "country")
145
+ list_display = ("identifier", "name", "county", "state", "country")
146
146
  list_select_related = ("country", "county", "state")
147
147
  ordering = ("-identifier",)
148
148
  search_fields = (
@@ -154,13 +154,9 @@ class LocationAdmin(admin.ModelAdmin):
154
154
  )
155
155
  readonly_fields = ("identifier",)
156
156
 
157
- @admin.display(description=_("Name / Byname"))
158
- def names(self, obj):
159
- return format_html("%s<br/>%s" % (obj.name, obj.byname))
160
-
161
157
  def formfield_for_dbfield(self, db_field, request, **kwargs):
162
158
  field = super().formfield_for_dbfield(db_field, request, **kwargs)
163
- if db_field.name == "byname":
159
+ if db_field.name == "original":
164
160
  field.widget = TextInput(attrs={"class": "vLargeTextField"})
165
161
  elif db_field.name == "name":
166
162
  field.widget = TextInput(attrs={"class": "vLargeTextField"})
@@ -271,19 +267,15 @@ class ObservationAdmin(admin.ModelAdmin):
271
267
 
272
268
  @admin.register(models.Observer)
273
269
  class ObserverAdmin(admin.ModelAdmin):
274
- list_display = ("names", "identifier", "multiple", "enabled")
270
+ list_display = ("name", "identifier", "multiple", "enabled")
275
271
  ordering = ("name",)
276
272
  search_fields = ("name", "identifier")
277
273
  list_filter = ("multiple", "enabled")
278
274
  form = ObservationForm
279
275
 
280
- @admin.display(description=_("Name / Byname"))
281
- def names(self, obj):
282
- return format_html("%s<br/>%s" % (obj.name, obj.byname))
283
-
284
276
  def formfield_for_dbfield(self, db_field, request, **kwargs):
285
277
  field = super().formfield_for_dbfield(db_field, request, **kwargs)
286
- if db_field.name == "byname":
278
+ if db_field.name == "original":
287
279
  field.widget = TextInput(attrs={"class": "vTextField"})
288
280
  elif db_field.name == "name":
289
281
  field.widget = TextInput(attrs={"class": "vTextField"})
@@ -303,19 +303,33 @@ class APILoader:
303
303
 
304
304
  def get_observer(self, data: dict) -> Observer:
305
305
  name: str = data.get("userDisplayName", "Anonymous eBirder")
306
- observer, created = Observer.objects.get_or_create(
307
- original=name, defaults={"name": name}
308
- )
309
- if observer.multiple:
306
+ is_multiple = Observer.objects.filter(original=name, multiple=True).exists()
307
+ if is_multiple:
308
+ identifier = self.get_observer_identifier(data)
310
309
  observer, created = Observer.objects.get_or_create(
311
- identifier=self.get_observer_identifier(data),
312
- defaults={"original": random_word(8), "name": name},
310
+ identifier=identifier,
311
+ defaults={
312
+ "name":name,
313
+ "original": random_word(8)
314
+ }
313
315
  )
314
- elif observer.identifier == "":
315
- observer.identifier = self.get_observer_identifier(data)
316
- observer.save()
316
+ else:
317
+ if observer := Observer.objects.filter(original=name).first():
318
+ created = False
319
+ else:
320
+ identifier = self.get_observer_identifier(data)
321
+ observer = Observer.objects.create(
322
+ identifier=identifier,
323
+ name=name,
324
+ original=name,
325
+ )
326
+ created = True
327
+
317
328
  if created:
318
- logger.info("Added observer: %s", observer.name)
329
+ logger.info("Added observer: %s", name)
330
+ if Observer.objects.filter(original=name).count() > 1:
331
+ logger.error("Multiple observers exist with same name: %s", name)
332
+
319
333
  return observer
320
334
 
321
335
  def add_checklist(self, identifier: str) -> Checklist | None: