Skip to content

Commit

Permalink
feat: make clade_membership node attribute optional
Browse files Browse the repository at this point in the history
Not all trees have `clade_membership` attribute on the nodes. Here I made this attribute optional in Nextclade.

If `clade_membership` is not present:

 - output JSON/NDJSON result entries will not contain `clade` field

 - `clade` column in output CSV/TSV will be empty

 - `clade` column in Nextclade Web will be empty


it does not affect any other parts of the application. Notably clade-like attributes (from `.meta.extensions.nextclade.clade_node_attrs` are still assigned and being written to the output).

Tested on `sars-cov-2` dataset and `clade_membership` node attribute removed using `jq`:

```bash
jq 'walk(if (type == "object" and .clade_membership) then del(.clade_membership) else . end )' tree.original.json > tree.json
```

(in real trees you might also need to change metadata, such as colorings)


### Further work:

 - We might remove empty `clade` column from CSV/TSV and from Web. Thought it might be a bit involved - the results are streamed one at a time and we don't know whether there will be any clades or not until very end. But at that point it is too late - everything has been already written.
  • Loading branch information
ivan-aksamentov committed May 23, 2024
1 parent 3475c1b commit bb039ed
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function ColumnClade({ analysisResult }: ColumnCladeProps) {

const { clade, seqName, index } = analysisResult
const id = getSafeId('col-clade', { index, seqName })
const cladeText = clade ?? t('Pending...')
const cladeText = clade ?? ''

const onMouseEnter = useCallback(() => setShowTooltip(true), [])
const onMouseLeave = useCallback(() => setShowTooltip(false), [])
Expand Down
2 changes: 1 addition & 1 deletion packages/nextclade-web/src/filtering/filterByClades.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export function filterByClades(cladesFilter: string) {
}

const { clade } = result.result.analysisResult
return cladesFilters.some((filter) => clade.toLowerCase().startsWith(filter.toLowerCase()))
return cladesFilters.some((filter) => clade?.toLowerCase().startsWith(filter.toLowerCase()))
}
}
2 changes: 1 addition & 1 deletion packages/nextclade/src/io/nextclade_csv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl<W: VecWriter> NextcladeResultsCsvWriter<W> {
self.add_entry("index", index)?;
self.add_entry("seqName", seq_name)?;

self.add_entry("clade", clade)?;
self.add_entry("clade", &clade.as_deref().unwrap_or_default())?;
self.add_entry("qc.overallScore", &format_qc_score(qc.overall_score))?;
self.add_entry("qc.overallStatus", &qc.overall_status.to_string())?;
self.add_entry("totalSubstitutions", &total_substitutions.to_string())?;
Expand Down
8 changes: 5 additions & 3 deletions packages/nextclade/src/run/nextclade_run_one.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct NextcladeResultWithAa {

#[derive(Default)]
struct NextcladeResultWithGraph {
clade: String,
clade: Option<String>,
private_nuc_mutations: PrivateNucMutations,
private_aa_mutations: BTreeMap<String, PrivateAaMutations>,
phenotype_values: Option<Vec<PhenotypeValue>>,
Expand Down Expand Up @@ -306,8 +306,10 @@ pub fn nextclade_run_one(
.iter()
.filter_map(|phenotype_data| {
let PhenotypeData { name, cds, ignore, .. } = phenotype_data;
if ignore.clades.contains(&clade) {
return None;
if let Some(clade) = &clade {
if ignore.clades.contains(clade) {
return None;
}
}
let phenotype = calculate_phenotype(phenotype_data, &aa_substitutions);
Some(PhenotypeValue {
Expand Down
11 changes: 8 additions & 3 deletions packages/nextclade/src/tree/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ pub struct TreeNodeAttrs {
#[serde(skip_serializing_if = "Option::is_none")]
pub div: Option<f64>,

pub clade_membership: TreeNodeAttr,
#[serde(skip_serializing_if = "Option::is_none")]
pub clade_membership: Option<TreeNodeAttr>,

#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Node type")]
Expand Down Expand Up @@ -243,8 +244,12 @@ impl From<&AuspiceTreeNode> for AuspiceGraphNodePayload {

impl AuspiceGraphNodePayload {
/// Extracts clade of the node
pub fn clade(&self) -> String {
self.node_attrs.clade_membership.value.clone()
pub fn clade(&self) -> Option<String> {
self
.node_attrs
.clade_membership
.as_ref()
.map(|clade_membership| clade_membership.value.clone())
}

/// Extracts clade-like node attributes, given a list of key descriptions
Expand Down
2 changes: 1 addition & 1 deletion packages/nextclade/src/tree/tree_attach_new_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub fn create_new_auspice_node(
},
node_attrs: TreeNodeAttrs {
div: Some(new_divergence),
clade_membership: TreeNodeAttr::new(&result.clade),
clade_membership: result.clade.as_ref().map(|clade| TreeNodeAttr::new(clade)),
node_type: Some(TreeNodeAttr::new("New")),
region: Some(TreeNodeAttr::new(AUSPICE_UNKNOWN_VALUE)),
country: Some(TreeNodeAttr::new(AUSPICE_UNKNOWN_VALUE)),
Expand Down
3 changes: 2 additions & 1 deletion packages/nextclade/src/types/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ pub struct NextcladeOutputs {
pub aa_unsequenced_ranges: BTreeMap<String, Vec<AaRefRange>>,
pub pcr_primer_changes: Vec<PcrPrimerChange>,
pub total_pcr_primer_changes: usize,
pub clade: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub clade: Option<String>,
pub private_nuc_mutations: PrivateNucMutations,
pub private_aa_mutations: BTreeMap<String, PrivateAaMutations>,
pub warnings: Vec<PeptideWarning>,
Expand Down

0 comments on commit bb039ed

Please sign in to comment.