Skip to content

fix(spp_dci_client_dr): parse DCI v1.0.0 spec envelope and record fields#203

Merged
gonzalesedwin1123 merged 2 commits into
19.0from
fix/spp-dci-dr-spec-envelope
May 18, 2026
Merged

fix(spp_dci_client_dr): parse DCI v1.0.0 spec envelope and record fields#203
gonzalesedwin1123 merged 2 commits into
19.0from
fix/spp-dci-dr-spec-envelope

Conversation

@jeremi
Copy link
Copy Markdown
Member

@jeremi jeremi commented May 17, 2026

Summary

  • The sync DR path treated search_response[*].data as a flat record (it is actually a {version, reg_record_type, reg_records: [...]} envelope per DCI v1.0.0), and both sync and callback paths read flat fields that do not exist on spec-shape records. A spec-conformant DR server was silently classified as 'no disability'.
  • New stateless services/dr_parsing.py centralizes envelope unwrap, record-field extraction (disability_status string + disability_details[*].impairment_type), and ISO date coercion. Both sync (dr_service.py) and callback (routers/callback.py) now route through it.
  • Status tokens narrowed to spec-only (approved/registered -> True, rejected/denied -> False, empty -> impairment-list fallback, unknown -> WARN + fallback). Legacy yes/true tokens are no longer treated as approved.

Notable changes

  • disability_details: None on the wire no longer crashes the parser (or [] guard).
  • unwrap_search_data rejects non-dict envelopes and non-list reg_records, logging a WARN and returning [] rather than passing garbage downstream.
  • assessment_date prefers last_updated, falls back to registration_date, returns None (with WARN) on unparseable values.
  • extract_functional_scores always returns {}. DCI v1.0.0 has no numeric scoring; the hook is kept for future spec versions.

Out-of-scope flag

  • In routers/callback.py, last_sync_date was switched from datetime.now(UTC) to fields.Datetime.now() while editing the same vals block. It is the correct Odoo idiom for a Datetime field, but it is technically unrelated to the parser fix. Happy to split into a separate commit if preferred.

Test plan

  • ./spp t spp_dci_client_dr -> 92 tests, 0 failed, 0 errors (was 46 before).
  • pre-commit run ruff --files <changed> clean.
  • pre-commit run ruff-format --files <changed> clean.
  • New tests/test_dr_parsing.py covers: envelope unwrap (None/empty/wrong-type/missing-key/non-list), status resolution (approved/rejected/empty/unknown + case insensitivity), impairment extraction (null-safety, missing impairment_type skipped), date coercion (ISO datetime with Z, plain ISO date, fallback chain, unparseable -> None + WARN), and extract_functional_scores always {}.
  • New tests/test_callback.py covers _process_dr_search_result end-to-end with spec-shape envelopes: approved creates record, rejected creates has_disability=False record, non-success status is skipped, repeat call updates instead of duplicating.
  • tests/test_dr_service.py rebuilt around the spec envelope (mock responses now wrap data in {version, reg_record_type, reg_records: [...]}).

🤖 Generated with Claude Code

The sync DR path treated `search_response[*].data` as a flat record,
and both sync and callback paths read flat fields that do not exist
in spec-shape records (`disability_status` string,
`disability_details[*].impairment_type`). A spec-conformant DR server
was silently classified as "no disability".

Route both paths through a shared `dr_parsing` module so any
spec-shape envelope is correctly unwrapped and record fields are
derived from `disability_status` and `disability_details`. Date
coercion (ISO datetime / `Z` suffix) is centralized so both sync and
callback hand the ORM a `date` object.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new dr_parsing service containing stateless helpers to standardize the extraction of DCI v1.0.0 Disability Registry records. The DRService and callback router have been refactored to delegate parsing logic to this module, enhancing testability and specification compliance. Comprehensive tests were also added for the new parsing logic and callback processing. A review comment suggests improving the handling of null values for the disability_status field to prevent incorrect "Unknown status" warnings.

disability_types = [d["impairment_type"] for d in details if isinstance(d, dict) and d.get("impairment_type")]

# Resolve has_disability from the disability_status string
status_str = str(record.get("disability_status", "")).strip().lower()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When the disability_status field is explicitly null in the JSON payload, record.get("disability_status", "") returns None. Passing this to str() results in the string "none" (after .lower()), which is not one of the expected status tokens. This causes the parser to fall through to the else block at line 113, logging an "Unknown disability_status value: None" warning. Using or "" inside the str() call ensures that null values are treated as empty strings, which are correctly handled as an ambiguous status without triggering a warning.

Suggested change
status_str = str(record.get("disability_status", "")).strip().lower()
status_str = str(record.get("disability_status") or "").strip().lower()

@codecov
Copy link
Copy Markdown

codecov Bot commented May 17, 2026

Codecov Report

❌ Patch coverage is 96.82540% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.37%. Comparing base (b3510d8) to head (55a219d).

Files with missing lines Patch % Lines
spp_dci_client_dr/services/dr_parsing.py 95.91% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #203      +/-   ##
==========================================
+ Coverage   66.08%   66.37%   +0.28%     
==========================================
  Files          86      124      +38     
  Lines        7501    11404    +3903     
==========================================
+ Hits         4957     7569    +2612     
- Misses       2544     3835    +1291     
Flag Coverage Δ
spp_base_common 90.26% <ø> (ø)
spp_dci_client_dr 63.70% <96.82%> (?)
spp_farmer_registry_demo 54.01% <ø> (?)
spp_mis_demo_v2 73.61% <ø> (?)
spp_programs 64.84% <ø> (+0.01%) ⬆️
spp_security 66.66% <ø> (ø)
spp_starter_social_registry 0.00% <ø> (?)
spp_starter_sp_mis 81.25% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_dci_client_dr/routers/callback.py 53.57% <100.00%> (ø)
spp_dci_client_dr/services/__init__.py 100.00% <100.00%> (ø)
spp_dci_client_dr/services/dr_service.py 85.21% <100.00%> (ø)
spp_dci_client_dr/services/dr_parsing.py 95.91% <95.91%> (ø)

... and 42 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…r_parsing

The callback path duplicated `data.get("reg_records", [])` without the
type guards and WARN logging now centralized in `unwrap_search_data`.
A malformed `data` envelope was silently coerced to `{}` and skipped,
hiding spec drift from operators. Route through the shared helper so
both sync and callback paths get identical validation behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@gonzalesedwin1123 gonzalesedwin1123 merged commit 4a98bdb into 19.0 May 18, 2026
23 checks passed
@gonzalesedwin1123 gonzalesedwin1123 deleted the fix/spp-dci-dr-spec-envelope branch May 18, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants