Skip to content

Initial commits for change of checkbox logic for multiselection case …#255

Open
Kunalbizbrolly wants to merge 1 commit intorelease-5.0from
work_module_rmnch_flow_touch_up
Open

Initial commits for change of checkbox logic for multiselection case …#255
Kunalbizbrolly wants to merge 1 commit intorelease-5.0from
work_module_rmnch_flow_touch_up

Conversation

@Kunalbizbrolly
Copy link
Copy Markdown

@Kunalbizbrolly Kunalbizbrolly commented May 4, 2026

…(none),

📋 Description

JIRA ID: MHWC-281, MHWC-266

Infant Registration Form Validation Issue (Birth Dose Vaccine Given),Even after the woman has delivered, it shows up in the ANC listing


✅ Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)
  • New feature (non-breaking change which adds functionality)
  • 🔥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 🛠 Refactor (change that is neither a fix nor a new feature)
  • ⚙️ Config change (configuration file or build script updates)
  • 📚 Documentation (updates to docs or readme)
  • 🧪 Tests (adding new or updating existing tests)
  • 🎨 UI/UX (changes that affect the user interface)
  • 🚀 Performance (improves performance)
  • 🧹 Chore (miscellaneous changes that don't modify src or test files)

ℹ️ Additional Information

Please describe how the changes were tested, and include any relevant screenshots, logs, or other information that provides additional context.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved handling of "None" vaccine and complication options across different language versions
    • Form fields now refresh properly when values are updated
    • Fixed null user lookup handling
  • Changes

    • Birth weight is now recorded in grams
    • ANC visit count calculation methodology updated
    • Updated complication options in Assamese and Hindi language variants

@Kunalbizbrolly Kunalbizbrolly self-assigned this May 4, 2026
@Kunalbizbrolly Kunalbizbrolly added the bug Something isn't working label May 4, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Walkthrough

This PR introduces a new reactive force-refresh mechanism for form mutations, refactors ANC patient querying from count-based to patient-list-based filtering, implements locale-neutral "None" option handling for checkbox selections, converts birth weight representation to milliliter units, and updates localized string arrays.

Changes

Form Element Mutation & Refresh Handling

Layer / File(s) Summary
Infrastructure
Dataset.kt
Adds forceRefreshIdFlow: SharedFlow<Int> and forceRefreshId(id: Int) helper to emit element IDs when form values are mutated in-place, enabling explicit adapter rebinds when DiffUtil cannot detect changes.
Form Data Conversion
ChildBeneficiaryRegistrationDataset.kt
Birth weight is converted from raw kilograms to milligrams via (weight * 1000).toInt().toString() during setup.
Checkbox Selection Logic
ChildRegistrationDataset.kt, NeonatalOutcomeDataset.kt
Implements locale-neutral "None" option detection using englishResources to find the English index and map to the current locale's label; enforces mutually exclusive selection (clearing other options when "None" is selected, or removing "None" when another option is checked); calls forceRefreshId() after mutating form values.
Localized String Resources
values-as/strings.xml, values-hi/strings.xml
Removes extraneous complications options (Meconium aspiration, Other) from no_newborn_complications_array, leaving only the "None" locale-specific label.
ViewModel Null Safety
EligibleCoupleTrackingFormViewModel.kt
Handles nullable getLoggedInUser() result; defaults empty strings for createdBy and updatedBy when user is absent.
Fragment Refresh Listener
ChildRegistrationFragment.kt
Observes dataset.forceRefreshIdFlow to detect which form row element was mutated; finds the corresponding adapter position and triggers notifyItemChanged() to force a rebind.

ANC Patient Querying Refactoring

Layer / File(s) Summary
Data Access
MaternalHealthDao.kt
Removes getANCEligibleCount(lmpCutoffMillis) count query; adds getAllPatientsWithANC() returning Flow<List<PatientWithPwrCache>> via LEFT OUTER JOIN PREGNANCY_REGISTER filtered by active female patients (statusOfWomanID IN (2)).
Repository Aggregation
MaternalHealthRepo.kt
Replaces DAO-backed count with getAllPatientsWithANC(), combining ANC patients from DAO with eligible-couple-tracking PWR patients, deduplicating by patientID, and sorting by latest active PWR createdDate; introduces getANCCount() that derives eligible count in-memory by filtering for active PWR, female gender, age 15–49, and LMP within a 35-day window.
UI Data Binding
RMNCHAIconDataset.kt, ANCVisitsFragment.kt
Updates ANC icon count to use getANCCount() instead of getANCEligibleCount(); updates patient observer in ANCVisitsFragment to collect from getAllPatientsWithANC() instead of getAllPatientsWithPWR().

Sequence Diagram

sequenceDiagram
    participant FormElement as FormElement<br/>(Checkbox)
    participant Dataset as Dataset<br/>(ChildRegistration)
    participant Fragment as ChildRegistration<br/>Fragment
    participant Adapter as FormAdapter<br/>(RecyclerView)
    participant UI as UI<br/>(rendered row)
    
    rect rgba(100, 150, 200, 0.5)
        Note over FormElement,UI: User selects checkbox in a form row
        FormElement->>Dataset: handleListOnValueChanged(element)
        Dataset->>FormElement: mutate element.value in-place<br/>(clear/add options)
        Dataset->>Dataset: forceRefreshId(elementId)
        Dataset->>Dataset: _forceRefreshIdFlow.tryEmit(elementId)
    end
    
    rect rgba(150, 100, 200, 0.5)
        Note over Dataset,UI: Fragment detects mutation signal
        Dataset-->>Fragment: emit(elementId) via forceRefreshIdFlow
        Fragment->>Fragment: collect emission in lifecycleScope
        Fragment->>Adapter: notifyItemChanged(position)
    end
    
    rect rgba(100, 200, 150, 0.5)
        Note over Adapter,UI: Adapter rebinds row with updated value
        Adapter->>FormElement: onBindViewHolder (rebind)
        FormElement->>UI: render updated element.value
        UI-->>Fragment: user sees new state
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A humble rabbit hops with glee,
Where forms now refresh reactively!
With locale-wise "Nones" and counts so clean,
ANC patients dance between screens. 🌿
Birth weights bloom in milliliter grace,
A boundless refresh throughout this place!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and incomplete, using ellipsis without clearly conveying the main change. Terms like 'Initial commits' and 'multiselection case' lack specificity about which features are affected. Revise the title to be more specific and complete, e.g., 'Fix checkbox validation logic for vaccine and complications multi-selection' or similar that clearly describes the primary changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch work_module_rmnch_flow_touch_up

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 4, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
25.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@Kunalbizbrolly
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/org/piramalswasthya/cho/ui/home/rmncha/maternal_health/ANCVisitsFragment.kt (1)

128-152: ⚠️ Potential issue | 🟠 Major

observePatients() lacks the pregnantWomanDelivered guard that getAncDueList() uses, creating an inconsistency.

getAncDueList() explicitly skips delivered women:

if (ancRecords.any { it.pregnantWomanDelivered == true }) return@mapNotNull null

But observePatients() and getANCCount() rely exclusively on the DAO's statusOfWomanID IN (2) predicate, with no check for delivery. If statusOfWomanID is not reliably updated when a delivery outcome is recorded, delivered women could still appear in the ANC listing.

Note: The field pregnantWomanDelivered exists only on PregnantWomanAncCache, not on the PregnantWomanRegistrationCache (PWR) domain model, so the suggested guard would need to either:

  • Access the ANC records directly (like getAncDueList() does), or
  • Check patient.patient.statusOfWomanID != 3 (post-natal status) to avoid the field path issue.

Verify that statusOfWomanID is consistently updated to 3 on delivery; if not, add a defensive check similar to getAncDueList() or use the status ID condition to suppress post-delivery women from the ANC list.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/org/piramalswasthya/cho/ui/home/rmncha/maternal_health/ANCVisitsFragment.kt`
around lines 128 - 152, observePatients() currently doesn't exclude delivered
women like getAncDueList() does; update observePatients() (and similarly
getANCCount()) to defensively filter out post-delivery patients by either
fetching the patient's ANC records and skipping when any PregnantWomanAncCache
has pregnantWomanDelivered == true (same approach as getAncDueList()), or by
ensuring patient.patient.statusOfWomanID != 3 before including them (if you can
guarantee statusOfWomanID is updated). Locate the filtering block in
observePatients() (the map/filter pipeline that builds allPatients) and add the
chosen guard there; mirror the same guard in getANCCount() to keep
listing/counts consistent. Ensure you reference PregnantWomanAncCache when using
the ANC-records approach so you can check pregnantWomanDelivered.
🧹 Nitpick comments (1)
app/src/main/java/org/piramalswasthya/cho/database/room/dao/MaternalHealthDao.kt (1)

104-115: ⚡ Quick win

LEFT OUTER JOIN allows patients without a PWR record into the ANC result set, contradicting the KDoc.

The WHERE (pwr.patientID IS NULL OR pwr.active = 1) condition lets any PATIENT row with statusOfWomanID = 2 but no pregnancy registration pass through (the NULL branch). The KDoc at line 87 states "Returns only patients who have an active pregnancy registration," which is incorrect for the null-join case.

These unregistered patients are ultimately discarded by the hasActivePWR predicate in both ANCVisitsFragment.observePatients() and getANCCount(), so there is no visible bug. However, Room still hydrates the extra rows and the @Relation fetches their (empty) PWR lists.

Since ANC eligibility explicitly requires an active PWR, consider replacing the LEFT OUTER JOIN with an INNER JOIN and dropping the NULL branch:

♻️ Proposed refactor
     `@Transaction`
     `@Query`("""
         SELECT DISTINCT p.* FROM PATIENT p
-        LEFT OUTER JOIN PREGNANCY_REGISTER pwr ON p.patientID = pwr.patientID
-        WHERE (pwr.patientID IS NULL OR pwr.active = 1)
-        AND p.genderID = 2
+        INNER JOIN PREGNANCY_REGISTER pwr ON p.patientID = pwr.patientID
+        WHERE pwr.active = 1
+        AND p.genderID = 2
         AND p.maritalStatusID = 2
         AND p.statusOfWomanID IN (2)
         AND p.age BETWEEN 15 AND 49
         ORDER BY p.registrationDate DESC
     """)
     fun getAllPatientsWithANC(): Flow<List<PatientWithPwrCache>>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/org/piramalswasthya/cho/database/room/dao/MaternalHealthDao.kt`
around lines 104 - 115, The query in getAllPatientsWithANC currently uses a LEFT
OUTER JOIN and a WHERE clause that allows rows with no pregnancy registration
(pwr.patientID IS NULL), which contradicts the KDoc saying only patients with an
active pregnancy registration should be returned; change the JOIN to an INNER
JOIN and remove the NULL branch so the predicate becomes (pwr.active = 1) only,
keeping the other filters (p.genderID, p.maritalStatusID, p.statusOfWomanID,
p.age) and the ORDER BY intact to ensure Room only hydrates patients that have
an active PREGNANCY_REGISTER record.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/org/piramalswasthya/cho/configuration/ChildRegistrationDataset.kt`:
- Around line 774-783: The checkbox callback currently conflates position and
checked-state by using signed index values (as enforced by FormInputAdapter's
contract) which makes position 0 ambiguous; update the encoding/decoding so
position 0 is distinguishable—either change the adapter/callback signature to
accept (position: Int, checked: Boolean) or switch to encoding as value =
(position + 1) * (if checked then 1 else -1) and decode with realIndex =
abs(value) - 1 and isChecked = value > 0; apply the corresponding decoding in
ChildRegistrationDataset where birthDoseVaccinesGiven handlers compute
realIndex/isChecked (replace current realIndex/ isChecked logic) and update any
places that construct the signed index to use the new (position+1) scheme or the
separate parameters.

In `@app/src/main/java/org/piramalswasthya/cho/configuration/Dataset.kt`:
- Around line 117-126: The updateList path currently mutates a FormElement's
errorText in place and emits only a shallow-copied list so validation-only
changes can remain visually stale; update updateList (or the caller that sets
errorStateChanged and updateIndex) to call forceRefreshId(id) when
errorStateChanged is true and updateIndex == -1 (or alternatively replace the
mutated FormElement instance before emitting the list) so the existing
_forceRefreshIdFlow/forceRefreshIdFlow path is triggered; reference the existing
helper forceRefreshId(id), the boolean errorStateChanged, and the updateIndex
check to implement this wiring.

In
`@app/src/main/java/org/piramalswasthya/cho/configuration/NeonatalOutcomeDataset.kt`:
- Around line 354-364: The checkbox state encoding is ambiguous for the first
option because FormInputAdapter's callback uses index*(if(b)1 else -1),
producing 0 for index=0 whether checked or not; update the call to
onValueChanged in FormInputAdapter (where formValueListener?.onValueChanged is
invoked) to encode as (index + 1) * (if (b) 1 else -1) so position 0 maps to ±1
and checked/unchecked are distinguishable, and ensure NeonatalOutcomeDataset.kt
logic that reads the encoded value (variables like realIndex, isChecked)
continues to decode using sign and absolute value as before.
- Around line 409-415: The restore path is still checking
saved.birthDoseVaccinesGiven?.contains("None") which breaks for non-English
locales; update restoreConditionalFields() to perform locale-neutral comparisons
by mapping the saved CSV values through the Dataset's resources/englishResources
lookup (the same way noneLocalized is derived) or by comparing against the
englishResources token instead of hardcoded "None", so that the logic that
computes noneIsSelected (using noneLocalized, birthDoseVaccinesGiven and
toggleDependant) and the restoreConditionalFields() branch that toggles
reasonForNoVaccines use the same resource-backed token resolution (use
Dataset.resources or englishResources and the same lookup function used
elsewhere) to detect the saved "None" value in any locale.

In
`@app/src/main/java/org/piramalswasthya/cho/repositories/MaternalHealthRepo.kt`:
- Around line 373-390: getANCCount() should guard against records where
pregnantWomanDelivered is true before counting; inside the Flow mapping over
getAllPatientsWithANC() (the lambda receiving "list"), add a check like if
(list.any { it.pregnantWomanDelivered == true }) return 0 (use the proper
return-from-lambda form, e.g. return@map 0) so delivered women are excluded even
if DAO/Room updates are inconsistent; update the logic in getANCCount() (the
mapping lambda over getAllPatientsWithANC()) to perform this check before
converting items with asDomainModel() and counting.

In
`@app/src/main/java/org/piramalswasthya/cho/ui/commons/eligible_couple/tracking/form/EligibleCoupleTrackingFormViewModel.kt`:
- Around line 106-115: When getLoggedInUser() returns null the code currently
writes empty strings into EligibleCoupleTrackingCache.createdBy/updatedBy which
can cause silent sync failures; change the logic in the block that builds
eligibleCoupleTracking (around getLoggedInUser(), EligibleCoupleTrackingCache,
createdBy/updatedBy) to: if asha is null, emit a warning log (using the app
logger or Log/TAG) noting missing logged-in user and do NOT construct/persist a
cache record with empty audit fields — instead set the form/save state to a
failure state (e.g., SyncState.SAVE_FAILED or a local SAVE_FAILED equivalent)
and short-circuit the save flow so the form cannot be submitted without
attribution; if asha is present, continue to populate createdBy/updatedBy with
asha.userName as before.

---

Outside diff comments:
In
`@app/src/main/java/org/piramalswasthya/cho/ui/home/rmncha/maternal_health/ANCVisitsFragment.kt`:
- Around line 128-152: observePatients() currently doesn't exclude delivered
women like getAncDueList() does; update observePatients() (and similarly
getANCCount()) to defensively filter out post-delivery patients by either
fetching the patient's ANC records and skipping when any PregnantWomanAncCache
has pregnantWomanDelivered == true (same approach as getAncDueList()), or by
ensuring patient.patient.statusOfWomanID != 3 before including them (if you can
guarantee statusOfWomanID is updated). Locate the filtering block in
observePatients() (the map/filter pipeline that builds allPatients) and add the
chosen guard there; mirror the same guard in getANCCount() to keep
listing/counts consistent. Ensure you reference PregnantWomanAncCache when using
the ANC-records approach so you can check pregnantWomanDelivered.

---

Nitpick comments:
In
`@app/src/main/java/org/piramalswasthya/cho/database/room/dao/MaternalHealthDao.kt`:
- Around line 104-115: The query in getAllPatientsWithANC currently uses a LEFT
OUTER JOIN and a WHERE clause that allows rows with no pregnancy registration
(pwr.patientID IS NULL), which contradicts the KDoc saying only patients with an
active pregnancy registration should be returned; change the JOIN to an INNER
JOIN and remove the NULL branch so the predicate becomes (pwr.active = 1) only,
keeping the other filters (p.genderID, p.maritalStatusID, p.statusOfWomanID,
p.age) and the ORDER BY intact to ensure Room only hydrates patients that have
an active PREGNANCY_REGISTER record.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb88ecd7-0d63-444d-ac5c-470a48ba29d1

📥 Commits

Reviewing files that changed from the base of the PR and between 7d1ea86 and 3b8ac3d.

📒 Files selected for processing (12)
  • app/src/main/java/org/piramalswasthya/cho/configuration/ChildBeneficiaryRegistrationDataset.kt
  • app/src/main/java/org/piramalswasthya/cho/configuration/ChildRegistrationDataset.kt
  • app/src/main/java/org/piramalswasthya/cho/configuration/Dataset.kt
  • app/src/main/java/org/piramalswasthya/cho/configuration/NeonatalOutcomeDataset.kt
  • app/src/main/java/org/piramalswasthya/cho/configuration/RMNCHAIconDataset.kt
  • app/src/main/java/org/piramalswasthya/cho/database/room/dao/MaternalHealthDao.kt
  • app/src/main/java/org/piramalswasthya/cho/repositories/MaternalHealthRepo.kt
  • app/src/main/java/org/piramalswasthya/cho/ui/commons/eligible_couple/tracking/form/EligibleCoupleTrackingFormViewModel.kt
  • app/src/main/java/org/piramalswasthya/cho/ui/home/rmncha/child_care/ChildRegistrationFragment.kt
  • app/src/main/java/org/piramalswasthya/cho/ui/home/rmncha/maternal_health/ANCVisitsFragment.kt
  • app/src/main/res/values-as/strings.xml
  • app/src/main/res/values-hi/strings.xml
💤 Files with no reviewable changes (2)
  • app/src/main/res/values-hi/strings.xml
  • app/src/main/res/values-as/strings.xml

Comment on lines +774 to +783
val realIndex = if (index < 0) -index else index
val localizedEntries = birthDoseVaccinesGiven.entries
// Locale-neutral "None" identification.
val noneIndex = englishResources
.getStringArray(birthDoseVaccinesGiven.arrayId)
.indexOf("None")
val noneLocalized = localizedEntries?.getOrNull(noneIndex)
val clickedOption = localizedEntries?.getOrNull(realIndex)
val isNoneOption = noneIndex >= 0 && realIndex == noneIndex
val isChecked = index > 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -i 'FormInputAdapter.kt' | while read -r f; do
  echo "== $f =="
  rg -n -C4 'FormValueListener|CheckBox|onCheckedChangeListener|setOnCheckedChangeListener|index' "$f"
done

Repository: PSMRI/HWC-Mobile-App

Length of output: 11782


🏁 Script executed:

cat -n app/src/main/java/org/piramalswasthya/cho/configuration/ChildRegistrationDataset.kt | sed -n '770,800p'

Repository: PSMRI/HWC-Mobile-App

Length of output: 1762


🏁 Script executed:

cat -n app/src/main/java/org/piramalswasthya/cho/configuration/ChildRegistrationDataset.kt | sed -n '840,860p'

Repository: PSMRI/HWC-Mobile-App

Length of output: 1260


Fix position–checked-state ambiguity in checkbox handlers.

Lines 783 and 855 use index > 0 to detect a checked event, but the callback contract from FormInputAdapter (line 546) encodes both position and checked-state using the sign of the same integer: index * (if (b) 1 else -1). This makes position 0 impossible to distinguish: both a checked and unchecked first option produce 0, so isChecked = index > 0 is always false when position is 0. The selection handler at lines 785–797 and 857–870 then silently skips the first checkbox option.

Switch the callback contract to separate position and checked-state, either using (position, checked) as distinct parameters or encoding as ±(position + 1) to shift position space away from zero.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/org/piramalswasthya/cho/configuration/ChildRegistrationDataset.kt`
around lines 774 - 783, The checkbox callback currently conflates position and
checked-state by using signed index values (as enforced by FormInputAdapter's
contract) which makes position 0 ambiguous; update the encoding/decoding so
position 0 is distinguishable—either change the adapter/callback signature to
accept (position: Int, checked: Boolean) or switch to encoding as value =
(position + 1) * (if checked then 1 else -1) and decode with realIndex =
abs(value) - 1 and isChecked = value > 0; apply the corresponding decoding in
ChildRegistrationDataset where birthDoseVaccinesGiven handlers compute
realIndex/isChecked (replace current realIndex/ isChecked logic) and update any
places that construct the signed index to use the new (position+1) scheme or the
separate parameters.

Comment on lines +117 to +126
/** Signals that a specific FormElement's row needs the adapter to call
* notifyItemChanged. Used when we mutate a FormElement's `value` in place
* (e.g. "None" mutual exclusion) — DiffUtil cannot detect it because the
* list still holds the same reference, so the fragment must force a
* rebind explicitly. */
private val _forceRefreshIdFlow = MutableSharedFlow<Int>(extraBufferCapacity = 8)
val forceRefreshIdFlow = _forceRefreshIdFlow.asSharedFlow()

protected fun forceRefreshId(id: Int) {
_forceRefreshIdFlow.tryEmit(id)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wire this refresh path into validation-only row updates too.

This helper only fixes the branches that explicitly call it. updateList() still mutates errorText in place and emits only a shallow copy of list, so rows whose state changed only because of validation can still stay visually stale. Please also trigger forceRefreshId(formId) when errorStateChanged is true and updateIndex == -1, or replace the mutated FormElement instance before emitting the list.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/org/piramalswasthya/cho/configuration/Dataset.kt` around
lines 117 - 126, The updateList path currently mutates a FormElement's errorText
in place and emits only a shallow-copied list so validation-only changes can
remain visually stale; update updateList (or the caller that sets
errorStateChanged and updateIndex) to call forceRefreshId(id) when
errorStateChanged is true and updateIndex == -1 (or alternatively replace the
mutated FormElement instance before emitting the list) so the existing
_forceRefreshIdFlow/forceRefreshIdFlow path is triggered; reference the existing
helper forceRefreshId(id), the boolean errorStateChanged, and the updateIndex
check to implement this wiring.

Comment on lines +354 to +364
val realIndex = if (index < 0) -index else index
val localizedEntries = newbornComplications.entries
// Identify "None" by its position in the English array so the
// logic works in every locale (Hindi, Assamese, etc.).
val noneIndex = englishResources
.getStringArray(newbornComplications.arrayId)
.indexOf("None")
val noneLocalized = localizedEntries?.getOrNull(noneIndex)
val clickedOption = localizedEntries?.getOrNull(realIndex) ?: return -1
val isNoneOption = noneIndex >= 0 && realIndex == noneIndex
val isChecked = index > 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -i 'FormInputAdapter.kt' | while read -r f; do
  echo "== $f =="
  rg -n -C4 'FormValueListener|CheckBox|onCheckedChangeListener|setOnCheckedChangeListener|index' "$f"
done

Repository: PSMRI/HWC-Mobile-App

Length of output: 11782


Fix the checkbox callback encoding to distinguish the first option's checked state.

The checkbox callback in FormInputAdapter.kt (line 546) uses index * (if (b) 1 else -1), which is ambiguous for the first option (index=0): both checked and unchecked produce 0. This causes NeonatalOutcomeDataset.kt (lines 364 and 393) to misinterpret the state, since isChecked = index > 0 treats 0 as always unchecked.

Change the callback to encode position as (index + 1) * (if (b) 1 else -1) so position 0 maps to ±1, making checked and unchecked states distinguishable.

FormInputAdapter.kt line 546
formValueListener?.onValueChanged(
    item, (index + 1) * (if (b) 1 else -1)  // Changed from: index * (if (b) 1 else -1)
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/org/piramalswasthya/cho/configuration/NeonatalOutcomeDataset.kt`
around lines 354 - 364, The checkbox state encoding is ambiguous for the first
option because FormInputAdapter's callback uses index*(if(b)1 else -1),
producing 0 for index=0 whether checked or not; update the call to
onValueChanged in FormInputAdapter (where formValueListener?.onValueChanged is
invoked) to encode as (index + 1) * (if (b) 1 else -1) so position 0 maps to ±1
and checked/unchecked are distinguishable, and ensure NeonatalOutcomeDataset.kt
logic that reads the encoded value (variables like realIndex, isChecked)
continues to decode using sign and absolute value as before.

Comment on lines +409 to +415
val noneIsSelected = noneLocalized != null && (birthDoseVaccinesGiven.value ?: "")
.split(",")
.map { it.trim() }
.any { it == noneLocalized }
toggleDependant(
source = birthDoseVaccinesGiven,
condition = birthDoseVaccinesGiven.value?.contains("None") == true,
condition = noneIsSelected,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the saved-value restore path locale-neutral too.

This branch now toggles reasonForNoVaccines with noneLocalized, but restoreConditionalFields() at Line 234 still checks saved.birthDoseVaccinesGiven?.contains("None"). Reopening a saved non-English form will skip the dependant field even when localized “None” was selected.

Suggested fix
-        if (saved.birthDoseVaccinesGiven?.contains("None") == true) {
+        val noneIndex = englishResources
+            .getStringArray(birthDoseVaccinesGiven.arrayId)
+            .indexOf("None")
+        val noneLocalized = birthDoseVaccinesGiven.entries?.getOrNull(noneIndex)
+        if (!noneLocalized.isNullOrBlank() &&
+            saved.birthDoseVaccinesGiven
+                ?.split(",")
+                ?.any { it.trim() == noneLocalized } == true
+        ) {
             formElements.add(formElements.indexOf(birthDoseVaccinesGiven) + 1, reasonForNoVaccines)
         }

Based on learnings: In Dataset implementations in org.piramalswasthya.cho.configuration (Kotlin/Android), always use Dataset.resources (and englishResources where applicable) for all user-facing titles/options/alerts/comparisons.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/org/piramalswasthya/cho/configuration/NeonatalOutcomeDataset.kt`
around lines 409 - 415, The restore path is still checking
saved.birthDoseVaccinesGiven?.contains("None") which breaks for non-English
locales; update restoreConditionalFields() to perform locale-neutral comparisons
by mapping the saved CSV values through the Dataset's resources/englishResources
lookup (the same way noneLocalized is derived) or by comparing against the
englishResources token instead of hardcoded "None", so that the logic that
computes noneIsSelected (using noneLocalized, birthDoseVaccinesGiven and
toggleDependant) and the restoreConditionalFields() branch that toggles
reasonForNoVaccines use the same resource-backed token resolution (use
Dataset.resources or englishResources and the same lookup function used
elsewhere) to detect the saved "None" value in any locale.

Comment on lines +373 to 390
fun getANCCount(): Flow<Int> {
val ancEligibilityWindowMillis = 35L * 24 * 60 * 60 * 1000
return maternalHealthDao.getANCEligibleCount(System.currentTimeMillis() - ancEligibilityWindowMillis)
return getAllPatientsWithANC().map { list ->
val now = System.currentTimeMillis()
val cutoff = now - ancEligibilityWindowMillis
list.map { it.asDomainModel() }
.count { domain ->
val hasActivePWR = domain.pwr != null && domain.isActive()
val isFemale = domain.patient.genderID == 2
val age = domain.patient.age ?: 0
val isReproductiveAge = age in 15..49
val isEligibleForANC = domain.pwr?.lmpDate?.let { lmp ->
lmp <= cutoff
} ?: false
hasActivePWR && isFemale && isReproductiveAge && isEligibleForANC
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find every place statusOfWomanID is written/updated in Kotlin source
rg -n "statusOfWomanID" --type=kt -A 3 -B 1

Repository: PSMRI/HWC-Mobile-App

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Find Kotlin files first, then search for statusOfWomanID usage
echo "=== Searching for statusOfWomanID in Kotlin files ==="
fd -e kt | head -50

echo ""
echo "=== Searching for statusOfWomanID mentions ==="
rg -n "statusOfWomanID" -A 3 -B 1 | head -100

Repository: PSMRI/HWC-Mobile-App

Length of output: 16927


🏁 Script executed:

#!/bin/bash
# Search for delivery-related code and pregnantWomanDelivered usage
echo "=== Searching for pregnantWomanDelivered ==="
rg -n "pregnantWomanDelivered" -A 2 -B 1

echo ""
echo "=== Searching for delivery recording/outcome code ==="
rg -n "delivery|Delivery|DELIVERY" --type kotlin -i | grep -i "record\|save\|update\|insert" | head -20

Repository: PSMRI/HWC-Mobile-App

Length of output: 12150


🏁 Script executed:

#!/bin/bash
# Find the MaternalHealthRepo file and examine delivery-related methods
echo "=== Finding MaternalHealthRepo file ==="
fd -n "MaternalHealthRepo.kt"

echo ""
echo "=== Searching for methods that update delivery status ==="
rg -n "fun.*[Dd]eliver" -A 5

Repository: PSMRI/HWC-Mobile-App

Length of output: 50625


🏁 Script executed:

#!/bin/bash
# Look at the getANCCount method and getAllPatientsWithANC method
echo "=== Examining MaternalHealthRepo.kt around getANCCount ==="
cat -n app/src/main/java/org/piramalswasthya/cho/repositories/MaternalHealthRepo.kt | sed -n '370,410p'

echo ""
echo "=== Find getAllPatientsWithANC definition ==="
rg -n "fun getAllPatientsWithANC" -A 20

Repository: PSMRI/HWC-Mobile-App

Length of output: 7090


🏁 Script executed:

#!/bin/bash
# Find the STATUS_POST_NATAL_MOTHER constant
echo "=== Find STATUS_POST_NATAL_MOTHER constant ==="
rg -n "STATUS_POST_NATAL_MOTHER" -B 2 -A 2

echo ""
echo "=== Check what value statusOfWomanID should be for pregnant women ==="
rg -n "statusOfWomanID.*=.*2\|IN.*2\)" -B 1 -A 1 | head -40

Repository: PSMRI/HWC-Mobile-App

Length of output: 3163


🏁 Script executed:

#!/bin/bash
# Find getAncDueList and getAncCompletedList to see the pregnantWomanDelivered check
echo "=== Examine getAncDueList implementation ==="
rg -n "fun getAncDueList" -A 40 | head -60

echo ""
echo "=== Examine getAncCompletedList implementation ==="
rg -n "fun getAncCompletedList" -A 40 | head -60

Repository: PSMRI/HWC-Mobile-App

Length of output: 11100


🏁 Script executed:

#!/bin/bash
# Look at where pregnantWomanDelivered is set to understand if it's atomic with statusOfWomanID
echo "=== Check PwAncFormViewModel where pregnantWomanDelivered and statusOfWomanID are updated ==="
rg -n "pregnantWomanDelivered.*true" -B 5 -A 5 | head -50

echo ""
echo "=== Verify getAllPatientsWithANC DAO query - check if it filters statusOfWomanID ==="
cat -n app/src/main/java/org/piramalswasthya/cho/database/room/dao/MaternalHealthDao.kt | sed -n '100,135p'

Repository: PSMRI/HWC-Mobile-App

Length of output: 8273


getANCCount() lacks pregnantWomanDelivered check—delivered women risk being incorrectly included if delivery status updates are not reliable.

The DAO query getAllPatientsWithANC() does filter by statusOfWomanID IN (2), which should exclude delivered women when statusOfWomanID is updated to 3. However, getANCCount() itself performs no pregnantWomanDelivered check, unlike getAncDueList() (line 182) and getAncCompletedList() (line 233), which both explicitly filter delivered women.

The concern is valid: if the delivery recording flow fails to update statusOfWomanID atomically or reliably in Room, delivered women will pass the DAO filter and be counted. The pattern of checking pregnantWomanDelivered exists elsewhere in the codebase precisely because this is recognized as necessary.

Add if (ancRecords.any { it.pregnantWomanDelivered == true }) return 0 at the start of getANCCount() to match the pattern used in due/completed lists and guard against incomplete delivery status updates.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/org/piramalswasthya/cho/repositories/MaternalHealthRepo.kt`
around lines 373 - 390, getANCCount() should guard against records where
pregnantWomanDelivered is true before counting; inside the Flow mapping over
getAllPatientsWithANC() (the lambda receiving "list"), add a check like if
(list.any { it.pregnantWomanDelivered == true }) return 0 (use the proper
return-from-lambda form, e.g. return@map 0) so delivered women are excluded even
if DAO/Room updates are inconsistent; update the logic in getANCCount() (the
mapping lambda over getAllPatientsWithANC()) to perform this check before
converting items with asDomainModel() and counting.

Comment on lines +106 to +115
val asha = userRepo.getLoggedInUser()
val ben = patientRepo.getPatientDisplay(patientID)?.also { ben ->
_benName.value =
"${ben.patient.firstName} ${if (ben.patient.lastName == null) "" else ben.patient.lastName}"
_benAgeGender.value = "${ben.patient.age} ${ben.ageUnit?.name} | ${ben.gender?.genderName}"
eligibleCoupleTracking = EligibleCoupleTrackingCache(
patientID = patientID,
syncState = SyncState.UNSYNCED,
createdBy = asha.userName,
updatedBy = asha.userName,
createdBy = asha?.userName ?: "",
updatedBy = asha?.userName ?: "",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Silent empty-string audit fields when getLoggedInUser() returns null may cause undetectable sync failures.

The null-safe guard prevents the crash that !! would cause, but substituting "" for createdBy/updatedBy silently persists records with no attribution. In a healthcare sync context:

  1. Backend APIs typically validate createdBy is non-empty; a blank value will likely cause a silent server-side rejection during sync, with no user-visible feedback.
  2. There is no log entry to diagnose when or why the user was unavailable, making field-level debugging impossible.

At minimum, emit a warning log when asha is null and consider posting a SAVE_FAILED-equivalent state to prevent the form from being submitted with empty audit fields.

🛡️ Proposed fix — warn on null user and guard record creation
-            val asha = userRepo.getLoggedInUser()
+            val asha = userRepo.getLoggedInUser()
+            if (asha == null) {
+                Timber.w("getLoggedInUser() returned null during ECT init for patient $patientID — createdBy/updatedBy will be empty")
+            }
             val ben = patientRepo.getPatientDisplay(patientID)?.also { ben ->
                 _benName.value = ...
                 eligibleCoupleTracking = EligibleCoupleTrackingCache(
                     patientID = patientID,
                     syncState = SyncState.UNSYNCED,
-                    createdBy = asha?.userName ?: "",
-                    updatedBy = asha?.userName ?: "",
+                    createdBy = asha?.userName ?: "",   // empty only if session is unexpectedly null
+                    updatedBy = asha?.userName ?: "",
                 )
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val asha = userRepo.getLoggedInUser()
val ben = patientRepo.getPatientDisplay(patientID)?.also { ben ->
_benName.value =
"${ben.patient.firstName} ${if (ben.patient.lastName == null) "" else ben.patient.lastName}"
_benAgeGender.value = "${ben.patient.age} ${ben.ageUnit?.name} | ${ben.gender?.genderName}"
eligibleCoupleTracking = EligibleCoupleTrackingCache(
patientID = patientID,
syncState = SyncState.UNSYNCED,
createdBy = asha.userName,
updatedBy = asha.userName,
createdBy = asha?.userName ?: "",
updatedBy = asha?.userName ?: "",
val asha = userRepo.getLoggedInUser()
if (asha == null) {
Timber.w("getLoggedInUser() returned null during ECT init for patient $patientID — createdBy/updatedBy will be empty")
}
val ben = patientRepo.getPatientDisplay(patientID)?.also { ben ->
_benName.value =
"${ben.patient.firstName} ${if (ben.patient.lastName == null) "" else ben.patient.lastName}"
_benAgeGender.value = "${ben.patient.age} ${ben.ageUnit?.name} | ${ben.gender?.genderName}"
eligibleCoupleTracking = EligibleCoupleTrackingCache(
patientID = patientID,
syncState = SyncState.UNSYNCED,
createdBy = asha?.userName ?: "", // empty only if session is unexpectedly null
updatedBy = asha?.userName ?: "",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/org/piramalswasthya/cho/ui/commons/eligible_couple/tracking/form/EligibleCoupleTrackingFormViewModel.kt`
around lines 106 - 115, When getLoggedInUser() returns null the code currently
writes empty strings into EligibleCoupleTrackingCache.createdBy/updatedBy which
can cause silent sync failures; change the logic in the block that builds
eligibleCoupleTracking (around getLoggedInUser(), EligibleCoupleTrackingCache,
createdBy/updatedBy) to: if asha is null, emit a warning log (using the app
logger or Log/TAG) noting missing logged-in user and do NOT construct/persist a
cache record with empty audit fields — instead set the form/save state to a
failure state (e.g., SyncState.SAVE_FAILED or a local SAVE_FAILED equivalent)
and short-circuit the save flow so the form cannot be submitted without
attribution; if asha is present, continue to populate createdBy/updatedBy with
asha.userName as before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant