Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .cirro/singlecell_full_gex/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,33 @@ def prepare_sample_sheet(ds):
ds.logger.warning(f"sample_sheet is missing required column '{colname}'. Populating with NaN.")
sample_sheet[colname] = np.nan

try:
# The PATIENT step (GIANA + GLIPH2) pools samples by params.patient_col. If that column
# is absent every sample becomes its own patient and the clustering is silently wrong -
# no error, just useless results - so say so loudly here. samplesheet_from_params()'s
# fallback frame in particular carries only sample/path.
# ds.params may be a plain dict or a params object depending on cirro version, and a
# warning must never be the thing that breaks preprocessing - so degrade to the default.
try:
patient_col = dict(ds.params).get("patient_col") or "patient_id"
except Exception:
patient_col = "patient_id"
if patient_col not in sample_sheet.columns:
ds.logger.warning(
f"sample_sheet has no '{patient_col}' column. GIANA and GLIPH2 pool samples per "
"patient, so each sample will be treated as its own patient and cross-sample "
"clustering within a patient will be lost. Add the column to the dataset "
"samplesheet, or set 'Patient column' to one that exists."
)
elif sample_sheet[patient_col].isna().any():
missing = sample_sheet.loc[sample_sheet[patient_col].isna(), "sample"].tolist()
ds.logger.warning(f"Samples with no {patient_col}: {missing}. These will not pool with any patient.")
else:
n_pat = sample_sheet[patient_col].nunique()
ds.logger.info(f"{len(sample_sheet)} samples across {n_pat} patients (by '{patient_col}')")
except Exception as e:
ds.logger.warning(f"patient-column check skipped: {e}")

sample_sheet.to_csv('sample_sheet.csv', index=None)
ds.add_param('sample_sheet', 'sample_sheet.csv')
ds.logger.info(sample_sheet.to_dict())
Expand Down
24 changes: 24 additions & 0 deletions .cirro/singlecell_full_gex/process-form.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,30 @@
"description": "Render the master summary report.",
"title": "Run master summary report",
"type": "boolean"
},
"patient_col": {
"type": "string",
"title": "Patient column",
"description": "Sample-sheet column holding the patient/subject ID. GIANA and GLIPH2 pool samples per patient using this; if it is wrong, every sample is treated as its own patient.",
"default": "patient_id"
},
"run_tcri": {
"type": "boolean",
"title": "Run TCRi",
"description": "Immunogenicity scoring on the enriched Seurat object. Requires a GEX object. Adds roughly an hour of runtime.",
"default": true
},
"giana_dedup_clonotypes": {
"type": "boolean",
"title": "Deduplicate clonotypes before GIANA",
"description": "Collapse to one row per clonotype before GIANA. Patient pooling stacks per-sample rows, so without this GIANA clusters cross-sample duplicates rather than similar sequences.",
"default": true
},
"tcrdist_radius": {
"type": "integer",
"title": "TCRdist radius",
"description": "Distance threshold for TCRdist3 connected-component clustering.",
"default": 24
}
}
},
Expand Down
4 changes: 4 additions & 0 deletions .cirro/singlecell_full_gex/process-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,9 @@
"run_consensus": "$.params.dataset.paramJson.run_consensus",
"run_repertoire": "$.params.dataset.paramJson.run_repertoire",
"run_master_summary": "$.params.dataset.paramJson.run_master_summary",
"patient_col": "$.params.dataset.paramJson.patient_col",
"run_tcri": "$.params.dataset.paramJson.run_tcri",
"giana_dedup_clonotypes": "$.params.dataset.paramJson.giana_dedup_clonotypes",
"tcrdist_radius": "$.params.dataset.paramJson.tcrdist_radius",
"outdir": "$.params.dataset.s3|/data/"
}
27 changes: 27 additions & 0 deletions .cirro/singlecell_vdjonly/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,33 @@ def prepare_sample_sheet(ds):
ds.logger.warning(f"sample_sheet is missing required column '{colname}'. Populating with NaN.")
sample_sheet[colname] = np.nan

