workflows¶
workflows
¶
High-level composite operations built on the per-table CRUD modules.
Each workflow takes an optional cursor (cur=None). When the caller
provides a cursor, the workflow piggybacks on the caller's transaction —
all writes are part of the same atomic unit, and a later exception in
the caller's block rolls them back. When cur is None, the
workflow opens its own :func:noxdb.transaction block, so the
multi-step operation is still atomic when called standalone (e.g. from a
notebook):
# Notebook style — workflow owns the transaction:
sid, vid = workflows.register_subject_with_visit(
subject_code="S1", sex="F",
timepoint="baseline", group_test="ctrl", age=42,
visit_metadata={"bmi": 24.1, "smoker": False},
)
# Composed inside a larger transaction:
with transaction() as cur:
sid, vid = workflows.register_subject_with_visit(cur, ...)
for spec in sample_specs:
workflows.register_sample_with_files(cur, visit_id=vid, **spec)
register_subject_with_visit
¶
register_subject_with_visit(cur=None, *, subject_code: str, sex: str, origin: str | None = None, timepoint: str, group_test: str, age: int, visit_metadata: dict[str, Any] | None = None) -> tuple[int, int]
Idempotently create a subject and its first visit. Atomic.
Uses subjects.get_or_create
and visits.get_or_create, so
re-running with the same natural keys yields the same IDs without
duplicating rows. Existing rows are NOT updated by this helper —
call the CRUD update directly to change attributes of a row
that already exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Optional cursor from |
None
|
|
subject_code
|
str
|
Globally unique subject code. |
required |
sex
|
str
|
|
required |
origin
|
str | None
|
Used only when inserting a new subject. |
None
|
timepoint
|
str
|
Visit timepoint. Must be non-NULL — the underlying
|
required |
group_test
|
str
|
Used only when inserting a new visit. |
required |
age
|
int
|
Used only when inserting a new visit. |
required |
visit_metadata
|
dict[str, Any] | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
tuple[int, int]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/noxdb/workflows.py
register_sample_with_files
¶
register_sample_with_files(cur=None, *, visit_id: int, sample_name: str, sample_type: str, ipr: str, iprp: str, sqr: str, sqrp: str, library: str, antibody_class: str | None = None, sample_metadata: dict[str, Any] | None = None, files_spec: list[dict[str, Any]] | None = None, compute_md5: bool = False) -> tuple[int, list[int]]
Idempotently create a sample, its metadata, and its files. Atomic.
The sample is upserted via
samples.get_or_create keyed
on the globally-UNIQUE sample_name; existing samples keep their
original visit_id even when called with a different one.
Any disk/path validation error from
files is raised inside the transaction,
which rolls back the sample insert too — the unit is the
sample-plus-files bundle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Optional cursor from |
None
|
|
visit_id
|
int
|
Parent visit (used only when inserting a new sample). |
required |
sample_name
|
str
|
Globally unique sample name. |
required |
sample_type
|
str
|
See |
required |
ipr
|
str
|
IP run, from the IP-runs overview sheet. Used only on insert. |
required |
iprp
|
str
|
IP plate. Used only on insert. |
required |
sqr
|
str
|
Sequencing run. Used only on insert. |
required |
sqrp
|
str
|
Sequencing plate. Used only on insert. |
required |
library
|
str
|
Used only on insert. |
required |
antibody_class
|
str | None
|
Used only on insert. |
None
|
sample_metadata
|
dict[str, Any] | None
|
Optional |
None
|
files_spec
|
list[dict[str, Any]] | None
|
Optional list of dicts forwarded to
|
None
|
compute_md5
|
bool
|
Default for entries in |
False
|
Returns:
| Type | Description |
|---|---|
int
|
|
list[int]
|
|
Source code in src/noxdb/workflows.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | |