Posda Tools
  1. Posda Tools

oneposda

Public
AuthorCommitMessageCommit dateIssues
Michael RutherfordMichael Rutherford
11cf6e7bab7Add a zip download of a QC assignment's seriesGET /distribution/qc/assignments/{id}/series/{uid}/download zips exactly the files the QC file list returns, in display order, named 0001_<SOPInstanceUID>.dcm and so on. The file list and the download share one query, so the zip can't drift from what the reviewer saw. Entries never carry server paths: asynczip gains stream_named_files for that, and stream_files is unchanged. Co-Authored-By: Cl...
Michael RutherfordMichael Rutherford
58917beafceFix NiftiPhiScan crash on oversized tag values and close each fileThe oversized-value message used tag.name on a plain string. Each parser is now closed once its header and extensions are read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
4ce99942184Drop per-file email lines from the NIfTI populate scriptCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quasar JaroszQuasar Jarosz
4226dbeba3dInstall nginx default.conf where the nginxinc image reads itThe final stage moved from bitnami/nginx to nginxinc/nginx-unprivileged, but the COPY still targeted the Bitnami server_blocks path, which that image never reads. Deployments have been mounting the config in to compensate. Replace the image's stock conf.d/default.conf instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
ea2639db6d1Count earlier timepoints as External in linkage reportsFound used to mean "in the current activity", so a reference that only resolved to an earlier timepoint looked healthy. After a UID change that left references untouched, stale links were reported as Found. Found now means the current timepoint. External covers earlier timepoints of the current activity, listed first, then other activities. The global lookup prefers the current activity, so a ...
Michael RutherfordMichael Rutherford
af5a0754ce7MPull request #450: Feature/TADMIN-1661 develop collection dataset release moduleMerge in PT/oneposda from feature/TADMIN-1661-develop-collection-dataset-release-module to master * commit 'db4e5516e79c6c8127dcb550e5a8b8f8cea999f0': Use the importer's shared steps in the WordPress pull Store generated manifests through the importer Split the import steps out of import_file for internal callersTADMIN-1661
Michael RutherfordMichael Rutherford
db4e5516e79Use the importer's shared steps in the WordPress pullReplaces its copy of the import steps with importer.import_temp_file, keeping its comment and source URL. No change in behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
5f134804d9bStore generated manifests through the importerAll four generators wrote the bytes straight to disk and inserted their own file and file_location rows, outside the importer. That left no import event, never set ready_to_process, overwrote existing files, and read the storage root from FILE_STORAGE_ROOT_ID while the importer uses FILE_STORAGE_ROOT. A new store_manifest calls importer.import_bytes in the generator's transaction, and also hold...
Michael RutherfordMichael Rutherford
cfab1c1c7a2Split the import steps out of import_file for internal callersimport_temp_file holds what import_file did after receiving the bytes, and import_bytes handles bytes already in memory. The caller owns the transaction, so the manifest generators and the WordPress pull can share this code instead of keeping their own copies. Nothing calls them yet except import_file, whose behavior is unchanged: the same checks gave identical results before and after. Co-Aut...
Quasar JaroszQuasar Jarosz
4abbd6746a0ImportInPlaceFromPublic: retry API calls and set expected_countWrap the import-event create/close and file_in_place calls in a request_with_retry helper. Connection errors and 5xx responses (e.g. a 502 while the API restarts) are retried with capped exponential backoff: 5s doubling to a 60s cap, 10 attempts, about six minutes total. 4xx responses such as 422 "no such file" are real failures and are not retried. Create the import event after the count quer...
Michael RutherfordMichael Rutherford
2d27d887271Let a curator hand an IDC version to IDC, and gate on its statusThe API now reads and writes status.json directly, sharing the daemon's lock. - GET /transfers/{id}/idc/bucket-status reads the status live. - POST /transfers/{id}/idc/set-ready is the curator's hand-off. - Queue refuses (409) once IDC has the version at Processing or Processed. - Go live refuses until Processed, which is IDC's published, or when the status can't be read. Adds google-cloud-s...
Michael RutherfordMichael Rutherford
52003c9ddcdWordPress transfers only upload; page writes move to DisseminateWordPress can't stage a page change, so a write is public at once. - stage_wp_recordset uploads each data file and leaves the page alone. - POST /transfers/{t}/recordsets/{rr}/wp/update-page makes the page change. - GET /datasets/releases/{id}/wp/pages reads each default destination's page state live; go-live refuses (409) while any page is stale, retriever manifests included. - The IDC cli...
Michael RutherfordMichael Rutherford
4fc4f4b0d61Derive an IDC transfer's bucket path when it is createdgs://<IDC_TRANSFER_BUCKET>/<dataset slug>/v<release>, filled on create and on read for older transfers rather than waiting for the dataset manifest. Never overwrites a set value. GET /transfers/{id}/idc also returns dataset_name and release_number for naming manifest downloads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
aa03bdaac1cRe-queueing a finished transfer re-verifies its filesThe daemon streams only pending rows, so re-queueing a successful transfer skipped every completed file and uploaded just the manifests. Reset the rows on that transition: identical objects are skipped by the MD5 check, missing or changed ones upload. A failed transfer keeps its completed rows and retries only the failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
d57192950afName retriever manifests after the dataset and recordset{dataset}-{recordset}-retriever-manifest.{ext}, built in one place and returned as manifest_file_name on the transfer's recordset list, so a download from Posda and from the public page have the same name. Drops the digest: WordPress handles name collisions and dedup is by file_id. No version, because identical bytes reuse one attachment across releases. Co-Authored-By: Claude Opus 5.5 <norepl...
Michael RutherfordMichael Rutherford
778e1e41840Let a download link name the saved fileOptional filename param, reduced to safe characters since it ends up in a response header. One file can belong to several releases, so the name comes with the link rather than being stored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
52f3a0a62c6Record the attachment a WordPress pull came fromThe WordPress transfer reuses an attachment through wp_object_map ('file' -> 'media'), but a pull never wrote that row, so syncing an unchanged pulled file uploaded a copy and re-pointed the download page at it. The pull now maps the imported file to the attachment it came from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
ccb609d8256Refuse to finalize a dataset release with unpublished membersA draft recordset release in the dataset release ships nothing, then turns up inside the finalized release once its draft publishes. Setting release_status to released now returns 409 UNPUBLISHED_MEMBERS naming them: publish or remove each first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
9eaa0881ff4Describe each recordset's version in the cycle payloadBundle reads each recordset's version on one line, so release_in_cycle gains its date, creator and file count, and each recordset reports how many published versions it has (released_version_count) -- Bundle offers Change version only when there is more than one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
cc200d3fc34Expose an open draft's own release in the cycle payloadBundle's Include put back only a recordset's latest published version, so a never-published recordset removed from the release had no way back in. open_draft now carries recordset_release_id -- the draft's release -- for Include to restore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
510fdb48f3aReturn release_status with a recordset's releasesThe recordset page's Releases table shows each release's status, like the dataset page's, so a draft release reads as one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
12493576719Cancel a deleted draft's unfinished QC reviewsDraft delete is a soft delete and left open and stale reviews live, so they stayed in the pickup queue, claims and the cycle rollup. Deleting a draft now cancels them in the same transaction; complete reviews stay as the record of finished work. 20261101_03_cancel_reviews_of_deleted_drafts backfills reviews of drafts already deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
fe552775c95Include the dataset in the QC assignment queueThe pickup queue lists slices by recordset, which says little without its dataset; left-join it so a recordset without one still lists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
7089a8ccdeeKeep non-DICOM file names unique within a draftA draft never holds two different non-DICOM files under one name; the name is the file's identity. Every add path resolves a collision the same way (_plan_non_dicom_names): on_name_conflict = replace (drop the draft's file) or skip (drop the incoming one); omitted, a collision is a 409 listing the names. mode is now add | replace_all, separate from it. Release merge moves server-side (content/...
Michael RutherfordMichael Rutherford
84759d67069Number same-day migrations so they sort in run orderSorted filename order is apply order, so two migrations dated the same day sorted by name, not by when they must run. Same-day files now carry a sequence after the date (YYYYMMDD_NN_). The two 20261029 files and 20261101_01_qc_unit_file_snapshot are renamed in the order they were applied; none has reached production. The README documents the rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthr...
Michael RutherfordMichael Rutherford
b692de79ae4Make QC file units snapshots, like series unitsA file unit pointed at recordset_draft_file_id with ON DELETE CASCADE, so removing or replacing a draft file deleted the unit and its decision history, leaving a stale review empty. A series unit instead records its identity (UID) and content (hash) with no link to the draft. File units now do the same: qc_unit gains file_id (content) and file_name (identity), and recordset_draft_file_id, its ...
Michael RutherfordMichael Rutherford
98b2eb8fffaLet every draft add source merge or replacefiles/add (body) and files/from-wp (query) take mode = add | replace_same_name | replace_all, applied in the insert's transaction. replace_same_name drops a draft file only when the name matches and the content differs. content/replace-all accepts compare_release_id as an alternative to a timepoint. New lookups size the UI's same-name option: files/name-matches and files/from-wp/preview. Co-Au...
Michael RutherfordMichael Rutherford
8618650819cClone a stale non-DICOM QC reviewThe clone redraws every non-DICOM file in the draft. A file whose content hash was in the source keeps its decision, with the note re-recorded as "carried from review #N"; anything else starts pending, so a fixed file comes back for review. A stale non-DICOM review's breakdown counts new hashes only: a removed file's unit cascades away with the draft file. Co-Authored-By: Claude Opus 5.5 <nore...
Michael RutherfordMichael Rutherford
c706a6abfc4Return the reason behind each QC file's current statusThe review files list joins the history entry that set qc_status, so the UI can show a rejection's reason, who gave it and when. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
6556f907dcaAdd files_rejected to the cycle's QC rollupThe file share of series_rejected, like files_pending, so Verify can name a rejection as "1 file" or "1 series". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
54c60fa5e87Add rejected and flagged counts to the cycle's QC rollupVerify's recordset rows draw a segmented progress bar, and need the rejected and flagged shares alongside approved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
167b961ad1dList a QC review's files and let a reviewer reject a sliceGET /qc/reviews/{id}/files returns a non-DICOM review's file units with their names (optionally one slice's), for the review and manage modals. POST /qc/assignments/{id}/reject-units rejects every file in a slice and requires a reason; the slice completes but the review stays open, so the publish gate holds. qc_approve_all_units becomes qc_decide_all_units, taking the status to set. Co-Authore...
Michael RutherfordMichael Rutherford
1e166acb72eRetire a stale QC review as 'replaced' when it is clonedA clone left its stale source stale, so the publish gate (no stale reviews) could never pass and the source kept offering itself for re-clone. Cloning a stale review now marks it 'replaced' in the same transaction, then syncs the new review so a fully carried-forward clone completes at once. 'replaced' is terminal and counted like 'cancelled': excluded from the cycle QC rollup, the assignment ...
Michael RutherfordMichael Rutherford
7651ed7b153Document the 'replaced' QC review statusreview_status is free text with no CHECK constraint; this only updates the column comment to list the new value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
824387883ecAutomate the QC review lifecycle and enforce the publish gate- Reviews complete when every unit is approved and reopen when one no longer is (qc_sync_review_status, run from qc_sync_assignment_status). Stale and cancelled reviews are left alone. - Completed work is locked: releasing or reassigning a completed slice, or cancelling a completed review, returns 409. - Publishing a draft refuses with 422 QC_INCOMPLETE unless it has a complete review a...
Michael RutherfordMichael Rutherford
75f5306cf58Stale complete QC reviews too, scoped to the files each reviewsReviews now complete themselves, so a complete review must also go stale when its draft's files change, or the publish gate passes on QC that no longer covers the draft. Staleness is also narrowed to the kind of file a review covers -- file_series rows for a DICOM review, non-DICOM files for a non_dicom one -- so adding a CSV no longer stales the DICOM review. Redefines qc_mark_open_reviews_st...
Michael RutherfordMichael Rutherford
829f09bd181Report pending QC file units separately in the dataset cycleThe cycle's qc rollup counts every qc_unit -- series and non-DICOM files -- under series_*. Add files_pending, the file share of series_pending, so the UI can say "52 series and 2 files" instead of calling them all series. Additive: existing fields are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
cb076b3e41aReturn the orphaned WordPress downloads in the dataset cyclecount_orphaned_wp_downloads already fetched each orphaned download to skip trashed ones; find_orphaned_wp_downloads returns them in the WP search-result shape instead of just counting them, so Setup can offer them as link suggestions. The cycle payload gains orphaned_downloads; orphaned_download_count is kept as its length, so existing readers are unchanged. Co-Authored-By: Claude Opus 5.5 <no...
Michael RutherfordMichael Rutherford
2706dd96d0dAdd collection_name and relative_file_url to the IDC imaging manifestcollection_name is the CTP Project Name, (0013,"CTP",10), read per file from ctp_file -- an analysis result's DICOM carries the collection it points at. It is LEFT joined: files without group 13 tags are expected and are fixed in curation, and an inner join would drop them and change dataset_hash. Blank values are emitted empty and counted in the response as missing_collection_name so the UI ca...
Michael RutherfordMichael Rutherford
e1ff09df797Use the SeriesInstanceUID header in general download manifestsThe TCIA Data Retriever matches "SeriesInstanceUID" (or "Series UID") case-sensitively, so the series_instance_uid header made it reject the IDC/GC manifests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
f233c37288aStop waiting on transfers to unconfigured destinationsWhen no released member recordset routes to a destination any more, the transfer to it has nothing to ship, yet the publish gate still required it to succeed, blocking the release for good. Count only transfers to destinations still configured, and expose the same test as `configured` on the cycle payload's transfers so the UI can match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Michael RutherfordMichael Rutherford
29bfc9c66c6Build the WordPress transferQueueing a wp transfer now delivers it in-process: each recordset release's single data file is uploaded to the WordPress media library under its original name (skipped when those bytes are already mapped), recorded in transfer_recordset.download_file_id, and set as its download page's download_file, with download_size refreshed 1000-based to match the wiki-migration tooling. The transfer settl...
Michael RutherfordMichael Rutherford
fced04bddcdPrepare transfer_recordset for the WordPress transferRename transfer_recordset.retriever_manifest_file_id to download_file_id: a WordPress transfer puts the recordset release's own data file on the download page the same way manifest/attach puts the retriever manifest there, so one column serves both, discriminated by destination. Drop transfer_wp, whose columns were never populated or read, along with its GET/PUT routes and TransferWpUpdate. Th...
Michael RutherfordMichael Rutherford
e0176c22cdaFill in the IDC dataset manifest's missing and mis-sourced fieldsAdds the three fields that were never implemented and fixes two that were silently emitting the wrong thing: - dataset_version_doi from dataset_release.release_doi. - license_url / license_long_name / license_short_name resolved through WordPress. recordset_license was dropped 2026-09-01, so licensing is WP's: the transfer's recordsets -> wp_object_map -> download.data_license -> a Licen...
Quasar JaroszQuasar Jarosz
da242cd3fbfstart-background-process: --queue overrides the work queue nameAd hoc workers pop lists that are not work_queue_<priority>. --queue NAME sets work.background_queue_name and the Redis list the work id is pushed on to the full given text, in place of the name derived from the operation's worker_priority. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vmmhn9w7RwWUt2SJV5aD5Y
Quasar JaroszQuasar Jarosz
1f450a94786update import comment to include site and background_id, for tracking
Quasar JaroszQuasar Jarosz
a0dca16c06aMove release version into a top-level VERSION fileBumps are now one-line commits that never touch build rules, and other tooling can read the version without parsing make. Bumps to 1.1.21. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Quasar JaroszQuasar Jarosz
1c12d7efb77Makefile: add push-manifest, drive push targets from IMAGE_DIRSCo-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Quasar JaroszQuasar Jarosz
99ef5a57a82common.mk: per-arch tags and push-manifest merge targetpush now always publishes GIT_TAG-ARCH and VERSION-ARCH tags, and only publishes the plain tags when running on PRIMARY_ARCH (amd64). An arm64 laptop can therefore run make push without overwriting what the servers pull. push-manifest merges the per-arch tags for one commit into multi-platform plain tags using buildx imagetools, entirely in the registry, so every arch is built natively and noth...
Quasar JaroszQuasar Jarosz
bb1da206316Makefile: add push-multiplatform target for webCo-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>