try:
# The PATIENT step (GIANA + GLIPH2) pools samples by params.patient_col. If that column
# is absent every sample becomes its own patient and the clustering is silently wrong -
# no error, just useless results - so say so loudly here. samplesheet_from_params()'s
# fallback frame in particular carries only sample/path.
# ds.params may be a plain dict or a params object depending on cirro version, and a
# warning must never be the thing that breaks preprocessing - so degrade to the default.
try:
patient_col = dict(ds.params).get("patient_col") or "patient_id"
except Exception:
patient_col = "patient_id"
if patient_col not in sample_sheet.columns:
ds.logger.warning(
f"sample_sheet has no '{patient_col}' column. GIANA and GLIPH2 pool samples per "
"patient, so each sample will be treated as its own patient and cross-sample "
"clustering within a patient will be lost. Add the column to the dataset "
"samplesheet, or set 'Patient column' to one that exists."
)
elif sample_sheet[patient_col].isna().any():
missing = sample_sheet.loc[sample_sheet[patient_col].isna(), "sample"].tolist()
ds.logger.warning(f"Samples with no {patient_col}: {missing}. These will not pool with any patient.")
else:
n_pat = sample_sheet[patient_col].nunique()
ds.logger.info(f"{len(sample_sheet)} samples across {n_pat} patients (by '{patient_col}')")
except Exception as e:
ds.logger.warning(f"patient-column check skipped: {e}")

sample_sheet.to_csv('sample_sheet.csv', index=None)
ds.add_param('sample_sheet', 'sample_sheet.csv')
ds.logger.info(sample_sheet.to_dict())
Expand Down
18 changes: 18 additions & 0 deletions .cirro/singlecell_vdjonly/process-form.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@
"drop",
"hard_stop"
]
},
"patient_col": {
"type": "string",
"title": "Patient column",
"description": "Sample-sheet column holding the patient/subject ID. GIANA and GLIPH2 pool samples per patient using this; if it is wrong, every sample is treated as its own patient.",
"default": "patient_id"
},
"giana_dedup_clonotypes": {
"type": "boolean",
"title": "Deduplicate clonotypes before GIANA",
"description": "Collapse to one row per clonotype before GIANA. Patient pooling stacks per-sample rows, so without this GIANA clusters cross-sample duplicates rather than similar sequences.",
"default": true
},
"tcrdist_radius": {
"type": "integer",
"title": "TCRdist radius",
"description": "Distance threshold for TCRdist3 connected-component clustering.",
"default": 24
}
}
},
Expand Down
3 changes: 3 additions & 0 deletions .cirro/singlecell_vdjonly/process-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
"pseudobulk_qc_min_clones": "$.params.dataset.paramJson.pseudobulk_qc_min_clones",
"pseudobulk_qc_min_cells": "$.params.dataset.paramJson.pseudobulk_qc_min_cells",
"pseudobulk_qc_mode": "$.params.dataset.paramJson.pseudobulk_qc_mode",
"patient_col": "$.params.dataset.paramJson.patient_col",
"giana_dedup_clonotypes": "$.params.dataset.paramJson.giana_dedup_clonotypes",
"tcrdist_radius": "$.params.dataset.paramJson.tcrdist_radius",
"outdir": "$.params.dataset.s3|/data/"
}
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,16 @@ tmp
.vscode/*

.e2e_test_tmp/

## Run artifacts — existing rules only match bare `work` and `results*`,
## so custom -w / --outdir names slip through.
# NB: not `work*/` - that also matches workflows/, the source directory.
work/
work_*/
test_results*/
test_v2port*/
logs/

## Let the report notebooks through: the `notebooks/*` rule above would
## otherwise silently skip every template_*.qmd, which the bulk route needs.
!notebooks/template_*.qmd
Loading
Loading