pyinaturalist 0.19.0.dev0__tar.gz → 0.19.0.dev2__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 (304) hide show
  1. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/CONTRIBUTORS.md +3 -1
  2. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/HISTORY.md +7 -1
  3. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/PKG-INFO +8 -8
  4. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/__init__.py +1 -1
  5. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/constants.py +9 -0
  6. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/docs/model_docs.py +1 -1
  7. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/docs/templates.py +15 -4
  8. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/formatters.py +2 -0
  9. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/__init__.py +1 -1
  10. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/checklist.py +6 -3
  11. pyinaturalist-0.19.0.dev0/pyinaturalist/models/photo.py → pyinaturalist-0.19.0.dev2/pyinaturalist/models/media.py +55 -21
  12. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/observation.py +59 -4
  13. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/taxon.py +1 -2
  14. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/request_params.py +3 -2
  15. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/session.py +44 -19
  16. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/observations.py +2 -2
  17. pyinaturalist-0.19.0.dev2/pyinaturalist/v2/__init__.py +6 -0
  18. pyinaturalist-0.19.0.dev2/pyinaturalist/v2/observations.py +442 -0
  19. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyproject.toml +11 -11
  20. pyinaturalist-0.19.0.dev2/test/sample_data/get_observation_with_sounds.json +581 -0
  21. pyinaturalist-0.19.0.dev2/test/sample_data/get_observations_v2_full.json +407 -0
  22. pyinaturalist-0.19.0.dev2/test/sample_data/get_observations_v2_full.py +350 -0
  23. pyinaturalist-0.19.0.dev2/test/sample_data/get_observations_v2_minimal.json +11 -0
  24. pyinaturalist-0.19.0.dev2/test/sample_data/get_observations_v2_minimal.py +11 -0
  25. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data.py +2 -1
  26. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_converters.py +4 -4
  27. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_formatters.py +2 -0
  28. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_models.py +98 -1
  29. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_session.py +16 -15
  30. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_observations.py +8 -13
  31. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_search.py +1 -1
  32. pyinaturalist-0.19.0.dev2/test/v2/test_observations_v2.py +110 -0
  33. pyinaturalist-0.19.0.dev0/pyinaturalist/v2/__init__.py +0 -1
  34. pyinaturalist-0.19.0.dev0/test/sample_data/get_observation_with_sounds.json +0 -574
  35. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/.readthedocs.yml +0 -0
  36. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/CONTRIBUTING.md +0 -0
  37. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/LICENSE +0 -0
  38. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/README.md +0 -0
  39. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docker-compose.yml +0 -0
  40. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_static/collapsible_container.css +0 -0
  41. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_static/collapsible_container.js +0 -0
  42. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_static/colors.css +0 -0
  43. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_static/favicon.ico +0 -0
  44. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_static/pyinaturalist_logo.png +0 -0
  45. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_static/python-logo-green.png +0 -0
  46. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_static/table.css +0 -0
  47. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_templates/autosummary/module.rst_t +0 -0
  48. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_templates/autosummary_core/class.rst +0 -0
  49. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_templates/header.html +0 -0
  50. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_templates/localtoc.html +0 -0
  51. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_templates/module.rst_t +0 -0
  52. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/_templates/package.rst_t +0 -0
  53. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/automodapi.rst +0 -0
  54. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/code_of_conduct.md +0 -0
  55. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/conf.py +0 -0
  56. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/contributing.md +0 -0
  57. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/contributors.md +0 -0
  58. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/endpoints.md +0 -0
  59. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/examples.md +0 -0
  60. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/history.md +0 -0
  61. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-new-application-complete.png +0 -0
  62. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-new-application.png +0 -0
  63. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-identifiers.png +0 -0
  64. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-observers.png +0 -0
  65. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-page-annotated.odg +0 -0
  66. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-page-annotated.png +0 -0
  67. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-page.png +0 -0
  68. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-search-bar.png +0 -0
  69. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-search-filters.png +0 -0
  70. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-observation-species-counts.png +0 -0
  71. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-taxon-autocomplete.png +0 -0
  72. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-taxon-page.png +0 -0
  73. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/inat-user-login.png +0 -0
  74. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/open-in-vscode.svg +0 -0
  75. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/password_manager_keying.png +0 -0
  76. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pprint_table.png +0 -0
  77. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo.png +0 -0
  78. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo.xcf +0 -0
  79. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_gh_preview.png +0 -0
  80. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_gh_preview.xcf +0 -0
  81. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_hex.png +0 -0
  82. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_hex.xcf +0 -0
  83. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_med.png +0 -0
  84. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_stacked.xcf +0 -0
  85. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_white.png +0 -0
  86. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/pyinaturalist_logo_white.xcf +0 -0
  87. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/python-logo-green.png +0 -0
  88. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/images/python-logo-green.xcf +0 -0
  89. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/index.md +0 -0
  90. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/pyinaturalist.controllers.rst +0 -0
  91. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/pyinaturalist.models.rst +0 -0
  92. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/reference.md +0 -0
  93. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/docs/user_guide.md +0 -0
  94. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/Data Visualizations - Matplotlib.ipynb +0 -0
  95. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/Data Visualizations - Regional Activity Report.ipynb +0 -0
  96. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/Data Visualizations - Regional Observation Stats.ipynb +0 -0
  97. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/Data Visualizations - Seaborn.ipynb +0 -0
  98. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/README.md +0 -0
  99. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/Tutorial_1_Observations.ipynb +0 -0
  100. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/Tutorial_2_Taxa.ipynb +0 -0
  101. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/Tutorial_3_Data_Visualizations.ipynb +0 -0
  102. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/__init__.py +0 -0
  103. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/combined_activity_stats.png +0 -0
  104. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/histogram_by_iconic_taxon.png +0 -0
  105. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/identifiers_by_month.png +0 -0
  106. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/observation_image_grid.png +0 -0
  107. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/observation_map.png +0 -0
  108. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/observations_by_date_and_time.png +0 -0
  109. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/observations_by_month.png +0 -0
  110. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/observations_by_quality_grade.png +0 -0
  111. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/observations_by_year.png +0 -0
  112. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/observers_by_month.png +0 -0
  113. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/taxa_by_month.png +0 -0
  114. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/total_observations_by_iconic_taxon.png +0 -0
  115. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/images/total_taxa_by_iconic_taxon.png +0 -0
  116. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/observation_photo_metadata.py +0 -0
  117. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/observations_to_gpx.py +0 -0
  118. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/observations_to_tsp.py +0 -0
  119. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/sample_data/fips_to_inat_place_ids.json +0 -0
  120. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/sample_data/observation_fields.parquet +0 -0
  121. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/sample_data/observations.gpx +0 -0
  122. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/sample_data/observations.tsp +0 -0
  123. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/sample_data/photo_info.html +0 -0
  124. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/sample_data/us_county_fips_codes.csv +0 -0
  125. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/examples/sample_data.py +0 -0
  126. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinat/__init__.py +0 -0
  127. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/auth.py +0 -0
  128. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/client.py +0 -0
  129. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/__init__.py +0 -0
  130. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/base_controller.py +0 -0
  131. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/controlled_term_controller.py +0 -0
  132. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/observation_controller.py +0 -0
  133. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/place_controller.py +0 -0
  134. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/project_controller.py +0 -0
  135. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/taxon_controller.py +0 -0
  136. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/controllers/user_controller.py +0 -0
  137. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/converters.py +0 -0
  138. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/docs/__init__.py +0 -0
  139. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/docs/docstrings.py +0 -0
  140. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/docs/emoji.py +0 -0
  141. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/docs/signatures.py +0 -0
  142. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/exceptions.py +0 -0
  143. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/base.py +0 -0
  144. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/conservation_status.py +0 -0
  145. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/controlled_term.py +0 -0
  146. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/identification.py +0 -0
  147. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/lazy_property.py +0 -0
  148. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/life_list.py +0 -0
  149. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/message.py +0 -0
  150. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/observation_field.py +0 -0
  151. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/place.py +0 -0
  152. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/project.py +0 -0
  153. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/search.py +0 -0
  154. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/models/user.py +0 -0
  155. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/node_api.py +0 -0
  156. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/paginator.py +0 -0
  157. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/py.typed +0 -0
  158. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/rest_api.py +0 -0
  159. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v0/__init__.py +0 -0
  160. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v0/observation_fields.py +0 -0
  161. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v0/observations.py +0 -0
  162. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/__init__.py +0 -0
  163. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/controlled_terms.py +0 -0
  164. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/identifications.py +0 -0
  165. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/messages.py +0 -0
  166. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/observation_fields.py +0 -0
  167. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/places.py +0 -0
  168. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/posts.py +0 -0
  169. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/projects.py +0 -0
  170. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/search.py +0 -0
  171. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/taxa.py +0 -0
  172. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/pyinaturalist/v1/users.py +0 -0
  173. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/__init__.py +0 -0
  174. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/conftest.py +0 -0
  175. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/controllers/test_controlled_terms_controller.py +0 -0
  176. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/controllers/test_observation_controller.py +0 -0
  177. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/controllers/test_place_controller.py +0 -0
  178. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/controllers/test_project_controller.py +0 -0
  179. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/controllers/test_taxon_controller.py +0 -0
  180. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/controllers/test_user_controller.py +0 -0
  181. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/docs/test_docstrings.py +0 -0
  182. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/docs/test_signatures.py +0 -0
  183. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/add_project_observation.json +0 -0
  184. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/create_observation_fail.json +0 -0
  185. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/create_observation_result.json +0 -0
  186. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/create_observation_v1.json +0 -0
  187. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_access_token.json +0 -0
  188. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_access_token_401.json +0 -0
  189. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_all_observation_identifiers_ex_results.json +0 -0
  190. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_all_observation_observers_ex_results.json +0 -0
  191. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_all_observation_species_counts_ex_results.json +0 -0
  192. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_all_observation_species_counts_page1.json +0 -0
  193. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_all_observation_species_counts_page2.json +0 -0
  194. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_controlled_terms.json +0 -0
  195. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_controlled_terms_for_taxon.json +0 -0
  196. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_identifications.json +0 -0
  197. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_identifications.py +0 -0
  198. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_jwt.json +0 -0
  199. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_messages.json +0 -0
  200. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_nonexistent_observation.json +0 -0
  201. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation.json +0 -0
  202. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation.py +0 -0
  203. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_2.json +0 -0
  204. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_fields.py +0 -0
  205. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_fields_page1.json +0 -0
  206. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_fields_page2.json +0 -0
  207. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_day.json +0 -0
  208. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_day.py +0 -0
  209. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_hour.json +0 -0
  210. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_hour.py +0 -0
  211. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_month.json +0 -0
  212. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_month.py +0 -0
  213. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_month_of_year.json +0 -0
  214. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_month_of_year.py +0 -0
  215. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_week.json +0 -0
  216. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_week.py +0 -0
  217. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_week_of_year.json +0 -0
  218. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_histogram_week_of_year.py +0 -0
  219. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_identifiers_ex_results.json +0 -0
  220. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_identifiers_node_page1.json +0 -0
  221. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_identifiers_node_page2.json +0 -0
  222. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_observers_ex_results.json +0 -0
  223. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_observers_node_page1.json +0 -0
  224. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_observers_node_page2.json +0 -0
  225. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_popular_field_values.json +0 -0
  226. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_popular_field_values.py +0 -0
  227. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_species_counts.json +0 -0
  228. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_species_counts.py +0 -0
  229. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_taxon_summary.py +0 -0
  230. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_taxon_summary_conserved.json +0 -0
  231. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_taxon_summary_listed.json +0 -0
  232. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_taxonomy.json +0 -0
  233. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_with_ofvs.json +0 -0
  234. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observation_with_quality_metrics.json +0 -0
  235. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations.atom +0 -0
  236. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations.csv +0 -0
  237. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations.dwc +0 -0
  238. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations.js +0 -0
  239. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations.json +0 -0
  240. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations.kml +0 -0
  241. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations_by_id.json +0 -0
  242. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations_by_id.py +0 -0
  243. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations_node.py +0 -0
  244. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations_node_page1.json +0 -0
  245. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_observations_node_page2.json +0 -0
  246. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_places_autocomplete.json +0 -0
  247. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_places_autocomplete.py +0 -0
  248. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_places_by_id.json +0 -0
  249. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_places_by_id.py +0 -0
  250. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_places_nearby.json +0 -0
  251. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_places_nearby.py +0 -0
  252. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_posts_login.json +0 -0
  253. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_posts_project.json +0 -0
  254. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_projects.json +0 -0
  255. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_projects.py +0 -0
  256. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_projects_by_id.json +0 -0
  257. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_projects_by_id.py +0 -0
  258. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_projects_obs_fields.json +0 -0
  259. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_search.json +0 -0
  260. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_search.py +0 -0
  261. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa.json +0 -0
  262. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa.py +0 -0
  263. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_autocomplete.json +0 -0
  264. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_autocomplete.py +0 -0
  265. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_autocomplete_minified.py +0 -0
  266. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_by_id.json +0 -0
  267. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_by_id.py +0 -0
  268. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_by_id_conservation_statuses.json +0 -0
  269. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_map_layers.json +0 -0
  270. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_with_all_names.json +0 -0
  271. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_taxa_with_preferred_place.json +0 -0
  272. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_user_by_id.json +0 -0
  273. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_user_by_id.py +0 -0
  274. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_users_autocomplete.json +0 -0
  275. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/get_users_autocomplete.py +0 -0
  276. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/obs_image.jpg +0 -0
  277. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/obs_sound.wav +0 -0
  278. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/post_observation_photos.json +0 -0
  279. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/post_observation_photos_list.json +0 -0
  280. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/post_observation_sounds.json +0 -0
  281. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/post_observation_sounds_list.json +0 -0
  282. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/post_put_observation_field_value.json +0 -0
  283. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/put_projects.json +0 -0
  284. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/update_observation_result.json +0 -0
  285. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/upload_photos.json +0 -0
  286. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/upload_photos_and_sounds.json +0 -0
  287. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/sample_data/upload_sounds.json +0 -0
  288. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_auth.py +0 -0
  289. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_client.py +0 -0
  290. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_compat.py +0 -0
  291. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_paginator.py +0 -0
  292. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/test_request_params.py +0 -0
  293. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v0/test_observation_fields_v0.py +0 -0
  294. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v0/test_observations_v0.py +0 -0
  295. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_controlled_terms.py +0 -0
  296. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_identifications.py +0 -0
  297. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_messages.py +0 -0
  298. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_node_api.py +0 -0
  299. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_observation_fields.py +0 -0
  300. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_places.py +0 -0
  301. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_posts.py +0 -0
  302. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_projects.py +0 -0
  303. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_taxa.py +0 -0
  304. {pyinaturalist-0.19.0.dev0 → pyinaturalist-0.19.0.dev2}/test/v1/test_users.py +0 -0
