Pure mapping from a Kipu casefile's program history to ProgramStayInput[].
Shared by the per-episode sync (KipuAdapter.fetchEpisodeProgramStays, which
sources programHistory + programIdByName from the EMR) and the in-DB
backfill (which sources the same inputs from already-synced patient_charts
and facility_programs). The behavior is identical in both paths:
Drop entries missing a program name or a start_date.
Sort the survivors by start_date ascending so the chain is chronological.
Each stay's end_date is the NEXT stay's start_date. The last stay is open-ended on
Kipu (program history carries only a start), so it's capped at the episode dischargeDate
when one is given and falls on/after the stay's start; otherwise it stays open-ended (null).
Programs resolve by NAME downstream ((facility_id, name) identity), so the stay
carries only program_name; program_external_id is left null (EMR program ids churn and are
no longer the key).
Kipu carries no level-of-care on program history, so the LOC fields are null.
external_id is ${casefileId}:${program}:${start_date}, stable across runs.
dischargeDate is Kipu-specific: LS/Sunwave open-ended stays are handled by their own adapters.
Pure mapping from a Kipu casefile's program history to
ProgramStayInput[].Shared by the per-episode sync (
KipuAdapter.fetchEpisodeProgramStays, which sourcesprogramHistory+programIdByNamefrom the EMR) and the in-DB backfill (which sources the same inputs from already-syncedpatient_chartsandfacility_programs). The behavior is identical in both paths:programname or astart_date.start_dateascending so the chain is chronological.end_dateis the NEXT stay'sstart_date. The last stay is open-ended on Kipu (program history carries only a start), so it's capped at the episodedischargeDatewhen one is given and falls on/after the stay's start; otherwise it stays open-ended (null).(facility_id, name)identity), so the stay carries onlyprogram_name;program_external_idis left null (EMR program ids churn and are no longer the key).external_idis${casefileId}:${program}:${start_date}, stable across runs.dischargeDateis Kipu-specific: LS/Sunwave open-ended stays are handled by their own adapters.