files¶
files
¶
Registration wrapper for the sample_files table.
Unlike the other CRUD modules, files.register doubles as a gate
between application code and the schema: it stats the file on disk,
validates that the path lives in the right storage tier, and only then
hands the row to MariaDB. The schema's CHECKs (absolute path, MD5 format,
UNIQUE path) are still in place but are now a backstop, not the first
line of defense.
Storage tier policy¶
File-type → tier is fixed by lab convention:
fastq_r1 / fastq_r2 / fastq_single / bam -> archive
counts / beer_norm / zigp_norm / edger_norm /
zigp_loose / fastq_qc -> work
Roots are configurable via env vars (defaults shown):
NOXDB_ARCHIVE_ROOT default /lisc/archive
NOXDB_WORK_ROOT default /lisc/work
Callers can override storage_tier to 'scratch' or 'external'
(escape hatches with no path-prefix check); overriding to swap
archive/work against the type-derived value is rejected.
Files inside a tar¶
A row can point at one member of a tar archive (schema 006): file_path
is the .tar, archive_member the member's name inside it and
archive_offset the byte position of its data. Size and checksum then
describe the member and must come from the caller, since the member cannot
be stat'ed. Plain files have archive_member = ''.
register
¶
register(cur, sample_id: int, file_path: str, file_type: str, *, compute_md5: bool = False, checksum_md5: str | None = None, storage_tier: str | None = None, skip_disk_check: bool = False, archive_member: str = '', archive_offset: int | None = None, file_size_bytes: int | None = None) -> int
Validate a file on disk and insert a sample_files row.
Filesystem checks (path is absolute, regular file exists, extension
matches file_type, path lives under the tier root via realpath)
run before any SQL is executed, so a failure leaves the
transaction untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
sample_id
|
int
|
Parent sample. Must already exist. |
required |
file_path
|
str
|
Absolute path on disk. |
required |
file_type
|
str
|
One of the known types ( |
required |
compute_md5
|
bool
|
If |
False
|
checksum_md5
|
str | None
|
Caller-supplied 32-char lowercase-hex MD5. |
None
|
storage_tier
|
str | None
|
Override the file-type-derived tier. Only
|
None
|
skip_disk_check
|
bool
|
Skip the path-prefix and on-disk checks. |
False
|
archive_member
|
str
|
Name of the file inside the tar at
|
''
|
archive_offset
|
int | None
|
Byte position of the member's data in the tar.
Only with |
None
|
file_size_bytes
|
int | None
|
The member's size. Only with |
None
|
Returns:
| Type | Description |
|---|---|
int
|
The newly inserted |
Raises:
| Type | Description |
|---|---|
ValueError
|
Relative path, unknown |
FileNotFoundError
|
If the path does not exist. |
IsADirectoryError
|
If the path is a directory. |
IntegrityError
|
Unknown |
Source code in src/noxdb/files.py
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | |
get_or_register
¶
get_or_register(cur, sample_id: int, file_path: str, file_type: str, *, compute_md5: bool = False, checksum_md5: str | None = None, storage_tier: str | None = None, skip_disk_check: bool = False, archive_member: str = '', archive_offset: int | None = None, file_size_bytes: int | None = None) -> tuple[int, bool]
Idempotently register a file. Returns (file_id, registered).
If a row with this file_path and archive_member already exists, it is returned
as-is — the file is NOT re-stat'd and the other arguments are not
used to update the existing row. This means a stale path that was
registered in the past keeps returning its id even if the file has
since been deleted; call
restat if you need to refresh it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
sample_id
|
int
|
Parent sample (used only on insert). |
required |
file_path
|
str
|
Absolute path on disk. Globally unique. |
required |
file_type
|
str
|
See |
required |
compute_md5
|
bool
|
Used only on insert. |
False
|
checksum_md5
|
str | None
|
Used only on insert. |
None
|
storage_tier
|
str | None
|
Used only on insert. |
None
|
skip_disk_check
|
bool
|
Used only on insert. |
False
|
archive_member
|
str
|
Part of the lookup key; see
|
''
|
archive_offset
|
int | None
|
Used only on insert. |
None
|
file_size_bytes
|
int | None
|
Used only on insert. |
None
|
Returns:
| Type | Description |
|---|---|
int
|
|
bool
|
this call inserted the row. |
Raises:
| Type | Description |
|---|---|
IntegrityError
|
If the race-recovery fetch also misses. |
Source code in src/noxdb/files.py
get
¶
Return the file row for a given id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
file_id
|
int
|
Primary key to look up. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
The row as |
Source code in src/noxdb/files.py
get_by_path
¶
Return the file row for a given path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
file_path
|
str
|
Absolute path on disk (the tar, for a tar member). |
required |
archive_member
|
str
|
Member name inside the tar; empty for a plain file. |
''
|
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
The row as |
Source code in src/noxdb/files.py
list_for_sample
¶
Return all sample_files rows for a sample.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
sample_id
|
int
|
Sample to list. |
required |
order_by
|
str
|
Column name to order by. Must be a column of |
'file_id'
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
All matching rows as |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/noxdb/files.py
count_for_sample
¶
Return the number of files registered for a sample.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
sample_id
|
int
|
Sample to count. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of |
Source code in src/noxdb/files.py
update
¶
update(cur, file_id: int, *, file_size_bytes: int | None = None, checksum_md5: str | None = None, storage_tier: str | None = None) -> bool
Partial update of a file row.
Only kwargs with non-None values are written. file_path,
file_type, sample_id, and created_at are NOT updatable
— those describe a different file. Use
restat to refresh size/checksum from
disk after a file is rewritten in place.
Updating storage_tier enforces the same file_type → tier
invariant as register: flipping
archive ↔ work is rejected; scratch / external
overrides are allowed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
file_id
|
int
|
Row to update. |
required |
file_size_bytes
|
int | None
|
New size (if not None). |
None
|
checksum_md5
|
str | None
|
New 32-char lowercase-hex MD5 (if not None). |
None
|
storage_tier
|
str | None
|
New tier (if not None). |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
Malformed |
Source code in src/noxdb/files.py
restat
¶
Re-read size (and optionally md5) from disk for an existing row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
file_id
|
int
|
Row to refresh. |
required |
compute_md5
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
|
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the path no longer resolves. No SQL runs in that case. |
ValueError
|
If the row is a tar member; the tar's size and checksum are not the member's. |
Source code in src/noxdb/files.py
delete
¶
Delete a file row.
Only removes the database record. The file on disk is untouched — clean it up separately.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
file_id
|
int
|
Row to delete. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/noxdb/files.py
exists
¶
exists(cur, file_id: int | None = None, *, path: str | None = None, archive_member: str = '') -> bool
Return whether a file with the given id or path exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cur
|
Audit-logging cursor from |
required | |
file_id
|
int | None
|
Id to check (exclusive with |
None
|
path
|
str | None
|
Path to check (exclusive with |
None
|
archive_member
|
str
|
With |
''
|
Returns:
| Type | Description |
|---|---|
bool
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If both or neither of |