@@ -3,12 +3,14 @@ Thanks to the following individuals for providing pull requests, feedback, bug r
3
3
  questions, and other contributions that have helped to improve pyinaturalist:
4
4
 
5
5
  * [AE Hamrick](https://github.com/AEHamrick)
6
+ * [abubelinha](https://github.com/abubelinha)
6
7
  * [Ben Armstrong](https://github.com/synrg)
7
8
  * [Dr. Tom August](https://github.com/AugustT)
8
9
  * [Eduardo Ramírez](https://github.com/eduramirezh)
9
10
  * [Felipe S Barros](https://github.com/FelipeSBarros)
10
- * [Ken-ichi Ueda](https://github.com/kueda/)
11
+ * [Ken-ichi Ueda](https://github.com/kueda)
11
12
  * [Matt Muir](https://forum.inaturalist.org/u/muir)
13
+ * [Nicolas Noé](https://github.com/niconoe)
12
14
  * [Patrick Leary](https://github.com/pleary)
13
15
  * [Peter Desmet](https://github.com/peterdesmet)
14
16
  * [pisum](https://forum.inaturalist.org/u/pisum)
@@ -6,7 +6,7 @@
6
6
  * Added new **User** endpoint: `get_current_user()`
7
7
 
8
8
  ### Modified Endpoints
9
- Add support for searching observations by observation fields, using a new `fields` param for the following functions:
9
+ Add support for searching observations by observation fields, using a new `observation_fields` param for the following functions:
10
10
  * `get_observations()`
11
11
  * `get_observation_histogram()`
12
12
  * `get_observation_identifiers()`
@@ -14,6 +14,12 @@ Add support for searching observations by observation fields, using a new `field
14
14
  * `get_observation_popular_field_values()`
15
15
  * `get_observation_species_counts()`
16
16
 
17
+ ### Models
18
+ * Add `Observation.ident_taxon_ids` dynamic property to get all identification taxon IDs (with ancestors)
19
+ * Add `Observation.cumulative_ids` dynamic property to calculate agreements/total community identifications
20
+ * Add `Sound` model for `Observation.sounds`
21
+ * Fix initialization of `ListedTaxon.place`
22
+
17
23
  ## 0.18.0 (2023-02-27)
18
24
 
19
25
  ### New Endpoints
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinaturalist
3
- Version: 0.19.0.dev0
3
+ Version: 0.19.0.dev2
4
4
  Summary: iNaturalist API client for python
5
5
  Home-page: https://github.com/pyinat/pyinaturalist
6
6
  License: MIT
7
7
  Keywords: inaturalist,biodiversity,conservation,citizen-science,community-science,api,api-client,data-model,requests
8
- Author: Nicolas Noé
9
- Author-email: nicolas@niconoe.eu
8
+ Author: Jordan Cook
9
+ Author-email: jordan.cook.git@proton.me
10
10
  Requires-Python: >=3.7,<4.0
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
@@ -24,13 +24,13 @@ Classifier: Typing :: Typed
24
24
  Provides-Extra: all
25
25
  Provides-Extra: docs
26
26
  Requires-Dist: attrs (>=21.2)
27
- Requires-Dist: furo (>=2022.9,<2023.0) ; extra == "docs"
27
+ Requires-Dist: furo (>=2023.3,<2024.0) ; extra == "docs"
28
28
  Requires-Dist: ipython (>=7.25.0,<8.0.0) ; extra == "docs"
29
29
  Requires-Dist: keyring (>=22.3)
30
30
  Requires-Dist: linkify-it-py (>=1.0.1,<2.0.0) ; extra == "docs"
31
- Requires-Dist: myst-parser (>=0.19,<0.20) ; extra == "docs"
32
- Requires-Dist: nbsphinx (>=0.8,<0.9) ; extra == "docs"
33
- Requires-Dist: platformdirs (>=2.5)
31
+ Requires-Dist: myst-parser (>=1.0,<2.0) ; extra == "docs"
32
+ Requires-Dist: nbsphinx (>=0.9,<0.10) ; extra == "docs"
33
+ Requires-Dist: platformdirs (>=2.6)
34
34
  Requires-Dist: python-dateutil (>=2.0)
35
35
  Requires-Dist: python-forge (>=18.6)
36
36
  Requires-Dist: requests (>=2.22)
@@ -43,7 +43,7 @@ Requires-Dist: sphinx-automodapi (>=0.14,<0.15) ; extra == "docs"
43
43
  Requires-Dist: sphinx-copybutton (>=0.5) ; extra == "docs"
44
44
  Requires-Dist: sphinx-design (>=0.2.0) ; extra == "docs"
45
45
  Requires-Dist: sphinxcontrib-apidoc (>=0.3,<0.4) ; extra == "docs"
46
- Requires-Dist: ujson (>=5.4.0,<6.0.0) ; extra == "all"
46
+ Requires-Dist: ujson (>5.0) ; extra == "all"
47
47
  Project-URL: Documentation, https://pyinaturalist.readthedocs.io
48
48
  Project-URL: Repository, https://github.com/pyinat/pyinaturalist
49
49
  Description-Content-Type: text/markdown
@@ -12,10 +12,10 @@ from pyinaturalist.request_params import get_interval_ranges
12
12
  from pyinaturalist.session import ClientSession, clear_cache
13
13
  from pyinaturalist.v0 import *
14
14
  from pyinaturalist.v1 import *
15
- from pyinaturalist.v2 import *
16
15
 
17
16
  # For disambiguation
18
17
  from pyinaturalist.v0 import create_observation as create_observation_v0
19
18
  from pyinaturalist.v0 import get_observations as get_observations_v0
20
19
  from pyinaturalist.v0 import update_observation as update_observation_v0
21
20
  from pyinaturalist.v0 import delete_observation as delete_observation_v0
21
+ from pyinaturalist.v2 import get_observations as get_observations_v2
@@ -196,6 +196,15 @@ SOURCES = ['places', 'projects', 'taxa', 'users']
196
196
  # Endpoint-specific options for multiple choice parameters
197
197
  V0_OBS_ORDER_BY_PROPERTIES = ['date_added', 'observed_on']
198
198
  V1_OBS_ORDER_BY_PROPERTIES = ['created_at', 'id', 'observed_on', 'species_guess', 'votes']
199
+ V2_OBS_ORDER_BY_PROPERTIES = [
200
+ 'created_at',
201
+ 'id',
202
+ 'observed_on',
203
+ 'species_guess',
204
+ 'updated_at',
205
+ 'votes',
206
+ 'random',
207
+ ]
199
208
  PROJECT_ORDER_BY_PROPERTIES = ['created', 'distance', 'featured', 'recent_posts', 'updated']
200
209
 
201
210
  # Multiple-choice request parameters, with keys mapped to their possible choices (non-endpoint-specific)
@@ -14,7 +14,7 @@ from sphinx_autodoc_typehints import format_annotation
14
14
  from pyinaturalist.constants import DOCS_DIR
15
15
  from pyinaturalist.models import LazyProperty, get_lazy_properties
16
16
 
17
- IGNORE_PROPERTIES = ['row']
17
+ IGNORE_PROPERTIES = ['_row', '_str_attrs']
18
18
  MODEL_DOC_DIR = join(DOCS_DIR, 'models')
19
19
 
20
20
 
@@ -188,7 +188,7 @@ def _observation_v1(
188
188
  verifiable: Optional[bool] = None,
189
189
  not_id: Optional[MultiInt] = None,
190
190
  sound_license: Optional[MultiStr] = None,
191
- fields: Optional[Union[List, Dict]] = None,
191
+ observation_fields: Optional[Union[List, Dict]] = None,
192
192
  ofv_datatype: Optional[MultiStr] = None,
193
193
  place_id: Optional[MultiInt] = None,
194
194
  project_id: Optional[MultiInt] = None,
@@ -260,7 +260,7 @@ def _observation_v1(
260
260
  project_id: Must be added to the project this ID or slug
261
261
  rank: Taxon must have this rank
262
262
  site_id: Must be affiliated with the iNaturalist network website with this ID
263
- fields: Must have these observation fields (optionally with values)
263
+ observation_fields: Must have these observation fields (optionally with values)
264
264
  ofv_datatype: Must have an observation field value with this datatype
265
265
  sound_license: Must have at least one sound with this license
266
266
  without_taxon_id: Exclude observations of these taxa and their descendants
@@ -333,13 +333,24 @@ def _observation_v0(
333
333
  on: Filter by date string
334
334
  extra: Retrieve additional information.
335
335
  **'projects'** returns info about the projects the observations have been added to,
336
- **'fields'** returns observation field values,
336
+ **'observation_fields'** returns observation field values,
337
337
  **'observation_photos'** returns information about the photos' relationship with the
338
338
  observation, like their order.
339
339
  response_format: A supported response format to return
340
340
  """
341
341
 
342
342
 
343
+ # Observation params that are only in the v2 API
344
+ def _observation_v2(
345
+ fields: Optional[Union[List[str], Dict[str, Any]]] = None,
346
+ except_fields: Optional[List[str]] = None,
347
+ ):
348
+ """Args:
349
+ fields: Data fields to return in the response
350
+ except_fields: Data fields to exclude from the response (and include all others)
351
+ """
352
+
353
+
343
354
  def _observation_histogram(
344
355
  date_field: str = 'observed',
345
356
  interval: str = 'month_of_year',
@@ -384,7 +395,7 @@ def _create_observation(
384
395
  ):
385
396
  """Args:
386
397
  species_guess: Equivalent to the 'What did you see?' field on the observation form.
387
- iNat will try to choose a single taxon based on this, but it may fail if it's ambuguous
398
+ iNat will try to choose a single taxon based on this, but it may fail if it's ambiguous
388
399
  taxon_id: ID of the taxon to associate with this observation
389
400
  observed_on: Alias for ``observed_on_string``; accepts :py:class:`~datetime.datetime` objects.
390
401
  observed_on_string: Date/time of the observation. Time zone will default to the user's
@@ -36,6 +36,7 @@ from pyinaturalist.models import (
36
36
  Project,
37
37
  ResponseOrObjects,
38
38
  SearchResult,
39
+ Sound,
39
40
  Taxon,
40
41
  TaxonCount,
41
42
  TaxonSummary,
@@ -130,6 +131,7 @@ UNIQUE_RESPONSE_ATTRS = {
130
131
  'score': SearchResult,
131
132
  'last_observation_id': ListedTaxon,
132
133
  'listed_taxon': TaxonSummary,
134
+ 'native_sound_id': Sound,
133
135
  'count': TaxonCount,
134
136
  'rank': Taxon,
135
137
  'roles': User,
@@ -70,7 +70,7 @@ def upper(value) -> Optional[str]:
70
70
  # Models imported in order of dependencies
71
71
  # --------------------------------------------------------
72
72
 
73
- from pyinaturalist.models.photo import IconPhoto, Photo
73
+ from pyinaturalist.models.media import IconPhoto, Photo, Sound
74
74
  from pyinaturalist.models.place import Place
75
75
  from pyinaturalist.models.user import User, UserCount, UserCounts
76
76
  from pyinaturalist.models.checklist import Checklist, EstablishmentMeans, ListedTaxon
@@ -126,7 +126,8 @@ class ListedTaxon(EstablishmentMeans):
126
126
 
127
127
  @place_id.setter
128
128
  def place_id(self, value: int):
129
- self.place = Place(id=value)
129
+ self.place = self.place or Place()
130
+ self.place.id = value
130
131
 
131
132
  @property
132
133
  def updater_id(self) -> Optional[int]:
@@ -134,7 +135,8 @@ class ListedTaxon(EstablishmentMeans):
134
135
 
135
136
  @updater_id.setter
136
137
  def updater_id(self, value: int):
137
- self.updater = User(id=value)
138
+ self.updater = self.updater or User()
139
+ self.updater.id = value
138
140
 
139
141
  @property
140
142
  def user_id(self) -> Optional[int]:
@@ -142,7 +144,8 @@ class ListedTaxon(EstablishmentMeans):
142
144
 
143
145
  @user_id.setter
144
146
  def user_id(self, value: int):
145
- self.user = User(id=value)
147
+ self.user = self.user or User()
148
+ self.user.id = value
146
149
 
147
150
  @property
148
151
  def _row(self) -> TableRow:
@@ -19,13 +19,7 @@ from pyinaturalist.models import BaseModel, define_model, field
19
19
 
20
20
 
21
21
  @define_model
22
- class Photo(BaseModel):
23
- """:fa:`camera` An observation photo, based on the schema of photos from:
24
-
25
- * `GET /observations <https://api.inaturalist.org/v1/docs/#!/Observations/get_observations>`_
26
- * `GET /taxa <https://api.inaturalist.org/v1/docs/#!/Taxa/get_taxa>`
27
- """
28
-
22
+ class BaseMedia(BaseModel):
29
23
  attribution: str = field(default=None, doc='License attribution')
30
24
  license_code: str = field(
31
25
  default=None,
@@ -33,6 +27,30 @@ class Photo(BaseModel):
33
27
  options=ALL_LICENSES,
34
28
  doc='Creative Commons license code',
35
29
  )
30
+
31
+ @property
32
+ def ext(self) -> str:
33
+ """File extension from URL"""
34
+ return self.url.lower().split('.')[-1].split('?')[0] # type: ignore
35
+
36
+ @property
37
+ def has_cc_license(self) -> bool:
38
+ """Determine if this photo has a Creative Commons license"""
39
+ return self.license_code in CC_LICENSES
40
+
41
+ @property
42
+ def _str_attrs(self) -> List[str]:
43
+ return ['id', 'license_code', 'url']
44
+
45
+
46
+ @define_model
47
+ class Photo(BaseMedia):
48
+ """:fa:`camera` An observation photo, based on the schema of photos from:
49
+
50
+ * `GET /observations <https://api.inaturalist.org/v1/docs/#!/Observations/get_observations>`_
51
+ * `GET /taxa <https://api.inaturalist.org/v1/docs/#!/Taxa/get_taxa>`
52
+ """
53
+
36
54
  observation_id: int = field(default=None, doc='Associated observation ID')
37
55
  original_dimensions: Tuple[int, int] = field(
38
56
  converter=format_dimensions, default=(0, 0), doc='Dimensions of original image'
@@ -65,21 +83,11 @@ class Photo(BaseModel):
65
83
  value = value['photo']
66
84
  return super(Photo, cls).from_json(value, **kwargs)
67
85
 
68
- @property
69
- def ext(self) -> str:
70
- """File extension from URL"""
71
- return self.url.lower().split('.')[-1].split('?')[0]
72
-
73
86
  @property
74
87
  def dimensions_str(self) -> str:
75
88
  """Dimensions as a string, formatted as ``{width}x{height}``"""
76
89
  return f'{self.original_dimensions[0]}x{self.original_dimensions[1]}'
77
90
 
78
- @property
79
- def has_cc_license(self) -> bool:
80
- """Determine if this photo has a Creative Commons license"""
81
- return self.license_code in CC_LICENSES
82
-
83
91
  @property
84
92
  def info_url(self) -> str:
85
93
  """Photo info URL on iNaturalist.org"""
@@ -147,10 +155,6 @@ class Photo(BaseModel):
147
155
  'URL': self.original_url,
148
156
  }
149
157
 
150
- @property
151
- def _str_attrs(self) -> List[str]:
152
- return ['id', 'license_code', 'url']
153
-
154
158
 
155
159
  @define_model
156
160
  class IconPhoto(Photo):
@@ -179,3 +183,33 @@ class IconPhoto(Photo):
179
183
  @property
180
184
  def _str_attrs(self) -> List[str]:
181
185
  return ['iconic_taxon_name', 'url']
186
+
187
+
188
+ @define_model
189
+ class Sound(BaseMedia):
190
+ file_content_type: str = field(default=None)
191
+ file_url: str = field(default=None)
192
+ native_sound_id: str = field(default=None)
193
+ secret_token: str = field(default=None)
194
+ subtype: str = field(default=None)
195
+
196
+ # Unused attributes
197
+ # flags: List = field(factory=list)
198
+ # play_local: bool = field(default=None)
199
+
200
+ # Aliases
201
+ @property
202
+ def mimetype(self) -> str:
203
+ return self.file_content_type
204
+
205
+ @property
206
+ def url(self) -> str:
207
+ return self.file_url
208
+
209
+ @property
210
+ def _row(self) -> TableRow:
211
+ return {
212
+ 'ID': self.id,
213
+ 'License': self.license_code,
214
+ 'URL': self.url,
215
+ }
@@ -1,5 +1,7 @@
1
1
  from datetime import datetime
2
- from typing import Any, Dict, List
2
+ from itertools import chain
3
+ from typing import Any, Dict, List, Tuple
4
+ from warnings import warn
3
5
 
4
6
  from pyinaturalist.constants import (
5
7
  ALL_LICENSES,
@@ -22,6 +24,7 @@ from pyinaturalist.models import (
22
24
  ObservationFieldValue,
23
25
  Photo,
24
26
  ProjectObservation,
27
+ Sound,
25
28
  Taxon,
26
29
  User,
27
30
  coordinate_pair,
@@ -40,15 +43,20 @@ class Observation(BaseModel):
40
43
  :v1:`GET /observations <Observations/get_observations>`
41
44
  """
42
45
 
46
+ application: Dict = field(factory=dict, doc='Application that created the observation')
43
47
  created_at: datetime = datetime_now_field(doc='Date and time the observation was created')
44
48
  captive: bool = field(
45
49
  default=None, doc='Indicates if the organism is non-wild (captive or cultivated)'
46
50
  )
47
51
  community_taxon_id: int = field(default=None, doc='The current community identification taxon')
52
+ context_geoprivacy: str = field(default=None)
53
+ context_taxon_geoprivacy: str = field(default=None)
54
+ context_user_geoprivacy: str = field(default=None)
48
55
  description: str = field(default=None, doc='Observation description')
49
56
  faves: List[Dict] = field(
50
57
  factory=list, doc='Details on users who have favorited the observation'
51
58
  )
59
+ flags: List[Dict] = field(factory=list)
52
60
  geoprivacy: str = field(default=None, options=GEOPRIVACY_LEVELS, doc='Location privacy level')
53
61
  identifications_count: int = field(default=0, doc='Total number of identifications')
54
62
  identifications_most_agree: bool = field(
@@ -126,16 +134,19 @@ class Observation(BaseModel):
126
134
  site_id: int = field(
127
135
  default=None, doc='Site ID for iNaturalist network members, or ``1`` for inaturalist.org'
128
136
  )
129
- sounds: List[Dict] = field(factory=list, doc='Observation sound files')
130
137
  species_guess: str = field(
131
138
  default=None, doc="Taxon name from observer's initial identification"
132
139
  )
133
140
  tags: List[str] = field(factory=list, doc='Arbitrary user tags added to the observation')
141
+ taxon_geoprivacy: str = field(default=None)
134
142
  updated_at: DateTime = datetime_field(doc='Date and time the observation was last updated')
135
143
  uri: str = field(default=None, doc='Link to observation details page')
136
144
  uuid: str = field(
137
145
  default=None, doc='Universally unique ID; generally preferred over ``id`` where possible'
138
146
  )
147
+ viewer_trusted_by_observer: bool = field(
148
+ default=None, doc='Observer trusts the authenticated user with access to hidden coordinates'
149
+ )
139
150
  votes: List[Dict] = field(factory=list, doc='Votes on data quality assessment metrics')
140
151
 
141
152
  # Lazy-loaded model objects
@@ -161,6 +172,9 @@ class Observation(BaseModel):
161
172
  type=List[ProjectObservation],
162
173
  doc='Details on any projects that the observation has been added to',
163
174
  )
175
+ sounds: property = LazyProperty(
176
+ Sound.from_json_list, type=List[Sound], doc='Observation sound files'
177
+ )
164
178
  taxon: property = LazyProperty(Taxon.from_json, type=Taxon, doc='Observation taxon')
165
179
  user: property = LazyProperty(User.from_json, type=User, doc='Observer')
166
180
 
@@ -170,15 +184,16 @@ class Observation(BaseModel):
170
184
  # created_at_details: Dict = field(factory=dict)
171
185
  # created_time_zone: str = field(default=None)
172
186
  # faves_count: int = field(default=None)
173
- # flags: List = field(factory=list)
174
187
  # geojson: Dict = field(factory=dict)
175
188
  # id_please: bool = field(default=None)
176
189
  # map_scale: int = field(default=None)
177
190
  # non_owner_ids: List = field(factory=list)
191
+ # non_traditional_projects: property = LazyProperty(Project.from_json_list, type=List[Project])
178
192
  # observed_on_details: Dict = field(factory=dict)
179
193
  # observed_on_string: str = field(default=None)
180
194
  # observation_photos: List[Photo] = field(converter=Photo.from_dict_list, factory=list)
181
195
  # observed_time_zone: str = field(default=None)
196
+ # private_geojson: Dict = field(factory=dict)
182
197
  # spam: bool = field(default=None)
183
198
  # time_observed_at: DateTime = datetime_attr
184
199
  # time_zone_offset: str = field(default=None)
@@ -191,9 +206,11 @@ class Observation(BaseModel):
191
206
  observed_on = kwargs.pop('time_observed_at', None)
192
207
  if not isinstance(kwargs['observed_on'], datetime) and observed_on:
193
208
  kwargs['observed_on'] = observed_on
209
+
194
210
  # Set default URL based on observation ID
195
211
  if not kwargs.get('uri'):
196
212
  kwargs['uri'] = f'{INAT_BASE_URL}/observations/{kwargs.get("id", "")}'
213
+
197
214
  # Set identifications_count if missing
198
215
  if kwargs.get('identifications') and not kwargs.get('identifications_count'):
199
216
  kwargs['identifications_count'] = len(kwargs['identifications'])
@@ -201,12 +218,41 @@ class Observation(BaseModel):
201
218
 
202
219
  @classmethod
203
220
  def from_id(cls, id: int):
204
- """Lookup and create a new Observation object from an ID"""
221
+ """**[Deprecated]** Lookup and create a new Observation object from an ID"""
205
222
  from pyinaturalist.v1 import get_observation
206
223
 
224
+ warn(
225
+ DeprecationWarning(
226
+ 'This method is deprecated; please use iNatClient.observations() instead'
227
+ )
228
+ )
207
229
  json = get_observation(id)
208
230
  return cls.from_json(json)
209
231
 
232
+ @property
233
+ def cumulative_ids(self) -> Tuple[int, int]:
234
+ """Calculate the cumulative community ID score (agreements/total), as shown on the observation UI
235
+
236
+ Returns:
237
+ ``(agreements, total)``
238
+ """
239
+ idents_count = 0
240
+ idents_agree = 0
241
+ ident_taxon_ids = self.ident_taxon_ids
242
+
243
+ for ident in filter(lambda i: i.current, self.identifications):
244
+ user_taxon_ids = ident.taxon.ancestor_ids + [ident.taxon.id]
245
+ if self.community_taxon_id in user_taxon_ids:
246
+ # Count towards total & agree:
247
+ if ident.taxon.id in ident_taxon_ids:
248
+ idents_count += 1
249
+ idents_agree += 1
250
+ else:
251
+ # Maverick counts against:
252
+ idents_count += 1
253
+
254
+ return (idents_agree, idents_count)
255
+
210
256
  @property
211
257
  def default_photo(self) -> Photo:
212
258
  """Get the default observation photo if any; otherwise use taxon default photo or icon"""
@@ -217,6 +263,15 @@ class Observation(BaseModel):
217
263
  else:
218
264
  return IconPhoto.from_iconic_taxon('unknown')
219
265
 
266
+ @property
267
+ def ident_taxon_ids(self) -> List[int]:
268
+ """Get all taxon IDs (including ancestors) from identifications"""
269
+ ident_taxa = [
270
+ ident.taxon for ident in self.identifications if ident.taxon and ident.current
271
+ ]
272
+ ident_ids = chain.from_iterable([t.ancestor_ids + [t.id] for t in ident_taxa])
273
+ return list(set(ident_ids))
274
+
220
275
  @property
221
276
  def _row(self) -> TableRow:
222
277
  return {
@@ -224,10 +224,9 @@ class Taxon(BaseModel):
224
224
  """Info URL on iNaturalist.org"""
225
225
  return f'{INAT_BASE_URL}/taxa/{self.id}'
226
226
 
227
- # TODO: Experimental; not sure if I want to have API calls in model classes
228
227
  @classmethod
229
228
  def from_id(cls, id: int) -> 'Taxon':
230
- """Lookup and create a new Taxon object by ID"""
229
+ """**[Deprecated]** Lookup and create a new Taxon object by ID"""
231
230
  from pyinaturalist.v1 import get_taxa_by_id
232
231
 
233
232
  warn(DeprecationWarning('This method is deprecated; please use iNatClient.taxa() instead'))
@@ -177,7 +177,8 @@ def convert_observation_field_params(params: RequestParams) -> RequestParams:
177
177
 
178
178
 
179
179
  def convert_observation_field_filters(params: RequestParams) -> RequestParams:
180
- fields = params.pop('fields', None)
180
+ """Convert observation field filters from simplified format to full request params"""
181
+ fields = params.pop('observation_fields', None)
181
182
  if not fields:
182
183
  return params
183
184
  # List of fields to filter on (any value)
@@ -357,7 +358,7 @@ def _validate_multiple_choice_param(
357
358
  return True
358
359
  if not isinstance(value, list):
359
360
  value = [value]
360
- return all([v in choices for v in value])
361
+ return all(v in choices for v in value)
361
362
 
362
363
  def normalize(value):
363
364
  if not value: