Skip to content

Commit 0f41cc2

Browse files
Merge pull request #214 from ImperialCollegeLondon/213-improve-rendering-of-metadata-on-zenodo-for-sequenced-taxonomy-sheets
Improve rendering of Zenodo metadata for sequenced taxonomy sheets
2 parents 18c4281 + 8f0221b commit 0f41cc2

6 files changed

Lines changed: 100 additions & 26 deletions

File tree

‎safedata_validator/taxa.py‎

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
blank_value,
4949
)
5050

51-
GBIF_BACKBONE_RANKS = [
51+
ALL_BACKBONE_RANKS = [
52+
"domain",
53+
"superkingdom",
5254
"kingdom",
5355
"phylum",
5456
"class",
@@ -59,19 +61,16 @@
5961
"subspecies",
6062
]
6163

62-
# Possible top level ranks one of which must be provided
63-
SEQ_TOP_RANKS = ["domain", "superkingdom", "kingdom"]
64+
# GBIF backbone doesn't include the two highest-level ranks ("domain", "superkingdom")
65+
GBIF_BACKBONE_RANKS = ALL_BACKBONE_RANKS[2:]
6466

65-
# List of additional ranks (in descending order) can be used to describe the taxonomy
66-
# further
67-
SEQ_ADDITIONAL_RANKS = [
68-
"phylum",
69-
"class",
70-
"order",
71-
"family",
72-
"genus",
73-
"species",
74-
]
67+
# For sequenced taxonomy the top level rank can be any of the top three ranks ("domain",
68+
# "superkingdom", "kingdom")
69+
SEQ_TOP_RANKS = ALL_BACKBONE_RANKS[0:3]
70+
71+
# Sequenced taxonomy can then also include anything else as a backbone rank apart from
72+
# subspecies
73+
SEQ_ADDITIONAL_RANKS = ALL_BACKBONE_RANKS[3:-1]
7574

7675
# NBCI name regex
7776
NCBI_prefix_re = re.compile("^[a-z]__")
@@ -1392,7 +1391,10 @@ def repeat_names(self) -> set[str]:
13921391

13931392

13941393
def taxon_index_to_text(
1395-
taxa: list[dict], html: bool = False, indent_width: int = 4
1394+
taxa: list[dict],
1395+
html: bool = False,
1396+
indent_width: int = 4,
1397+
lowest_taxa: str | None = None,
13961398
) -> str | tags.div:
13971399
"""Render a taxon index as text or html.
13981400
@@ -1404,6 +1406,8 @@ def taxon_index_to_text(
14041406
taxa: A list of taxon dictionaries containing the taxa for a dataset.
14051407
html: Render as html or text.
14061408
indent_width: The indentation width to use for successive taxonomic ranks.
1409+
lowest_taxa: The lowest taxonomic rank that the index renders, if no rank is
1410+
provided then the index is rendered for all ranks.
14071411
14081412
Returns:
14091413
Either a HTML or text representation of the taxa tree.
@@ -1468,8 +1472,26 @@ def _format_name(tx: dict, use_html: bool = html):
14681472
if first_nm != taxon["worksheet_name"]:
14691473
surp_tx_ids.append(idx)
14701474

1475+
# Eliminate any taxa with ranks below the minimum
1476+
if lowest_taxa:
1477+
# Check that the lowest rank appears in the full set of taxa
1478+
if lowest_taxa not in ALL_BACKBONE_RANKS:
1479+
raise ValueError(
1480+
f"Rank provided to render taxa tree down to {lowest_taxa} is not a "
1481+
f"backbone rank! Should be one of: {ALL_BACKBONE_RANKS}"
1482+
)
1483+
1484+
# Generate the full list of ranks that should be rendered
1485+
rendered_ranks = ALL_BACKBONE_RANKS[: ALL_BACKBONE_RANKS.index(lowest_taxa) + 1]
1486+
1487+
# Then add any taxa that have ranks that aren't in the list of rendered ranks to
1488+
# the superfluous taxa index
1489+
for idx, taxon in enumerate(taxa):
1490+
if taxon["taxon_rank"] not in rendered_ranks:
1491+
surp_tx_ids.append(idx)
1492+
14711493
# Delete taxa that are superfluous by index
1472-
for index in sorted(surp_tx_ids, reverse=True):
1494+
for index in sorted(set(surp_tx_ids), reverse=True):
14731495
del taxa[index]
14741496

14751497
# group taxa by their parent id

‎safedata_validator/templates/description_template.html‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,25 @@ <h2>GBIF taxa details</h2>
141141
<h2>Bioinformatics taxonomy details</h2>
142142

143143
<p>The dataset contains taxa that were identified as part of a bioinformatics
144-
workkflow. The taxa listed in the dataset are compiled to provide the taxonomic
145-
summary shown below. These taxa are not independently validated as they have been
146-
generated using established reference databases.
144+
workflow. These taxa are not independently validated as they have been generated using
145+
established reference databases. Details of the reference databases used by each
146+
sequenced taxonomy worksheet are provided below. Taxonomy trees (resolved to phylum
147+
level) are also provided. For more detailed information on the taxa contained in the
148+
dataset, you will need to download the data.
147149
</p>
148-
{{ seq_taxa }}
150+
{% for sheet_name, details in seq_taxa.items() %}
151+
<h3>{{ sheet_name }} taxonomy sheet</h3>
152+
<h4>Reference taxonomy database details</h4>
153+
<ul>
154+
<li>Reference taxonomy database: {{ details.database_name }}</li>
155+
<li>Database version used: {{ details.database_version }}</li>
156+
{% if details.database_link is not none %}
157+
<li>Database link: <a href="{{details.database_link}}">{{ details.database_link }}</a></li>
158+
{% endif %}
159+
</ul>
160+
<h4>Taxa tree (to phylum level)</h4>
161+
<p>{{details.index}}</p>
162+
{% endfor %}
149163
{% endif %}
150164

151165
{% endif %}

‎safedata_validator/zenodo.py‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -684,19 +684,17 @@ def dataset_description(
684684
sequenced_taxa_sheets = dataset_metadata["sequenced_taxa"]
685685
if sequenced_taxa_sheets:
686686
context_dict["seq_taxa"] = {}
687-
context_dict["seq_taxa_metadata"] = {}
688687
for sheet_name, taxon_data in sequenced_taxa_sheets.items():
689-
context_dict["seq_taxa"][f"{sheet_name}"] = taxon_index_to_text(
690-
taxa=taxon_data["taxon_index"], html=True
691-
)
692688
context_dict["seq_taxa"][f"{sheet_name}"] = {
693689
"database_name": taxon_data["database_name"],
694690
"database_version": taxon_data["database_version"],
695691
"database_link": taxon_data["database_link"],
692+
"index": taxon_index_to_text(
693+
taxa=taxon_data["taxon_index"], html=True, lowest_taxa="phylum"
694+
),
696695
}
697696
else:
698697
context_dict["seq_taxa"] = None
699-
context_dict["seq_taxa_metadata"] = None
700698

701699
html = template.render(context_dict)
702700

‎test/conftest.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import certifi
1010
import openpyxl
1111
import pytest
12+
import simplejson
1213
from dotmap import DotMap
1314

1415
from safedata_validator.field import Dataset, DataWorksheet
@@ -47,7 +48,7 @@ def fixture_files():
4748
("good_seq_taxa_file", "Test_format_good_Seq.xlsx"),
4849
("bad_seq_taxa_file", "Test_format_bad_Seq.xlsx"),
4950
("good_seq_file_dataset_json", "Test_format_good_Seq.json"),
50-
("good_seq_file_zenodo_json", "zenodo_214116.json"),
51+
("good_seq_file_zenodo_json", "zenodo_292625.json"),
5152
]
5253

5354
real_files = {ky: os.path.join(fixture_dir, vl) for ky, vl in real_files}
@@ -305,6 +306,16 @@ def example_seq_files(config_filesystem, request):
305306
return wb
306307

307308

309+
@pytest.fixture()
310+
def example_dataset_metadata(config_filesystem):
311+
"""Fixture providing an example dataset metadata to use in testing."""
312+
313+
with open(FIXTURE_FILES.rf.good_seq_file_dataset_json) as f:
314+
dataset_metadata = simplejson.load(f)
315+
316+
return dataset_metadata
317+
318+
308319
@pytest.fixture()
309320
def fixture_gbif_validator(fixture_resources):
310321
"""Fixture to return GBIF taxon validators."""

0 commit comments

Comments
 (0)