Repository navigation
Conversation
…extension, typed lookups, single Excel open - Scan, rewrite, and append only within the reader's logical rows (before the first empty row). - Extend an existing ProtectedDonor sheet's headers for copied fields, or reject unsafe shapes before mutating. - Resolve non-token references (UUID/accession) as ProtectedDonors via workbook identifiers or typed portal lookup. - Report permission/outage lookup failures distinctly from definitive absence. - Open the Excel workbook once in StructuredDataSet so the progress counting pass cannot pre-create the staged output.
…ed-path clobbering
Coverage Report for CI Build 38955004726Coverage increased (+0.8%) to 75.437%Details
Uncovered Changes
Coverage Regressions1 previously-covered line in 1 file lost coverage.
Coverage Stats
💛 - Coveralls |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Investigate the three correctness issues raised on #339 at reviewed head 460ef38. Reproduce each issue, compare the failing and proven paths, inspect relevant history, and report what you discover before any implementation.
What Changed
dcicutils/submitr/donor_transformer.py. It looks at thedonorreferences in the logical rows of the five protected-item sheets (Demographic, DeathCircumstances, FamilyHistory, MedicalHistory, TissueCollection) and sorts each one as plain donor, protected donor, missing, invalid, permission denied or lookup failed, using typed portal lookups. The transformer then rewrites only the referenced plain Donors to_PROTECTED-DONOR_ids, adds theprotected_donorcolumns and appends the matching ProtectedDonor rows. Regression tests are intest/test_protected_donor_transform.py.CustomExcelhas two new options,transform_protected_donor(off by default) andtransformed_workbook_path. With the transform on, it rewrites the workbook in memory when it opens it and refreshes the visible sheet names. If a path is given, it also saves the result as a new.xlsxand refuses to overwrite an existing file or the input workbook.CustomExcel.with_portalnow takes extra default constructor options.StructuredDataSetnow opens an Excel workbook once and uses that same instance for both progress counting and parsing. It also applies the configured schema order to multi-type JSON inputs, and unneedednonlocaldeclarations were removed. CHANGELOG and Sphinx docs are updated for the new module.🤖 Generated with Claude Code
Risk Assessment
🚨 High: The change implements fixes even though the intent asked for an investigation report first, and the new ref_exists path turns permission and outage failures into definitive absence and skips the ProtectedDonor type check for accession lookups.
Testing
I ran the focused ProtectedDonor test file at b06f586 and it passes in full. Swapping in the 9af5177 source makes six of the new or affected tests fail, so they reproduce the round-1 regressions. A manual run through the real
structured_data.Portalandff_utilsrequest stack, with only the HTTP transport mocked, showed the fixed lookup behaviour end to end. A real ProtectedDonor accession is found. A plain Donor accession is rejected as an invalid reference. A 403 raises a permission-denied lookup error for both accession and submitted_id lookups, a 500 raises a lookup-failed error, and a real 404 is reported as absent. I attempted the same manual run against 9af5177 as a baseline, but my harness doesn't mock the schema fetches that code path needs, so I discarded it. The before/after unit-test run is the before-fix evidence. This is a library change with no UI, so there are no visual artifacts. Issue 3 (already ignored) and the scope question (already answered) are not re-reported.Evidence: End-to-end ProtectedDonor lookup transcript (real Portal and ff_utils stack, mocked transport)
1. accession of a real ProtectedDonor -> PROTECTED_DONOR (GET /SMAPD1234567) 2. accession of a plain Donor -> INVALID; transform: 'Invalid donor reference(s) ... SMADO1234567' 3. accession 403 -> PERMISSION_DENIED; ProtectedDonorLookupError 'permission denied' 4. accession 500 -> LOOKUP_FAILED; ProtectedDonorLookupError 'lookup failed' 5. submitted_id 403 -> PERMISSION_DENIED (GET /ProtectedDonor/...?frame=raw) 6. submitted_id 404 -> MISSING; 'absent from the workbook and portal'Evidence: Script that produced the transcript
Evidence: Focused tests with the 9af5177 source swapped in (they fail before the fix)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
dcicutils/submitr/donor_transformer.py:123- This change goes against the stated intent. The intent says: "Reproduce each issue, compare the failing and proven paths, inspect relevant history, and report what you discover before any implementation." The head commit 9af5177 ("Fix ProtectedDonor lookup and logical sheet handling") changes behavior anyway: it adds a newref_exists-first lookup path inlookup_protected_donor(lines 123-140) and two new tests. It also has no written findings report for the three PR Fm/protected donor dcicutils #339 issues. The user needs to decide whether to drop this commit and deliver an investigation report first, or to approve going ahead with the implementation.dcicutils/submitr/donor_transformer.py:139- With a real StructuredDataPortal(which has_ref_lookup_strategy), any falsyref_existsresult now becomes a definitive ABSENT. ButPortal.ref_lookup_uncached(structured_data.py:987-997) catches every exception, including 401/403, outages and timeouts, and returns None.ref_existsalso returns None when the schema can't be fetched (_is_valid_reffails). As a result, a permission or outage failure on a ProtectedDonor reference is reported as MISSING or INVALID, andtransformraises the absence error instead ofProtectedDonorLookupError. This undoes the guarantee from a9a82ae that permission and outage failures are reported separately from absence. Fix: only treat a truthyref_existsresult as conclusive. On a falsy result, fall through to theget_metadatapath, which keeps HTTP status (or route lookups through a call that exposes errors).dcicutils/submitr/donor_transformer.py:134- For accession values,ref_lookup_strategyroutes the lookup to the root path only (/<accession>).ref_existsthen returns{"type": "ProtectedDonor", ...}for whatever item has that accession, without checking@type. The oldget_metadatapath rejected items whose@typedid not include ProtectedDonor (line 165). Now a protected-itemdonorcell holding the accession of an existing plain Donor (or any other item) is classified as PROTECTED_DONOR, left unchanged, and passes the transformer. Fix: after a truthyref_exists, confirm the type, e.g. withget_metadata(f"/{identifier}")and an@typecheck.test/test_protected_donor_transform.py:148- The commit message says it fixes "logical sheet handling", but 9af5177 only changes source inlookup_protected_donor. The new testtest_prefixed_protected_item_sheet_uses_canonical_namecoverseffective_sheet_namebehavior that already existed in a9a82ae, so it probably passes without this commit and does not reproduce a failure. The new accession test uses a stub portal that is always found, so it does not cover the failure modes above.🔧 **Test** - 5 issues found → auto-fixed ✅
dcicutils/submitr/donor_transformer.py:126- The issue-1 fix sends lookups throughstructured_data.Portal.ref_exists. That path (a) catches every non-404 exception insideref_lookup_uncachedand returns None, and (b) returns{'type': 'ProtectedDonor', ...}without checking the item's real@type. Because the portal has_ref_lookup_strategy, a None result is treated as definitive absence. Results with mocked transport: an accession lookup that hits 403 or 500 gives ABSENT, then 'Invalid donor reference(s)'. A submitted_id lookup that hits 403 gives 'ProtectedDonor reference(s) are absent from the workbook and portal'; at a9a82ae it correctly raised ProtectedDonorLookupError (permission denied). The accession of a plain Donor (@type ['Donor', ...]) is classified as a ProtectedDonor and the transform returns False, silently leaving the protected item linked to a plain Donor. The reviewer explicitly asked to keep the@typecheck and the permission/outage distinctions.dcicutils/submitr/donor_transformer.py:521- Review issue 2 is still present. Protected-item sheets are matched usingCustomExcel.effective_sheet_name(prefix stripping only), but the reader also runsSchema.type_name/ to_camel_case on the title. ThroughCustomExcel(transform_protected_donor=True)+StructuredDataSet, the titles 'demographic', 'DSA_demographic' and 'Death Circumstances' are all read as Demographic/DeathCircumstances. None of them is transformed: no ProtectedDonor sheet is created and the parsed donor link stays 'GCC_DONOR_A', with no error. The newtest_prefixed_protected_item_sheet_uses_canonical_nameonly covers 'DSA_Demographic', which already worked before this commit.test/test_protected_donor_transform.py:477-test_repeated_construction_still_cannot_clobber_the_staged_pathfails every time, including when run alone (DID NOT RAISE ValueError). The class-levelCustomExcel._saved_transformed_workbook_path_pairsallowlist lets a second construction with the same input/output pair overwrite the staged workbook, so the no-clobber protection the test asserts does not hold. The test was added in a9a82ae, and 9af5177 does not touchcustom_excel.py, so this is a branch-level failure that CI will hit. Either the save guard or the test's expectation needs to change.dcicutils/submitr/donor_transformer.py:397- Review issue 3 (the source Donor guard scanning past the reader's terminator) does not reproduce here. The reviewed head 460ef38 is not in local history, and this branch has no source-Donor 'has content beyond its header columns' guard; only the destination check in_validate_append_targetexists. The reviewer's workbook (stale row 4 plus a note in E4) transforms cleanly, with protected_donor written to E1/E2 and the note left alone.NO_SERVER_FIXTURES=TRUE USE_SAMPLE_ENVUTILS=TRUE poetry run pytest -q test/test_protected_donor_transform.py(32 passed, 1 failed: test_repeated_construction_still_cannot_clobber_the_staged_path)... pytest -k clobber, run alone to confirm the failure is deterministicpoetry run python $EVIDENCE/repro_pr339.pyat 9af5177: issue 1 with a real structured_data.Portal + portal_utils._lookup_strategy and mocked transport (existing accession, existing UUID, plain-Donor accession, 403, 500, submitted_id 403); issue 2 via CustomExcel(transform_protected_donor=True) + StructuredDataSet with the titles Demographic, demographic, DSA_demographic and 'Death Circumstances'; issue 3 with the reviewer's Donor workbook, with and without the E4 noteThe same repro script run againstgit archive a9a82ae dcicutilsfor comparisongit log -Son the clobber test to find which commit added it🔧 Fix: Fix ProtectedDonor lookup types, sheet names, staged-path clobbering
✅ Re-checked - no issues remain.
NO_SERVER_FIXTURES=TRUE USE_SAMPLE_ENVUTILS=TRUE poetry run pytest test/test_protected_donor_transform.py -qon b06f586: all pass, including the previously failingtest_repeated_construction_still_cannot_clobber_the_staged_pathThe same test file with the 9af5177donor_transformer.pyandcustom_excel.pyswapped in temporarily: 6 tests fail (root-path accession lookup, accession with no@type, the three sheet-name variants throughCustomExcel+StructuredDataSet, and the staged-path clobber test). This confirms they catch the regressions; the worktree was restored afterwardsManual end-to-end check (e2e_lookup_check.py): the realstructured_data.Portalandff_utilsrequest/retry stack, with only the GET transport mocked, run six lookup scenarios throughProtectedDonorWorkbookTransformer.analyzeandtransform🔧 **Document** - 1 issue found → auto-fixed ✅
CHANGELOG.rst:10- This change adds a public module (dcicutils.submitr.donor_transformer) and new CustomExcel options (transform_protected_donor, transformed_workbook_path, with_portal(**options)). However, pyproject.toml is still at 8.19.0 and CHANGELOG.rst has no entry for it. Because the version is the release source of truth and the intent here is investigation-only, picking the version bump and writing the changelog entry is left to the author.🔧 Fix: Add ProtectedDonor CHANGELOG entry under 8.19.0
✅ Re-checked - no issues remain.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.