Skip to content

Commit

Permalink
Handle case when all available ranks are filtered out in make_tree()
Browse files Browse the repository at this point in the history
  • Loading branch information
JWCook committed Aug 13, 2024
1 parent 39b4725 commit 171d180
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 4 deletions.
5 changes: 3 additions & 2 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
* Add `User.annotated_observations_count` field
* 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
* `Taxon.flatten(hide_root=True)` now only hides the root taxon if it was automatically inserted by `make_tree()`
* Fix `taxon.make_tree()` rank filtering to handle all available ranks filtered out
* Update `taxon.make_tree()` to return copies of original taxon objects instead of modifying them in-place
* Update `Taxon.flatten(hide_root=True)` to only hide the root taxon if it was automatically inserted by `make_tree()`
* 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
Expand Down
7 changes: 5 additions & 2 deletions pyinaturalist/models/taxon.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,11 @@ def _find_root(

def _find_and_graft_root(taxa: Iterable[Taxon], include_ranks: Optional[List[str]] = None) -> Taxon:
taxa_by_id = {t.id: t for t in taxa}
max_rank = max(t.rank_level for t in taxa if not include_ranks or t.rank in include_ranks)
root_taxa = [t for t in taxa if t.rank_level == max_rank]
rank_levels = [t.rank_level for t in taxa if not include_ranks or t.rank in include_ranks]
if not rank_levels:
logger.warning('All taxon ranks excluded; returning default root')
return deepcopy(DEFAULT_ROOT)
root_taxa = [t for t in taxa if t.rank_level == max(rank_levels)]

# Add any ungrafted taxa and deduplicate
ungrafted = [
Expand Down
9 changes: 9 additions & 0 deletions test/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,15 @@ def test_make_tree__filtered():
]


def test_make_tree__all_filtered():
"""When all available ranks are filtered out, a single root node should be created"""
root = make_tree(
Taxon.from_json_list(j_life_list_2),
include_ranks=['infraclass'],
)
assert root.name == 'Life' and not root.children


def test_make_tree__preserves_originals():
"""Children/ancestors of original taxon objects should be preserved"""
taxa = Taxon.from_json_list(j_life_list_2)
Expand Down

0 comments on commit 171d180

Please sign in to comment.