Skip to content

Commit

Permalink
Merge pull request #577 from pyinat/taxonomy
Browse files Browse the repository at this point in the history
Add shortcut properties to `Taxon` for ancestors of common ranks
  • Loading branch information
JWCook authored Jul 23, 2024
2 parents 7a3a43e + f7fb2bf commit a58e1a4
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 0 deletions.
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
* Add `root_id` filter to `taxon.make_tree()` to explicitly set the root taxon instead of determining it automatically
* Fix `taxon.make_tree()` rank filtering to allow skipping any number of rank levels
* `taxon.make_tree()` now returns copies of original taxon objects instead of modifying them in-place
* Add shortcut properties to `Taxon` for ancestors of common ranks:
`Taxon.kingdom`, `phylum`, `class_` (note the `_`; 'class' is a reserved keyword), `order`, `family`, `genus`
* Update `Observation.taxon.ancestors` based on identification data, if available

### Rate limits, timeouts, and error handling
Expand Down
27 changes: 27 additions & 0 deletions pyinaturalist/models/taxon.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,33 @@ def from_sorted_json_list(cls, value: JsonResponse, **kwargs) -> List['Taxon']:
taxa.sort(key=_sort_rank_name)
return taxa

@property
def kingdom(self) -> Optional['Taxon']:
return self._get_ancestor_by_rank('kingdom')

@property
def phylum(self) -> Optional['Taxon']:
return self._get_ancestor_by_rank('phylum')

@property
def class_(self) -> Optional['Taxon']:
return self._get_ancestor_by_rank('class')

@property
def order(self) -> Optional['Taxon']:
return self._get_ancestor_by_rank('order')

@property
def family(self) -> Optional['Taxon']:
return self._get_ancestor_by_rank('family')

@property
def genus(self) -> Optional['Taxon']:
return self._get_ancestor_by_rank('genus')

def _get_ancestor_by_rank(self, rank: str) -> Optional['Taxon']:
return next((t for t in self.ancestors if t.rank == rank), None)

@property
def child_ids(self) -> List[int]:
"""Taxon IDs of direct children, sorted by rank then name"""
Expand Down
10 changes: 10 additions & 0 deletions test/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,16 @@ def test_taxon__ancestor_ids_from_ancestor_objs():
assert taxon.ancestor_ids == ids


def test_taxon_ancestor_shortcuts():
taxon = Taxon.from_json(j_taxon_1)
assert taxon.kingdom.id == 1 and taxon.kingdom.name == 'Animalia'
assert taxon.phylum.id == 47120 and taxon.phylum.name == 'Arthropoda'
assert taxon.class_.id == 47158 and taxon.class_.name == 'Insecta'
assert taxon.order.id == 47208 and taxon.order.name == 'Coleoptera'
assert taxon.family.id == 53849 and taxon.family.name == 'Silphidae'
assert taxon.genus.id == 53850 and taxon.genus.name == 'Nicrophorus'


def test_taxon__all_names():
taxon = Taxon.from_json(j_taxon_8_all_names)
assert taxon.names[1] == {
Expand Down

0 comments on commit a58e1a4

Please sign in to comment.