From 7b4b769f104be839cc3fabdd7370b6a7318600a3 Mon Sep 17 00:00:00 2001 From: KevinMLanderos Date: Sun, 30 Aug 2026 21:49:23 -0400 Subject: [PATCH 1/2] Split detail report notebooks by workflow level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the part1/part2 detail reports so each is generated only for the workflow_level stages that ran, replacing the on/off template swap hack. template_details_part1.qmd -> template_details_sample.qmd Rename only. Includes template_sample.qmd; rendered when `sample` runs. template_details_part2.qmd -> split into: template_details_compare.qmd — template_overlap.qmd + template_sharing.qmd, plus a static pointer to the patient report. Rendered when `compare` runs. template_details_patient.qmd — template_giana.qmd + template_gliph.qmd. Rendered when `patient` runs; no longer gated behind `compare`. Drop template_patient_clustering_on/off.qmd and the template_patient_clustering.qmd staging - the patient report is simply not emitted when the patient stage is absent. template_giana.qmd: import os in the first cell. It used os.path in early cells but only imported os later, relying on template_overlap.qmd (which ran first in the old combined part2) to have imported it. It's now the first include in template_details_patient.qmd. Wiring: rename params in nextflow.config / nextflow_schema.json (template_details_part1 -> _sample; part2 + patient_clustering_on/off -> _compare + _patient), rework the report channels in subworkflows/local/bulktcr_analysis.nf, and update the README report table. Verified with nf-test: Bulk {sample,compare}, {sample,patient}, {sample,patient,compare} all pass. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WhjujvKjf1RV5gABqZnMFB --- README.md | 19 +-- nextflow.config | 7 +- nextflow_schema.json | 20 ++- notebooks/template_details_compare.qmd | 117 ++++++++++++++++ ...part2.qmd => template_details_patient.qmd} | 11 +- ..._part1.qmd => template_details_sample.qmd} | 0 notebooks/template_giana.qmd | 1 + notebooks/template_patient_clustering_off.qmd | 5 - notebooks/template_patient_clustering_on.qmd | 3 - subworkflows/local/bulktcr_analysis.nf | 127 +++++++++--------- 10 files changed, 205 insertions(+), 105 deletions(-) create mode 100644 notebooks/template_details_compare.qmd rename notebooks/{template_details_part2.qmd => template_details_patient.qmd} (93%) rename notebooks/{template_details_part1.qmd => template_details_sample.qmd} (100%) delete mode 100644 notebooks/template_patient_clustering_off.qmd delete mode 100644 notebooks/template_patient_clustering_on.qmd diff --git a/README.md b/README.md index 5b52318..8da3766 100644 --- a/README.md +++ b/README.md @@ -63,22 +63,23 @@ Levels can be combined: `--workflow_level sample,patient,compare` ## HTML Reports -After the pipeline finishes, `TCRtoolkit` generates interactive HTML reports using [Quarto](https://quarto.org/). Four main report notebooks are rendered automatically: +After the pipeline finishes, `TCRtoolkit` generates interactive HTML reports using [Quarto](https://quarto.org/). Report notebooks are rendered automatically depending on which `--workflow_level` stages ran: -| Notebook | Description | -|---|---| -| `template_qc.qmd` | Quality control metrics and filtering summary | -| `template_discovery_brief.qmd` | Repertoire discovery most relevant information | -| `template_details_part1.qmd` | Detailed repertoire analysis, part 1 | -| `template_details_part2.qmd` | Detailed repertoire analysis, part 2 | +| Notebook | Rendered when | Description | +|---|---|---| +| `template_qc.qmd` | `sample` | Quality control metrics and filtering summary | +| `template_discovery_brief.qmd` | `sample` + `compare` | Highest-impact repertoire findings | +| `template_details_sample.qmd` | `sample` | Detailed per-sample repertoire analysis | +| `template_details_compare.qmd` | `compare` | Detailed cross-sample / longitudinal comparison analysis | +| `template_details_patient.qmd` | `patient` | Patient-level clonotype clustering (GIANA, and GLIPH2 when `--use_gliph2`) | ### Conditional Report Sections Certain sub-reports are automatically appended based on input and workflow options: - `--input_format adaptive` → includes bulk phenotype report -- `--workflow_level sample,patient,compare` (Patient workflow enabled) → includes patient-level clonotype analysis -- `--use_gliph2` → additionally includes GLIPH2 clustering report +- `--workflow_level` includes `patient` → renders `template_details_patient.qmd` (patient-level clonotype analysis) +- `--use_gliph2` → additionally includes GLIPH2 clustering in the patient details report ## Key Parameters diff --git a/nextflow.config b/nextflow.config index a97baf9..bd65ca7 100644 --- a/nextflow.config +++ b/nextflow.config @@ -71,11 +71,10 @@ params { //reports template_qc = "${projectDir}/notebooks/template_qc.qmd" template_discovery_brief = "${projectDir}/notebooks/template_discovery_brief.qmd" - template_details_part1 = "${projectDir}/notebooks/template_details_part1.qmd" - template_details_part2 = "${projectDir}/notebooks/template_details_part2.qmd" + template_details_sample = "${projectDir}/notebooks/template_details_sample.qmd" + template_details_compare = "${projectDir}/notebooks/template_details_compare.qmd" + template_details_patient = "${projectDir}/notebooks/template_details_patient.qmd" template_pheno_bulk = "${projectDir}/notebooks/template_pheno_bulk.qmd" - template_patient_clustering_on = "${projectDir}/notebooks/template_patient_clustering_on.qmd" - template_patient_clustering_off = "${projectDir}/notebooks/template_patient_clustering_off.qmd" // ══════════════════════════════════════════════════════════════════════ // SINGLE-CELL modality (integration) — see IMPLEMENTATION_SPEC.md diff --git a/nextflow_schema.json b/nextflow_schema.json index a6ffdc3..837a37e 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -124,25 +124,21 @@ "type": "string", "description": "Path to discovery brief notebook template." }, - "template_details_part1": { + "template_details_sample": { "type": "string", - "description": "Path to details (part 1) notebook template." + "description": "Path to the sample-level details notebook template (rendered when the sample workflow_level is run)." }, - "template_details_part2": { + "template_details_compare": { "type": "string", - "description": "Path to details (part 2) notebook template." + "description": "Path to the comparison-level details notebook template (rendered when the compare workflow_level is run)." }, - "template_pheno_bulk": { - "type": "string", - "description": "Path to bulk (TCRpheno) phenotype notebook template." - }, - "template_patient_clustering_on": { + "template_details_patient": { "type": "string", - "description": "Path to patient-level clustering (GIANA/GLIPH2) notebook template, used when patient workflow_level is run." + "description": "Path to the patient-level details notebook template with GIANA/GLIPH2 clustering (rendered when the patient workflow_level is run)." }, - "template_patient_clustering_off": { + "template_pheno_bulk": { "type": "string", - "description": "Path to placeholder notebook template used when patient workflow_level is not run." + "description": "Path to bulk (TCRpheno) phenotype notebook template." } } }, diff --git a/notebooks/template_details_compare.qmd b/notebooks/template_details_compare.qmd new file mode 100644 index 0000000..0f38fdb --- /dev/null +++ b/notebooks/template_details_compare.qmd @@ -0,0 +1,117 @@ +--- +title: "Details — Comparison" +format: + html: + theme: flatly + toc: true + toc_depth: 3 + code-fold: true + embed-resources: true + number-sections: true + smooth-scroll: true + grid: + body-width: 1000px + margin-width: 300px +execute: + cache: false + warnings: false +jupyter: python3 +--- + +Thank you for using TCRtoolkit! This report is generated from the data you provided. + +:::{.callout-note collapse="true"} +## Document Information +**Current Version:** 1.0-beta +**Last Updated:** March 2026 +**Maintainer:** BTC Data Science Team +**Notes:** +::: + +::: {.callout-note collapse="true"} +## Notebook Analysis Scope +This notebook provides a detailed cross-sample and longitudinal comparison analysis +(clonal dynamics, repertoire overlap, and TCR publicity). +::: + +```{python} +#| tags: [parameters] +#| include: false + +# --------------------------------------------------------- +# BASE PARAMETERS +# --------------------------------------------------------- +workflow_cmd = '' +project_name='' +project_dir='' +sample_table='' + +timepoint_col = 'timepoint' +timepoint_order_col = 'timepoint_order' +# Ordered comma-separated list of timepoint values, e.g. "Base,Week4,EOT" - +# rank = position in the list. Empty string means: rank numerically if every +# timepoint value parses as a number, otherwise alphabetically. +timepoint_order = '' +alias_col = 'alias' +subject_col = 'subject_id' + +``` + +```{python} +#| include: false + +# --------------------------------------------------------- +# DERIVED PATHS +# --------------------------------------------------------- + +# Define files +project_dir=f"{project_dir}/{project_name}" + +``` + +# Before You Begin + +This pipeline can be used to analyze both **single-cell and bulk TCR data**. Please see the note below to understand some of the **implications** depending on the data type you have: + +::: {.callout-note title="Single-cell vs Bulk Data analysis" collapse="true"} +**Definition of “counts”** +- **Single-cell**: + `counts` represent the number of distinct cells carrying a specific clonotype. For example, a count of 12 indicates that 12 individual cells were encapsulated and sequenced. +- **Bulk**: + `counts` represent the abundance of sequencing reads (or UMIs) supporting a clonotype. The biological interpretation depends heavily on the starting material: + + - **RNA (cDNA):** Counts are a composite metric of Cellular Abundance $\times$ Transcriptional Expression. Since activation status affects TCR mRNA levels, a high count could indicate a large clone or a highly active small clone. Normalization strategies can mitigate, but not eliminate, this expression bias. + - **DNA (gDNA):** Counts are a direct proxy for Cell Number (e.g., Adaptive ImmunoSEQ). Because T-cell genomic templates are constant (one productive rearrangement per cell), DNA sequencing avoids expression bias and allows for accurate estimation of clone size. + +**TCR chains** +- **Single-cell**: + It's common to have paired α/β chains per cell. However, we only focus on the Beta chain here. +- **Bulk**: + In bulk repertoire sequencing, you usually amplify TCRα and TCRβ chains separately. The resulting data contains lists of α clonotypes and lists of β clonotypes, but no information about which α and β belong to the same T cell. We focus only on the Beta chain. + +**Diversity & clonality metrics** +- **Single-cell**: + Sensitive to sampling (10^3 – 10^5 cells typical). + Rare clonotypes may be missed, but you can study functional heterogeneity within clones. +- **Bulk**: + Captures broad repertoire diversity (10^5 – 10^6 clonotypes). + More accurate for richness, evenness, overlap across samples. + +**Downstream biological analyses** +- **Single-cell**: + It is possible to link TCRs to phenotypic states (exhaustion, activation, tissue localization), which allows the study of clonotype heterogeneity. +- **Bulk**: + It focuses on population-level measures +::: + +{{< include ./template_overlap.qmd >}} + +{{< include ./template_sharing.qmd >}} + +## Patient-level TCR clustering (GIANA/GLIPH2) + +::: {.callout-note} +Patient-level clonotype clustering (GIANA and GLIPH2) is reported separately in +the **Patient Details** report (`template_details_patient.html`), which is +generated when `patient` is included in `--workflow_level`. +::: diff --git a/notebooks/template_details_part2.qmd b/notebooks/template_details_patient.qmd similarity index 93% rename from notebooks/template_details_part2.qmd rename to notebooks/template_details_patient.qmd index cec4edd..5ac5082 100644 --- a/notebooks/template_details_part2.qmd +++ b/notebooks/template_details_patient.qmd @@ -1,5 +1,5 @@ --- -title: "Details" +title: "Details — Patient" format: html: theme: flatly @@ -30,7 +30,8 @@ Thank you for using TCRtoolkit! This report is generated from the data you provi ::: {.callout-note collapse="true"} ## Notebook Analysis Scope -This notebook provides a more detailed analysis of the samples being analyzed. +This notebook provides patient-level clonotype clustering analysis (GIANA, and GLIPH2 +when `--use_gliph2` is enabled), aggregating all samples belonging to each individual. ::: ```{python} @@ -103,8 +104,6 @@ This pipeline can be used to analyze both **single-cell and bulk TCR data**. Ple It focuses on population-level measures ::: -{{< include ./template_overlap.qmd >}} +{{< include ./template_giana.qmd >}} -{{< include ./template_sharing.qmd >}} - -{{< include ./template_patient_clustering.qmd >}} +{{< include ./template_gliph.qmd >}} diff --git a/notebooks/template_details_part1.qmd b/notebooks/template_details_sample.qmd similarity index 100% rename from notebooks/template_details_part1.qmd rename to notebooks/template_details_sample.qmd diff --git a/notebooks/template_giana.qmd b/notebooks/template_giana.qmd index ec4894b..6e2c56c 100644 --- a/notebooks/template_giana.qmd +++ b/notebooks/template_giana.qmd @@ -18,6 +18,7 @@ notebook_results_dir = f"{project_dir}/notebook-analysis/" #| code-fold: true # 1. Load Packages +import os import pandas as pd import plotly.express as px import plotly.graph_objects as go diff --git a/notebooks/template_patient_clustering_off.qmd b/notebooks/template_patient_clustering_off.qmd deleted file mode 100644 index 505afce..0000000 --- a/notebooks/template_patient_clustering_off.qmd +++ /dev/null @@ -1,5 +0,0 @@ -## Patient-level TCR clustering (GIANA/GLIPH2) - -::: {.callout-note} -This section requires patient-level clonotype clustering (GIANA and GLIPH2), which was not run for this project. Include `patient` in `--workflow_level` to generate it. -::: diff --git a/notebooks/template_patient_clustering_on.qmd b/notebooks/template_patient_clustering_on.qmd deleted file mode 100644 index f56d020..0000000 --- a/notebooks/template_patient_clustering_on.qmd +++ /dev/null @@ -1,3 +0,0 @@ -{{< include ./template_giana.qmd >}} - -{{< include ./template_gliph.qmd >}} diff --git a/subworkflows/local/bulktcr_analysis.nf b/subworkflows/local/bulktcr_analysis.nf index 74201ec..97dedac 100644 --- a/subworkflows/local/bulktcr_analysis.nf +++ b/subworkflows/local/bulktcr_analysis.nf @@ -126,7 +126,7 @@ workflow BULKTCR_ANALYSIS { } ch_reports = ch_reports.mix(ch_qc_report) - ch_details_part1_report = sample_stats_agg + ch_details_sample_report = sample_stats_agg .combine(ANNOTATE.out.concat_cdr3_sorted) .combine(ch_v_family) .combine(ch_j_family) @@ -135,7 +135,7 @@ workflow BULKTCR_ANALYSIS { .combine(ch_vdjdb_files.map { l -> [l] }) .combine(ch_convergence_files.map { l -> [l] }) .map { sample_stats_csv, concat_cdr3_sorted, v_family_file, j_family_file, tcrdist_files_l, olga_files_l, vdjdb_files_l, convergence_files_l -> - def include_files = [file("${file(params.template_details_part1).parent}/template_sample.qmd")] + def include_files = [file("${file(params.template_details_sample).parent}/template_sample.qmd")] def report_files = [sample_stats_csv, concat_cdr3_sorted, v_family_file, j_family_file] + tcrdist_files_l + olga_files_l + vdjdb_files_l + convergence_files_l + include_files def staged_layout = [ @@ -148,12 +148,12 @@ workflow BULKTCR_ANALYSIS { vdjdb_files_l.collect { f -> ["${params.project_name}/vdjdb/${f.name}", f.name] } + convergence_files_l.collect { f -> ["${params.project_name}/convergence/${f.name}", f.name] } tuple( - file(params.template_details_part1), + file(params.template_details_sample), report_files, staged_layout ) } - ch_reports = ch_reports.mix(ch_details_part1_report) + ch_reports = ch_reports.mix(ch_details_sample_report) if (levels.contains('compare')) { ch_discovery_report = sample_stats_agg @@ -185,73 +185,68 @@ workflow BULKTCR_ANALYSIS { } ch_reports = ch_reports.mix(ch_discovery_report) - def details_part2_base = sample_stats_agg + def details_notebooks_dir = file(params.template_details_compare).parent + + ch_details_compare_report = sample_stats_agg .combine(ANNOTATE.out.concat_cdr3_sorted) .combine(ch_shared_cdr3) - - def run_patient_clustering = levels.contains('patient') - def patient_clustering_notebook = run_patient_clustering - ? file(params.template_patient_clustering_on) - : file(params.template_patient_clustering_off) - - def part2_notebooks_dir = file(params.template_details_part2).parent - def part2_include_files = [ - file("${part2_notebooks_dir}/template_overlap.qmd"), - file("${part2_notebooks_dir}/template_sharing.qmd"), - file("${part2_notebooks_dir}/template_giana.qmd"), - file("${part2_notebooks_dir}/template_gliph.qmd"), - patient_clustering_notebook - ] - def part2_staged_layout_extra = [ - ["template_patient_clustering.qmd", patient_clustering_notebook.name] - ] - - if (run_patient_clustering) { - ch_details_part2_report = details_part2_base - .combine(ch_giana_files.map { l -> [l] }) - .combine(ch_gliph2_all_motifs.map { l -> [l] }) - .combine(ch_gliph2_clone_network.map { l -> [l] }) - .combine(ch_gliph2_cluster_member_details.map { l -> [l] }) - .combine(ch_gliph2_global_similarities.map { l -> [l] }) - .map { sample_stats_csv, concat_cdr3_sorted, shared_cdr3_file, giana_files_l, - all_motifs_pairs, clone_network_pairs, cluster_member_pairs, global_sim_pairs -> - def report_files = [sample_stats_csv, concat_cdr3_sorted, shared_cdr3_file] + giana_files_l + - all_motifs_pairs.collect { p -> p[1] } + - clone_network_pairs.collect { p -> p[1] } + - cluster_member_pairs.collect { p -> p[1] } + - global_sim_pairs.collect { p -> p[1] } + - part2_include_files - def staged_layout = [ + .map { sample_stats_csv, concat_cdr3_sorted, shared_cdr3_file -> + def include_files = [ + file("${details_notebooks_dir}/template_overlap.qmd"), + file("${details_notebooks_dir}/template_sharing.qmd") + ] + tuple( + file(params.template_details_compare), + [sample_stats_csv, concat_cdr3_sorted, shared_cdr3_file] + include_files, + [ ["${params.project_name}/sample/${sample_stats_csv.name}", sample_stats_csv.name], ["${params.project_name}/annotate/${concat_cdr3_sorted.name}", concat_cdr3_sorted.name], ["${params.project_name}/tcrsharing/${shared_cdr3_file.name}", shared_cdr3_file.name] - ] + giana_files_l.collect { f -> ["${params.project_name}/giana/${f.name}", f.name] } + - all_motifs_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + - clone_network_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + - cluster_member_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + - global_sim_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + - part2_staged_layout_extra - tuple( - file(params.template_details_part2), - report_files, - staged_layout - ) - } - } else { - ch_details_part2_report = details_part2_base - .map { sample_stats_csv, concat_cdr3_sorted, shared_cdr3_file -> - tuple( - file(params.template_details_part2), - [sample_stats_csv, concat_cdr3_sorted, shared_cdr3_file] + part2_include_files, - [ - ["${params.project_name}/sample/${sample_stats_csv.name}", sample_stats_csv.name], - ["${params.project_name}/annotate/${concat_cdr3_sorted.name}", concat_cdr3_sorted.name], - ["${params.project_name}/tcrsharing/${shared_cdr3_file.name}", shared_cdr3_file.name] - ] + part2_staged_layout_extra - ) - } - } - ch_reports = ch_reports.mix(ch_details_part2_report) + ] + ) + } + ch_reports = ch_reports.mix(ch_details_compare_report) + } + + // Patient-level details report (GIANA, plus GLIPH2 when --use_gliph2). Rendered + // whenever the patient stage ran; it does not depend on the compare stage. + if (levels.contains('patient')) { + def patient_notebooks_dir = file(params.template_details_patient).parent + def patient_include_files = [ + file("${patient_notebooks_dir}/template_giana.qmd"), + file("${patient_notebooks_dir}/template_gliph.qmd") + ] + + ch_details_patient_report = sample_stats_agg + .combine(ANNOTATE.out.concat_cdr3_sorted) + .combine(ch_giana_files.map { l -> [l] }) + .combine(ch_gliph2_all_motifs.map { l -> [l] }) + .combine(ch_gliph2_clone_network.map { l -> [l] }) + .combine(ch_gliph2_cluster_member_details.map { l -> [l] }) + .combine(ch_gliph2_global_similarities.map { l -> [l] }) + .map { sample_stats_csv, concat_cdr3_sorted, giana_files_l, + all_motifs_pairs, clone_network_pairs, cluster_member_pairs, global_sim_pairs -> + def report_files = [sample_stats_csv, concat_cdr3_sorted] + giana_files_l + + all_motifs_pairs.collect { p -> p[1] } + + clone_network_pairs.collect { p -> p[1] } + + cluster_member_pairs.collect { p -> p[1] } + + global_sim_pairs.collect { p -> p[1] } + + patient_include_files + def staged_layout = [ + ["${params.project_name}/sample/${sample_stats_csv.name}", sample_stats_csv.name], + ["${params.project_name}/annotate/${concat_cdr3_sorted.name}", concat_cdr3_sorted.name] + ] + giana_files_l.collect { f -> ["${params.project_name}/giana/${f.name}", f.name] } + + all_motifs_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + + clone_network_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + + cluster_member_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + + global_sim_pairs.collect { p -> ["${params.project_name}/gliph2/${p[0]}/${p[1].name}", p[1].name] } + tuple( + file(params.template_details_patient), + report_files, + staged_layout + ) + } + ch_reports = ch_reports.mix(ch_details_patient_report) } REPORT( ch_reports ) From 43de124bf8bdf94033a8cd2a2528dd27eaad4e20 Mon Sep 17 00:00:00 2001 From: dltamayo Date: Tue, 1 Sep 2026 15:39:06 -0400 Subject: [PATCH 2/2] Fix docs/comments to note patient/compare reports also require sample Bulk HTML report rendering is gated on run_reports (levels.contains 'sample'); the README table/bullet, nextflow_schema.json descriptions, the compare notebook's callout, and a code comment all implied the compare/patient details reports render whenever compare/patient ran, without the sample requirement. Flagged by Copilot in PR #100 review. Co-Authored-By: Claude Sonnet 5 --- README.md | 6 +++--- nextflow_schema.json | 4 ++-- notebooks/template_details_compare.qmd | 2 +- subworkflows/local/bulktcr_analysis.nf | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8da3766..62e6da9 100644 --- a/README.md +++ b/README.md @@ -70,15 +70,15 @@ After the pipeline finishes, `TCRtoolkit` generates interactive HTML reports usi | `template_qc.qmd` | `sample` | Quality control metrics and filtering summary | | `template_discovery_brief.qmd` | `sample` + `compare` | Highest-impact repertoire findings | | `template_details_sample.qmd` | `sample` | Detailed per-sample repertoire analysis | -| `template_details_compare.qmd` | `compare` | Detailed cross-sample / longitudinal comparison analysis | -| `template_details_patient.qmd` | `patient` | Patient-level clonotype clustering (GIANA, and GLIPH2 when `--use_gliph2`) | +| `template_details_compare.qmd` | `sample` + `compare` | Detailed cross-sample / longitudinal comparison analysis | +| `template_details_patient.qmd` | `sample` + `patient` | Patient-level clonotype clustering (GIANA, and GLIPH2 when `--use_gliph2`) | ### Conditional Report Sections Certain sub-reports are automatically appended based on input and workflow options: - `--input_format adaptive` → includes bulk phenotype report -- `--workflow_level` includes `patient` → renders `template_details_patient.qmd` (patient-level clonotype analysis) +- `--workflow_level` includes `sample` and `patient` → renders `template_details_patient.qmd` (patient-level clonotype analysis) - `--use_gliph2` → additionally includes GLIPH2 clustering in the patient details report ## Key Parameters diff --git a/nextflow_schema.json b/nextflow_schema.json index 837a37e..aa393ad 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -130,11 +130,11 @@ }, "template_details_compare": { "type": "string", - "description": "Path to the comparison-level details notebook template (rendered when the compare workflow_level is run)." + "description": "Path to the comparison-level details notebook template (rendered when the sample and compare workflow_levels are run)." }, "template_details_patient": { "type": "string", - "description": "Path to the patient-level details notebook template with GIANA/GLIPH2 clustering (rendered when the patient workflow_level is run)." + "description": "Path to the patient-level details notebook template with GIANA/GLIPH2 clustering (rendered when the sample and patient workflow_levels are run)." }, "template_pheno_bulk": { "type": "string", diff --git a/notebooks/template_details_compare.qmd b/notebooks/template_details_compare.qmd index 0f38fdb..d51528c 100644 --- a/notebooks/template_details_compare.qmd +++ b/notebooks/template_details_compare.qmd @@ -113,5 +113,5 @@ This pipeline can be used to analyze both **single-cell and bulk TCR data**. Ple ::: {.callout-note} Patient-level clonotype clustering (GIANA and GLIPH2) is reported separately in the **Patient Details** report (`template_details_patient.html`), which is -generated when `patient` is included in `--workflow_level`. +generated when `sample` and `patient` are both included in `--workflow_level`. ::: diff --git a/subworkflows/local/bulktcr_analysis.nf b/subworkflows/local/bulktcr_analysis.nf index 97dedac..857ba17 100644 --- a/subworkflows/local/bulktcr_analysis.nf +++ b/subworkflows/local/bulktcr_analysis.nf @@ -209,7 +209,8 @@ workflow BULKTCR_ANALYSIS { } // Patient-level details report (GIANA, plus GLIPH2 when --use_gliph2). Rendered - // whenever the patient stage ran; it does not depend on the compare stage. + // when the patient stage ran (this block already requires run_reports && sample); + // it does not additionally depend on the compare stage. if (levels.contains('patient')) { def patient_notebooks_dir = file(params.template_details_patient).parent def patient_include_files = [