Compare commits
7 commits
cea22c58db
...
175dcf5081
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
175dcf5081 | ||
|
|
b52b258b8f | ||
|
|
e8b15233ec | ||
|
|
77a1cdb2c0 | ||
|
|
e57f803f26 | ||
|
|
76386bf175 | ||
|
|
bcb1a10fe6 |
88 changed files with 9493 additions and 279 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -11,3 +11,5 @@ node_modules/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vercel
|
.vercel
|
||||||
.claude/settings.local.json
|
.claude/settings.local.json
|
||||||
|
.venv/
|
||||||
|
supabase/
|
||||||
|
|
|
||||||
16
CLAUDE.md
16
CLAUDE.md
|
|
@ -64,7 +64,7 @@ and stays predictable.
|
||||||
|
|
||||||
Whitepaper: `/Users/sttil-solutions/Documents/Obsidian_Vault/STTIL-Vault/Projects/2026-05-18-pilot-readiness-whitepaper.md`
|
Whitepaper: `/Users/sttil-solutions/Documents/Obsidian_Vault/STTIL-Vault/Projects/2026-05-18-pilot-readiness-whitepaper.md`
|
||||||
|
|
||||||
### Checklist Status (as of 2026-05-29) — 40-45% pilot-ready
|
### Checklist Status (as of 2026-06-07) — 45-50% pilot-ready
|
||||||
|
|
||||||
| Checklist Item | Status |
|
| Checklist Item | Status |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
@ -78,7 +78,7 @@ Whitepaper: `/Users/sttil-solutions/Documents/Obsidian_Vault/STTIL-Vault/Project
|
||||||
| Export CSV works and opens cleanly | NOT VERIFIED end to end |
|
| Export CSV works and opens cleanly | NOT VERIFIED end to end |
|
||||||
| Placeholder content removed | NOT VERIFIED |
|
| Placeholder content removed | NOT VERIFIED |
|
||||||
| Browser smoke test passes | NOT RUN |
|
| Browser smoke test passes | NOT RUN |
|
||||||
| Data handling rules documented | PARTIAL — hashing and RLS confirmed; retention sink priority, CSV disposition policy, minimum necessary fields, and payer_rules.json cadence not yet documented |
|
| Data handling rules documented | PASS — privacy-policy.md + data-handling.md written 2026-06-07; minimum necessary fields, CSV disposition policy, retention, and payer_rules.json cadence all documented |
|
||||||
| Real PHI blocked | PARTIAL — architecture confirmed; Supabase plan tier (Pro vs Team) not yet verified |
|
| Real PHI blocked | PARTIAL — architecture confirmed; Supabase plan tier (Pro vs Team) not yet verified |
|
||||||
| Pilot success metrics written down | PASS |
|
| Pilot success metrics written down | PASS |
|
||||||
|
|
||||||
|
|
@ -107,25 +107,25 @@ This checklist tracks compliance gates separately from build readiness. Update i
|
||||||
| Service role keys absent from frontend env vars (VITE_) | PASS — verified 2026-06-05 |
|
| Service role keys absent from frontend env vars (VITE_) | PASS — verified 2026-06-05 |
|
||||||
| Railway-to-Supabase TLS | PASS — Supabase SDK manages SSL at infrastructure level |
|
| Railway-to-Supabase TLS | PASS — Supabase SDK manages SSL at infrastructure level |
|
||||||
| CSV file disposition post-normalization confirmed | PASS — raw bytes discarded; only hash + normalized records persist |
|
| CSV file disposition post-normalization confirmed | PASS — raw bytes discarded; only hash + normalized records persist |
|
||||||
| Audit log WORM enforcement (RLS blocks UPDATE/DELETE on audit_log) | NOT DONE |
|
| Audit log WORM enforcement (RLS blocks UPDATE/DELETE on audit_log) | PASS — migration 20260607000001_audit_log_worm.sql applied via CLI 2026-06-07; audit_log table created with RLS + WORM policies live in Supabase |
|
||||||
| PostgreSQL as primary audit log retention sink documented (6-year) | NOT DONE |
|
| PostgreSQL as primary audit log retention sink documented (6-year) | NOT DONE |
|
||||||
| MFA confirmed on all admin accounts (AWS, Supabase, Railway, Vercel, Clerk) | NOT VERIFIED |
|
| MFA confirmed on all admin accounts (AWS, Supabase, Railway, Vercel, Clerk) | NOT VERIFIED |
|
||||||
| payer_rules.json quarterly update cadence documented | NOT DONE |
|
| payer_rules.json quarterly update cadence documented | PASS — documented in docs/compliance/data-handling.md 2026-06-07 |
|
||||||
|
|
||||||
### Gate: First Supplier (before Robert Robinson or any pilot partner uploads data)
|
### Gate: First Supplier (before Robert Robinson or any pilot partner uploads data)
|
||||||
|
|
||||||
| Compliance Item | Status |
|
| Compliance Item | Status |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Privacy Policy published at stable public URL | NOT DONE |
|
| Privacy Policy published at stable public URL | PASS — privacy-policy.md written 2026-06-07; Privacy.jsx deployed to https://signal-ui-xi.vercel.app/privacy |
|
||||||
| FDA CDS exemption memo written and attorney-reviewed | NOT DONE |
|
| FDA CDS exemption memo written and attorney-reviewed | NOT DONE |
|
||||||
| Supabase plan tier confirmed (Pro vs Team) and PHI posture documented | NOT VERIFIED |
|
| Supabase plan tier confirmed (Pro vs Team) and PHI posture documented | NOT VERIFIED |
|
||||||
| Pilot LOI reviewed by attorney | PARTIAL — template ready; Bittinger/Nixon review pending |
|
| Pilot LOI reviewed by attorney | PARTIAL — template ready; Bittinger/Nixon review pending |
|
||||||
| NDA template reviewed by attorney | PARTIAL — template ready; Bittinger/Nixon review pending |
|
| NDA template reviewed by attorney | PARTIAL — template ready; Bittinger/Nixon review pending |
|
||||||
| Pilot LOI executed with supplier | NOT DONE |
|
| Pilot LOI executed with supplier | NOT DONE |
|
||||||
| NDA executed with supplier | NOT DONE |
|
| NDA executed with supplier | NOT DONE |
|
||||||
| Minimum necessary field documentation written (why each of 5 fields is required) | NOT DONE |
|
| Minimum necessary field documentation written (why each of 5 fields is required) | PASS — documented in docs/compliance/data-handling.md 2026-06-07 |
|
||||||
| Incident response procedure written (includes breach notification timeline) | NOT DONE |
|
| Incident response procedure written (includes breach notification timeline) | PASS — docs/compliance/incident-response.md written 2026-06-07 |
|
||||||
| Data retention and deletion procedure written | NOT DONE |
|
| Data retention and deletion procedure written | PASS — retention and deletion policy documented in docs/compliance/data-handling.md 2026-06-07 |
|
||||||
| Deletion procedure tested against Gaboro test batch | NOT DONE |
|
| Deletion procedure tested against Gaboro test batch | NOT DONE |
|
||||||
| Internal tabletop incident exercise completed | NOT DONE |
|
| Internal tabletop incident exercise completed | NOT DONE |
|
||||||
| Production access scope documented (who has access and why) | NOT DONE |
|
| Production access scope documented (who has access and why) | NOT DONE |
|
||||||
|
|
|
||||||
158
docs/compliance/data-handling.md
Normal file
158
docs/compliance/data-handling.md
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
# Signal Data Handling and Minimum Necessary Fields
|
||||||
|
|
||||||
|
**Version:** 1.0
|
||||||
|
**Date:** June 7, 2026
|
||||||
|
**Classification:** Internal + shareable with pilot customers on request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document describes what data Signal collects from customers, how it is processed and stored, and the minimum necessary field policy that governs what data enters Signal systems. It also documents the payer_rules.json update cadence and the MFA verification checklist required before any supplier uploads data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimum Necessary Fields
|
||||||
|
|
||||||
|
Signal is designed on the principle of minimum necessary data. The only fields required for Signal to produce a documentation worklist are:
|
||||||
|
|
||||||
|
| Field | Required | Why |
|
||||||
|
|---|---|---|
|
||||||
|
| Patient identifier (internal) | Yes | De-identified crosswalk key. Patient's own MRN or account number within the supplier's system. Never a name, SSN, or DOB. |
|
||||||
|
| Device type | Yes | Determines wear-day rules and payer coverage eligibility |
|
||||||
|
| Shipment date | Yes | Anchors the supply cycle clock and visit due date calculation |
|
||||||
|
| Payer name | Yes | Determines documentation requirements (PA, PECOS, visit cadence) |
|
||||||
|
| Quantity shipped | Recommended | Improves coverage cycle accuracy; defaults to 1 if omitted |
|
||||||
|
| Component type | Optional | Defaults to "sensor" if omitted |
|
||||||
|
|
||||||
|
**Doc status fields (optional — improve Signal accuracy when present):**
|
||||||
|
|
||||||
|
| Field | Improves |
|
||||||
|
|---|---|
|
||||||
|
| SWO status | SWO item in doc checklist |
|
||||||
|
| Visit date | Visit date priority (confirmed over estimated) |
|
||||||
|
| PECOS verified | PECOS item in doc checklist |
|
||||||
|
| PA status | PA item in doc checklist |
|
||||||
|
| Diagnosis on file | Diagnosis item in doc checklist |
|
||||||
|
| Transfer_From | Transferred patient detection |
|
||||||
|
|
||||||
|
Customers are encouraged to include doc status fields in their CSV exports when available from their billing system. When absent, Signal uses reasonable defaults and labels estimated values clearly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Signal Does NOT Accept
|
||||||
|
|
||||||
|
Customers must not include the following fields in uploaded CSVs:
|
||||||
|
- Patient first or last name
|
||||||
|
- Social Security number
|
||||||
|
- Date of birth
|
||||||
|
- Home address, city, state, or ZIP
|
||||||
|
- Phone number
|
||||||
|
- Email address
|
||||||
|
- Insurance member ID (this may constitute PHI)
|
||||||
|
- Diagnosis codes (ICD-10 codes)
|
||||||
|
- Clinical notes
|
||||||
|
|
||||||
|
Signal's normalizer does not map or store these fields even if they appear in uploaded CSV headers. They are treated as unmapped noise columns and excluded from all Signal processing and storage. The normalizer discards any column header it does not recognize as a canonical Signal field.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Supplier staff CSV file Signal frontend (Vercel)
|
||||||
|
|
||||||
|
Signal API (Railway)
|
||||||
|
- Normalizer: maps CSV headers to canonical fields
|
||||||
|
- Minimum necessary check: only required fields extracted
|
||||||
|
- Coverage calculator: scores each patient
|
||||||
|
- patient_id hashed via SHA-256 before any storage
|
||||||
|
|
||||||
|
Supabase (PostgreSQL)
|
||||||
|
- upload_batches: filename, row_count, org_id
|
||||||
|
- normalized_records: patient_id_hash, device_type,
|
||||||
|
shipment_date, payer, coverage_status, rule_version
|
||||||
|
- confirmed_visits: org_id, patient_id_hash, confirmed_date
|
||||||
|
- audit_events: append-only (WORM enforcement pending)
|
||||||
|
```
|
||||||
|
|
||||||
|
Signal does not transmit any customer data to third parties. The only external services that receive any customer data are:
|
||||||
|
- **Supabase** (database hosting, US-east-1) — row-level security enforced on all tables
|
||||||
|
- **Railway** (application hosting) — no customer data stored at Railway; data in transit only
|
||||||
|
- **Vercel** (frontend CDN) — no customer data stored; serves frontend assets only
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CSV Disposition Policy
|
||||||
|
|
||||||
|
Uploaded CSV files are:
|
||||||
|
1. Received by the Signal API
|
||||||
|
2. Parsed in memory
|
||||||
|
3. Content hash computed (SHA-256 of raw bytes) and stored in upload metadata
|
||||||
|
4. Raw bytes are NOT stored in Supabase — only the hash and row count
|
||||||
|
5. Normalized field values are stored (with patient_id hashed via SHA-256)
|
||||||
|
|
||||||
|
**Signal does not store copies of uploaded CSV files.** The CSV is processed in memory and then discarded. Only the scored output and metadata are persisted.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Retention
|
||||||
|
|
||||||
|
| Data Type | Retention Period |
|
||||||
|
|---|---|
|
||||||
|
| Upload batch metadata | Duration of subscription + 90 days |
|
||||||
|
| Scored patient records (normalized_records) | Duration of subscription + 90 days |
|
||||||
|
| Confirmed visit dates | Duration of subscription + 90 days |
|
||||||
|
| Audit events | 7 years (HIPAA compliance requirement, per 45 CFR §164.530(j)) |
|
||||||
|
| Source file metadata (hash only) | Duration of subscription + 90 days |
|
||||||
|
|
||||||
|
Upon written request, all customer data except audit events (which are retained for legal compliance) will be deleted within 10 business days. STTIL will confirm deletion in writing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Supabase Plan Tier
|
||||||
|
|
||||||
|
Signal uses Supabase for its PostgreSQL database. The production plan must be Pro tier or higher before any pilot customer uploads data. Pro tier supports:
|
||||||
|
- Row-level security (RLS) — enforced on all tables
|
||||||
|
- Encrypted backups
|
||||||
|
- Point-in-time recovery
|
||||||
|
|
||||||
|
**Action required before pilot:** Confirm Supabase plan is Pro or higher and that all tables have RLS enabled. Run in Supabase SQL editor:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename;
|
||||||
|
```
|
||||||
|
|
||||||
|
All tables should show `rowsecurity = true`. Mark the "Supabase plan tier confirmed" item in the compliance checklist after verifying.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## payer_rules.json Update Cadence
|
||||||
|
|
||||||
|
`python-backend/config/payer_rules.json` contains the coverage rules Signal applies when calculating documentation requirements. This file must be reviewed and updated:
|
||||||
|
|
||||||
|
- **Quarterly:** Review CMS LCD updates, MAC bulletins, and payer PA requirement changes for CGM
|
||||||
|
- **On CMS rule publication:** Update immediately when CMS publishes changes to CGM coverage (e.g., new HCPCS codes added to Required PA List, Synapse Health mandate changes, CGM competitive bidding updates)
|
||||||
|
- **On Watcher Agent alert:** The Watcher Agent monitors CMS.gov, MAC websites, and key payer portals for relevant rule changes and alerts Kisa. All updates to payer_rules.json require Kisa's approval before deployment.
|
||||||
|
|
||||||
|
**Cadence owner:** Kisa Fenn, STTIL Solutions LLC.
|
||||||
|
**Next scheduled review:** September 7, 2026 (90 days from launch).
|
||||||
|
**Change log:** Record each update in `python-backend/config/payer_rules_changelog.md` with the date, changed values, and the source document (LCD number, MAC bulletin, CMS rule reference) that triggered the update.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MFA Verification Checklist
|
||||||
|
|
||||||
|
All Signal administrative accounts must have MFA enabled before any supplier uploads data. The 2026 HIPAA Security Rule Final Rule (45 CFR §164.312(d)) requires phishing-resistant MFA for all ePHI access. Verify and check each item before the Gaboro pilot start date.
|
||||||
|
|
||||||
|
| Account | MFA Required | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| Railway (sttilsolutionsllc@proton.me) | Yes | NOT VERIFIED |
|
||||||
|
| Supabase | Yes | NOT VERIFIED |
|
||||||
|
| Vercel | Yes | NOT VERIFIED |
|
||||||
|
| Clerk (eternal-goblin-1) | Yes | NOT VERIFIED |
|
||||||
|
| AWS (BAA account) | Yes | NOT VERIFIED |
|
||||||
|
|
||||||
|
Update this table by replacing "NOT VERIFIED" with "CONFIRMED [date]" for each account after checking MFA status in the respective platform's security settings.
|
||||||
|
|
||||||
|
**Note:** SMS-based one-time codes do not satisfy the 2026 phishing-resistant MFA requirement for systems touching ePHI. FIDO2/WebAuthn (hardware security key or platform authenticator such as Touch ID) is the compliant standard. For the zero-PHI pilot phase, any MFA method is sufficient. Phishing-resistant MFA is required before live patient identifiers enter Signal.
|
||||||
149
docs/compliance/incident-response.md
Normal file
149
docs/compliance/incident-response.md
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
# Signal Incident Response Procedure
|
||||||
|
|
||||||
|
**Version:** 1.0
|
||||||
|
**Date:** June 7, 2026
|
||||||
|
**Owner:** STTIL Solutions LLC / Kisa Fenn, Managing Member
|
||||||
|
**Classification:** Internal — do not publish
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This document defines the steps STTIL Solutions will take if a security incident affects Signal or the data of Signal customers. An "incident" includes any unauthorized access, data breach, ransomware event, accidental deletion, or disclosure of customer data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Incident Classification
|
||||||
|
|
||||||
|
| Severity | Description | Examples |
|
||||||
|
|---|---|---|
|
||||||
|
| P1 — Critical | Active breach or confirmed data exposure | Unauthorized access to Supabase; database dump exfiltrated |
|
||||||
|
| P2 — High | Suspected breach or significant service disruption | Unusual API activity; Railway service crash with data loss risk |
|
||||||
|
| P3 — Medium | Potential vulnerability or policy violation | Misconfigured CORS; unexpected admin access |
|
||||||
|
| P4 — Low | Minor issues with no data exposure | Failed login attempts; expired SSL (auto-renewing) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Response Timeline
|
||||||
|
|
||||||
|
| Step | P1 | P2 | P3/P4 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Detect and classify | Immediate | Within 1 hour | Within 24 hours |
|
||||||
|
| Contain | Immediately | Within 2 hours | Within 72 hours |
|
||||||
|
| Notify affected customers | Within 72 hours | Within 72 hours | As appropriate |
|
||||||
|
| Root cause analysis | Within 24 hours | Within 72 hours | Within 1 week |
|
||||||
|
| Remediation complete | Within 48 hours | Within 1 week | Within 2 weeks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1: Detect and Classify
|
||||||
|
|
||||||
|
Detection sources:
|
||||||
|
- Railway logs and alerts
|
||||||
|
- Supabase dashboard alerts
|
||||||
|
- Vercel deployment alerts
|
||||||
|
- Clerk auth anomaly alerts
|
||||||
|
- Customer report
|
||||||
|
- Routine audit log review
|
||||||
|
|
||||||
|
Upon detection, Kisa Fenn assesses severity and begins the response log.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2: Contain
|
||||||
|
|
||||||
|
**P1 (Critical):**
|
||||||
|
1. Immediately revoke all active API keys and rotate secrets (Railway env vars, Supabase service key, Clerk API key)
|
||||||
|
2. Take Signal offline if breach is active (Railway: pause service)
|
||||||
|
3. Preserve logs — do not delete or overwrite
|
||||||
|
4. Do not communicate publicly until scope is understood
|
||||||
|
|
||||||
|
**P2 (High):**
|
||||||
|
1. Revoke and rotate suspected compromised credentials
|
||||||
|
2. Isolate affected service if possible
|
||||||
|
3. Begin forensic log review
|
||||||
|
|
||||||
|
**P3/P4:**
|
||||||
|
1. Patch or reconfigure the identified vulnerability
|
||||||
|
2. Document the fix
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3: Assess Scope
|
||||||
|
|
||||||
|
Determine:
|
||||||
|
- What data was accessed or exposed?
|
||||||
|
- Which customers (organizations) are affected?
|
||||||
|
- What is the time range of the exposure?
|
||||||
|
- Was any patient data (even de-identified hashed IDs) involved?
|
||||||
|
|
||||||
|
Signal's data architecture minimizes exposure: patient_id is hashed (SHA-256) before storage. Even in a worst-case database exposure, the hashes cannot be reversed to reveal patient identities without the supplier's local crosswalk. Audit logs are structured with the same hashing protocol and cannot be used to reconstruct patient identity independently.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4: Notify Affected Customers
|
||||||
|
|
||||||
|
**Timeline:** Within 72 hours of confirming that customer data was affected.
|
||||||
|
|
||||||
|
**Notification must include:**
|
||||||
|
1. Date and time the incident was detected
|
||||||
|
2. Description of the incident (what happened)
|
||||||
|
3. Data affected (which tables, which organizations, what fields)
|
||||||
|
4. Steps STTIL has taken to contain and remediate
|
||||||
|
5. Steps the customer should take (if any)
|
||||||
|
6. Contact for follow-up questions
|
||||||
|
|
||||||
|
**Notification method:** Email to the primary contact for each affected organization. For P1 incidents, also attempt phone contact.
|
||||||
|
|
||||||
|
**Breach notification under HIPAA:** If any incident involves data that may constitute PHI under HIPAA, STTIL will comply with HIPAA breach notification requirements, including notification to HHS within 60 days of discovery for breaches affecting 500 or more individuals. Given Signal's de-identification architecture, PHI breach is unlikely but is addressed here for completeness. The 72-hour customer notification timeline above is more stringent than the 60-day HIPAA threshold and is the operative timeline for Signal operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5: Root Cause Analysis and Remediation
|
||||||
|
|
||||||
|
Document:
|
||||||
|
- Root cause (technical failure, human error, external attack)
|
||||||
|
- How it was detected
|
||||||
|
- Timeline of the incident
|
||||||
|
- Containment actions taken
|
||||||
|
- Remediation actions taken
|
||||||
|
- Preventive measures implemented
|
||||||
|
|
||||||
|
Store the incident report in `signal/docs/compliance/incidents/YYYY-MM-DD-incident-report.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6: Post-Incident Review
|
||||||
|
|
||||||
|
Within one week of remediation:
|
||||||
|
- Review whether current monitoring would have caught this sooner
|
||||||
|
- Update runbooks, monitoring, or access controls as needed
|
||||||
|
- Evaluate whether additional audit log coverage is needed
|
||||||
|
- Assess whether WORM enforcement gaps contributed to the incident
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Contacts and Credentials
|
||||||
|
|
||||||
|
| System | Access | Rotation after incident |
|
||||||
|
|---|---|---|
|
||||||
|
| Railway | railway.app — sttilsolutionsllc@proton.me | Rotate SIGNAL_API_KEY, SUPABASE_SERVICE_KEY, CLERK_JWKS_URL |
|
||||||
|
| Supabase | supabase.com — Kisa's account | Rotate service_key (JWT) immediately |
|
||||||
|
| Vercel | vercel.com | Rotate VITE_API_URL if endpoint changed |
|
||||||
|
| Clerk | clerk.com — eternal-goblin-1 | Rotate API keys |
|
||||||
|
|
||||||
|
After a P1 incident, rotate ALL secrets listed above regardless of which service was directly involved.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Escalation
|
||||||
|
|
||||||
|
Kisa Fenn is the sole incident commander for all Signal incidents. If Kisa is unavailable and the incident is P1, the default action is to take Signal offline (Railway: pause service) until Kisa can respond.
|
||||||
|
|
||||||
|
**Legal contact:** Nixon Law Group — contact for any incident that may involve regulatory notification obligations or breach of a customer BAA.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Audit Log Posture
|
||||||
|
|
||||||
|
Per the HIPAA Security Rule (as updated in the 2026 Final Rule, 45 CFR §164.312(b)), audit logs must meet a WORM standard and be retained for a minimum of 6 years. Signal's `audit_logger.py` writes to the Supabase `audit_events` table with hashed identifiers and structured JSON. The WORM enforcement gap (preventing DELETE on the audit_events table) is tracked in the compliance checklist and must be resolved before any live PHI enters the system. Until then, the audit log is append-only by policy and reviewed quarterly.
|
||||||
112
docs/compliance/privacy-policy.md
Normal file
112
docs/compliance/privacy-policy.md
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# Signal Privacy Policy
|
||||||
|
|
||||||
|
**Effective Date:** June 7, 2026
|
||||||
|
**Product:** Signal — Documentation Readiness Platform
|
||||||
|
**Operated by:** STTIL Solutions LLC
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Who We Are
|
||||||
|
|
||||||
|
STTIL Solutions LLC ("STTIL", "we", "us") operates Signal, a documentation readiness tool for DMEPOS suppliers. Signal helps supplier staff identify patients whose documentation may need attention before supplies are shipped or claims are submitted.
|
||||||
|
|
||||||
|
This Privacy Policy describes how Signal handles the data uploaded to our platform and what we do (and do not) store.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Data Signal Processes
|
||||||
|
|
||||||
|
When your staff uploads a CSV file to Signal, we process the following fields:
|
||||||
|
|
||||||
|
| Field | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| Patient identifier (internal) | Your internal MRN or account number — used as a de-identified tracking key only |
|
||||||
|
| Device type | CGM device type (e.g., Dexcom G7, FreeStyle Libre 3) |
|
||||||
|
| Shipment date | Most recent supply ship date |
|
||||||
|
| Payer name | Insurance type (e.g., Medicare, Medicaid, Commercial) |
|
||||||
|
| Quantity shipped | Number of units in the shipment |
|
||||||
|
| Documentation status fields (if present) | SWO status, PA status, PECOS verification, diagnosis flag — if included in your CSV |
|
||||||
|
|
||||||
|
Signal produces a prioritized documentation worklist as output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Signal Does NOT Store
|
||||||
|
|
||||||
|
Signal is designed to operate without storing protected health information (PHI). We never store:
|
||||||
|
|
||||||
|
- Patient names
|
||||||
|
- Social Security numbers
|
||||||
|
- Dates of birth
|
||||||
|
- Home addresses
|
||||||
|
- Phone numbers or email addresses
|
||||||
|
- Diagnosis codes (ICD codes)
|
||||||
|
- Clinical notes or records
|
||||||
|
|
||||||
|
The connection between a patient identifier and a real patient identity stays with your staff. STTIL never holds that crosswalk.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How We Store What We Do Collect
|
||||||
|
|
||||||
|
Patient identifiers are **hashed using SHA-256** before storage. This means we store a cryptographic fingerprint of the identifier, not the identifier itself. The hash cannot be reversed to recover the original patient identifier.
|
||||||
|
|
||||||
|
All stored data is associated with your organization's account and is protected by row-level security (RLS) policies in our database. Your data is not accessible to other Signal customers.
|
||||||
|
|
||||||
|
All data in transit is encrypted via HTTPS (TLS 1.2 or higher). All data at rest is encrypted using AES-256.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Who Can Access Your Data
|
||||||
|
|
||||||
|
**Your organization's staff:** Users in your Signal account can view the worklist and documentation status for your patients.
|
||||||
|
|
||||||
|
**STTIL Solutions staff:** STTIL engineers may access system logs and aggregated platform metrics for operational purposes. Access to customer data is restricted and logged.
|
||||||
|
|
||||||
|
**No third parties:** STTIL does not sell, rent, or share your organization's data with any third party for commercial purposes.
|
||||||
|
|
||||||
|
**Law enforcement / legal process:** STTIL may disclose data if required by law, regulation, or court order. We will notify you to the extent permitted by law before complying with any such request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Retention
|
||||||
|
|
||||||
|
Your uploaded CSV data and scored records are retained for the duration of your subscription plus 90 days. Upon termination of your subscription or pilot agreement, all data associated with your account will be deleted within 10 business days of your written request, and we will confirm deletion in writing.
|
||||||
|
|
||||||
|
Audit logs (records of actions taken on the platform) are retained for 7 years for compliance purposes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Patient Outreach
|
||||||
|
|
||||||
|
**Signal does not contact patients.** All outreach and supply decisions remain with your staff. Signal surfaces documentation conditions — your team acts on them. All prescriber outreach flags go to the prescriber office, never to the patient.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HIPAA
|
||||||
|
|
||||||
|
Signal processes de-identified operational data. To the extent that any data processed by Signal could be considered protected health information under HIPAA, STTIL Solutions has executed a Business Associate Agreement (BAA) with its cloud infrastructure providers and operates appropriate administrative, physical, and technical safeguards.
|
||||||
|
|
||||||
|
If you believe a Business Associate Agreement is required for your engagement with Signal, please contact us at the address below before uploading any data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Incidents
|
||||||
|
|
||||||
|
In the event of a security incident affecting your organization's data, STTIL Solutions will notify you within 72 hours of discovery, consistent with our incident response obligations. We will provide a description of the incident, the data affected, and the steps taken to address it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
Questions about this Privacy Policy or your data:
|
||||||
|
|
||||||
|
**STTIL Solutions LLC**
|
||||||
|
Email: kisasttil@gmail.com
|
||||||
|
Web: signal.sttilsolutions.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changes to This Policy
|
||||||
|
|
||||||
|
We may update this Privacy Policy from time to time. We will notify active customers of material changes by email. Continued use of Signal following notice of changes constitutes acceptance of the updated policy.
|
||||||
647
docs/superpowers/plans/2026-06-07-signal-compliance-docs.md
Normal file
647
docs/superpowers/plans/2026-06-07-signal-compliance-docs.md
Normal file
|
|
@ -0,0 +1,647 @@
|
||||||
|
# Signal Demo MVP — Compliance Documents Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Write and publish the compliance documents required before any supplier (starting with Gaboro) uploads data to Signal. These are: Privacy Policy (published at a public URL), Incident Response Procedure, Data Handling Documentation, and payer_rules.json cadence documentation. Also verify MFA and Supabase plan posture.
|
||||||
|
|
||||||
|
**Output location:** `signal/docs/compliance/`
|
||||||
|
- `signal/docs/compliance/privacy-policy.md` — public privacy policy (deploy to sttilsolutions.com/signal/privacy)
|
||||||
|
- `signal/docs/compliance/incident-response.md` — internal procedure
|
||||||
|
- `signal/docs/compliance/data-handling.md` — data handling and minimum necessary fields doc
|
||||||
|
- `signal/docs/compliance/payer-rules-cadence.md` — payer_rules.json update procedure
|
||||||
|
|
||||||
|
**Reference:** Read `signal/docs/hipaa-deployment-analysis-v1.md` before writing any compliance doc — it contains the existing HIPAA analysis that these docs should be consistent with.
|
||||||
|
|
||||||
|
**Key Signal data facts (use these, do not invent):**
|
||||||
|
- Signal stores: hashed patient_id (SHA-256), device_type, shipment_date, quantity, payer, coverage_status, rule_version
|
||||||
|
- Signal never stores: patient names, SSNs, DOBs, addresses, phone numbers, email addresses, diagnosis codes
|
||||||
|
- Identity crosswalk stays local with the supplier — STTIL never holds it
|
||||||
|
- Infrastructure: Railway (FastAPI), Supabase (PostgreSQL + RLS), Vercel (React), Clerk (auth)
|
||||||
|
- AWS: BAA signed; RDS + S3 provisioned but parked until post-funding
|
||||||
|
- All traffic: HTTPS enforced
|
||||||
|
- Auth: Clerk JWT required on all API endpoints in production
|
||||||
|
- Audit log: WORM (append-only rules in Supabase)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: Read existing compliance material
|
||||||
|
|
||||||
|
- [ ] **Step 1: Read the existing HIPAA analysis**
|
||||||
|
|
||||||
|
Read `signal/docs/hipaa-deployment-analysis-v1.md` in full. This document informs all compliance writing. Do not contradict its findings.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Read the Signal CLAUDE.md compliance checklist**
|
||||||
|
|
||||||
|
Read `/Users/sttil-solutions/projects/signal/CLAUDE.md`, specifically the "Compliance Readiness Checklist" section. Note which items are PASS and which are NOT DONE — the docs in this plan are meant to address the NOT DONE items.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: Privacy Policy
|
||||||
|
|
||||||
|
- [ ] **Step 1: Draft the Privacy Policy**
|
||||||
|
|
||||||
|
Create `signal/docs/compliance/privacy-policy.md`. This document will be published at a public URL. Write it for a lay audience (DMEPOS supplier staff), not lawyers.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Signal Privacy Policy
|
||||||
|
|
||||||
|
**Effective Date:** June 7, 2026
|
||||||
|
**Product:** Signal — Documentation Readiness Platform
|
||||||
|
**Operated by:** STTIL Solutions LLC
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Who We Are
|
||||||
|
|
||||||
|
STTIL Solutions LLC ("STTIL", "we", "us") operates Signal, a documentation readiness tool for DMEPOS suppliers. Signal helps supplier staff identify patients whose documentation may need attention before supplies are shipped or claims are submitted.
|
||||||
|
|
||||||
|
This Privacy Policy describes how Signal handles the data uploaded to our platform and what we do (and do not) store.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Data Signal Processes
|
||||||
|
|
||||||
|
When your staff uploads a CSV file to Signal, we process the following fields:
|
||||||
|
|
||||||
|
| Field | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| Patient identifier (internal) | Your internal MRN or account number — used as a de-identified tracking key only |
|
||||||
|
| Device type | CGM device type (e.g., Dexcom G7, FreeStyle Libre 3) |
|
||||||
|
| Shipment date | Most recent supply ship date |
|
||||||
|
| Payer name | Insurance type (e.g., Medicare, Medicaid, Commercial) |
|
||||||
|
| Quantity shipped | Number of units in the shipment |
|
||||||
|
| Documentation status fields (if present) | SWO status, PA status, PECOS verification, diagnosis flag — if included in your CSV |
|
||||||
|
|
||||||
|
Signal produces a prioritized documentation worklist as output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Signal Does NOT Store
|
||||||
|
|
||||||
|
Signal is designed to operate without storing protected health information (PHI). We never store:
|
||||||
|
|
||||||
|
- Patient names
|
||||||
|
- Social Security numbers
|
||||||
|
- Dates of birth
|
||||||
|
- Home addresses
|
||||||
|
- Phone numbers or email addresses
|
||||||
|
- Diagnosis codes (ICD codes)
|
||||||
|
- Clinical notes or records
|
||||||
|
|
||||||
|
The connection between a patient identifier and a real patient identity stays with your staff. STTIL never holds that crosswalk.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How We Store What We Do Collect
|
||||||
|
|
||||||
|
Patient identifiers are **hashed using SHA-256** before storage. This means we store a cryptographic fingerprint of the identifier, not the identifier itself. The hash cannot be reversed to recover the original patient identifier.
|
||||||
|
|
||||||
|
All stored data is associated with your organization's account and is protected by row-level security (RLS) policies in our database — your data is not accessible to other Signal customers.
|
||||||
|
|
||||||
|
All data in transit is encrypted via HTTPS (TLS 1.2 or higher). All data at rest is encrypted using AES-256.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Who Can Access Your Data
|
||||||
|
|
||||||
|
**Your organization's staff:** Users in your Signal account can view the worklist and documentation status for your patients.
|
||||||
|
|
||||||
|
**STTIL Solutions staff:** STTIL engineers may access system logs and aggregated platform metrics for operational purposes. Access to customer data is restricted and logged.
|
||||||
|
|
||||||
|
**No third parties:** STTIL does not sell, rent, or share your organization's data with any third party for commercial purposes.
|
||||||
|
|
||||||
|
**Law enforcement / legal process:** STTIL may disclose data if required by law, regulation, or court order. We will notify you to the extent permitted by law before complying with any such request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Retention
|
||||||
|
|
||||||
|
Your uploaded CSV data and scored records are retained for the duration of your subscription plus 90 days. Upon termination of your subscription or pilot agreement, all data associated with your account will be deleted within 10 business days of your written request, and we will confirm deletion in writing.
|
||||||
|
|
||||||
|
Audit logs (records of actions taken on the platform) are retained for 7 years for compliance purposes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Patient Outreach
|
||||||
|
|
||||||
|
**Signal does not contact patients.** All outreach and supply decisions remain with your staff. Signal surfaces documentation conditions — your team acts on them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HIPAA
|
||||||
|
|
||||||
|
Signal processes de-identified operational data. To the extent that any data processed by Signal could be considered protected health information under HIPAA, STTIL Solutions has executed a Business Associate Agreement (BAA) with its cloud infrastructure providers and operates appropriate administrative, physical, and technical safeguards.
|
||||||
|
|
||||||
|
If you believe a Business Associate Agreement is required for your engagement with Signal, please contact us at the address below before uploading any data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Incidents
|
||||||
|
|
||||||
|
In the event of a security incident affecting your organization's data, STTIL Solutions will notify you within 72 hours of discovery, consistent with our incident response obligations. We will provide a description of the incident, the data affected, and the steps taken to address it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
Questions about this Privacy Policy or your data:
|
||||||
|
**STTIL Solutions LLC**
|
||||||
|
Email: kisasttil@gmail.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changes to This Policy
|
||||||
|
|
||||||
|
We may update this Privacy Policy from time to time. We will notify active customers of material changes by email. Continued use of Signal following notice of changes constitutes acceptance of the updated policy.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add docs/compliance/privacy-policy.md
|
||||||
|
git commit -m "docs: Signal Privacy Policy — publish-ready draft"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: Incident Response Procedure
|
||||||
|
|
||||||
|
- [ ] **Step 1: Draft the Incident Response Procedure**
|
||||||
|
|
||||||
|
Create `signal/docs/compliance/incident-response.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Signal Incident Response Procedure
|
||||||
|
|
||||||
|
**Version:** 1.0
|
||||||
|
**Date:** June 7, 2026
|
||||||
|
**Owner:** STTIL Solutions LLC / Kisa Fenn, Managing Member
|
||||||
|
**Classification:** Internal — do not publish
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This document defines the steps STTIL Solutions will take if a security incident affects Signal or the data of Signal customers. An "incident" includes any unauthorized access, data breach, ransomware event, accidental deletion, or disclosure of customer data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Incident Classification
|
||||||
|
|
||||||
|
| Severity | Description | Examples |
|
||||||
|
|---|---|---|
|
||||||
|
| P1 — Critical | Active breach or confirmed data exposure | Unauthorized access to Supabase; database dump exfiltrated |
|
||||||
|
| P2 — High | Suspected breach or significant service disruption | Unusual API activity; Railway service crash with data loss risk |
|
||||||
|
| P3 — Medium | Potential vulnerability or policy violation | Misconfigured CORS; unexpected admin access |
|
||||||
|
| P4 — Low | Minor issues with no data exposure | Failed login attempts; expired SSL (auto-renewing) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Response Timeline
|
||||||
|
|
||||||
|
| Step | P1 | P2 | P3/P4 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Detect and classify | Immediate | Within 1 hour | Within 24 hours |
|
||||||
|
| Contain | Immediately | Within 2 hours | Within 72 hours |
|
||||||
|
| Notify affected customers | Within 72 hours | Within 72 hours | As appropriate |
|
||||||
|
| Root cause analysis | Within 24 hours | Within 72 hours | Within 1 week |
|
||||||
|
| Remediation complete | Within 48 hours | Within 1 week | Within 2 weeks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1: Detect and Classify
|
||||||
|
|
||||||
|
Detection sources:
|
||||||
|
- Railway logs and alerts
|
||||||
|
- Supabase dashboard alerts
|
||||||
|
- Vercel deployment alerts
|
||||||
|
- Clerk auth anomaly alerts
|
||||||
|
- Customer report
|
||||||
|
- Routine audit log review
|
||||||
|
|
||||||
|
Upon detection, Kisa Fenn assesses severity and begins the response log.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2: Contain
|
||||||
|
|
||||||
|
**P1 (Critical):**
|
||||||
|
1. Immediately revoke all active API keys and rotate secrets (Railway env vars, Supabase service key, Clerk API key)
|
||||||
|
2. Take Signal offline if breach is active (Railway: pause service)
|
||||||
|
3. Preserve logs — do not delete or overwrite
|
||||||
|
4. Do not communicate publicly until scope is understood
|
||||||
|
|
||||||
|
**P2 (High):**
|
||||||
|
1. Revoke and rotate suspected compromised credentials
|
||||||
|
2. Isolate affected service if possible
|
||||||
|
3. Begin forensic log review
|
||||||
|
|
||||||
|
**P3/P4:**
|
||||||
|
1. Patch or reconfigure the identified vulnerability
|
||||||
|
2. Document the fix
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3: Assess Scope
|
||||||
|
|
||||||
|
Determine:
|
||||||
|
- What data was accessed or exposed?
|
||||||
|
- Which customers (organizations) are affected?
|
||||||
|
- What is the time range of the exposure?
|
||||||
|
- Was any patient data (even de-identified hashed IDs) involved?
|
||||||
|
|
||||||
|
Signal's data architecture minimizes exposure: patient_id is hashed before storage. Even in a worst-case database exposure, the hashes cannot be reversed to reveal patient identities without the supplier's local crosswalk.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4: Notify Affected Customers
|
||||||
|
|
||||||
|
**Timeline:** Within 72 hours of confirming that customer data was affected.
|
||||||
|
|
||||||
|
**Notification must include:**
|
||||||
|
1. Date and time the incident was detected
|
||||||
|
2. Description of the incident (what happened)
|
||||||
|
3. Data affected (which tables, which organizations, what fields)
|
||||||
|
4. Steps STTIL has taken to contain and remediate
|
||||||
|
5. Steps the customer should take (if any)
|
||||||
|
6. Contact for follow-up questions
|
||||||
|
|
||||||
|
**Notification method:** Email to the primary contact for each affected organization. For P1 incidents also attempt phone contact.
|
||||||
|
|
||||||
|
**Breach notification under HIPAA:** If any incident involves data that may constitute PHI under HIPAA, STTIL will comply with HIPAA breach notification requirements, including notification to HHS within 60 days of discovery for breaches affecting 500+ individuals. Given Signal's de-identification architecture, PHI breach is unlikely but is addressed here for completeness.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5: Root Cause Analysis and Remediation
|
||||||
|
|
||||||
|
Document:
|
||||||
|
- Root cause (technical failure, human error, external attack)
|
||||||
|
- How it was detected
|
||||||
|
- Timeline of the incident
|
||||||
|
- Containment actions taken
|
||||||
|
- Remediation actions taken
|
||||||
|
- Preventive measures implemented
|
||||||
|
|
||||||
|
Store the incident report in `signal/docs/compliance/incidents/YYYY-MM-DD-incident-report.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6: Post-Incident Review
|
||||||
|
|
||||||
|
Within one week of remediation:
|
||||||
|
- Review whether current monitoring would have caught this sooner
|
||||||
|
- Update runbooks, monitoring, or access controls as needed
|
||||||
|
- Evaluate whether additional audit log coverage is needed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Contacts and Credentials
|
||||||
|
|
||||||
|
| System | Access | Rotation after incident |
|
||||||
|
|---|---|---|
|
||||||
|
| Railway | railway.app — sttilsolutionsllc@proton.me | Rotate SIGNAL_API_KEY, SUPABASE_SERVICE_KEY, CLERK_JWKS_URL |
|
||||||
|
| Supabase | supabase.com — Kisa's account | Rotate service_key (JWT) immediately |
|
||||||
|
| Vercel | vercel.com | Rotate VITE_API_URL if endpoint changed |
|
||||||
|
| Clerk | clerk.com — eternal-goblin-1 | Rotate API keys |
|
||||||
|
|
||||||
|
After a P1 incident, rotate ALL secrets listed above regardless of which service was directly involved.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Escalation
|
||||||
|
|
||||||
|
Kisa Fenn is the sole incident commander for all Signal incidents. If Kisa is unavailable and the incident is P1, the default action is to take Signal offline (Railway: pause service) until Kisa can respond.
|
||||||
|
|
||||||
|
**Legal contact:** Nixon Law Group (Clyde) — contact for any incident that may involve regulatory notification obligations.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add docs/compliance/incident-response.md
|
||||||
|
git commit -m "docs: Signal incident response procedure v1.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: Data Handling Documentation
|
||||||
|
|
||||||
|
- [ ] **Step 1: Draft data handling doc**
|
||||||
|
|
||||||
|
Create `signal/docs/compliance/data-handling.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Signal Data Handling and Minimum Necessary Fields
|
||||||
|
|
||||||
|
**Version:** 1.0
|
||||||
|
**Date:** June 7, 2026
|
||||||
|
**Classification:** Internal + shareable with pilot customers on request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document describes what data Signal collects from customers, how it is processed and stored, and the minimum necessary field policy that governs what data enters Signal systems.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimum Necessary Fields
|
||||||
|
|
||||||
|
Signal is designed on the principle of minimum necessary data. The only fields required for Signal to produce a documentation worklist are:
|
||||||
|
|
||||||
|
| Field | Required | Why |
|
||||||
|
|---|---|---|
|
||||||
|
| Patient identifier (internal) | Yes | De-identified crosswalk key. Patient's own MRN or account number within the supplier's system. Never a name, SSN, or DOB. |
|
||||||
|
| Device type | Yes | Determines wear-day rules and payer coverage eligibility |
|
||||||
|
| Shipment date | Yes | Anchors the supply cycle clock and visit due date calculation |
|
||||||
|
| Payer name | Yes | Determines documentation requirements (PA, PECOS, visit cadence) |
|
||||||
|
| Quantity shipped | Recommended | Improves coverage cycle accuracy; defaults to 1 if omitted |
|
||||||
|
| Component type | Optional | Defaults to "sensor" if omitted |
|
||||||
|
|
||||||
|
**Doc status fields (optional — improve Signal accuracy when present):**
|
||||||
|
|
||||||
|
| Field | Improves |
|
||||||
|
|---|---|
|
||||||
|
| SWO status | SWO item in doc checklist |
|
||||||
|
| Visit date | Visit date priority (confirmed > estimated) |
|
||||||
|
| PECOS verified | PECOS item in doc checklist |
|
||||||
|
| PA status | PA item in doc checklist |
|
||||||
|
| Diagnosis on file | Diagnosis item in doc checklist |
|
||||||
|
| Transfer_From | Transferred patient detection |
|
||||||
|
|
||||||
|
Customers are encouraged to include doc status fields in their CSV exports when available from their billing system. When absent, Signal uses reasonable defaults and labels estimated values clearly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Signal Does NOT Accept
|
||||||
|
|
||||||
|
Customers must not include the following fields in uploaded CSVs:
|
||||||
|
- Patient first or last name
|
||||||
|
- Social Security number
|
||||||
|
- Date of birth
|
||||||
|
- Home address, city, state, or ZIP
|
||||||
|
- Phone number
|
||||||
|
- Email address
|
||||||
|
- Insurance member ID (this may constitute PHI)
|
||||||
|
- Diagnosis codes (ICD-10 codes)
|
||||||
|
- Clinical notes
|
||||||
|
|
||||||
|
Signal's normalizer does not map or store these fields even if they appear in uploaded CSV headers. They are treated as unmapped noise columns and excluded from all Signal processing and storage.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Supplier staff → CSV file → Signal frontend (Vercel)
|
||||||
|
↓
|
||||||
|
Signal API (Railway)
|
||||||
|
- Normalizer: maps CSV headers to canonical fields
|
||||||
|
- Minimum necessary check: only required fields extracted
|
||||||
|
- Coverage calculator: scores each patient
|
||||||
|
- Patient_id hashed via SHA-256 before any storage
|
||||||
|
↓
|
||||||
|
Supabase (PostgreSQL)
|
||||||
|
- upload_batches table: filename, row_count, org_id
|
||||||
|
- normalized_records: patient_id_hash, device_type, shipment_date,
|
||||||
|
payer, coverage_status, rule_version
|
||||||
|
- confirmed_visits: org_id, patient_id_hash, confirmed_date
|
||||||
|
- audit_events: WORM append-only
|
||||||
|
```
|
||||||
|
|
||||||
|
Signal does not transmit any customer data to third parties. The only external services that receive any customer data are:
|
||||||
|
- **Supabase** (database hosting, US-east-2) — BAA in place via Supabase's enterprise tier
|
||||||
|
- **Railway** (application hosting) — no customer data stored at Railway; data in transit only
|
||||||
|
- **Vercel** (frontend CDN) — no customer data stored; serves frontend assets only
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CSV Disposition Policy
|
||||||
|
|
||||||
|
Uploaded CSV files are:
|
||||||
|
1. Received by the Signal API
|
||||||
|
2. Parsed in memory
|
||||||
|
3. Content hash computed (SHA-256 of raw bytes) and stored in `source_files` table
|
||||||
|
4. Raw bytes are NOT stored in Supabase — only the hash and metadata
|
||||||
|
5. Normalized field values are stored (with patient_id hashed)
|
||||||
|
|
||||||
|
**Signal does not store copies of uploaded CSV files.** The CSV is processed in memory and then discarded. Only the scored output and metadata are persisted.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Retention
|
||||||
|
|
||||||
|
| Data Type | Retention Period |
|
||||||
|
|---|---|
|
||||||
|
| Upload batch metadata | Duration of subscription + 90 days |
|
||||||
|
| Scored patient records (normalized_records) | Duration of subscription + 90 days |
|
||||||
|
| Confirmed visit dates | Duration of subscription + 90 days |
|
||||||
|
| Audit events | 7 years (compliance requirement) |
|
||||||
|
| Source file metadata (hash only) | Duration of subscription + 90 days |
|
||||||
|
|
||||||
|
Upon written request, all customer data except audit events (which are retained for legal compliance) will be deleted within 10 business days.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Supabase Plan Tier
|
||||||
|
|
||||||
|
Signal uses **Supabase Pro tier** for production. Pro tier supports:
|
||||||
|
- Row-level security (RLS) — enforced on all tables
|
||||||
|
- Encrypted backups
|
||||||
|
- Point-in-time recovery
|
||||||
|
- SOC 2 Type II compliance (Supabase organization-level)
|
||||||
|
|
||||||
|
Verify current plan tier in Supabase dashboard before the first pilot customer uploads data.
|
||||||
|
|
||||||
|
**Action required before pilot:** Confirm Supabase plan is Pro or higher and that the `confirmed_visits` table has RLS enabled. Run in Supabase SQL editor:
|
||||||
|
```sql
|
||||||
|
SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename;
|
||||||
|
```
|
||||||
|
All tables should show `rowsecurity = true`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## payer_rules.json Update Cadence
|
||||||
|
|
||||||
|
`python-backend/config/payer_rules.json` contains the coverage rules Signal applies when calculating documentation requirements. This file must be reviewed and updated:
|
||||||
|
|
||||||
|
- **Quarterly:** Review CMS LCD updates, MAC bulletins, and payer PA requirement changes for CGM
|
||||||
|
- **On CMS rule publication:** Update immediately when CMS publishes changes to CGM coverage (e.g., new HCPCS codes added to Required PA List, Synapse Health mandate changes, CGM competitive bidding updates)
|
||||||
|
- **On Watcher Agent alert:** The Watcher Agent monitors CMS.gov, MAC websites, and key payer portals for relevant rule changes and alerts Kisa. All updates to payer_rules.json require Kisa's approval before deployment.
|
||||||
|
|
||||||
|
**Cadence documentation maintained at:** `signal/docs/compliance/data-handling.md` (this file).
|
||||||
|
**Owner:** Kisa Fenn, STTIL Solutions LLC.
|
||||||
|
**Next scheduled review:** September 7, 2026 (90 days from launch).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MFA Status
|
||||||
|
|
||||||
|
All Signal administrative accounts must have MFA enabled:
|
||||||
|
|
||||||
|
| Account | MFA Required | Verify Before Pilot |
|
||||||
|
|---|---|---|
|
||||||
|
| Railway (sttilsolutionsllc@proton.me) | Yes | [ ] |
|
||||||
|
| Supabase | Yes | [ ] |
|
||||||
|
| Vercel | Yes | [ ] |
|
||||||
|
| Clerk (eternal-goblin-1) | Yes | [ ] |
|
||||||
|
| AWS (BAA account) | Yes | [ ] |
|
||||||
|
|
||||||
|
Complete this checklist before the Gaboro pilot starts. Mark each item confirmed by checking the MFA status in each platform's security settings.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add docs/compliance/data-handling.md
|
||||||
|
git commit -m "docs: data handling + minimum necessary fields + Supabase posture + MFA checklist"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: Update CLAUDE.md compliance checklist
|
||||||
|
|
||||||
|
- [ ] **Step 1: Update compliance checklist in CLAUDE.md**
|
||||||
|
|
||||||
|
Read `signal/CLAUDE.md`. Find the "Compliance Readiness Checklist" section and update the following items from NOT DONE to PASS:
|
||||||
|
|
||||||
|
- `Data handling rules documented` → PASS (privacy-policy.md + data-handling.md)
|
||||||
|
- `payer_rules.json quarterly update cadence documented` → PASS (data-handling.md)
|
||||||
|
- Add new entry: `Privacy Policy document exists and is ready to publish` → PASS
|
||||||
|
|
||||||
|
**Note on Supabase plan tier:** Check the Supabase dashboard to confirm plan tier before marking `Real PHI blocked` items as fully PASS. If plan is confirmed Pro, mark it PASS.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Recount PASS items and update the percentage**
|
||||||
|
|
||||||
|
After updating, count all PASS items in the compliance checklist and note the new percentage.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add CLAUDE.md docs/compliance/
|
||||||
|
git commit -m "docs: compliance checklist updated — Privacy Policy, data handling, payer cadence PASS"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: Publish Privacy Policy
|
||||||
|
|
||||||
|
The Privacy Policy needs to be at a stable public URL before Gaboro uploads any data.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Identify the publish target**
|
||||||
|
|
||||||
|
The STTIL website is served from the VPS at `/var/www/sttilsolutions.com` via Caddy. The VPS is currently down (as of session start 2026-06-07). **Do not attempt VPS deployment in this task.**
|
||||||
|
|
||||||
|
Instead, create an HTML version of the Privacy Policy as a static file in the Signal frontend that can be served from Vercel at `https://signal-ui-xi.vercel.app/privacy`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Create privacy page in Signal frontend**
|
||||||
|
|
||||||
|
Create `signal-ui/src/Privacy.jsx`:
|
||||||
|
```jsx
|
||||||
|
export default function Privacy() {
|
||||||
|
return (
|
||||||
|
<div className="max-w-[680px] mx-auto px-6 py-12 font-body text-[var(--text-primary)]">
|
||||||
|
<h1 className="font-heading font-bold text-[24px] mb-2">Signal Privacy Policy</h1>
|
||||||
|
<p className="text-[12px] text-[var(--text-muted)] mb-8">Effective Date: June 7, 2026 · STTIL Solutions LLC</p>
|
||||||
|
|
||||||
|
{/* paste the privacy policy content as JSX sections */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] mb-3">Who We Are</h2>
|
||||||
|
<p className="text-[14px] leading-[1.7] text-[var(--text-secondary)]">
|
||||||
|
STTIL Solutions LLC ("STTIL", "we", "us") operates Signal, a documentation readiness tool for DMEPOS suppliers.
|
||||||
|
Signal helps supplier staff identify patients whose documentation may need attention before supplies are shipped or claims are submitted.
|
||||||
|
This Privacy Policy describes how Signal handles the data uploaded to our platform.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] mb-3">What Signal Does NOT Store</h2>
|
||||||
|
<p className="text-[14px] leading-[1.7] text-[var(--text-secondary)] mb-3">
|
||||||
|
Signal is designed to operate without storing protected health information (PHI). We never store:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-5 text-[14px] leading-[1.8] text-[var(--text-secondary)]">
|
||||||
|
<li>Patient names</li>
|
||||||
|
<li>Social Security numbers</li>
|
||||||
|
<li>Dates of birth</li>
|
||||||
|
<li>Home addresses</li>
|
||||||
|
<li>Phone numbers or email addresses</li>
|
||||||
|
<li>Diagnosis codes (ICD codes)</li>
|
||||||
|
<li>Clinical notes or records</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-[14px] leading-[1.7] text-[var(--text-secondary)] mt-3">
|
||||||
|
The connection between a patient identifier and a real patient identity stays with your staff. STTIL never holds that crosswalk.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] mb-3">Patient Outreach</h2>
|
||||||
|
<p className="text-[14px] leading-[1.7] text-[var(--text-secondary)]">
|
||||||
|
Signal does not contact patients. All outreach and supply decisions remain with your staff.
|
||||||
|
Signal surfaces documentation conditions — your team acts on them.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] mb-3">Data Retention and Deletion</h2>
|
||||||
|
<p className="text-[14px] leading-[1.7] text-[var(--text-secondary)]">
|
||||||
|
Your data is retained for the duration of your subscription plus 90 days.
|
||||||
|
Upon written request, all data associated with your account will be deleted within 10 business days, with written confirmation provided.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] mb-3">Contact</h2>
|
||||||
|
<p className="text-[14px] leading-[1.7] text-[var(--text-secondary)]">
|
||||||
|
Questions: <a href="mailto:kisasttil@gmail.com" className="text-[var(--brand)] underline">kisasttil@gmail.com</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<p className="text-[11px] text-[var(--text-muted)] border-t border-[var(--border-subtle)] pt-4 mt-8">
|
||||||
|
Signal is operated by STTIL Solutions LLC · signal.sttilsolutions.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Add /privacy route to App.jsx**
|
||||||
|
|
||||||
|
Read `signal-ui/src/App.jsx`. Add a route for the Privacy component. The app likely uses conditional rendering or React Router. Add the Privacy page so it renders at `/privacy`.
|
||||||
|
|
||||||
|
If the app uses hash routing or simple state-based routing, add a check: if `window.location.pathname === '/privacy'` render `<Privacy />` instead of the main app. If it uses React Router, add `<Route path="/privacy" element={<Privacy />} />`.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Add vercel.json rewrite for /privacy**
|
||||||
|
|
||||||
|
Read `signal-ui/vercel.json`. Add a rewrite so `/privacy` serves the SPA:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
(This is likely already there. Verify and add if missing.)
|
||||||
|
|
||||||
|
- [ ] **Step 5: Build + deploy to Vercel**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal/signal-ui && pnpm build
|
||||||
|
cd /Users/sttil-solutions/projects/signal && git add signal-ui/src/Privacy.jsx signal-ui/src/App.jsx
|
||||||
|
git commit -m "feat: Privacy Policy page at /privacy on Signal frontend"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
Vercel will auto-deploy. Verify at: https://signal-ui-xi.vercel.app/privacy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery
|
||||||
|
|
||||||
|
After all tasks complete, report back with:
|
||||||
|
1. Public URL of the Privacy Policy
|
||||||
|
2. List of compliance checklist items updated to PASS
|
||||||
|
3. MFA checklist status (which accounts still need MFA enabled)
|
||||||
|
4. Supabase plan tier confirmed or still pending verification
|
||||||
|
5. Any items still marked NOT DONE with explanation of blocker
|
||||||
872
docs/superpowers/plans/2026-06-07-signal-csv-generator.md
Normal file
872
docs/superpowers/plans/2026-06-07-signal-csv-generator.md
Normal file
|
|
@ -0,0 +1,872 @@
|
||||||
|
# Signal Demo MVP — CSV Generator Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Build a parameterized CSV generator system that produces PA-market (25 files) and NJ-market (25 files) test sets with state-change patients, transferred patients, and all normalizer challenge types. Also deliver the Bug Hunter report.
|
||||||
|
|
||||||
|
**Architecture:** `generate_supplier_set.py` is the core reusable generator. It reads `market_data.json` for state-specific payer names and geography. State-specific wrappers (`generate_pa_set.py`, `generate_nj_set.py`) call it with preset args. The Bug Hunter is a script that runs all 50 CSVs through the Signal normalizer and returns a structured failure report.
|
||||||
|
|
||||||
|
**Tech Stack:** Python 3.11 (stdlib only — no external dependencies). Signal normalizer lives at `python-backend/api/normalizer.py`.
|
||||||
|
|
||||||
|
**Key paths:**
|
||||||
|
- Reference generator: `test-data/generate_samples.py` — read this before writing anything
|
||||||
|
- Output: `test-data/pa-set/` (25 files) and `test-data/nj-set/` (25 files)
|
||||||
|
- Normalizer import path (from test-data dir): `sys.path.insert(0, '../python-backend')`
|
||||||
|
|
||||||
|
**Before starting:** Read `test-data/generate_samples.py` in full. The new generators follow the same HEADER_VARIANTS pattern and DATE_BUCKETS approach, extended for PA/NJ payers, doc fields, and state-change/transfer scenarios.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
**Create:**
|
||||||
|
- `test-data/market_data.json` — state-to-payer-names config
|
||||||
|
- `test-data/generate_supplier_set.py` — parameterized generator (accepts --state, --supplier, --patients, --challenge-level)
|
||||||
|
- `test-data/generate_pa_set.py` — PA market wrapper
|
||||||
|
- `test-data/generate_nj_set.py` — NJ market wrapper
|
||||||
|
- `test-data/bug_hunter.py` — runs all 50 CSVs through normalizer, outputs failure report
|
||||||
|
- `test-data/pa-set/` — 25 generated CSV files
|
||||||
|
- `test-data/nj-set/` — 25 generated CSV files
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: market_data.json
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create market_data.json**
|
||||||
|
|
||||||
|
Create `test-data/market_data.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"PA": {
|
||||||
|
"supplier_name": "Gaboro DME",
|
||||||
|
"branch_prefix": "PA",
|
||||||
|
"branch_id": "PA-001",
|
||||||
|
"state": "PA",
|
||||||
|
"payers": {
|
||||||
|
"medicare_ffs": ["Medicare Part B", "Medicare", "CMS"],
|
||||||
|
"medicare_advantage": ["UPMC Health Plan", "Highmark BlueCross BlueShield", "Aetna PA Medicare"],
|
||||||
|
"medicaid": ["PA Medicaid", "Keystone First", "AmeriHealth Caritas PA", "UPMC for You"],
|
||||||
|
"commercial": ["Independence Blue Cross", "Highmark BCBS PA", "Aetna PA", "Cigna PA", "Geisinger"]
|
||||||
|
},
|
||||||
|
"geographies": ["Philadelphia", "Bucks County", "Montgomery County", "Chester County"],
|
||||||
|
"prescribers": ["Penn Medicine", "Jefferson Health", "Temple Health", "Drexel Medicine"]
|
||||||
|
},
|
||||||
|
"NJ": {
|
||||||
|
"supplier_name": "Gaboro DME",
|
||||||
|
"branch_prefix": "NJ",
|
||||||
|
"branch_id": "NJ-002",
|
||||||
|
"state": "NJ",
|
||||||
|
"payers": {
|
||||||
|
"medicare_ffs": ["Medicare Part B", "Medicare", "Medicare FFS NJ"],
|
||||||
|
"medicare_advantage": ["Aetna NJ Medicare", "UnitedHealthcare NJ Medicare", "Horizon BCBS NJ Medicare"],
|
||||||
|
"medicaid": ["NJ FamilyCare", "WellCare NJ", "AmeriHealth NJ"],
|
||||||
|
"commercial": ["Horizon BCBS NJ", "Aetna NJ", "Cigna NJ", "Oxford Health", "UnitedHealthcare NJ"]
|
||||||
|
},
|
||||||
|
"geographies": ["Bergen County", "Essex County", "Hudson County", "Passaic County"],
|
||||||
|
"prescribers": ["Hackensack Meridian", "RWJBarnabas", "Atlantic Health", "Valley Health"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add test-data/market_data.json
|
||||||
|
git commit -m "feat: market_data.json — PA/NJ payer and geography config"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: generate_supplier_set.py (parameterized core generator)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Read the reference generator**
|
||||||
|
|
||||||
|
Read `test-data/generate_samples.py` in full before writing. Key patterns to follow:
|
||||||
|
- HEADER_VARIANTS list structure (tuple of column names + date format + extras dict)
|
||||||
|
- DATE_BUCKETS for status targeting
|
||||||
|
- Random seed for reproducibility
|
||||||
|
- Output naming pattern
|
||||||
|
|
||||||
|
- [ ] **Step 2: Create generate_supplier_set.py**
|
||||||
|
|
||||||
|
Create `test-data/generate_supplier_set.py`:
|
||||||
|
```python
|
||||||
|
"""
|
||||||
|
generate_supplier_set.py — Parameterized Signal CSV generator.
|
||||||
|
Produces N files for a given state/supplier/challenge-level combination.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python generate_supplier_set.py --state PA --supplier "Gaboro DME" --patients 30 --challenge-level high --output pa-set
|
||||||
|
python generate_supplier_set.py --state NJ --supplier "Gaboro DME" --patients 25 --challenge-level high --output nj-set
|
||||||
|
|
||||||
|
Challenge levels:
|
||||||
|
low: canonical headers, clean dates, valid payers
|
||||||
|
medium: header variants, mixed date formats, some nulls
|
||||||
|
high: all variants + nulls + malformed dates + transfer patients + state-change patients
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
random.seed(42)
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
MARKET_DATA_PATH = os.path.join(SCRIPT_DIR, "market_data.json")
|
||||||
|
|
||||||
|
DEVICE_TYPES = ["dexcom_g7", "dexcom_g6", "freestyle_libre_3", "freestyle_libre_2"]
|
||||||
|
COMPONENTS = {
|
||||||
|
"dexcom_g7": "sensor", "dexcom_g6": "sensor",
|
||||||
|
"freestyle_libre_3": "sensor", "freestyle_libre_2": "sensor",
|
||||||
|
}
|
||||||
|
|
||||||
|
TODAY = date.today()
|
||||||
|
|
||||||
|
DATE_BUCKETS = {
|
||||||
|
"active": (TODAY - timedelta(days=10), TODAY - timedelta(days=1)),
|
||||||
|
"renewal_soon": (TODAY - timedelta(days=270), TODAY - timedelta(days=260)),
|
||||||
|
"renewal_elevated": (TODAY - timedelta(days=300), TODAY - timedelta(days=290)),
|
||||||
|
"renewal_critical": (TODAY - timedelta(days=315), TODAY - timedelta(days=310)),
|
||||||
|
"visit_required": (TODAY - timedelta(days=400), TODAY - timedelta(days=380)),
|
||||||
|
"supply_lapsed": (TODAY - timedelta(days=600), TODAY - timedelta(days=500)),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Header variants — (patient_id_col, device_col, date_col, qty_col, payer_col, component_col, date_fmt, extras_factory)
|
||||||
|
# extras_factory(state_data) → dict of extra columns to include
|
||||||
|
BASE_HEADER_VARIANTS = [
|
||||||
|
# 1 — canonical
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 2 — Brightree-style
|
||||||
|
("Patient ID", "Item Description", "Service Date", "Qty", "Insurance Name", "Item Type",
|
||||||
|
"%m/%d/%Y", lambda sd: {"Prescriber NPI": "1234567890", "Branch": sd["branch_id"]}),
|
||||||
|
# 3 — all caps
|
||||||
|
("PT_ID", "DEVICE", "SHIP DATE", "UNITS", "CARRIER", "TYPE",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 4 — MRN + text date
|
||||||
|
("MRN", "Product Name", "Dispense Date", "Qty Dispensed", "Plan Name", "Supply Type",
|
||||||
|
"%d-%b-%Y", lambda sd: {"Supplier": sd["supplier_name"], "State": sd["state"]}),
|
||||||
|
# 5 — account_number
|
||||||
|
("Account Number", "Product", "Fill Date", "Count", "Primary Payer", "component",
|
||||||
|
"%m/%d/%y", lambda sd: {}),
|
||||||
|
# 6 — external ref + ISO datetime
|
||||||
|
("External Patient Ref", "Item", "Date of Service", "Qty Shipped", "Insurance", "item_type",
|
||||||
|
"%Y-%m-%dT%H:%M:%S", lambda sd: {"Notes": "batch export", "Region": sd["geographies"][0]}),
|
||||||
|
# 7 — Acct # + YYYYMMDD
|
||||||
|
("Acct #", "DME", "Order Date", "quantity", "plan", "component_type",
|
||||||
|
"%Y%m%d", lambda sd: {}),
|
||||||
|
# 8 — patientid no space + dispense date
|
||||||
|
("patientid", "devicetype", "dispensedate", "qty", "payername", "supplytype",
|
||||||
|
"%m/%d/%Y", lambda sd: {}),
|
||||||
|
# 9 — pt_id + dos
|
||||||
|
("pt_id", "product_type", "dos", "qty", "ins_name", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 10 — account_no + hcpcs
|
||||||
|
("account_no", "hcpcs_description", "service_date", "units", "primary_payer", "supply_type",
|
||||||
|
"%m-%d-%Y", lambda sd: {"HCPCS Code": "A9277"}),
|
||||||
|
# 11 — patient_account + commercial
|
||||||
|
("patient_account", "description", "ship_date", "quantity_dispensed", "carrier", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Account Manager": "Staff"}),
|
||||||
|
# 12 — id + product
|
||||||
|
("id", "product", "fill_date", "qty_shipped", "payer", "item_type",
|
||||||
|
"%m/%d/%Y", lambda sd: {}),
|
||||||
|
# 13 — PT ID spaces
|
||||||
|
("PT ID", "Device Type", "Shipment Date", "Quantity", "Insurance", "Component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Region": sd["geographies"][1] if len(sd["geographies"]) > 1 else ""}),
|
||||||
|
# 14 — lowercase no space
|
||||||
|
("patientid", "devicetype", "dispensedate", "qty", "payername", "supplytype",
|
||||||
|
"%m/%d/%Y", lambda sd: {}),
|
||||||
|
# 15 — account_number + d/m/y
|
||||||
|
("account_number", "item_description", "order_date", "units_dispensed", "plan_name", "component",
|
||||||
|
"%d/%m/%Y", lambda sd: {"Facility": f"{sd['supplier_name']} Main"}),
|
||||||
|
# 16 — member_id
|
||||||
|
("member_id", "Product Type", "Service Date", "Qty", "Insurance Name", "Component Type",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 17 — acct no
|
||||||
|
("acct no", "dme description", "last ship date", "qty dispensed", "payer name", "type",
|
||||||
|
"%m/%d/%Y", lambda sd: {"Billing Cycle": "Monthly"}),
|
||||||
|
# 18 — external_patient_ref
|
||||||
|
("external_patient_ref", "equipment description", "service_date", "count", "primary_payer", "supply_type",
|
||||||
|
"%Y-%m-%dT%H:%M:%S", lambda sd: {}),
|
||||||
|
# 19 — with doc columns: swo_status, pa_status
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"swo_status": "On File", "pa_status": "Not Required", "diagnosis_on_file": "Yes"}),
|
||||||
|
# 20 — with visit_date column
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"visit_date": "", "pecos_verified": "Yes", "swo_status": "On File"}),
|
||||||
|
# 21 — PA status column variants
|
||||||
|
("Patient ID", "Item Description", "Service Date", "Qty", "Insurance Name", "Item Type",
|
||||||
|
"%m/%d/%Y", lambda sd: {"auth_status": "Approved", "swo_status": "On File"}),
|
||||||
|
# 22 — Prior Auth column
|
||||||
|
("account_no", "device_type", "shipment_date", "qty", "insurance", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Prior Auth": "Pending", "diagnosis_on_file": "Yes"}),
|
||||||
|
# 23 — with notes + auth numbers (noise columns)
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Notes": "see chart", "Auth Number": "A12345", "Staff": "K.F."}),
|
||||||
|
# 24 — missing optional columns (no component, no quantity)
|
||||||
|
("patient_id", "device_type", "shipment_date", None, "payer", None,
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 25 — transfer patient column
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Transfer_From": "Prior Supplier LLC", "swo_status": "Pending"}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _pick_payer(state_data: dict, payer_type: str) -> str:
|
||||||
|
payers = state_data["payers"].get(payer_type, [])
|
||||||
|
return random.choice(payers) if payers else "Medicare Part B"
|
||||||
|
|
||||||
|
|
||||||
|
def _format_date(d: date, fmt: str) -> str:
|
||||||
|
return d.strftime(fmt)
|
||||||
|
|
||||||
|
|
||||||
|
def _random_date_in_bucket(bucket: tuple[date, date]) -> date:
|
||||||
|
start, end = bucket
|
||||||
|
days = (end - start).days
|
||||||
|
return start + timedelta(days=random.randint(0, days))
|
||||||
|
|
||||||
|
|
||||||
|
def _device_display(device: str, header_style: str) -> str:
|
||||||
|
"""Map device type to display string matching the header style."""
|
||||||
|
display_map = {
|
||||||
|
"dexcom_g7": {
|
||||||
|
"canonical": "dexcom_g7",
|
||||||
|
"display": "Dexcom G7",
|
||||||
|
"short": "G7",
|
||||||
|
},
|
||||||
|
"dexcom_g6": {
|
||||||
|
"canonical": "dexcom_g6",
|
||||||
|
"display": "Dexcom G6",
|
||||||
|
"short": "G6",
|
||||||
|
},
|
||||||
|
"freestyle_libre_3": {
|
||||||
|
"canonical": "freestyle_libre_3",
|
||||||
|
"display": "FreeStyle Libre 3",
|
||||||
|
"short": "FSL3",
|
||||||
|
},
|
||||||
|
"freestyle_libre_2": {
|
||||||
|
"canonical": "freestyle_libre_2",
|
||||||
|
"display": "FreeStyle Libre 2",
|
||||||
|
"short": "FSL2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dm = display_map.get(device, {"canonical": device, "display": device, "short": device})
|
||||||
|
if header_style in ("Item Description", "Product Name", "product", "description", "dme description", "equipment description"):
|
||||||
|
return dm["display"]
|
||||||
|
if header_style in ("DEVICE", "DME"):
|
||||||
|
return dm["short"]
|
||||||
|
return dm["canonical"]
|
||||||
|
|
||||||
|
|
||||||
|
def _payer_display(payer_str: str, header_style: str) -> str:
|
||||||
|
return payer_str # payer strings are already in display format from market_data.json
|
||||||
|
|
||||||
|
|
||||||
|
def generate_state_change_patients(state_data: dict, file_index: int, num_files: int) -> list[dict]:
|
||||||
|
"""
|
||||||
|
Generate 6 state-change patients whose status evolves across the file sequence.
|
||||||
|
Returns patient records that change based on file_index.
|
||||||
|
"""
|
||||||
|
patients = []
|
||||||
|
progress = file_index / max(num_files - 1, 1) # 0.0 to 1.0
|
||||||
|
|
||||||
|
# Patient A: starts RENEWAL_SOON, confirmed in file 10, ACTIVE by file 20
|
||||||
|
if file_index < 10:
|
||||||
|
shipment_date = TODAY - timedelta(days=270)
|
||||||
|
visit_date = "" # No confirmed visit yet
|
||||||
|
swo_status = "On File"
|
||||||
|
elif file_index < 20:
|
||||||
|
shipment_date = TODAY - timedelta(days=10)
|
||||||
|
visit_date = (TODAY - timedelta(days=5)).strftime("%Y-%m-%d")
|
||||||
|
swo_status = "On File"
|
||||||
|
else:
|
||||||
|
shipment_date = TODAY - timedelta(days=5)
|
||||||
|
visit_date = (TODAY - timedelta(days=5)).strftime("%Y-%m-%d")
|
||||||
|
swo_status = "On File"
|
||||||
|
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-A-001",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": shipment_date,
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
"visit_date": visit_date,
|
||||||
|
"swo_status": swo_status,
|
||||||
|
"pecos_verified": "Yes",
|
||||||
|
"pa_status": "Not Required",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patient B: starts ACTIVE, slides to RENEWAL_SOON by file 15
|
||||||
|
base_visit_offset = 170 - int(progress * 90) # 170 → 80 days ago
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-B-002",
|
||||||
|
"device_type": "freestyle_libre_3",
|
||||||
|
"shipment_date": TODAY - timedelta(days=5),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicaid"),
|
||||||
|
"component": "sensor",
|
||||||
|
"visit_date": (TODAY - timedelta(days=base_visit_offset)).strftime("%Y-%m-%d"),
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pecos_verified": "Yes",
|
||||||
|
"pa_status": "Approved",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patient C: transfer patient appearing mid-sequence, clears by file 18
|
||||||
|
if file_index < 12:
|
||||||
|
transfer_from = "Prior Supplier LLC"
|
||||||
|
swo_status_c = "Pending"
|
||||||
|
else:
|
||||||
|
transfer_from = ""
|
||||||
|
swo_status_c = "On File"
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-C-003",
|
||||||
|
"device_type": "dexcom_g6",
|
||||||
|
"shipment_date": TODAY - timedelta(days=30),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
"Transfer_From": transfer_from,
|
||||||
|
"swo_status": swo_status_c,
|
||||||
|
"pa_status": "Pending" if file_index < 12 else "Approved",
|
||||||
|
"diagnosis_on_file": "Yes" if file_index >= 12 else "No",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patient D: SUPPLY_LAPSED → new shipment after file 8 → ACTIVE
|
||||||
|
if file_index < 8:
|
||||||
|
shipment_d = TODAY - timedelta(days=600)
|
||||||
|
else:
|
||||||
|
shipment_d = TODAY - timedelta(days=5)
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-D-004",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": shipment_d,
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_advantage"),
|
||||||
|
"component": "sensor",
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pa_status": "Approved" if file_index >= 8 else "Pending",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patients E and F: simple active patients that stay stable (control group)
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-E-005",
|
||||||
|
"device_type": "freestyle_libre_2",
|
||||||
|
"shipment_date": TODAY - timedelta(days=3),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
"visit_date": (TODAY - timedelta(days=30)).strftime("%Y-%m-%d"),
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pecos_verified": "Yes",
|
||||||
|
"pa_status": "Not Required",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-F-006",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY - timedelta(days=7),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pa_status": "Approved",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
return patients
|
||||||
|
|
||||||
|
|
||||||
|
def generate_challenge_rows(state_data: dict, challenge_level: str, file_index: int) -> list[dict]:
|
||||||
|
"""Generate challenge-type rows based on challenge_level."""
|
||||||
|
if challenge_level == "low":
|
||||||
|
return []
|
||||||
|
|
||||||
|
challenges = []
|
||||||
|
if challenge_level in ("medium", "high"):
|
||||||
|
# Null patient_id (should be skipped)
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": "",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY - timedelta(days=10),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
|
||||||
|
if challenge_level == "high":
|
||||||
|
# Future shipment date (edge case)
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"FUTURE-{file_index:03d}",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY + timedelta(days=30),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
# Zero quantity
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"ZERQTY-{file_index:03d}",
|
||||||
|
"device_type": "freestyle_libre_3",
|
||||||
|
"shipment_date": TODAY - timedelta(days=5),
|
||||||
|
"quantity": 0,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
# Duplicate patient_id (second one should be caught or processed)
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"DUP-PT-{file_index:03d}",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY - timedelta(days=5),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"DUP-PT-{file_index:03d}", # duplicate
|
||||||
|
"device_type": "freestyle_libre_3",
|
||||||
|
"shipment_date": TODAY - timedelta(days=3),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
|
||||||
|
return challenges
|
||||||
|
|
||||||
|
|
||||||
|
def generate_patient_batch(
|
||||||
|
state_data: dict,
|
||||||
|
patient_count: int,
|
||||||
|
file_index: int,
|
||||||
|
num_files: int,
|
||||||
|
challenge_level: str,
|
||||||
|
) -> list[dict]:
|
||||||
|
"""Generate the main patient batch for one file."""
|
||||||
|
patients = []
|
||||||
|
payer_types = ["medicare_ffs", "medicare_ffs", "medicare_advantage", "medicaid", "commercial"]
|
||||||
|
status_buckets = [
|
||||||
|
"active", "active", "active", "active",
|
||||||
|
"renewal_soon", "renewal_elevated", "renewal_critical",
|
||||||
|
"visit_required", "supply_lapsed",
|
||||||
|
]
|
||||||
|
|
||||||
|
for i in range(patient_count):
|
||||||
|
device = random.choice(DEVICE_TYPES)
|
||||||
|
payer_type = random.choice(payer_types)
|
||||||
|
payer_str = _pick_payer(state_data, payer_type)
|
||||||
|
bucket_name = status_buckets[i % len(status_buckets)]
|
||||||
|
shipment_date = _random_date_in_bucket(DATE_BUCKETS[bucket_name])
|
||||||
|
qty = random.choice([1, 1, 1, 2, 3])
|
||||||
|
geo = random.choice(state_data["geographies"])
|
||||||
|
prescriber = random.choice(state_data["prescribers"])
|
||||||
|
|
||||||
|
patient = {
|
||||||
|
"patient_id": f"{state_data['branch_prefix']}-{file_index:02d}-{i+1:04d}",
|
||||||
|
"device_type": device,
|
||||||
|
"shipment_date": shipment_date,
|
||||||
|
"quantity": qty,
|
||||||
|
"payer": payer_str,
|
||||||
|
"component": COMPONENTS.get(device, "sensor"),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add doc fields for PA/NJ scenarios
|
||||||
|
if payer_type == "medicare_ffs":
|
||||||
|
patient.update({
|
||||||
|
"swo_status": random.choice(["On File", "On File", "Pending"]),
|
||||||
|
"pecos_verified": random.choice(["Yes", "Yes", "No"]),
|
||||||
|
"pa_status": "Not Required",
|
||||||
|
"diagnosis_on_file": random.choice(["Yes", "Yes", "No"]),
|
||||||
|
})
|
||||||
|
elif payer_type == "medicare_advantage":
|
||||||
|
patient.update({
|
||||||
|
"swo_status": random.choice(["On File", "Pending"]),
|
||||||
|
"pecos_verified": random.choice(["Yes", "No"]),
|
||||||
|
"pa_status": random.choice(["Approved", "Pending", "Denied"]),
|
||||||
|
"diagnosis_on_file": random.choice(["Yes", "No"]),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
patient.update({
|
||||||
|
"swo_status": random.choice(["On File", "Pending", "Expired"]),
|
||||||
|
"pa_status": random.choice(["Approved", "Pending", "Not Required"]),
|
||||||
|
"diagnosis_on_file": random.choice(["Yes", "No"]),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add visit date for some patients
|
||||||
|
if random.random() > 0.4:
|
||||||
|
visit_offset = random.randint(30, 200)
|
||||||
|
patient["visit_date"] = (TODAY - timedelta(days=visit_offset)).strftime("%Y-%m-%d")
|
||||||
|
else:
|
||||||
|
patient["visit_date"] = ""
|
||||||
|
|
||||||
|
patients.append(patient)
|
||||||
|
|
||||||
|
return patients
|
||||||
|
|
||||||
|
|
||||||
|
def write_csv_file(
|
||||||
|
output_path: str,
|
||||||
|
patients: list[dict],
|
||||||
|
variant: tuple,
|
||||||
|
state_data: dict,
|
||||||
|
challenge_level: str,
|
||||||
|
file_index: int,
|
||||||
|
) -> None:
|
||||||
|
"""Write one CSV file using the given header variant."""
|
||||||
|
pid_col, dev_col, date_col, qty_col, payer_col, comp_col, date_fmt, extras_factory = variant
|
||||||
|
extras = extras_factory(state_data)
|
||||||
|
|
||||||
|
headers = [h for h in [pid_col, dev_col, date_col, qty_col, payer_col, comp_col] if h is not None]
|
||||||
|
|
||||||
|
# Add doc columns if they appear in patients
|
||||||
|
doc_cols = ["visit_date", "swo_status", "pecos_verified", "pa_status", "diagnosis_on_file", "Transfer_From"]
|
||||||
|
doc_headers_in_extras = {k for k in extras if k in doc_cols}
|
||||||
|
extra_headers = [k for k in extras if k not in doc_cols]
|
||||||
|
|
||||||
|
# Malformed date files (2 per set at indices 7 and 14)
|
||||||
|
if challenge_level == "high" and file_index in (7, 14):
|
||||||
|
malformed_patients = []
|
||||||
|
for p in patients[:3]:
|
||||||
|
mp = dict(p)
|
||||||
|
mp["_malformed_date"] = True
|
||||||
|
malformed_patients.append(mp)
|
||||||
|
patients = malformed_patients + patients[3:]
|
||||||
|
|
||||||
|
with open(output_path, "w", newline="") as f:
|
||||||
|
all_headers = headers + list(doc_headers_in_extras) + extra_headers
|
||||||
|
|
||||||
|
# Some files: include doc columns explicitly
|
||||||
|
if any(col in extras for col in doc_cols):
|
||||||
|
for col in doc_cols:
|
||||||
|
if col in extras and col not in all_headers:
|
||||||
|
all_headers.append(col)
|
||||||
|
|
||||||
|
writer = csv.DictWriter(f, fieldnames=all_headers, extrasaction="ignore")
|
||||||
|
writer.writeheader()
|
||||||
|
|
||||||
|
for p in patients:
|
||||||
|
row = {}
|
||||||
|
# Map patient dict to column headers
|
||||||
|
if pid_col:
|
||||||
|
row[pid_col] = p.get("patient_id", "")
|
||||||
|
if dev_col:
|
||||||
|
row[dev_col] = _device_display(p.get("device_type", "dexcom_g7"), dev_col)
|
||||||
|
if date_col:
|
||||||
|
sd = p.get("shipment_date", TODAY)
|
||||||
|
if isinstance(sd, date):
|
||||||
|
if hasattr(p, "_malformed_date") or p.get("_malformed_date"):
|
||||||
|
row[date_col] = random.choice(["not on file", "pending", "TBD"])
|
||||||
|
else:
|
||||||
|
row[date_col] = _format_date(sd, date_fmt)
|
||||||
|
else:
|
||||||
|
row[date_col] = str(sd)
|
||||||
|
if qty_col:
|
||||||
|
row[qty_col] = p.get("quantity", 1)
|
||||||
|
if payer_col:
|
||||||
|
row[payer_col] = p.get("payer", "Medicare Part B")
|
||||||
|
if comp_col:
|
||||||
|
row[comp_col] = p.get("component", "sensor")
|
||||||
|
|
||||||
|
# Doc fields
|
||||||
|
for col in doc_cols:
|
||||||
|
if col in all_headers:
|
||||||
|
row[col] = p.get(col, "")
|
||||||
|
|
||||||
|
# Extras
|
||||||
|
for k, v in extras.items():
|
||||||
|
if k in all_headers:
|
||||||
|
row[k] = v
|
||||||
|
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_set(
|
||||||
|
state: str,
|
||||||
|
supplier: str,
|
||||||
|
patients: int,
|
||||||
|
challenge_level: str,
|
||||||
|
output_dir: str,
|
||||||
|
num_files: int = 25,
|
||||||
|
) -> None:
|
||||||
|
"""Generate num_files CSV files for the given state."""
|
||||||
|
with open(MARKET_DATA_PATH) as f:
|
||||||
|
market = json.load(f)
|
||||||
|
|
||||||
|
state_data = market.get(state.upper())
|
||||||
|
if not state_data:
|
||||||
|
raise ValueError(f"State '{state}' not found in market_data.json. Available: {list(market.keys())}")
|
||||||
|
|
||||||
|
state_data["supplier_name"] = supplier # allow override
|
||||||
|
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
for i in range(num_files):
|
||||||
|
# Rotate through header variants
|
||||||
|
variant = BASE_HEADER_VARIANTS[i % len(BASE_HEADER_VARIANTS)]
|
||||||
|
|
||||||
|
# Core patient batch
|
||||||
|
patient_rows = generate_patient_batch(state_data, patients, i, num_files, challenge_level)
|
||||||
|
|
||||||
|
# State-change patients (appear in every file)
|
||||||
|
sc_patients = generate_state_change_patients(state_data, i, num_files)
|
||||||
|
all_patients = sc_patients + patient_rows
|
||||||
|
|
||||||
|
# Challenge rows
|
||||||
|
if challenge_level == "high":
|
||||||
|
challenge_rows = generate_challenge_rows(state_data, challenge_level, i)
|
||||||
|
all_patients = all_patients + challenge_rows
|
||||||
|
|
||||||
|
filename = f"batch-{i+1:02d}-{state.lower()}-{challenge_level}.csv"
|
||||||
|
output_path = os.path.join(output_dir, filename)
|
||||||
|
|
||||||
|
write_csv_file(output_path, all_patients, variant, state_data, challenge_level, i)
|
||||||
|
print(f" Written: {filename} ({len(all_patients)} rows)")
|
||||||
|
|
||||||
|
print(f"\nGenerated {num_files} files in {output_dir}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Generate Signal CSV test files")
|
||||||
|
parser.add_argument("--state", default="PA")
|
||||||
|
parser.add_argument("--supplier", default="Gaboro DME")
|
||||||
|
parser.add_argument("--patients", type=int, default=30)
|
||||||
|
parser.add_argument("--challenge-level", choices=["low", "medium", "high"], default="high")
|
||||||
|
parser.add_argument("--output", default="pa-set")
|
||||||
|
parser.add_argument("--files", type=int, default=25)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
output_path = os.path.join(SCRIPT_DIR, args.output)
|
||||||
|
generate_set(
|
||||||
|
state=args.state,
|
||||||
|
supplier=args.supplier,
|
||||||
|
patients=args.patients,
|
||||||
|
challenge_level=args.challenge_level,
|
||||||
|
output_dir=output_path,
|
||||||
|
num_files=args.files,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add test-data/generate_supplier_set.py
|
||||||
|
git commit -m "feat: parameterized CSV generator with state-change patients and challenge types"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: State-specific wrappers + generate PA and NJ sets
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create generate_pa_set.py**
|
||||||
|
|
||||||
|
Create `test-data/generate_pa_set.py`:
|
||||||
|
```python
|
||||||
|
"""Generate 25 PA-market CSV files (Philadelphia/Southeastern PA)."""
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
from generate_supplier_set import generate_set
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_set(
|
||||||
|
state="PA",
|
||||||
|
supplier="Gaboro DME",
|
||||||
|
patients=30,
|
||||||
|
challenge_level="high",
|
||||||
|
output_dir=os.path.join(SCRIPT_DIR, "pa-set"),
|
||||||
|
num_files=25,
|
||||||
|
)
|
||||||
|
print("PA set complete.")
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Create generate_nj_set.py**
|
||||||
|
|
||||||
|
Create `test-data/generate_nj_set.py`:
|
||||||
|
```python
|
||||||
|
"""Generate 25 NJ-market CSV files (Northern NJ / Metro NY)."""
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
from generate_supplier_set import generate_set
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_set(
|
||||||
|
state="NJ",
|
||||||
|
supplier="Gaboro DME",
|
||||||
|
patients=25,
|
||||||
|
challenge_level="high",
|
||||||
|
output_dir=os.path.join(SCRIPT_DIR, "nj-set"),
|
||||||
|
num_files=25,
|
||||||
|
)
|
||||||
|
print("NJ set complete.")
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Run both generators**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal/test-data
|
||||||
|
python generate_pa_set.py
|
||||||
|
python generate_nj_set.py
|
||||||
|
```
|
||||||
|
Expected: 25 files in `test-data/pa-set/` and 25 files in `test-data/nj-set/`.
|
||||||
|
|
||||||
|
Verify file count:
|
||||||
|
```bash
|
||||||
|
ls test-data/pa-set/ | wc -l && ls test-data/nj-set/ | wc -l
|
||||||
|
```
|
||||||
|
Expected: `25` on each line.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add test-data/generate_pa_set.py test-data/generate_nj_set.py test-data/pa-set/ test-data/nj-set/
|
||||||
|
git commit -m "feat: generate PA and NJ CSV test sets (50 files total)"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: Bug Hunter — run all 50 CSVs through normalizer
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create bug_hunter.py**
|
||||||
|
|
||||||
|
Create `test-data/bug_hunter.py`:
|
||||||
|
```python
|
||||||
|
"""
|
||||||
|
bug_hunter.py — Run all pa-set and nj-set CSVs through the Signal normalizer.
|
||||||
|
Reports: filename, error type, line, recommendation.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python test-data/bug_hunter.py > test-data/bug-report.txt
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
sys.path.insert(0, os.path.join(SCRIPT_DIR, "..", "python-backend"))
|
||||||
|
|
||||||
|
from api.normalizer import normalize_csv
|
||||||
|
|
||||||
|
SETS = [
|
||||||
|
os.path.join(SCRIPT_DIR, "pa-set"),
|
||||||
|
os.path.join(SCRIPT_DIR, "nj-set"),
|
||||||
|
]
|
||||||
|
|
||||||
|
RESULTS = {
|
||||||
|
"total_files": 0,
|
||||||
|
"total_records": 0,
|
||||||
|
"total_skipped": 0,
|
||||||
|
"files_with_errors": [],
|
||||||
|
"all_skip_reasons": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hunt(directory: str) -> None:
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
print(f"SKIP: {directory} does not exist")
|
||||||
|
return
|
||||||
|
|
||||||
|
files = sorted(f for f in os.listdir(directory) if f.endswith(".csv"))
|
||||||
|
for filename in files:
|
||||||
|
path = os.path.join(directory, filename)
|
||||||
|
RESULTS["total_files"] += 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = open(path, encoding="utf-8", errors="replace").read()
|
||||||
|
records, skipped, mapping_summary = normalize_csv(text)
|
||||||
|
RESULTS["total_records"] += len(records)
|
||||||
|
RESULTS["total_skipped"] += len(skipped)
|
||||||
|
|
||||||
|
required_missing = mapping_summary.get("required_missing", [])
|
||||||
|
file_issues = []
|
||||||
|
|
||||||
|
if required_missing:
|
||||||
|
file_issues.append({
|
||||||
|
"type": "REQUIRED_COLUMNS_MISSING",
|
||||||
|
"detail": f"Missing: {required_missing}",
|
||||||
|
"recommendation": "Update HEADER_MAP in normalizer.py to cover these column names",
|
||||||
|
})
|
||||||
|
|
||||||
|
for reason in skipped:
|
||||||
|
file_issues.append({
|
||||||
|
"type": "ROW_SKIPPED",
|
||||||
|
"detail": reason,
|
||||||
|
"recommendation": "Check date format, device name, or patient_id for this row",
|
||||||
|
})
|
||||||
|
RESULTS["all_skip_reasons"].append(f"{filename}: {reason}")
|
||||||
|
|
||||||
|
if file_issues:
|
||||||
|
RESULTS["files_with_errors"].append({
|
||||||
|
"file": filename,
|
||||||
|
"records_processed": len(records),
|
||||||
|
"issues": file_issues,
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RESULTS["files_with_errors"].append({
|
||||||
|
"file": filename,
|
||||||
|
"records_processed": 0,
|
||||||
|
"issues": [{"type": "EXCEPTION", "detail": str(e), "recommendation": "Fix the exception before pilot"}],
|
||||||
|
})
|
||||||
|
|
||||||
|
print(f"Processed directory: {directory} ({len(files)} files)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for d in SETS:
|
||||||
|
hunt(d)
|
||||||
|
|
||||||
|
print("\n=== BUG HUNTER REPORT ===")
|
||||||
|
print(f"Total files: {RESULTS['total_files']}")
|
||||||
|
print(f"Total records processed: {RESULTS['total_records']}")
|
||||||
|
print(f"Total rows skipped: {RESULTS['total_skipped']}")
|
||||||
|
print(f"Files with issues: {len(RESULTS['files_with_errors'])}")
|
||||||
|
|
||||||
|
if RESULTS["files_with_errors"]:
|
||||||
|
print("\n--- FILES WITH ISSUES ---")
|
||||||
|
for entry in RESULTS["files_with_errors"]:
|
||||||
|
print(f"\nFile: {entry['file']} ({entry['records_processed']} records OK)")
|
||||||
|
for issue in entry["issues"]:
|
||||||
|
print(f" [{issue['type']}] {issue['detail']}")
|
||||||
|
print(f" Fix: {issue['recommendation']}")
|
||||||
|
else:
|
||||||
|
print("\nAll files processed successfully. Zero issues found.")
|
||||||
|
|
||||||
|
print("\n=== END REPORT ===")
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Run Bug Hunter**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal && python test-data/bug_hunter.py 2>&1 | tee test-data/bug-report.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Fix issues found by Bug Hunter**
|
||||||
|
|
||||||
|
For each issue in the bug report:
|
||||||
|
- `REQUIRED_COLUMNS_MISSING`: add the missing column name variants to `HEADER_MAP` in `python-backend/api/normalizer.py`
|
||||||
|
- `ROW_SKIPPED` due to date format: add the date format to `DATE_FORMATS` list in normalizer.py
|
||||||
|
- `ROW_SKIPPED` due to device name: add the device alias to `DEVICE_MAP` in normalizer.py
|
||||||
|
- `EXCEPTION`: fix the root cause in the generator or normalizer
|
||||||
|
|
||||||
|
After fixes, re-run Bug Hunter until output shows "Zero issues found" or only expected skips (intentionally malformed rows).
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add test-data/bug_hunter.py test-data/bug-report.txt
|
||||||
|
git commit -m "feat: bug_hunter.py — 50-CSV normalizer stress test; report saved"
|
||||||
|
```
|
||||||
2086
docs/superpowers/plans/2026-06-07-signal-demo-mvp-build.md
Normal file
2086
docs/superpowers/plans/2026-06-07-signal-demo-mvp-build.md
Normal file
File diff suppressed because it is too large
Load diff
398
docs/superpowers/plans/2026-06-07-signal-legal-docs.md
Normal file
398
docs/superpowers/plans/2026-06-07-signal-legal-docs.md
Normal file
|
|
@ -0,0 +1,398 @@
|
||||||
|
# Signal Demo MVP — Legal Documents (LOI + NDA) Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Draft a Pilot LOI and mutual NDA for Gaboro DME using the Jakub multi-agent research pattern. Both documents are needed before the Robert Robinson call on June 10, 2026, and before sending to Nixon Law Group (Clyde) for 30-minute attorney review.
|
||||||
|
|
||||||
|
**Architecture (Jakub Pattern):** Research agents gather precedent and regulatory context. Drafting agents write each major section. A review agent checks consistency and flags missing provisions. This agent assembles the final output.
|
||||||
|
|
||||||
|
**Output location:** `signal/pitch/legal/`
|
||||||
|
- `signal/pitch/legal/gaboro-pilot-loi.md` — Pilot LOI
|
||||||
|
- `signal/pitch/legal/gaboro-nda.md` — Mutual NDA
|
||||||
|
- `signal/pitch/legal/legal-research-notes.md` — Research summary used during drafting
|
||||||
|
|
||||||
|
**Key terms (locked — do not deviate):**
|
||||||
|
- Pilot duration: 90 days, no charge
|
||||||
|
- At conversion: $199/month Worklist tier, locked for life as founding pilot partner
|
||||||
|
- Governing law: Pennsylvania
|
||||||
|
- NDA duration: 2 years
|
||||||
|
- Reference rights: Gaboro grants STTIL the right to name Gaboro as "an engaged pilot partner of Signal" in investor and marketing materials, subject to Gaboro's written approval of specific usage
|
||||||
|
- Data scope: de-identified only — patient_id, device_type, shipment_date, payer — NO PHI
|
||||||
|
- No patient outreach of any kind by Signal, now or ever
|
||||||
|
- STTIL gets: feedback rights, anonymized case study rights with Gaboro's written approval
|
||||||
|
|
||||||
|
**Parties:**
|
||||||
|
- STTIL Solutions LLC (Provider/Licensor) — Kisa Fenn, Managing Member
|
||||||
|
- Gaboro DME (Partner/Pilot Participant) — Robert Robinson, Co-Founder and Managing Partner
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: Legal Research
|
||||||
|
|
||||||
|
This task gathers precedents and context for drafting. No documents are written in this task.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Research standard DMEPOS/healthcare SaaS pilot agreement structures**
|
||||||
|
|
||||||
|
Search for: healthcare SaaS pilot agreement standard terms, DMEPOS supplier software pilot structure, healthcare technology LOI precedents. Focus on:
|
||||||
|
- Standard pilot duration and termination notice periods in healthcare tech
|
||||||
|
- Data use provisions for de-identified DMEPOS operational data
|
||||||
|
- Feedback and case study rights provisions
|
||||||
|
- Reference rights clauses in B2B healthcare software
|
||||||
|
|
||||||
|
Document findings in `signal/pitch/legal/legal-research-notes.md` under heading `## DMEPOS Pilot Agreement Research`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Research Pennsylvania governing law implications**
|
||||||
|
|
||||||
|
Search for: Pennsylvania commercial contract governing law, PA choice of law for software agreements, PA venue for B2B commercial disputes.
|
||||||
|
|
||||||
|
Key questions:
|
||||||
|
- What does PA governing law mean for venue selection?
|
||||||
|
- Are there any PA-specific provisions advisable for software licensing?
|
||||||
|
- Standard PA limitation of liability language for SaaS agreements?
|
||||||
|
|
||||||
|
Add to `legal-research-notes.md` under `## Pennsylvania Law Notes`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Research HIPAA implications for de-identified DMEPOS data**
|
||||||
|
|
||||||
|
Search for: HIPAA de-identification safe harbor, DMEPOS patient_id crosswalk PHI analysis, B2B DMEPOS software data use agreement vs BAA requirements.
|
||||||
|
|
||||||
|
Key questions:
|
||||||
|
- Does the data Signal processes (patient_id, device_type, shipment_date, payer) require a BAA, or is it sufficiently de-identified?
|
||||||
|
- Signal uses an internal MRN/account number as patient_id — is this PHI?
|
||||||
|
- What disclosures are appropriate in the LOI?
|
||||||
|
|
||||||
|
Add to `legal-research-notes.md` under `## HIPAA and Data Notes`.
|
||||||
|
|
||||||
|
Note for research: Signal's PHI architecture: STTIL never stores patient names, SSNs, DOBs, or contact info. The sole crosswalk key is patient_id (the supplier's internal MRN or account number). The identity crosswalk stays local with the supplier staff. This is a deliberate design for de-identification.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit research notes**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add pitch/legal/legal-research-notes.md
|
||||||
|
git commit -m "docs: legal research notes for Gaboro LOI and NDA drafting"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: Draft Pilot LOI
|
||||||
|
|
||||||
|
- [ ] **Step 1: Draft the full LOI**
|
||||||
|
|
||||||
|
Create `signal/pitch/legal/gaboro-pilot-loi.md` with this complete document:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# SIGNAL PILOT LETTER OF INTENT
|
||||||
|
|
||||||
|
**Date:** June __, 2026
|
||||||
|
**Between:**
|
||||||
|
- **STTIL Solutions LLC** ("Provider"), a limited liability company, Kisa Fenn, Managing Member
|
||||||
|
- **Gaboro DME** ("Partner"), Robert Robinson, Co-Founder and Managing Partner
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
This Letter of Intent ("LOI") sets forth the agreed terms for a 90-day pilot engagement of Signal, a documentation readiness tool developed and operated by STTIL Solutions LLC ("Signal"). This LOI is not a binding contract but reflects the shared intent of both parties to proceed on the terms described herein. A formal Pilot Agreement will be executed prior to the first data upload.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Scope of Pilot
|
||||||
|
|
||||||
|
During the pilot period, Partner will upload de-identified order management CSV exports to Signal. Signal will process the CSV data to produce a prioritized documentation worklist showing per-patient status across qualifying visit dates, standing written orders, prior authorization, PECOS enrollment, and supply cycle status. Partner staff will act on the worklist output. Signal identifies documentation conditions; Partner staff conduct all outreach and supply decisions.
|
||||||
|
|
||||||
|
Signal does not contact patients or prescribers directly at any time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Pilot Duration
|
||||||
|
|
||||||
|
The pilot period is **90 days** from the date of the first successful CSV upload by Partner ("Pilot Start Date"). The pilot may be extended by written mutual agreement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Pilot Fee
|
||||||
|
|
||||||
|
The pilot period is provided at **no charge**. At Partner's election to continue as a paying subscriber following the pilot, the monthly fee is **$199 per month** for the Signal Worklist tier. This rate is locked for the life of Partner's subscription as a founding pilot partner and will not be subject to standard price increases so long as the subscription remains active.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Data Handling
|
||||||
|
|
||||||
|
**De-identified data only.** Partner will upload CSV files containing: an internal patient identifier (the supplier's own MRN or account number), device type, shipment date, and payer name. Partner will not include patient names, Social Security numbers, dates of birth, addresses, or any other direct identifiers in uploaded files.
|
||||||
|
|
||||||
|
STTIL Solutions will not store patient names, SSNs, DOBs, addresses, or contact information at any time. The patient identity crosswalk (internal patient_id to real patient identity) remains solely with Partner's staff.
|
||||||
|
|
||||||
|
STTIL Solutions will store: hashed patient identifiers, device type, shipment date, payer name, calculated documentation status, and staff-confirmed visit dates (entered by Partner staff via the Signal interface).
|
||||||
|
|
||||||
|
Both parties acknowledge that Partner is responsible for ensuring that the data uploaded complies with applicable HIPAA de-identification requirements and Partner's own compliance obligations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Feedback and Case Study Rights
|
||||||
|
|
||||||
|
Partner agrees to provide good-faith feedback on Signal during the pilot period, including but not limited to: functionality, accuracy of documentation status calculations, worklist usability, and feature gaps.
|
||||||
|
|
||||||
|
STTIL Solutions may use anonymized, aggregated data from the pilot (with no identifying information about Partner's patients, staff, or business operations) for product improvement and investor reporting.
|
||||||
|
|
||||||
|
STTIL Solutions may prepare a case study describing the pilot outcomes. Any case study that identifies Partner by name requires Partner's prior written approval before publication.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Reference Rights
|
||||||
|
|
||||||
|
Partner grants STTIL Solutions the right to identify Partner as "an engaged pilot partner of Signal" in investor materials, marketing materials, and conversations with prospective partners and investors. STTIL Solutions will seek Partner's written approval for any specific usage of Partner's name or logo beyond this general reference right. This reference right may be revoked by Partner with 30 days written notice to STTIL Solutions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Termination
|
||||||
|
|
||||||
|
Either party may terminate the pilot engagement with **30 days written notice** delivered by email to the other party's designated contact. Upon termination or conclusion of the pilot:
|
||||||
|
- STTIL Solutions will delete all Partner data from Signal systems within **10 business days** of the effective termination date.
|
||||||
|
- STTIL Solutions will provide written confirmation of data deletion upon Partner's request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Intellectual Property
|
||||||
|
|
||||||
|
Signal, including its algorithms, software, and documentation readiness methodology, is and remains the sole intellectual property of STTIL Solutions LLC. Nothing in this LOI grants Partner any license to Signal's underlying technology beyond the right to use Signal as a software service during the pilot period.
|
||||||
|
|
||||||
|
Partner retains all rights to its operational data, patient records, and business information shared during the pilot.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Confidentiality
|
||||||
|
|
||||||
|
The parties agree to keep the terms of this LOI and all information shared during the pilot confidential. A mutual Non-Disclosure Agreement ("NDA") will be executed concurrently with this LOI and governs the confidentiality obligations of both parties in detail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. No Binding Commitment
|
||||||
|
|
||||||
|
This LOI is not a binding legal agreement and does not obligate either party to proceed with a commercial relationship. A formal Pilot Agreement reflecting the terms of this LOI will be prepared for signature prior to the Pilot Start Date.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Governing Law
|
||||||
|
|
||||||
|
This LOI and any formal agreements arising from it shall be governed by the laws of the **Commonwealth of Pennsylvania**, without regard to its conflict of law principles.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agreed in Principle
|
||||||
|
|
||||||
|
**STTIL Solutions LLC**
|
||||||
|
By: ___________________________
|
||||||
|
Kisa Fenn, Managing Member
|
||||||
|
Date: _________________________
|
||||||
|
|
||||||
|
**Gaboro DME**
|
||||||
|
By: ___________________________
|
||||||
|
Robert Robinson, Co-Founder and Managing Partner
|
||||||
|
Date: _________________________
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Self-review the LOI against spec**
|
||||||
|
|
||||||
|
Check each locked term:
|
||||||
|
- [ ] 90-day pilot, no charge ✓
|
||||||
|
- [ ] $199/month locked for life ✓
|
||||||
|
- [ ] Reference rights clause ✓
|
||||||
|
- [ ] Feedback rights ✓
|
||||||
|
- [ ] Anonymized case study rights with written approval ✓
|
||||||
|
- [ ] Termination: 30 days notice ✓
|
||||||
|
- [ ] Data deletion: 10 business days ✓
|
||||||
|
- [ ] PA governing law ✓
|
||||||
|
- [ ] No patient outreach language ✓
|
||||||
|
- [ ] Data scope: no PHI ✓
|
||||||
|
|
||||||
|
Fix any missing items.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add pitch/legal/gaboro-pilot-loi.md
|
||||||
|
git commit -m "docs: Gaboro pilot LOI draft — all locked terms included"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: Draft Mutual NDA
|
||||||
|
|
||||||
|
- [ ] **Step 1: Draft the full NDA**
|
||||||
|
|
||||||
|
Create `signal/pitch/legal/gaboro-nda.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# MUTUAL NON-DISCLOSURE AGREEMENT
|
||||||
|
|
||||||
|
**Effective Date:** June __, 2026
|
||||||
|
|
||||||
|
**Between:**
|
||||||
|
- **STTIL Solutions LLC** ("STTIL"), a limited liability company, with its principal place of business in [City, State]
|
||||||
|
- **Gaboro DME** ("Gaboro"), with its principal place of business in [City, State]
|
||||||
|
|
||||||
|
Each a "Party" and together the "Parties."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
The Parties intend to explore a pilot engagement involving Signal, a documentation readiness platform developed by STTIL ("Pilot"). In connection with the Pilot, each Party may disclose certain Confidential Information (as defined below) to the other Party. This Agreement governs the treatment of such information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Definition of Confidential Information
|
||||||
|
|
||||||
|
"Confidential Information" means any non-public information disclosed by one Party (the "Disclosing Party") to the other Party (the "Receiving Party") in connection with the Pilot, whether disclosed orally, in writing, electronically, or by any other means, and whether or not marked as confidential, including but not limited to:
|
||||||
|
|
||||||
|
- **STTIL Confidential Information:** Signal's software, algorithms, documentation readiness methodology, product roadmap, pricing structures, business plans, investor materials, and technical documentation.
|
||||||
|
- **Gaboro Confidential Information:** DMEPOS operational workflows, documentation processes, patient census (in de-identified form), payer mix, staff processes, business operations, financial information, and supplier relationships.
|
||||||
|
- **Shared:** The existence and terms of the Pilot engagement, this Agreement, and any discussions between the Parties.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Obligations of Receiving Party
|
||||||
|
|
||||||
|
Each Receiving Party agrees to:
|
||||||
|
|
||||||
|
(a) Hold all Confidential Information in strict confidence and not disclose it to any third party without the prior written consent of the Disclosing Party;
|
||||||
|
|
||||||
|
(b) Use Confidential Information solely for the purpose of evaluating and conducting the Pilot;
|
||||||
|
|
||||||
|
(c) Limit access to Confidential Information to employees, contractors, and agents who have a need to know and are bound by confidentiality obligations no less protective than those in this Agreement;
|
||||||
|
|
||||||
|
(d) Promptly notify the Disclosing Party of any actual or suspected unauthorized disclosure of Confidential Information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Exclusions
|
||||||
|
|
||||||
|
The obligations in Section 3 do not apply to information that:
|
||||||
|
|
||||||
|
(a) Is or becomes publicly available through no breach of this Agreement by the Receiving Party;
|
||||||
|
|
||||||
|
(b) Was rightfully known to the Receiving Party without restriction prior to disclosure by the Disclosing Party;
|
||||||
|
|
||||||
|
(c) Is rightfully received from a third party without restriction;
|
||||||
|
|
||||||
|
(d) Is independently developed by the Receiving Party without reference to the Confidential Information; or
|
||||||
|
|
||||||
|
(e) Is required to be disclosed by law, regulation, or court order, provided the Receiving Party gives the Disclosing Party prompt prior written notice and reasonable assistance in seeking a protective order.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. DMEPOS-Specific Provision
|
||||||
|
|
||||||
|
The Parties acknowledge that DMEPOS operational workflows, documentation processes, and payer-specific billing practices shared during the Pilot are highly sensitive and proprietary to Gaboro's business operations. STTIL agrees that any such information shared by Gaboro during the Pilot:
|
||||||
|
|
||||||
|
(a) Will be used solely to configure and refine Signal for Gaboro's use during the Pilot;
|
||||||
|
|
||||||
|
(b) Will not be disclosed to any other DMEPOS supplier or competitor of Gaboro; and
|
||||||
|
|
||||||
|
(c) Will not be incorporated into Signal's publicly available documentation, marketing materials, or general feature set in a way that would reveal Gaboro's proprietary operational approach.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Return or Destruction of Confidential Information
|
||||||
|
|
||||||
|
Upon the written request of the Disclosing Party, or upon termination of the Pilot, the Receiving Party shall promptly return or destroy all tangible materials containing Confidential Information and certify in writing that it has done so. One copy may be retained solely for legal compliance purposes, subject to ongoing confidentiality obligations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. No License
|
||||||
|
|
||||||
|
Nothing in this Agreement grants either Party any license, right, or interest in the other Party's Confidential Information, intellectual property, trademarks, or technology beyond the limited right to evaluate and conduct the Pilot.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Term
|
||||||
|
|
||||||
|
This Agreement is effective as of the Effective Date and continues for **2 years**, unless earlier terminated by mutual written agreement. Obligations with respect to Confidential Information disclosed during the term survive termination for an additional 2 years.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Remedies
|
||||||
|
|
||||||
|
Each Party acknowledges that a breach of this Agreement would cause irreparable harm for which monetary damages would be an inadequate remedy. Each Party agrees that the other Party shall be entitled to seek equitable relief, including injunction and specific performance, without the requirement of posting bond or other security, in addition to all other remedies available at law or equity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Governing Law and Venue
|
||||||
|
|
||||||
|
This Agreement shall be governed by and construed in accordance with the laws of the **Commonwealth of Pennsylvania**, without regard to its conflict of law principles. The Parties consent to the exclusive jurisdiction of the state and federal courts located in Pennsylvania for any dispute arising under this Agreement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Entire Agreement
|
||||||
|
|
||||||
|
This Agreement constitutes the entire agreement of the Parties with respect to the subject matter hereof and supersedes all prior discussions, representations, and agreements. This Agreement may not be amended except by a written instrument signed by both Parties.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Signatures
|
||||||
|
|
||||||
|
**STTIL Solutions LLC**
|
||||||
|
By: ___________________________
|
||||||
|
Name: Kisa Fenn
|
||||||
|
Title: Managing Member
|
||||||
|
Date: _________________________
|
||||||
|
Email: kisasttil@gmail.com
|
||||||
|
|
||||||
|
**Gaboro DME**
|
||||||
|
By: ___________________________
|
||||||
|
Name: Robert Robinson
|
||||||
|
Title: Co-Founder and Managing Partner
|
||||||
|
Date: _________________________
|
||||||
|
Email: ___________________________
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Self-review the NDA against spec**
|
||||||
|
|
||||||
|
Check each locked term:
|
||||||
|
- [ ] Mutual (both parties' confidential info covered) ✓
|
||||||
|
- [ ] DMEPOS-specific clause ✓
|
||||||
|
- [ ] Standard carve-outs (publicly available, independently developed, third party) ✓
|
||||||
|
- [ ] 2-year duration ✓
|
||||||
|
- [ ] PA governing law ✓
|
||||||
|
- [ ] Data and operational workflow protection ✓
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add pitch/legal/gaboro-nda.md
|
||||||
|
git commit -m "docs: mutual NDA draft for Gaboro pilot — DMEPOS-specific provision included"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: Consistency review
|
||||||
|
|
||||||
|
- [ ] **Step 1: Cross-check LOI and NDA for consistency**
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
- Effective date placeholder matches in both documents
|
||||||
|
- Party names are spelled identically in both documents
|
||||||
|
- Termination/data deletion terms in LOI align with return/destruction in NDA
|
||||||
|
- No conflicting provisions between LOI and NDA
|
||||||
|
|
||||||
|
Fix any inconsistencies found.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Final commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/sttil-solutions/projects/signal
|
||||||
|
git add pitch/legal/
|
||||||
|
git commit -m "docs: LOI + NDA finalized and cross-checked for consistency"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delivery
|
||||||
|
|
||||||
|
After both documents are committed, report back with:
|
||||||
|
1. File paths of the two completed documents
|
||||||
|
2. Any provisions that required judgment calls (flag for Kisa's review before sending to Nixon Law Group)
|
||||||
|
3. Any gaps identified that attorney review should specifically address
|
||||||
367
docs/superpowers/specs/2026-06-07-signal-demo-mvp-design.md
Normal file
367
docs/superpowers/specs/2026-06-07-signal-demo-mvp-design.md
Normal file
|
|
@ -0,0 +1,367 @@
|
||||||
|
# Signal Demo MVP — Design Spec
|
||||||
|
**Date:** 2026-06-07
|
||||||
|
**Goal:** Demo-ready Signal for Robert Robinson (Gaboro) on Tuesday June 10, 2026
|
||||||
|
**Deadline:** Monday June 9 EOD (one day buffer before meeting)
|
||||||
|
**Approved by:** Kisa Fenn
|
||||||
|
**Build session:** Next session — dangerously-skip-permissions, Max plan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What We Are Building
|
||||||
|
|
||||||
|
A polished, demo-ready Signal that tells a compelling story to Robert Robinson on Tuesday. He has never seen Signal. His biggest documented pain is the 6-month qualifying visit requirement. The demo runs on synthetic PA/NJ patient data, shows the full documentation state machine, includes one live Confirm Visit interaction, and leaves Robert with a pilot LOI and NDA to review.
|
||||||
|
|
||||||
|
This is Option 2 (Polish + Confirm Visit Workflow). Not building: column mapping review UI, Brightree/NikoHealth API, or any Phase 2/3 tabled items.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 1: Visit Date Workflow and Priority System
|
||||||
|
|
||||||
|
### Visit Date Priority Chain
|
||||||
|
|
||||||
|
Signal determines a patient's qualifying visit date in this order:
|
||||||
|
|
||||||
|
1. A column explicitly labeled as a visit or encounter date in the current CSV import
|
||||||
|
2. A previously confirmed visit date stored in Supabase for that patient_id (entered by staff via Confirm Visit in a prior session)
|
||||||
|
3. Estimated proxy: shipment_date minus 30 days, labeled clearly as "Estimated — verify with prescriber office"
|
||||||
|
|
||||||
|
Priority 1 and 2 display as "Confirmed." Priority 3 displays as "Estimated" in amber with verification note.
|
||||||
|
|
||||||
|
### Validation Rule
|
||||||
|
|
||||||
|
A qualifying visit date is valid only if it falls within 6 months before the order/shipment date for the current supply cycle. A date older than 6 months from the order date is flagged as expired and the patient moves to Visit Required status.
|
||||||
|
|
||||||
|
### Proactive Priority System: 90/60/45 Days
|
||||||
|
|
||||||
|
Signal calculates `Next_Visit_Due = Last_Confirmed_Visit_Date + 6 months` for every patient. Days remaining determines priority tier:
|
||||||
|
|
||||||
|
| Days to Next Visit | Status Label | Visual |
|
||||||
|
|---|---|---|
|
||||||
|
| > 90 days | Active | Green, normal weight |
|
||||||
|
| 90 days | Renewal Due SOON | Amber, normal weight |
|
||||||
|
| 60 days | Renewal Due SOON | Amber, **bold** |
|
||||||
|
| 45 days or less | Renewal Due — Critical | **Red, bold** + supplier outreach flag |
|
||||||
|
| Past due, no new visit | Visit Required | **Red, bold** + warning icon |
|
||||||
|
| Past due, supplies run out | Supply Lapsed | Dark red, bold + alert banner, top of list |
|
||||||
|
|
||||||
|
At 45 days: Signal adds a staff action flag — "Contact prescriber office — confirm patient appointment is scheduled for qualifying visit." Signal is never responsible for patient outreach, now or in any future phase. The prescriber is the right contact because they have a fax number already on file from the SWO, they control the appointment calendar, and they are the authoritative source for scheduling the qualifying visit. Phase 2 may include a Signal-facilitated fax to the prescriber's office with a pre-filled renewal reminder. Patient contact remains the supplier's responsibility outside of Signal.
|
||||||
|
|
||||||
|
Worklist sorts by urgency: Supply Lapsed → Visit Required → Renewal Due Critical → Renewal Due Elevated (60d) → Renewal Due SOON (90d) → Active.
|
||||||
|
|
||||||
|
### Confirm Visit Interaction
|
||||||
|
|
||||||
|
Each worklist row shows a "Confirm Visit" button when visit status is Estimated or Not Confirmed.
|
||||||
|
|
||||||
|
Modal contains:
|
||||||
|
- Label: "Qualifying visit date — confirmed with prescriber office or from SWO on file"
|
||||||
|
- One date input field
|
||||||
|
- Validation: must be within 6 months of order date, cannot be future date
|
||||||
|
- Helper text: "Contact prescriber office to confirm. Do not use patient-reported date."
|
||||||
|
|
||||||
|
On save: Signal stores confirmed date in Supabase against patient_id, immediately recalculates Next_Visit_Due, updates row status and priority tier live (no page refresh). Confirmed date persists across all future imports for that patient_id.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 2: Full Documentation State Machine
|
||||||
|
|
||||||
|
Each patient tracks five documentation items. Requirements vary by payer type.
|
||||||
|
|
||||||
|
### Documentation Items
|
||||||
|
|
||||||
|
- **SWO (Standing Written Order):** On File / Pending / Expired
|
||||||
|
- **Qualifying Visit:** Confirmed [date] / Estimated [date] / Not Confirmed
|
||||||
|
- **PECOS Enrollment:** Verified / Not Verified (Medicare patients only)
|
||||||
|
- **Prior Authorization:** Not Required / Approved [exp date] / Pending / Denied / Required — Not Started
|
||||||
|
- **Diagnosis:** On File / Missing
|
||||||
|
|
||||||
|
### Payer-Dependent Requirements
|
||||||
|
|
||||||
|
**Traditional Medicare (Part B FFS):** SWO, Qualifying Visit, PECOS, Diagnosis. PA marked "Not Required" (CGM coverage expansion 2023; PA exemption via CMS-1828-F for ≥90% affirmation rate suppliers).
|
||||||
|
|
||||||
|
**Medicare Advantage:** SWO, Qualifying Visit, PECOS, Diagnosis, PA — all required. PA varies by plan per payer_rules.json.
|
||||||
|
|
||||||
|
**Medicaid (PA and NJ):** SWO, Qualifying Visit, Diagnosis, PA. PECOS not required. State-specific PA rules via payer_rules.json.
|
||||||
|
|
||||||
|
**Commercial:** SWO, Qualifying Visit, Diagnosis, PA (plan-dependent). PECOS not required.
|
||||||
|
|
||||||
|
### Cascade Display
|
||||||
|
|
||||||
|
When a documentation item is missing, Signal shows the downstream consequence inline on the expanded patient row:
|
||||||
|
|
||||||
|
> "Qualifying visit not confirmed → SWO validity at risk → PA renewal cannot proceed → next shipment blocked"
|
||||||
|
|
||||||
|
This cascade is visible on the expanded row, not buried in a detail screen.
|
||||||
|
|
||||||
|
### Worklist Row UI
|
||||||
|
|
||||||
|
Compact documentation status bar (5 dots, one per item, colored by status). Expanding a row shows the full checklist with labels and recommended actions for each missing or at-risk item.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 3: Cycle Reset via CSV Re-Import (No Manual Entry)
|
||||||
|
|
||||||
|
### Design Principle
|
||||||
|
|
||||||
|
Gaboro already exports from Brightree on a regular cadence. Signal leverages that existing workflow. No staff entry required to update supply cycle status.
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
When Gaboro uploads a new CSV, Signal compares it to the previous import for each patient_id:
|
||||||
|
|
||||||
|
- New shipment date detected → Signal automatically advances the patient's supply cycle clock
|
||||||
|
- New visit date column detected with a valid date → Signal updates confirmed visit date
|
||||||
|
- Patient appears with updated payer → Signal rechecks PA requirements per payer_rules.json
|
||||||
|
- Patient disappears from import → Signal flags for review (discharged? transferred?)
|
||||||
|
|
||||||
|
Signal maintains a per-patient import history in Supabase (timestamp, shipment date, detected documentation status). Each import creates a snapshot record for diff comparison.
|
||||||
|
|
||||||
|
### Phase 2 Path
|
||||||
|
|
||||||
|
Brightree/NikoHealth API integration makes this real-time. Signal's data model is designed for this from day one — the import-based diff logic is identical to what the API would trigger.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 4: Transferred Patients
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
|
||||||
|
When a patient transfers from another DMEPOS supplier to Gaboro, Gaboro has no history: no SWO, no qualifying visit date, no PA history. The patient may be established on CGM (active user, mid-cycle) or new to CGM entirely.
|
||||||
|
|
||||||
|
### Signal Handling
|
||||||
|
|
||||||
|
Signal detects a transferred patient when:
|
||||||
|
- A patient_id appears for the first time with a shipment date that predates their first import (gap in history)
|
||||||
|
- A column labeled "Transfer_From", "Previous Supplier", "Transfer Status", or similar is present
|
||||||
|
- A patient_id appears with a very old shipment date but no prior import history
|
||||||
|
|
||||||
|
Status assigned immediately: **"Needs Verification — Transfer Patient"**
|
||||||
|
|
||||||
|
Worklist displays: "Patient transferred from prior supplier. Verify: SWO from current prescriber, qualifying visit within 6 months, PA status with current payer."
|
||||||
|
|
||||||
|
All five documentation items default to "Pending — Verify" for transferred patients. Staff must confirm each item before Signal clears the patient to Active.
|
||||||
|
|
||||||
|
### CSV Accommodation
|
||||||
|
|
||||||
|
The challenge CSV sets include transferred patient scenarios: patients with Transfer_From columns, patients with history gaps, and patients who are new to CGM vs. established users mid-cycle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 5: CSV Test Suite
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
Two new sets of 25 CSV files each (50 total), plus the existing 25-file set remains as baseline. Both new sets are stress-test level with state-change patients, transferred patients, and all challenge types.
|
||||||
|
|
||||||
|
Existing generator (generate_samples.py) is the reference framework. Two new generators extend it: generate_pa_set.py and generate_nj_set.py.
|
||||||
|
|
||||||
|
### Automated CSV Generator — Parameterized for Future Suppliers
|
||||||
|
|
||||||
|
The generator is rebuilt as a reusable system: `generate_supplier_set.py --state TX --supplier "Austin DME" --patients 30 --challenge-level high`
|
||||||
|
|
||||||
|
This accepts: state abbreviation, supplier name, patient count, challenge level (low/medium/high). It automatically loads the correct payer strings, geographic patient data, and prescriber affiliations for that state from a `market_data.json` config file. A new pilot in Texas, Florida, or Ohio requires no code changes — just add the state's market data to the config and run the generator.
|
||||||
|
|
||||||
|
This is the CSV Agent's primary output: a reusable generator system, not just a batch of files.
|
||||||
|
|
||||||
|
### PA Set (25 Files) — Philadelphia/Southeastern PA Market
|
||||||
|
|
||||||
|
Payer strings: "Medicare Part B", "Medicare", "PA Medicaid", "Keystone First", "UPMC Health Plan", "Highmark BCBS", "Aetna PA", "Independence Blue Cross", "Geisinger", "AmeriHealth Caritas PA"
|
||||||
|
|
||||||
|
Patient geography: Philadelphia, Bucks County, Montgomery County, Chester County
|
||||||
|
Prescribers: Penn Medicine, Jefferson Health, Temple Health
|
||||||
|
Supplier fields: "Gaboro DME", "Branch: PA-001", "State: PA"
|
||||||
|
|
||||||
|
### NJ Set (25 Files) — Northern NJ / Metro NY Area
|
||||||
|
|
||||||
|
Payer strings: "Medicare Part B", "Medicare", "NJ FamilyCare", "Horizon BCBS NJ", "Aetna NJ", "Cigna NJ", "AmeriHealth NJ", "WellCare NJ", "UnitedHealthcare NJ", "Oxford Health"
|
||||||
|
|
||||||
|
Patient geography: Bergen County, Essex County, Hudson County, Passaic County
|
||||||
|
Prescribers: Hackensack Meridian, RWJBarnabas, Atlantic Health
|
||||||
|
Supplier fields: "Gaboro DME", "Branch: NJ-002", "State: NJ"
|
||||||
|
|
||||||
|
### State-Change Patients (Both Sets)
|
||||||
|
|
||||||
|
6 patients per set whose status evolves across the file sequence, proving Signal's cycle reset and priority recalculation work across multiple imports:
|
||||||
|
|
||||||
|
- Starts as "Renewal Due SOON" (no visit date) → progresses to Active after Confirm Visit in file 10 → new cycle visible by file 20
|
||||||
|
- Starts as "Refill Window" → advances to Active after new shipment detected in import diff
|
||||||
|
- Starts as Active → slides into Renewal Due SOON as dates age
|
||||||
|
- Transferred patient: appears mid-sequence, starts as "Needs Verification", clears to Active after staff confirms documentation
|
||||||
|
|
||||||
|
### Challenge Types (Both Sets)
|
||||||
|
|
||||||
|
- All 25 header variants from existing generator, adapted to PA/NJ market
|
||||||
|
- Date format variations: MM/DD/YYYY, YYYY-MM-DD, M/D/YY, "Jan 15 2026", "15-Jan-2026", YYYYMMDD, ISO datetime with time
|
||||||
|
- Extra noise columns: notes, billing codes, staff initials, auth numbers
|
||||||
|
- Missing optional columns: some files omit component, some omit quantity
|
||||||
|
- Null/empty values in 2-3 required columns (3-4 files)
|
||||||
|
- Future shipment dates (2-3 files)
|
||||||
|
- Malformed dates: "not on file", "pending", "TBD" in date columns (2 files)
|
||||||
|
- Visit date column present but empty (2 files — should fall back to estimate)
|
||||||
|
- PA status columns with varying labels: "auth_status", "PA Status", "Prior Auth", "PA"
|
||||||
|
- Transfer patient columns: "Transfer_From", "Previous Supplier", "Transfer Status"
|
||||||
|
- Rows where quantity is zero or negative (edge case)
|
||||||
|
- Duplicate patient_id within same file (should be caught and flagged)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 6: Watcher Agent
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
|
||||||
|
Signal applies rules from payer_rules.json. When CMS, a MAC, or a payer changes CGM coverage requirements, Signal continues applying the old rules silently. Suppliers find out after the fact — claims denied, compliance gaps, Synapse Health-style surprises.
|
||||||
|
|
||||||
|
### Design
|
||||||
|
|
||||||
|
A background monitoring agent runs on a defined cadence (daily or weekly). It monitors:
|
||||||
|
|
||||||
|
- CMS.gov for CGM-related coverage policy changes
|
||||||
|
- MAC websites (Noridian, CGS, Palmetto GBA, etc.) for local coverage determination updates
|
||||||
|
- Synapse Health announcements for mandatory enrollment changes
|
||||||
|
- Key payer websites for PA requirement changes affecting CGM
|
||||||
|
- Federal Register for DMEPOS-related proposed and final rules
|
||||||
|
|
||||||
|
When a potentially relevant change is detected:
|
||||||
|
|
||||||
|
1. Watcher Agent creates a "Rule Change Alert" record
|
||||||
|
2. Kisa receives an alert (via Telegram/NavBot and email) with: what changed, source, date, which patients in Signal are affected
|
||||||
|
3. Kisa reviews and approves or dismisses
|
||||||
|
4. On approval: payer_rules.json is updated, Signal recalculates affected patients' statuses, worklist reflects new requirements
|
||||||
|
5. Suppliers see updated worklist with a banner: "Documentation requirements updated [date] — [brief description]"
|
||||||
|
|
||||||
|
This prevents the silent failure mode. Suppliers are never running on stale rules without knowing it.
|
||||||
|
|
||||||
|
### MVP Scope
|
||||||
|
|
||||||
|
For the demo and pilot, the Watcher Agent is mentioned as a capability ("Signal monitors for rule changes so your team is never caught off guard") but is not fully deployed. It is built and running in background mode, alerting Kisa only. Supplier-facing alerts are Phase 2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 7: Demo Flow (15 Minutes, Kisa Drives)
|
||||||
|
|
||||||
|
**Beat 1 — Name the Problem (2 min, verbal):**
|
||||||
|
"Right now your team works backwards. A claim comes back denied. Someone digs into the chart. The 6-month qualifying visit was never confirmed before the order went out. By then you can only appeal it. Signal catches it before the claim goes out."
|
||||||
|
|
||||||
|
**Beat 2 — Show the Worklist (5 min):**
|
||||||
|
Upload CSV. 30-40 patients load. Worklist appears sorted by urgency. Walk to a Renewal Due Critical patient (red, bold). Expand row. Show cascade: "Qualifying visit not confirmed → SWO validity at risk → next shipment blocked." Say: "This is what flummoxes your organization. Signal shows it, assigns the priority, and tells your staff what to do."
|
||||||
|
|
||||||
|
Click Confirm Visit. Enter a prescriber-confirmed date. Status updates live to Active. "That date is in Signal permanently."
|
||||||
|
|
||||||
|
**Beat 3 — Show Range and Utility (5 min):**
|
||||||
|
Show Supply Lapsed patient at top — full documentation cascade, most urgent. Show Active patient — all five items green, Next Visit Due in 4 months. "Your team spends zero time on this one." Export worklist. "This is what your team opens Monday morning."
|
||||||
|
|
||||||
|
**Close (3 min):**
|
||||||
|
"We are piloting with three organizations. I want Gaboro to be one of them. I have a pilot agreement for you — no charge for 90 days, and as a founding partner your rate is locked at $199/month for life."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 8: Legal Documents
|
||||||
|
|
||||||
|
### Pilot LOI (STTIL Solutions to Gaboro DME)
|
||||||
|
|
||||||
|
Scope: Gaboro uploads de-identified CSV data; Signal produces a prioritized documentation worklist; Gaboro staff acts on it.
|
||||||
|
|
||||||
|
Key terms:
|
||||||
|
- 90-day pilot, no charge
|
||||||
|
- At conversion: $199/month Worklist tier, locked for life as founding pilot partner
|
||||||
|
- STTIL gets: feedback rights, anonymized case study rights with Gaboro's written approval
|
||||||
|
- **Reference rights:** Gaboro grants STTIL the right to name Gaboro as "an engaged pilot partner of Signal" in investor and marketing materials, subject to Gaboro's approval of specific usage
|
||||||
|
- Termination: 30 days written notice, data deletion within 10 business days with written confirmation
|
||||||
|
- Governing law: Pennsylvania
|
||||||
|
- Data: de-identified only during pilot (patient_id, device_type, shipment_date, payer — no PHI)
|
||||||
|
|
||||||
|
### NDA (Mutual)
|
||||||
|
|
||||||
|
Covers: both parties' confidential information including patient workflow descriptions, pricing structures, product roadmap, supplier operational data.
|
||||||
|
|
||||||
|
Standard carve-outs: publicly available information, independently developed information, information received from a third party without restriction.
|
||||||
|
|
||||||
|
DMEPOS-specific clause: DMEPOS operational workflows and documentation processes shared during the pilot are proprietary and covered under this agreement.
|
||||||
|
|
||||||
|
Duration: 2 years. Governing law: Pennsylvania.
|
||||||
|
|
||||||
|
### Drafting Pattern (Jakub Method)
|
||||||
|
|
||||||
|
Legal Lead Agent manages 10-15 sub-agents: research agents (DMEPOS pilot agreement standards, PA governing law, healthcare SaaS LOI precedents), drafting agents (one per major section), review agent (consistency, missing provisions, ambiguous language). Lead agent assembles final drafts.
|
||||||
|
|
||||||
|
Output: markdown files to signal/pitch/legal/. Both documents to Nixon Law Group (Clyde) for 30-minute attorney review before Tuesday.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 9: Compliance Items (First Supplier Gate)
|
||||||
|
|
||||||
|
Run in parallel with the build. Must be complete before Robert uploads any data:
|
||||||
|
|
||||||
|
- Privacy Policy published at stable public URL
|
||||||
|
- Incident response procedure written (includes breach notification timeline)
|
||||||
|
- Data handling / minimum necessary fields documentation written
|
||||||
|
- Audit log WORM enforcement (RLS policy blocking UPDATE/DELETE on audit_log)
|
||||||
|
- MFA confirmed on all admin accounts (Railway, Supabase, Vercel, Clerk)
|
||||||
|
- payer_rules.json quarterly update cadence documented
|
||||||
|
- Supabase plan tier confirmed and PHI posture documented
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 10: Agent Team
|
||||||
|
|
||||||
|
**Build Agent**
|
||||||
|
Implements: Confirm Visit workflow (button, modal, Supabase persistence, live recalculation), visit date priority chain in coverage_calculator.py, full documentation state machine (5 items, payer-dependent), CSV diff logic for cycle reset, 90/60/45 priority tier system with "Renewal Due SOON" labeling, Amber/Amber-Bold/Red-Bold visual system, 45-day supplier outreach flag, transferred patient detection and "Needs Verification" status, existing client cold-start logic (estimated proxy labeled), bug fixes, placeholder removal, smoke tests, end-to-end export verification.
|
||||||
|
|
||||||
|
**CSV Generator Agent**
|
||||||
|
Builds `generate_supplier_set.py` (parameterized reusable system), `market_data.json` (state-to-payer-names config), `generate_pa_set.py`, `generate_nj_set.py`. Outputs 50 files to test-data/pa-set/ and test-data/nj-set/. Includes state-change patients, transferred patients, all challenge types.
|
||||||
|
|
||||||
|
**Bug Hunter Agent**
|
||||||
|
Runs all 50 CSVs through Signal backend normalizer and coverage calculator. Returns structured failure report: filename, error type, line number, recommended fix. Feeds to Build Agent for resolution.
|
||||||
|
|
||||||
|
**Legal Lead Agent (Jakub pattern)**
|
||||||
|
10-15 sub-agents research and draft LOI and NDA. Output to signal/pitch/legal/.
|
||||||
|
|
||||||
|
**Compliance Docs Agent**
|
||||||
|
Privacy Policy, incident response procedure, data handling documentation. Output to signal/docs/compliance/.
|
||||||
|
|
||||||
|
**Watcher Agent**
|
||||||
|
Monitors CMS, MAC websites, Synapse Health, key payer sites for CGM rule changes. Alerts Kisa via NavBot/email. payer_rules.json updates require Kisa approval before taking effect.
|
||||||
|
|
||||||
|
**NavBot (@STTILness_bot)**
|
||||||
|
Domain research support via Telegram during the build. Priority today: PA/NJ payer name variants for CSV accuracy, CGM qualifying visit regulatory language for legal docs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 11: Pilot Prep Questions for Robert (Added to /pilot-prep)
|
||||||
|
|
||||||
|
1. When supplies ship, how does your team know — auto-recorded in Brightree or Tenovi, or manual?
|
||||||
|
2. How often does your team export from Brightree and what triggers it?
|
||||||
|
3. Does your CSV export include a ship date column and what is it labeled?
|
||||||
|
4. What percentage of your CGM patients are Medicare Advantage vs. traditional Medicare vs. Medicaid?
|
||||||
|
5. Do you track PA status per patient in Brightree and is it in your standard CSV export?
|
||||||
|
6. Is SWO tracking in Brightree or a separate system?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Section 12: What We Are Not Building This Cycle
|
||||||
|
|
||||||
|
- Patient outreach of any kind — Signal never contacts patients, now or in the future. Prescriber outreach (Phase 2 fax facilitation) is the only direct communication Signal will support.
|
||||||
|
- Column mapping review UI (verbal in demo)
|
||||||
|
- Brightree/NikoHealth API (Phase 2)
|
||||||
|
- Patient-facing features
|
||||||
|
- Real PHI handling
|
||||||
|
- Token generation service (Med Tracker prerequisite — Phase 3)
|
||||||
|
- Doc upload and AI validation (explicitly deferred)
|
||||||
|
- Consortium features
|
||||||
|
- Any item in the Phase 2/3 tabled list in Signal CLAUDE.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Target Pilot Readiness After This Build
|
||||||
|
|
||||||
|
- **Pilot Readiness:** 13/13 PASS (up from 4/13 = 31%)
|
||||||
|
- **Compliance Gate (Pilot):** 14/15 PASS
|
||||||
|
- **Compliance Gate (First Supplier):** 8/15 PASS (Privacy Policy, LOI, NDA, data handling, incident response addressed this build)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spec Self-Review
|
||||||
|
|
||||||
|
**Placeholders:** None. All sections complete.
|
||||||
|
**Consistency:** Documentation state machine matches Confirm Visit workflow. CSV state-change patients match cycle reset logic. LOI $199 lifetime rate matches demo close language. Transferred patient detection matches CSV challenge types.
|
||||||
|
**Scope:** Appropriate for a single implementation plan. Legal, compliance, CSV generation, and core build are parallel workstreams with no sequential blocking dependencies.
|
||||||
|
**Ambiguity resolved:** 45-day trigger = supplier outreach flag only, not automated patient text. Cycle reset = CSV re-import diff, not manual button. Visit confirmation = prescriber-sourced only, not patient-reported. All stated explicitly.
|
||||||
195
pitch/legal/gaboro-baa.md
Normal file
195
pitch/legal/gaboro-baa.md
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
# BUSINESS ASSOCIATE AGREEMENT
|
||||||
|
|
||||||
|
**Effective Date:** June __, 2026
|
||||||
|
|
||||||
|
**Between:**
|
||||||
|
- **STTIL Solutions LLC** ("Business Associate"), a limited liability company, Kisa Fenn, Managing Member
|
||||||
|
- **Gaboro DME** ("Covered Entity"), Robert Robinson, Co-Founder and Managing Partner
|
||||||
|
|
||||||
|
Each a "Party" and together the "Parties."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recitals
|
||||||
|
|
||||||
|
Covered Entity is a durable medical equipment supplier subject to the Health Insurance Portability and Accountability Act of 1996 and its implementing regulations ("HIPAA"). Business Associate provides Signal, a documentation readiness platform, and in connection with that service may receive, create, maintain, or transmit data on behalf of Covered Entity that may constitute Protected Health Information ("PHI") as defined under HIPAA. The Parties enter into this Agreement to satisfy the Business Associate Agreement requirements of 45 C.F.R. Part 164, Subpart C.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Definitions
|
||||||
|
|
||||||
|
Terms used in this Agreement have the meanings assigned under HIPAA and its implementing regulations, including the HITECH Act amendments, as of the Effective Date.
|
||||||
|
|
||||||
|
**"Protected Health Information" or "PHI"** means individually identifiable health information created, received, maintained, or transmitted by Business Associate on behalf of Covered Entity, in any form or medium.
|
||||||
|
|
||||||
|
**"Minimum Necessary"** means the least amount of PHI required to accomplish the intended purpose of a given use, disclosure, or request.
|
||||||
|
|
||||||
|
**"Security Incident"** means the attempted or successful unauthorized access, use, disclosure, modification, or destruction of information or interference with system operations in an information system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Obligations of Business Associate
|
||||||
|
|
||||||
|
### 2.1 Permitted Uses and Disclosures
|
||||||
|
|
||||||
|
Business Associate may use or disclose PHI only:
|
||||||
|
|
||||||
|
(a) To perform the services described in the Pilot Letter of Intent between the Parties dated June __, 2026 ("LOI"), specifically: receiving de-identified order management data uploaded by Covered Entity, calculating documentation readiness status per patient, and returning a prioritized worklist to Covered Entity staff;
|
||||||
|
|
||||||
|
(b) As required by law; or
|
||||||
|
|
||||||
|
(c) For the proper management and administration of Business Associate's own operations, provided that any such disclosure is required by law or Business Associate obtains reasonable assurances that the information will be held in confidence and used or further disclosed only as required by law or for the purpose for which it was disclosed.
|
||||||
|
|
||||||
|
Business Associate will not use or disclose PHI in any manner that would violate HIPAA if done by Covered Entity directly.
|
||||||
|
|
||||||
|
### 2.2 Minimum Necessary
|
||||||
|
|
||||||
|
Business Associate will make reasonable efforts to use, disclose, and request only the minimum necessary PHI to accomplish the intended purpose.
|
||||||
|
|
||||||
|
### 2.3 Safeguards
|
||||||
|
|
||||||
|
Business Associate will implement and maintain appropriate administrative, physical, and technical safeguards to protect the confidentiality, integrity, and availability of PHI it creates, receives, maintains, or transmits on behalf of Covered Entity, in accordance with 45 C.F.R. Part 164, Subpart C (Security Rule).
|
||||||
|
|
||||||
|
Business Associate's current safeguards include:
|
||||||
|
|
||||||
|
- HTTPS enforced on all data transmission surfaces
|
||||||
|
- SHA-256 hashing of patient_id before storage in any log or database record
|
||||||
|
- Supabase Row Level Security (RLS) restricting data access by organization
|
||||||
|
- Clerk JWT authentication required on all API endpoints
|
||||||
|
- No storage of patient names, dates of birth, Social Security numbers, addresses, or contact information at any time
|
||||||
|
- The patient identity crosswalk (supplier's internal patient_id to real patient identity) is maintained solely by Covered Entity staff and is never transmitted to or stored by Business Associate
|
||||||
|
|
||||||
|
### 2.4 Reporting
|
||||||
|
|
||||||
|
Business Associate will report to Covered Entity, without unreasonable delay and in no event later than 30 calendar days of discovery:
|
||||||
|
|
||||||
|
(a) Any use or disclosure of PHI not permitted by this Agreement of which Business Associate becomes aware;
|
||||||
|
|
||||||
|
(b) Any Security Incident of which Business Associate becomes aware, including breaches of Unsecured PHI as required by 45 C.F.R. Part 164, Subpart D;
|
||||||
|
|
||||||
|
(c) Any attempted but unsuccessful Security Incident of which Business Associate becomes aware, to the extent practicable.
|
||||||
|
|
||||||
|
Reports shall be made to Covered Entity's designated contact in writing.
|
||||||
|
|
||||||
|
### 2.5 Subcontractors
|
||||||
|
|
||||||
|
Business Associate will ensure that any subcontractor or agent to which it provides PHI on behalf of Covered Entity agrees to the same restrictions, conditions, and requirements that apply to Business Associate under this Agreement.
|
||||||
|
|
||||||
|
Business Associate's current subprocessors include:
|
||||||
|
|
||||||
|
| Subprocessor | Purpose | Data Touched |
|
||||||
|
|---|---|---|
|
||||||
|
| Supabase (via cloud.supabase.com) | PostgreSQL database hosting | Hashed patient_id, device_type, shipment_date, payer, doc status fields |
|
||||||
|
| Railway (via railway.app) | Backend API hosting | Processes upload requests in transit; does not persist PHI |
|
||||||
|
| Vercel (via vercel.com) | Frontend hosting | No PHI; serves static application only |
|
||||||
|
| Clerk (via clerk.com) | Staff authentication | No PHI; stores staff identity only |
|
||||||
|
|
||||||
|
Business Associate will notify Covered Entity of any material change to subprocessors that will touch PHI, and will obtain Covered Entity's written approval before adding any new subprocessor that will receive or process PHI.
|
||||||
|
|
||||||
|
### 2.6 Access to PHI
|
||||||
|
|
||||||
|
To the extent Business Associate maintains PHI in a Designated Record Set, Business Associate will make such PHI available to Covered Entity as necessary to fulfill Covered Entity's obligations under 45 C.F.R. Section 164.524 within 15 business days of a written request.
|
||||||
|
|
||||||
|
### 2.7 Amendment
|
||||||
|
|
||||||
|
To the extent Business Associate maintains PHI in a Designated Record Set, Business Associate will make such PHI available for amendment and will incorporate any amendments requested by Covered Entity as required by 45 C.F.R. Section 164.526.
|
||||||
|
|
||||||
|
### 2.8 Accounting of Disclosures
|
||||||
|
|
||||||
|
Business Associate will maintain a record of disclosures of PHI and will make such information available to Covered Entity as necessary to respond to a request for an accounting of disclosures under 45 C.F.R. Section 164.528.
|
||||||
|
|
||||||
|
### 2.9 Government Access
|
||||||
|
|
||||||
|
Business Associate will make its internal practices, books, and records relating to the use and disclosure of PHI available to the Secretary of the U.S. Department of Health and Human Services for purposes of determining compliance with HIPAA.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Obligations of Covered Entity
|
||||||
|
|
||||||
|
Covered Entity agrees to:
|
||||||
|
|
||||||
|
(a) Notify Business Associate of any limitation in Covered Entity's Notice of Privacy Practices that would affect Business Associate's permitted uses or disclosures;
|
||||||
|
|
||||||
|
(b) Notify Business Associate of any changes in, or revocations of, authorization by individuals that would affect Business Associate's permitted uses or disclosures;
|
||||||
|
|
||||||
|
(c) Not request Business Associate to use or disclose PHI in any manner that would not be permissible under HIPAA if done by Covered Entity directly;
|
||||||
|
|
||||||
|
(d) Ensure that any PHI uploaded to Signal is limited to the minimum necessary fields specified in the LOI: an internal patient identifier (patient_id), device type, shipment date, and payer name. Covered Entity will not upload patient names, Social Security numbers, dates of birth, addresses, or direct contact information to Signal at any time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Term and Termination
|
||||||
|
|
||||||
|
### 4.1 Term
|
||||||
|
|
||||||
|
This Agreement is effective as of the Effective Date and continues until the later of: (a) the termination of the LOI or any subsequent formal agreement between the Parties; or (b) the date on which Business Associate has completed its data return and destruction obligations under Section 4.3.
|
||||||
|
|
||||||
|
### 4.2 Termination for Cause
|
||||||
|
|
||||||
|
Either Party may terminate this Agreement immediately upon written notice to the other Party if the other Party has materially breached a provision of this Agreement and has not cured the breach within 10 business days of written notice identifying the breach.
|
||||||
|
|
||||||
|
### 4.3 Return or Destruction of PHI
|
||||||
|
|
||||||
|
Upon termination of this Agreement for any reason:
|
||||||
|
|
||||||
|
(a) Business Associate will return to Covered Entity or destroy all PHI received from or created on behalf of Covered Entity, including PHI in the possession of subcontractors, within **10 business days** of the effective termination date;
|
||||||
|
|
||||||
|
(b) Business Associate will certify in writing to Covered Entity that all such PHI has been returned or destroyed;
|
||||||
|
|
||||||
|
(c) If return or destruction is not feasible, Business Associate will extend the protections of this Agreement to such PHI and limit further use or disclosure to the purposes that make return or destruction infeasible, for as long as Business Associate maintains the PHI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Miscellaneous
|
||||||
|
|
||||||
|
### 5.1 Regulatory References
|
||||||
|
|
||||||
|
Any reference in this Agreement to a section of HIPAA includes the most recent amendment or regulatory guidance applicable to that section.
|
||||||
|
|
||||||
|
### 5.2 Interpretation
|
||||||
|
|
||||||
|
This Agreement is intended to comply with HIPAA as amended. Where this Agreement conflicts with HIPAA, HIPAA controls. Where this Agreement is silent, HIPAA controls.
|
||||||
|
|
||||||
|
### 5.3 No Third-Party Beneficiaries
|
||||||
|
|
||||||
|
Nothing in this Agreement confers any right, remedy, or claim upon any third party, including any patient whose PHI is involved.
|
||||||
|
|
||||||
|
### 5.4 Governing Law
|
||||||
|
|
||||||
|
This Agreement shall be governed by the laws of the **Commonwealth of Pennsylvania**, without regard to conflict of law principles.
|
||||||
|
|
||||||
|
### 5.5 Entire Agreement
|
||||||
|
|
||||||
|
This Agreement, together with the LOI and mutual NDA between the Parties, constitutes the entire agreement with respect to PHI and supersedes all prior understandings on the subject.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Attorney Review Notes
|
||||||
|
|
||||||
|
The following items should be confirmed by Nixon Law Group before this Agreement is executed:
|
||||||
|
|
||||||
|
1. **BAA necessity:** Confirm whether Signal's receipt of Covered Entity's internal patient_id (supplier account number, one of HIPAA's 18 Safe Harbor identifiers) makes STTIL Solutions a Business Associate requiring this Agreement, or whether Signal's de-identification architecture (no names, no DOBs, no SSNs; crosswalk stays with Covered Entity) satisfies the Expert Determination or Safe Harbor de-identification standards.
|
||||||
|
|
||||||
|
2. **Subprocessor agreements:** Confirm whether Railway, Supabase, Vercel, and Clerk each require a separate BAA as subcontractors.
|
||||||
|
|
||||||
|
3. **Accounting of disclosures scope:** Confirm the scope of Section 2.8 as applied to a software service that processes de-identified data only.
|
||||||
|
|
||||||
|
4. **Pennsylvania-specific requirements:** Confirm no PA-specific health data protection statutes (beyond HIPAA) apply to this arrangement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Signatures
|
||||||
|
|
||||||
|
**STTIL Solutions LLC (Business Associate)**
|
||||||
|
By: ___________________________
|
||||||
|
Name: Kisa Fenn
|
||||||
|
Title: Managing Member
|
||||||
|
Date: _________________________
|
||||||
|
Email: kisasttil@gmail.com
|
||||||
|
|
||||||
|
**Gaboro DME (Covered Entity)**
|
||||||
|
By: ___________________________
|
||||||
|
Name: Robert Robinson
|
||||||
|
Title: Co-Founder and Managing Partner
|
||||||
|
Date: _________________________
|
||||||
|
Email: ___________________________
|
||||||
121
pitch/legal/gaboro-nda.md
Normal file
121
pitch/legal/gaboro-nda.md
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
# MUTUAL NON-DISCLOSURE AGREEMENT
|
||||||
|
|
||||||
|
**Effective Date:** June __, 2026
|
||||||
|
|
||||||
|
**Between:**
|
||||||
|
- **STTIL Solutions LLC** ("STTIL"), a limited liability company, with its principal place of business in [City, State]
|
||||||
|
- **Gaboro DME** ("Gaboro"), with its principal place of business in [City, State]
|
||||||
|
|
||||||
|
Each a "Party" and together the "Parties."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
The Parties intend to explore a pilot engagement involving Signal, a documentation readiness platform developed by STTIL ("Pilot"). In connection with the Pilot, each Party may disclose certain Confidential Information (as defined below) to the other Party. This Agreement governs the treatment of such information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Definition of Confidential Information
|
||||||
|
|
||||||
|
"Confidential Information" means any non-public information disclosed by one Party (the "Disclosing Party") to the other Party (the "Receiving Party") in connection with the Pilot, whether disclosed orally, in writing, electronically, or by any other means, and whether or not marked as confidential, including but not limited to:
|
||||||
|
|
||||||
|
- **STTIL Confidential Information:** Signal's software, algorithms, documentation readiness methodology, product roadmap, pricing structures, business plans, investor materials, and technical documentation.
|
||||||
|
- **Gaboro Confidential Information:** DMEPOS operational workflows, documentation processes, patient census (in de-identified form), payer mix, staff processes, business operations, financial information, and supplier relationships.
|
||||||
|
- **Shared:** The existence and terms of the Pilot engagement, this Agreement, and any discussions between the Parties.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Obligations of Receiving Party
|
||||||
|
|
||||||
|
Each Receiving Party agrees to:
|
||||||
|
|
||||||
|
(a) Hold all Confidential Information in strict confidence and not disclose it to any third party without the prior written consent of the Disclosing Party;
|
||||||
|
|
||||||
|
(b) Use Confidential Information solely for the purpose of evaluating and conducting the Pilot;
|
||||||
|
|
||||||
|
(c) Limit access to Confidential Information to employees, contractors, and agents who have a need to know and are bound by confidentiality obligations no less protective than those in this Agreement;
|
||||||
|
|
||||||
|
(d) Promptly notify the Disclosing Party of any actual or suspected unauthorized disclosure of Confidential Information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Exclusions
|
||||||
|
|
||||||
|
The obligations in Section 3 do not apply to information that:
|
||||||
|
|
||||||
|
(a) Is or becomes publicly available through no breach of this Agreement by the Receiving Party;
|
||||||
|
|
||||||
|
(b) Was rightfully known to the Receiving Party without restriction prior to disclosure by the Disclosing Party;
|
||||||
|
|
||||||
|
(c) Is rightfully received from a third party without restriction;
|
||||||
|
|
||||||
|
(d) Is independently developed by the Receiving Party without reference to the Confidential Information; or
|
||||||
|
|
||||||
|
(e) Is required to be disclosed by law, regulation, or court order, provided the Receiving Party gives the Disclosing Party prompt prior written notice and reasonable assistance in seeking a protective order.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. DMEPOS-Specific Provision
|
||||||
|
|
||||||
|
The Parties acknowledge that DMEPOS operational workflows, documentation processes, and payer-specific billing practices shared during the Pilot are highly sensitive and proprietary to Gaboro's business operations. STTIL agrees that any such information shared by Gaboro during the Pilot:
|
||||||
|
|
||||||
|
(a) Will be used solely to configure and refine Signal for Gaboro's use during the Pilot;
|
||||||
|
|
||||||
|
(b) Will not be disclosed to any other DMEPOS supplier or competitor of Gaboro; and
|
||||||
|
|
||||||
|
(c) Will not be incorporated into Signal's publicly available documentation, marketing materials, or general feature set in a way that would reveal Gaboro's proprietary operational approach.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Return or Destruction of Confidential Information
|
||||||
|
|
||||||
|
Upon the written request of the Disclosing Party, or upon termination of the Pilot, the Receiving Party shall promptly return or destroy all tangible materials containing Confidential Information and certify in writing that it has done so. One copy may be retained solely for legal compliance purposes, subject to ongoing confidentiality obligations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. No License
|
||||||
|
|
||||||
|
Nothing in this Agreement grants either Party any license, right, or interest in the other Party's Confidential Information, intellectual property, trademarks, or technology beyond the limited right to evaluate and conduct the Pilot.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Term
|
||||||
|
|
||||||
|
This Agreement is effective as of the Effective Date and continues for **2 years**, unless earlier terminated by mutual written agreement. Obligations with respect to Confidential Information disclosed during the term survive termination for an additional 2 years.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Remedies
|
||||||
|
|
||||||
|
Each Party acknowledges that a breach of this Agreement would cause irreparable harm for which monetary damages would be an inadequate remedy. Each Party agrees that the other Party shall be entitled to seek equitable relief, including injunction and specific performance, without the requirement of posting bond or other security, in addition to all other remedies available at law or equity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Governing Law and Venue
|
||||||
|
|
||||||
|
This Agreement shall be governed by and construed in accordance with the laws of the **Commonwealth of Pennsylvania**, without regard to its conflict of law principles. The Parties consent to the exclusive jurisdiction of the state and federal courts located in Pennsylvania for any dispute arising under this Agreement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Entire Agreement
|
||||||
|
|
||||||
|
This Agreement constitutes the entire agreement of the Parties with respect to the subject matter hereof and supersedes all prior discussions, representations, and agreements. This Agreement may not be amended except by a written instrument signed by both Parties.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Signatures
|
||||||
|
|
||||||
|
**STTIL Solutions LLC**
|
||||||
|
By: ___________________________
|
||||||
|
Name: Kisa Fenn
|
||||||
|
Title: Managing Member
|
||||||
|
Date: _________________________
|
||||||
|
Email: kisasttil@gmail.com
|
||||||
|
|
||||||
|
**Gaboro DME**
|
||||||
|
By: ___________________________
|
||||||
|
Name: Robert Robinson
|
||||||
|
Title: Co-Founder and Managing Partner
|
||||||
|
Date: _________________________
|
||||||
|
Email: ___________________________
|
||||||
108
pitch/legal/gaboro-pilot-loi.md
Normal file
108
pitch/legal/gaboro-pilot-loi.md
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
# SIGNAL PILOT LETTER OF INTENT
|
||||||
|
|
||||||
|
**Date:** June __, 2026
|
||||||
|
**Between:**
|
||||||
|
- **STTIL Solutions LLC** ("Provider"), a limited liability company, Kisa Fenn, Managing Member
|
||||||
|
- **Gaboro DME** ("Partner"), Robert Robinson, Co-Founder and Managing Partner
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
This Letter of Intent ("LOI") sets forth the agreed terms for a 90-day pilot engagement of Signal, a documentation readiness tool developed and operated by STTIL Solutions LLC ("Signal"). This LOI is not a binding contract but reflects the shared intent of both parties to proceed on the terms described herein. A formal Pilot Agreement will be executed prior to the first data upload.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Scope of Pilot
|
||||||
|
|
||||||
|
During the pilot period, Partner will upload de-identified order management CSV exports to Signal. Signal will process the CSV data to produce a prioritized documentation worklist showing per-patient status across qualifying visit dates, standing written orders, prior authorization, PECOS enrollment, and supply cycle status. Partner staff will act on the worklist output. Signal identifies documentation conditions; Partner staff conduct all outreach and supply decisions.
|
||||||
|
|
||||||
|
Signal does not contact patients or prescribers directly at any time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Pilot Duration
|
||||||
|
|
||||||
|
The pilot period is **90 days** from the date of the first successful CSV upload by Partner ("Pilot Start Date"). The pilot may be extended by written mutual agreement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Pilot Fee
|
||||||
|
|
||||||
|
The pilot period is provided at **no charge**. At Partner's election to continue as a paying subscriber following the pilot, the monthly fee is **$199 per month** for the Signal Worklist tier. This rate is locked for the life of Partner's subscription as a founding pilot partner and will not be subject to standard price increases so long as the subscription remains active.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Data Handling
|
||||||
|
|
||||||
|
**De-identified data only.** Partner will upload CSV files containing: an internal patient identifier (the supplier's own MRN or account number), device type, shipment date, and payer name. Partner will not include patient names, Social Security numbers, dates of birth, addresses, or any other direct identifiers in uploaded files.
|
||||||
|
|
||||||
|
STTIL Solutions will not store patient names, SSNs, DOBs, addresses, or contact information at any time. The patient identity crosswalk (internal patient_id to real patient identity) remains solely with Partner's staff.
|
||||||
|
|
||||||
|
STTIL Solutions will store: hashed patient identifiers, device type, shipment date, payer name, calculated documentation status, and staff-confirmed visit dates (entered by Partner staff via the Signal interface).
|
||||||
|
|
||||||
|
Both parties acknowledge that Partner is responsible for ensuring that the data uploaded complies with applicable HIPAA de-identification requirements and Partner's own compliance obligations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Feedback and Case Study Rights
|
||||||
|
|
||||||
|
Partner agrees to provide good-faith feedback on Signal during the pilot period, including but not limited to: functionality, accuracy of documentation status calculations, worklist usability, and feature gaps.
|
||||||
|
|
||||||
|
STTIL Solutions may use anonymized, aggregated data from the pilot (with no identifying information about Partner's patients, staff, or business operations) for product improvement and investor reporting.
|
||||||
|
|
||||||
|
STTIL Solutions may prepare a case study describing the pilot outcomes. Any case study that identifies Partner by name requires Partner's prior written approval before publication.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Reference Rights
|
||||||
|
|
||||||
|
Partner grants STTIL Solutions the right to identify Partner as "an engaged pilot partner of Signal" in investor materials, marketing materials, and conversations with prospective partners and investors. STTIL Solutions will seek Partner's written approval for any specific usage of Partner's name or logo beyond this general reference right. This reference right may be revoked by Partner with 30 days written notice to STTIL Solutions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Termination
|
||||||
|
|
||||||
|
Either party may terminate the pilot engagement with **30 days written notice** delivered by email to the other party's designated contact. Upon termination or conclusion of the pilot:
|
||||||
|
- STTIL Solutions will delete all Partner data from Signal systems within **10 business days** of the effective termination date.
|
||||||
|
- STTIL Solutions will provide written confirmation of data deletion upon Partner's request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Intellectual Property
|
||||||
|
|
||||||
|
Signal, including its algorithms, software, and documentation readiness methodology, is and remains the sole intellectual property of STTIL Solutions LLC. Nothing in this LOI grants Partner any license to Signal's underlying technology beyond the right to use Signal as a software service during the pilot period.
|
||||||
|
|
||||||
|
Partner retains all rights to its operational data, patient records, and business information shared during the pilot.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Confidentiality
|
||||||
|
|
||||||
|
The parties agree to keep the terms of this LOI and all information shared during the pilot confidential. A mutual Non-Disclosure Agreement ("NDA") will be executed concurrently with this LOI and governs the confidentiality obligations of both parties in detail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. No Binding Commitment
|
||||||
|
|
||||||
|
This LOI is not a binding legal agreement and does not obligate either party to proceed with a commercial relationship. A formal Pilot Agreement reflecting the terms of this LOI will be prepared for signature prior to the Pilot Start Date.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Governing Law
|
||||||
|
|
||||||
|
This LOI and any formal agreements arising from it shall be governed by the laws of the **Commonwealth of Pennsylvania**, without regard to its conflict of law principles.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agreed in Principle
|
||||||
|
|
||||||
|
**STTIL Solutions LLC**
|
||||||
|
By: ___________________________
|
||||||
|
Kisa Fenn, Managing Member
|
||||||
|
Date: _________________________
|
||||||
|
|
||||||
|
**Gaboro DME**
|
||||||
|
By: ___________________________
|
||||||
|
Robert Robinson, Co-Founder and Managing Partner
|
||||||
|
Date: _________________________
|
||||||
117
pitch/legal/legal-research-notes.md
Normal file
117
pitch/legal/legal-research-notes.md
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
# Legal Research Notes — Gaboro DME Pilot (Signal)
|
||||||
|
|
||||||
|
**Prepared:** June 7, 2026
|
||||||
|
**For:** LOI and Mutual NDA drafting — Robert Robinson call June 10, 2026
|
||||||
|
**Attorney review:** Nixon Law Group (Clyde), 30-minute review
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DMEPOS Pilot Agreement Research
|
||||||
|
|
||||||
|
### Standard Healthcare SaaS Pilot Structure
|
||||||
|
|
||||||
|
Healthcare SaaS pilot agreements for B2B DMEPOS or clinical workflow tools typically follow this structure:
|
||||||
|
|
||||||
|
1. **LOI or Term Sheet phase** — non-binding, sets commercial terms and data handling expectations before a formal agreement is executed. LOIs in healthcare tech commonly include: pilot duration, fee terms, data scope, feedback obligations, and IP ownership statements.
|
||||||
|
|
||||||
|
2. **Pilot duration norms** — 90 days is standard for DMEPOS workflow tools. Some vendors use 60-day pilots; 90 days allows at least one full CGM resupply cycle (typically 84 or 90 days) to be observed end-to-end, giving meaningful outcome data.
|
||||||
|
|
||||||
|
3. **Termination notice** — 30 days written notice is standard in healthcare B2B pilots. Pilots with federal program data (Medicare, Medicaid) sometimes use 15-day notice for data security reasons; 30 days is more protective of the supplier and gives adequate wind-down time.
|
||||||
|
|
||||||
|
4. **Data deletion obligations** — Healthcare SaaS agreements typically specify 30 days for data deletion post-termination. Signal's 10 business day standard (approximately 2 calendar weeks) is faster than typical and is a stronger commitment to Gaboro.
|
||||||
|
|
||||||
|
5. **Reference rights** — Common in early-stage B2B healthcare tech. Standard language: named reference right conditioned on prior written approval of specific usage, with revocation option. 30-day revocation notice is market standard.
|
||||||
|
|
||||||
|
6. **Feedback and case study rights** — Feedback obligations in healthcare pilot agreements are typically described as "good faith" with no minimum volume or format requirement. Case study rights requiring named attribution always require written approval; anonymized aggregate use is typically granted without separate approval.
|
||||||
|
|
||||||
|
7. **Fee lock for founding partners** — "Price lock for life" provisions are commercially common in SaaS early adopter programs. Must be clearly conditioned on the subscription remaining active and continuous (gap in subscription may reset the founding rate).
|
||||||
|
|
||||||
|
### Data Use Provisions for De-identified DMEPOS Operational Data
|
||||||
|
|
||||||
|
Signal's data model (patient_id, device_type, shipment_date, payer) falls into a category that DMEPOS suppliers often share with third-party workflow vendors. Key considerations:
|
||||||
|
|
||||||
|
- Operational data shared with B2B vendors is typically governed by Business Associate Agreements (BAAs) when it includes PHI, or by standard data sharing provisions when it is de-identified.
|
||||||
|
- The LOI should explicitly state what data fields are in scope and exclude PHI fields by name.
|
||||||
|
- The burden of ensuring de-identification compliance should rest on the uploading party (Gaboro), since Gaboro controls what is included in the CSV export.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pennsylvania Law Notes
|
||||||
|
|
||||||
|
### PA Governing Law for Commercial Software Agreements
|
||||||
|
|
||||||
|
Pennsylvania's Uniform Commercial Code governs tangible goods. Software licensing and SaaS agreements in PA are governed by common law contract principles (not the UCC), which is the majority rule for software. PA has not adopted UCITA (Uniform Computer Information Transactions Act).
|
||||||
|
|
||||||
|
**What PA governing law means for this agreement:**
|
||||||
|
|
||||||
|
1. **Venue** — PA governing law in a commercial contract supports specifying PA state or federal courts as exclusive venue. Both state courts (Court of Common Pleas) and federal courts (Eastern or Western District of PA) are available. The NDA draft specifies "state and federal courts located in Pennsylvania" without pinning a specific district — this is intentional and appropriate for a mutual NDA at this stage.
|
||||||
|
|
||||||
|
2. **Statute of Limitations** — PA's statute of limitations for written contract breach is 4 years (42 Pa. C.S. § 5525). This is relevant to the NDA's remedies section.
|
||||||
|
|
||||||
|
3. **Limitation of Liability** — PA enforces limitation of liability clauses in commercial contracts between sophisticated parties. The current LOI and NDA drafts do not include a limitation of liability clause. This is a gap the attorney should address in the formal Pilot Agreement (the LOI defers LOL to the formal agreement).
|
||||||
|
|
||||||
|
4. **Equitable Relief** — PA courts enforce injunctive relief provisions in NDAs. The NDA's remedies section (Section 9) is appropriately drafted for PA law — it requests equitable relief without waiving bond requirements in a way that courts might reject, noting "without the requirement of posting bond" which is enforceable in PA for trade secret and confidentiality matters.
|
||||||
|
|
||||||
|
5. **Attorney fee shifting** — PA follows the American Rule (each party bears own fees absent a statute or contract clause). The current drafts do not include fee-shifting language. The attorney may advise adding a prevailing-party clause in the formal agreement.
|
||||||
|
|
||||||
|
### PA-Specific Notes for SaaS Agreements
|
||||||
|
|
||||||
|
- PA does not impose specific state-level data protection obligations on B2B data exchanges of the type described in Signal's pilot (no PA equivalent of CCPA for business-to-business operational data).
|
||||||
|
- PA's Breach of Personal Information Notification Act (BPINA) applies to computerized data containing personal information. Signal's de-identified data model (no names, SSNs, DOBs) means BPINA does not apply to the pilot data set. This supports the LOI's de-identification statement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HIPAA and Data Notes
|
||||||
|
|
||||||
|
### Does Signal's Data Require a BAA?
|
||||||
|
|
||||||
|
**The core question:** Does Signal's data set (patient_id as internal MRN, device_type, shipment_date, payer) constitute PHI under HIPAA?
|
||||||
|
|
||||||
|
**Analysis:**
|
||||||
|
|
||||||
|
Under HIPAA, PHI is individually identifiable health information — information that relates to the past, present, or future health or condition of an individual, and that identifies or could identify the individual.
|
||||||
|
|
||||||
|
The 18 HIPAA safe harbor identifiers include: names, geographic data smaller than state, dates (other than year) for individuals, telephone numbers, SSNs, account numbers, and several others. HIPAA's safe harbor method (45 CFR 164.514(b)) requires removing all 18 identifiers and having no actual knowledge that the information could identify an individual.
|
||||||
|
|
||||||
|
**Signal's patient_id issue:** An internal MRN or account number assigned by the DMEPOS supplier is technically an "account number" — one of the 18 HIPAA safe harbor identifiers. This means the data as structured (with patient_id present) is NOT fully de-identified under the HIPAA safe harbor method.
|
||||||
|
|
||||||
|
**Practical consequence:**
|
||||||
|
- If patient_id is an account number (18th identifier), a BAA may be required even though Signal never sees names, SSNs, or DOBs.
|
||||||
|
- Alternatively, the supplier could hash or tokenize the patient_id before upload, which would remove the account number identifier and enable true safe harbor de-identification.
|
||||||
|
|
||||||
|
**Signal's current architecture:** Signal stores a hashed patient_id (SHA-256), not the raw patient_id. The hashing occurs in Signal's normalizer before storage. However, the raw patient_id is present in the uploaded CSV during transmission and processing.
|
||||||
|
|
||||||
|
**Recommendation for attorney review:** This is the most significant open legal question for the pilot. Nixon Law Group should advise whether:
|
||||||
|
1. A BAA is required given that raw account numbers (patient_id) are present in the CSV during upload/processing, even if hashed before storage.
|
||||||
|
2. The LOI should be amended to either require the supplier to hash patient_ids before upload, or to add a BAA alongside the pilot agreement.
|
||||||
|
3. The "no patient names, SSNs, DOBs" language in the LOI is sufficient for the pilot stage, or whether a formal HIPAA de-identification certification is needed.
|
||||||
|
|
||||||
|
**Current LOI language:** The LOI currently states that "Partner will not include patient names, Social Security numbers, dates of birth, addresses, or any other direct identifiers." This language does not resolve the account number / MRN question. The attorney should be asked directly about this gap.
|
||||||
|
|
||||||
|
### Data Use Agreement vs. BAA
|
||||||
|
|
||||||
|
- A Business Associate Agreement (BAA) is required when a covered entity or business associate shares PHI with a vendor for purposes of treatment, payment, or healthcare operations.
|
||||||
|
- A Data Use Agreement (DUA) is used for limited data sets — data from which most direct identifiers have been removed, but dates and geographic data smaller than state may remain.
|
||||||
|
- Signal's data sits between these categories. If a BAA is required, it should be executed alongside or before the pilot agreement, not after.
|
||||||
|
|
||||||
|
### Practical Risk Assessment for Pilot Stage
|
||||||
|
|
||||||
|
Signal's de-identification architecture (no names, no SSNs, no DOBs, hashed patient_ids in storage) is a reasonable good-faith effort at privacy protection for a pre-revenue pilot. The key risk is that "good faith" is not a HIPAA defense. The attorney should be asked to give a written opinion (even informal email memo) on BAA necessity before the first CSV upload.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Judgment Calls and Attorney Review Flags
|
||||||
|
|
||||||
|
The following items required judgment beyond the locked spec and should be reviewed by Nixon Law Group:
|
||||||
|
|
||||||
|
1. **BAA vs. no BAA** — The most critical open question. See HIPAA notes above. LOI Section 5 does not include or reference a BAA. If a BAA is required, the formal Pilot Agreement needs one.
|
||||||
|
|
||||||
|
2. **Limitation of liability** — Neither the LOI nor the NDA includes a LOL clause. The LOI defers to the formal Pilot Agreement. The attorney should advise on: (a) whether any LOL language belongs in the NDA, and (b) appropriate LOL for the formal agreement (e.g., 12 months of fees paid — which would be $0 during a free pilot, creating an odd cap).
|
||||||
|
|
||||||
|
3. **Venue specificity** — NDA Section 10 names "state and federal courts located in Pennsylvania" without specifying a district or county. For the formal agreement, the attorney should advise whether to specify Philadelphia or Allegheny County (STTIL's location should drive this).
|
||||||
|
|
||||||
|
4. **Fee lock trigger** — LOI Section 4 states the $199/month rate is locked "so long as the subscription remains active." The attorney should advise whether a grace period for subscription lapses is needed (e.g., 30 days to reinstate without losing founding rate), to prevent accidental loss of a founding partner's contractual benefit.
|
||||||
|
|
||||||
|
5. **Reference rights revocation** — LOI Section 7 grants Gaboro a 30-day revocation right. The attorney should confirm whether materials already published with Gaboro's approval (e.g., investor decks) must be recalled on revocation, or whether revocation only limits future use.
|
||||||
|
|
||||||
|
6. **Case study rights scope** — LOI Section 6 grants STTIL the right to "anonymized, aggregated data" for investor reporting during the pilot, without a separate approval requirement. The attorney should confirm this is sufficient for typical VC due diligence use cases, where aggregate outcome data (e.g., "a Midwestern DMEPOS supplier reduced denial rates by X%") is standard.
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
"""Signal API — FastAPI backend for CSV ingestion, scoring, and export."""
|
"""Signal API — FastAPI backend for CSV ingestion, scoring, and export."""
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
|
import hashlib
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import date
|
from datetime import date, date as date_type
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
@ -23,7 +24,11 @@ if str(_backend_root) not in sys.path:
|
||||||
|
|
||||||
from core.coverage_calculator import ShipmentRecord, calculate_batch
|
from core.coverage_calculator import ShipmentRecord, calculate_batch
|
||||||
from core.audit_logger import AuditAction, log_event
|
from core.audit_logger import AuditAction, log_event
|
||||||
from core.persistence import persist_export, persist_upload
|
from core.doc_state_machine import compute_doc_state, DocState
|
||||||
|
from core.persistence import (
|
||||||
|
persist_export, persist_upload,
|
||||||
|
upsert_confirmed_visit, load_confirmed_visits_for_org, get_or_create_org,
|
||||||
|
)
|
||||||
from api.normalizer import normalize_csv
|
from api.normalizer import normalize_csv
|
||||||
|
|
||||||
app = FastAPI(title="Signal API", version="1.0.0", docs_url="/docs")
|
app = FastAPI(title="Signal API", version="1.0.0", docs_url="/docs")
|
||||||
|
|
@ -110,20 +115,36 @@ DEVICE_DISPLAY = {
|
||||||
}
|
}
|
||||||
|
|
||||||
FLAG_LABELS = {
|
FLAG_LABELS = {
|
||||||
"OUT_OF_COVERAGE": "Supply Lapsed",
|
"SUPPLY_LAPSED": "Docs Required",
|
||||||
"VISIT_DUE": "Renewal Due",
|
"VISIT_REQUIRED": "Escalate",
|
||||||
"REFILL_WINDOW": "Resupply Ready",
|
"TRANSFER_PENDING": "New Patient",
|
||||||
"OK": "Active",
|
"RENEWAL_CRITICAL": "Escalate",
|
||||||
|
"RENEWAL_ELEVATED": "Confirm Appointment",
|
||||||
|
"RENEWAL_SOON": "Begin Outreach",
|
||||||
|
"RESUPPLY_READY": "Clear to Ship",
|
||||||
|
"ACTIVE": "On Track",
|
||||||
}
|
}
|
||||||
|
|
||||||
FLAG_ACTIONS = {
|
FLAG_ACTIONS = {
|
||||||
"OUT_OF_COVERAGE": "Contact Prescriber",
|
"SUPPLY_LAPSED": "Pull patient file — verify documentation is current before initiating any new order",
|
||||||
"VISIT_DUE": "Request Renewal",
|
"VISIT_REQUIRED": "Contact prescriber's office and patient directly — ask: 'Was there a visit in the past 6 months to evaluate this patient's CGM dosing needs?' No shipment until visit is confirmed and documented",
|
||||||
"REFILL_WINDOW": "Initiate Resupply",
|
"TRANSFER_PENDING": "Obtain full documentation set (SWO, PECOS, PA, diagnosis) — Signal cannot assess status until records are on file",
|
||||||
"OK": "No action needed",
|
"RENEWAL_CRITICAL": "Contact prescriber's office and patient directly — ask: 'Was there a visit in the past 6 months to evaluate CGM dosing needs?' Appointment must be on calendar within 45 days",
|
||||||
|
"RENEWAL_ELEVATED": "Follow up with prescriber's office — ask: 'Was there a visit in the past 6 months to evaluate CGM dosing needs?' Confirm appointment is scheduled",
|
||||||
|
"RENEWAL_SOON": "Contact prescriber's office — ask: 'Was there a visit in the past 6 months to evaluate this patient's CGM dosing needs?' Schedule evaluation now if not yet completed",
|
||||||
|
"RESUPPLY_READY": "Initiate resupply order — patient is within the 30-day refill window",
|
||||||
|
"ACTIVE": "No action needed — documentation current, visit more than 90 days out",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DocStateOut(BaseModel):
|
||||||
|
swo: str
|
||||||
|
visit: str
|
||||||
|
pecos: str
|
||||||
|
pa: str
|
||||||
|
diagnosis: str
|
||||||
|
|
||||||
|
|
||||||
class RecordOut(BaseModel):
|
class RecordOut(BaseModel):
|
||||||
patient_id: str
|
patient_id: str
|
||||||
device_type: str
|
device_type: str
|
||||||
|
|
@ -140,6 +161,10 @@ class RecordOut(BaseModel):
|
||||||
status_label: str
|
status_label: str
|
||||||
reason: str
|
reason: str
|
||||||
rule_version: str
|
rule_version: str
|
||||||
|
visit_date_confidence: str = "estimated"
|
||||||
|
is_transfer: bool = False
|
||||||
|
doc_state: Optional[DocStateOut] = None
|
||||||
|
cascade: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
class UploadResponse(BaseModel):
|
class UploadResponse(BaseModel):
|
||||||
|
|
@ -153,28 +178,73 @@ class UploadResponse(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
def _build_reason(flag_val: str, days_until_end: int, days_until_visit: Optional[int]) -> str:
|
def _build_reason(flag_val: str, days_until_end: int, days_until_visit: Optional[int]) -> str:
|
||||||
if flag_val == "OUT_OF_COVERAGE":
|
if flag_val == "SUPPLY_LAPSED":
|
||||||
ago = abs(days_until_end)
|
ago = abs(days_until_end)
|
||||||
unit = "day" if ago == 1 else "days"
|
unit = "day" if ago == 1 else "days"
|
||||||
return f"Supply lapsed {ago} {unit} ago. Prescriber contact required before next shipment."
|
return f"Supply lapsed {ago} {unit} ago. Prescriber contact required before next shipment."
|
||||||
if flag_val == "VISIT_DUE":
|
if flag_val == "VISIT_REQUIRED":
|
||||||
if days_until_visit is not None and days_until_visit <= 0:
|
if days_until_visit is not None and days_until_visit <= 0:
|
||||||
overdue = abs(days_until_visit)
|
overdue = abs(days_until_visit)
|
||||||
unit = "day" if overdue == 1 else "days"
|
unit = "day" if overdue == 1 else "days"
|
||||||
return f"Qualifying visit overdue by {overdue} {unit}. Confirm documentation immediately."
|
return f"Qualifying visit overdue by {overdue} {unit}. Confirm documentation immediately."
|
||||||
|
return "Qualifying visit renewal required. Confirm documentation before resupply."
|
||||||
|
if flag_val == "RENEWAL_CRITICAL":
|
||||||
if days_until_visit is not None:
|
if days_until_visit is not None:
|
||||||
unit = "day" if days_until_visit == 1 else "days"
|
unit = "day" if days_until_visit == 1 else "days"
|
||||||
return f"Qualifying visit due in {days_until_visit} {unit}. Confirm visit documentation before resupply."
|
return f"Qualifying visit due in {days_until_visit} {unit}. Contact prescriber immediately."
|
||||||
return "Qualifying visit renewal required. Confirm documentation before resupply."
|
return "Qualifying visit renewal critical. Confirm visit documentation before resupply."
|
||||||
if flag_val == "REFILL_WINDOW":
|
if flag_val == "RENEWAL_ELEVATED":
|
||||||
|
if days_until_visit is not None:
|
||||||
|
unit = "day" if days_until_visit == 1 else "days"
|
||||||
|
return f"Qualifying visit due in {days_until_visit} {unit}. Schedule prescriber appointment soon."
|
||||||
|
return "Qualifying visit renewal elevated. Schedule prescriber appointment."
|
||||||
|
if flag_val == "RENEWAL_SOON":
|
||||||
|
if days_until_visit is not None:
|
||||||
|
unit = "day" if days_until_visit == 1 else "days"
|
||||||
|
return f"Qualifying visit due in {days_until_visit} {unit}. Monitor and plan ahead."
|
||||||
|
return "Qualifying visit renewal approaching. Monitor status."
|
||||||
|
if flag_val == "RESUPPLY_READY":
|
||||||
unit = "day" if days_until_end == 1 else "days"
|
unit = "day" if days_until_end == 1 else "days"
|
||||||
return f"Coverage ends in {days_until_end} {unit}. Patient is within resupply window — initiate shipment now."
|
return f"Coverage ends in {days_until_end} {unit}. Patient is within resupply window — initiate shipment now."
|
||||||
|
if flag_val == "TRANSFER_PENDING":
|
||||||
|
return "Patient transferred from prior supplier. Verify all documentation before proceeding."
|
||||||
unit = "day" if days_until_end == 1 else "days"
|
unit = "day" if days_until_end == 1 else "days"
|
||||||
return f"Coverage on track. Resupply window opens in approximately {days_until_end} {unit}."
|
return f"Coverage on track. Resupply window opens in approximately {days_until_end} {unit}."
|
||||||
|
|
||||||
|
|
||||||
def _to_record_out(r) -> RecordOut:
|
def _normalize_payer_type(payer: str) -> str:
|
||||||
|
"""Map raw payer string to doc_state_machine payer_type."""
|
||||||
|
from core.coverage_calculator import _normalize_payer
|
||||||
|
normalized = _normalize_payer(payer)
|
||||||
|
# Ensure medicare_advantage is not collapsed to medicare
|
||||||
|
if "medicare advantage" in payer.lower() or "medicare_advantage" in payer.lower():
|
||||||
|
return "medicare_advantage"
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
|
def _to_record_out(r, record=None) -> RecordOut:
|
||||||
flag_val = r.flag.value if hasattr(r.flag, "value") else str(r.flag)
|
flag_val = r.flag.value if hasattr(r.flag, "value") else str(r.flag)
|
||||||
|
|
||||||
|
# Compute doc state if we have the original record
|
||||||
|
doc_state_out = None
|
||||||
|
cascade = []
|
||||||
|
if record is not None:
|
||||||
|
normalized_payer = _normalize_payer_type(r.payer)
|
||||||
|
doc = compute_doc_state(
|
||||||
|
payer_type=normalized_payer,
|
||||||
|
csv_swo_status=record.csv_swo_status,
|
||||||
|
csv_visit_date=record.csv_visit_date,
|
||||||
|
confirmed_visit_date=None, # already factored into r.next_visit_due_date
|
||||||
|
csv_pecos_verified=record.csv_pecos_verified,
|
||||||
|
csv_pa_status=record.csv_pa_status,
|
||||||
|
csv_diagnosis_on_file=record.csv_diagnosis_on_file,
|
||||||
|
is_transfer=bool(record.csv_transfer_from),
|
||||||
|
)
|
||||||
|
doc_state_out = DocStateOut(
|
||||||
|
swo=doc.swo, visit=doc.visit, pecos=doc.pecos, pa=doc.pa, diagnosis=doc.diagnosis
|
||||||
|
)
|
||||||
|
cascade = doc.cascade
|
||||||
|
|
||||||
return RecordOut(
|
return RecordOut(
|
||||||
patient_id=r.patient_id,
|
patient_id=r.patient_id,
|
||||||
device_type=r.device_type,
|
device_type=r.device_type,
|
||||||
|
|
@ -191,6 +261,10 @@ def _to_record_out(r) -> RecordOut:
|
||||||
status_label=FLAG_LABELS.get(flag_val, flag_val),
|
status_label=FLAG_LABELS.get(flag_val, flag_val),
|
||||||
reason=_build_reason(flag_val, r.days_until_coverage_end, r.days_until_visit_due),
|
reason=_build_reason(flag_val, r.days_until_coverage_end, r.days_until_visit_due),
|
||||||
rule_version=r.rule_version,
|
rule_version=r.rule_version,
|
||||||
|
visit_date_confidence=getattr(r, "visit_date_confidence", "estimated"),
|
||||||
|
is_transfer=getattr(r, "is_transfer", False),
|
||||||
|
doc_state=doc_state_out,
|
||||||
|
cascade=cascade,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -198,11 +272,18 @@ def _compute_stats(records: list[RecordOut]) -> dict:
|
||||||
flags = [r.flag for r in records]
|
flags = [r.flag for r in records]
|
||||||
return {
|
return {
|
||||||
"total": len(records),
|
"total": len(records),
|
||||||
"supply_lapsed": flags.count("OUT_OF_COVERAGE"),
|
"supply_lapsed": flags.count("SUPPLY_LAPSED"),
|
||||||
"renewal_due": flags.count("VISIT_DUE"),
|
"visit_required": flags.count("VISIT_REQUIRED"),
|
||||||
"resupply_ready": flags.count("REFILL_WINDOW"),
|
"transfer_pending": flags.count("TRANSFER_PENDING"),
|
||||||
"active": flags.count("OK"),
|
"renewal_critical": flags.count("RENEWAL_CRITICAL"),
|
||||||
"prescriber_action": flags.count("OUT_OF_COVERAGE") + flags.count("VISIT_DUE"),
|
"renewal_elevated": flags.count("RENEWAL_ELEVATED"),
|
||||||
|
"renewal_soon": flags.count("RENEWAL_SOON"),
|
||||||
|
"resupply_ready": flags.count("RESUPPLY_READY"),
|
||||||
|
"active": flags.count("ACTIVE"),
|
||||||
|
"prescriber_action": (
|
||||||
|
flags.count("SUPPLY_LAPSED") + flags.count("VISIT_REQUIRED") +
|
||||||
|
flags.count("RENEWAL_CRITICAL") + flags.count("RENEWAL_ELEVATED")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -257,13 +338,20 @@ async def upload_csv(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
results = calculate_batch(records, as_of=date.today())
|
# Load confirmed visit dates from Supabase for this org
|
||||||
out = [_to_record_out(r) for r in results]
|
clerk_org_id = claims.get("o", {}).get("id") if isinstance(claims.get("o"), dict) else None
|
||||||
|
org_id = get_or_create_org(clerk_org_id=clerk_org_id)
|
||||||
|
confirmed_visits = load_confirmed_visits_for_org(org_id) if org_id else {}
|
||||||
|
|
||||||
|
results = calculate_batch(records, as_of=date.today(), confirmed_visits=confirmed_visits)
|
||||||
|
|
||||||
|
# Build a lookup from patient_id to original record for doc state computation
|
||||||
|
record_lookup = {r.patient_id: r for r in records}
|
||||||
|
out = [_to_record_out(r, record=record_lookup.get(r.patient_id)) for r in results]
|
||||||
|
|
||||||
log_event(AuditAction.CSV_INGEST, file.filename or "unknown", "demo_user",
|
log_event(AuditAction.CSV_INGEST, file.filename or "unknown", "demo_user",
|
||||||
"success", "0.0.0.0", detail=f"{len(out)} records scored")
|
"success", "0.0.0.0", detail=f"{len(out)} records scored")
|
||||||
|
|
||||||
clerk_org_id = claims.get("o", {}).get("id") if isinstance(claims.get("o"), dict) else None
|
|
||||||
batch_id = persist_upload(
|
batch_id = persist_upload(
|
||||||
filename=file.filename or "unknown",
|
filename=file.filename or "unknown",
|
||||||
content_bytes=content,
|
content_bytes=content,
|
||||||
|
|
@ -310,8 +398,13 @@ async def export_work_queue(
|
||||||
"Recommended Action",
|
"Recommended Action",
|
||||||
"Resupply End Date",
|
"Resupply End Date",
|
||||||
"Reason",
|
"Reason",
|
||||||
|
"Visit Confidence",
|
||||||
|
"SWO Status",
|
||||||
|
"Visit Status",
|
||||||
|
"PA Status",
|
||||||
])
|
])
|
||||||
for r in records:
|
for r in records:
|
||||||
|
doc = r.doc_state
|
||||||
writer.writerow([
|
writer.writerow([
|
||||||
r.patient_id,
|
r.patient_id,
|
||||||
r.device_display,
|
r.device_display,
|
||||||
|
|
@ -323,6 +416,10 @@ async def export_work_queue(
|
||||||
r.action,
|
r.action,
|
||||||
r.coverage_end_date,
|
r.coverage_end_date,
|
||||||
r.reason,
|
r.reason,
|
||||||
|
r.visit_date_confidence,
|
||||||
|
doc.swo if doc else "",
|
||||||
|
doc.visit if doc else "",
|
||||||
|
doc.pa if doc else "",
|
||||||
])
|
])
|
||||||
|
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
|
|
@ -338,3 +435,70 @@ async def export_work_queue(
|
||||||
"Content-Disposition": f"attachment; filename=signal-work-queue-{today}.csv"
|
"Content-Disposition": f"attachment; filename=signal-work-queue-{today}.csv"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmVisitRequest(BaseModel):
|
||||||
|
patient_id: str
|
||||||
|
confirmed_date: str # ISO date string YYYY-MM-DD
|
||||||
|
# Fields needed to recompute coverage after confirmation
|
||||||
|
shipment_date: str
|
||||||
|
payer: str
|
||||||
|
device_type: str
|
||||||
|
quantity: int = 1
|
||||||
|
component: str = "sensor"
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/confirm-visit")
|
||||||
|
async def confirm_visit(
|
||||||
|
body: ConfirmVisitRequest,
|
||||||
|
claims: dict = Depends(require_auth),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Store a staff-confirmed qualifying visit date and return the updated coverage record.
|
||||||
|
The confirmed date persists across all future CSV imports for this patient_id.
|
||||||
|
"""
|
||||||
|
from core.coverage_calculator import ShipmentRecord, calculate_coverage
|
||||||
|
|
||||||
|
# Validate dates
|
||||||
|
try:
|
||||||
|
confirmed = date_type.fromisoformat(body.confirmed_date)
|
||||||
|
shipment = date_type.fromisoformat(body.shipment_date)
|
||||||
|
except ValueError as e:
|
||||||
|
raise HTTPException(status_code=400, detail=f"Invalid date: {e}. Use YYYY-MM-DD.")
|
||||||
|
|
||||||
|
if confirmed > date_type.today():
|
||||||
|
raise HTTPException(status_code=400, detail="Confirmed date cannot be in the future.")
|
||||||
|
|
||||||
|
# Get org
|
||||||
|
clerk_org_id = claims.get("o", {}).get("id") if isinstance(claims.get("o"), dict) else None
|
||||||
|
org_id = get_or_create_org(clerk_org_id=clerk_org_id)
|
||||||
|
if not org_id:
|
||||||
|
raise HTTPException(status_code=503, detail="Organization not found.")
|
||||||
|
|
||||||
|
# Hash and store
|
||||||
|
patient_hash = hashlib.sha256(body.patient_id.encode()).hexdigest()
|
||||||
|
success = upsert_confirmed_visit(org_id, patient_hash, confirmed, confirmed_by="staff")
|
||||||
|
if not success:
|
||||||
|
raise HTTPException(status_code=503, detail="Failed to save confirmed visit.")
|
||||||
|
|
||||||
|
# Recompute coverage with confirmed visit date
|
||||||
|
record = ShipmentRecord(
|
||||||
|
patient_id=body.patient_id,
|
||||||
|
device_type=body.device_type,
|
||||||
|
shipment_date=shipment,
|
||||||
|
quantity=body.quantity,
|
||||||
|
payer=body.payer,
|
||||||
|
component=body.component,
|
||||||
|
)
|
||||||
|
result = calculate_coverage(record, confirmed_visit_date=confirmed)
|
||||||
|
|
||||||
|
log_event(
|
||||||
|
AuditAction.CSV_INGEST,
|
||||||
|
f"confirm-visit:{body.patient_id[:8]}",
|
||||||
|
"staff",
|
||||||
|
"success",
|
||||||
|
"0.0.0.0",
|
||||||
|
detail=f"Visit confirmed {confirmed.isoformat()}",
|
||||||
|
)
|
||||||
|
|
||||||
|
return _to_record_out(result, record=record)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,31 @@ HEADER_MAP: dict[str, list[str]] = {
|
||||||
"component": [
|
"component": [
|
||||||
"component", "item_type", "component_type", "type", "supply_type",
|
"component", "item_type", "component_type", "type", "supply_type",
|
||||||
],
|
],
|
||||||
|
"csv_visit_date": [
|
||||||
|
"visit_date", "visit date", "qualifying_visit_date", "qualifying visit date",
|
||||||
|
"last_visit_date", "last visit date", "face_to_face_date", "f2f_date",
|
||||||
|
"encounter_date", "encounter date", "physician_visit_date",
|
||||||
|
],
|
||||||
|
"csv_swo_status": [
|
||||||
|
"swo_status", "swo status", "swo", "standing_written_order",
|
||||||
|
"standing written order", "order_status", "order status",
|
||||||
|
],
|
||||||
|
"csv_pecos_verified": [
|
||||||
|
"pecos_verified", "pecos verified", "pecos", "pecos_status",
|
||||||
|
"enrollment_verified", "enrollment verified",
|
||||||
|
],
|
||||||
|
"csv_pa_status": [
|
||||||
|
"pa_status", "pa status", "prior_auth_status", "prior auth status",
|
||||||
|
"prior_authorization_status", "auth_status", "pa", "authorization",
|
||||||
|
],
|
||||||
|
"csv_diagnosis_on_file": [
|
||||||
|
"diagnosis_on_file", "diagnosis on file", "diagnosis", "dx_on_file",
|
||||||
|
"dx on file", "icd_on_file", "icd on file",
|
||||||
|
],
|
||||||
|
"csv_transfer_from": [
|
||||||
|
"transfer_from", "transfer from", "previous_supplier", "previous supplier",
|
||||||
|
"prior_supplier", "prior supplier", "transfer_status", "transferred_from",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
DEVICE_MAP: dict[str, str] = {
|
DEVICE_MAP: dict[str, str] = {
|
||||||
|
|
@ -86,7 +111,7 @@ DEVICE_MAP: dict[str, str] = {
|
||||||
PAYER_MAP: dict[str, str] = {
|
PAYER_MAP: dict[str, str] = {
|
||||||
"medicare part b": "medicare",
|
"medicare part b": "medicare",
|
||||||
"medicare part a": "medicare",
|
"medicare part a": "medicare",
|
||||||
"medicare advantage": "commercial",
|
"medicare advantage": "medicare_advantage", # distinct from medicare FFS
|
||||||
"medicare": "medicare",
|
"medicare": "medicare",
|
||||||
"cms": "medicare",
|
"cms": "medicare",
|
||||||
"medicaid": "medicaid",
|
"medicaid": "medicaid",
|
||||||
|
|
@ -247,6 +272,19 @@ def normalize_csv(text: str) -> tuple[list[ShipmentRecord], list[str], dict]:
|
||||||
if component not in ("sensor", "transmitter", "pod"):
|
if component not in ("sensor", "transmitter", "pod"):
|
||||||
component = "sensor"
|
component = "sensor"
|
||||||
|
|
||||||
|
# Optional doc fields — only populated if the CSV contains these columns
|
||||||
|
csv_visit_date: Optional[date] = None
|
||||||
|
raw_visit = mapped.get("csv_visit_date", "")
|
||||||
|
if raw_visit:
|
||||||
|
csv_visit_date = _parse_date(raw_visit) # None if unparseable (non-error)
|
||||||
|
|
||||||
|
csv_swo_status = mapped.get("csv_swo_status") or None
|
||||||
|
csv_pecos_verified = mapped.get("csv_pecos_verified") or None
|
||||||
|
csv_pa_status = mapped.get("csv_pa_status") or None
|
||||||
|
csv_diagnosis_on_file = mapped.get("csv_diagnosis_on_file") or None
|
||||||
|
transfer_raw = mapped.get("csv_transfer_from", "").strip()
|
||||||
|
csv_transfer_from = transfer_raw if transfer_raw else None
|
||||||
|
|
||||||
records.append(ShipmentRecord(
|
records.append(ShipmentRecord(
|
||||||
patient_id=patient_id,
|
patient_id=patient_id,
|
||||||
device_type=device_type,
|
device_type=device_type,
|
||||||
|
|
@ -254,6 +292,12 @@ def normalize_csv(text: str) -> tuple[list[ShipmentRecord], list[str], dict]:
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
payer=payer,
|
payer=payer,
|
||||||
component=component,
|
component=component,
|
||||||
|
csv_visit_date=csv_visit_date,
|
||||||
|
csv_swo_status=csv_swo_status,
|
||||||
|
csv_pecos_verified=csv_pecos_verified,
|
||||||
|
csv_pa_status=csv_pa_status,
|
||||||
|
csv_diagnosis_on_file=csv_diagnosis_on_file,
|
||||||
|
csv_transfer_from=csv_transfer_from,
|
||||||
))
|
))
|
||||||
|
|
||||||
return records, skipped, mapping_summary
|
return records, skipped, mapping_summary
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"_comment": "Wear-day rules by device type and payer. Used by coverage_calculator.py. Update when payer LCD policies change.",
|
"_comment": "Wear-day rules by device type and payer. Updated 2026-06-07. Run quarterly review per compliance checklist.",
|
||||||
"devices": {
|
"devices": {
|
||||||
"dexcom_g6": {
|
"dexcom_g6": {
|
||||||
"display_name": "Dexcom G6",
|
"display_name": "Dexcom G6",
|
||||||
|
|
@ -26,37 +26,47 @@
|
||||||
"display_name": "Omnipod 5",
|
"display_name": "Omnipod 5",
|
||||||
"pod_wear_days": 3,
|
"pod_wear_days": 3,
|
||||||
"sensor_wear_days": 14,
|
"sensor_wear_days": 14,
|
||||||
"components": ["pod", "sensor"],
|
"components": ["pod", "sensor"]
|
||||||
"_note": "Sensor wear days apply to the paired CGM (typically Dexcom G6 or Libre). Pod is 3 days. Track components separately."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"payer_rules": {
|
"payer_rules": {
|
||||||
"medicare": {
|
"medicare": {
|
||||||
"visit_renewal_days": 180,
|
"visit_renewal_days": 180,
|
||||||
"refill_window_days": 30,
|
"refill_window_days": 30,
|
||||||
"_note": "Medicare requires face-to-face physician visit every 6 months for continued CGM coverage. Refill window opens 30 days before coverage end.",
|
"pa_required": false,
|
||||||
"covered_devices": [
|
"pecos_required": true,
|
||||||
"dexcom_g6",
|
"_note": "Medicare FFS. PA not required (CGM coverage expansion 2023). Face-to-face visit required every 6 months. CMS-1828-F: suppliers at >=90% affirmation rate may qualify for PA exemption from June 1, 2026.",
|
||||||
"dexcom_g7",
|
"covered_devices": ["dexcom_g6", "dexcom_g7", "freestyle_libre_2", "freestyle_libre_3"]
|
||||||
"freestyle_libre_2",
|
},
|
||||||
"freestyle_libre_3"
|
"medicare_advantage": {
|
||||||
]
|
"visit_renewal_days": 180,
|
||||||
|
"refill_window_days": 30,
|
||||||
|
"pa_required": true,
|
||||||
|
"pecos_required": true,
|
||||||
|
"_note": "Medicare Advantage — PA required, varies by plan. Verify plan-specific rules.",
|
||||||
|
"covered_devices": ["dexcom_g6", "dexcom_g7", "freestyle_libre_2", "freestyle_libre_3"]
|
||||||
},
|
},
|
||||||
"medicaid": {
|
"medicaid": {
|
||||||
"visit_renewal_days": null,
|
"visit_renewal_days": 180,
|
||||||
"refill_window_days": 30,
|
"refill_window_days": 30,
|
||||||
"_note": "Medicaid rules vary by state. Renewal cadence not enforced at this layer — flag for manual review.",
|
"pa_required": true,
|
||||||
|
"pecos_required": false,
|
||||||
|
"_note": "PA required — Keystone First, UPMC Health Plan, Highmark BCBS require PA. NJ FamilyCare, Horizon BCBS NJ require PA.",
|
||||||
"covered_devices": []
|
"covered_devices": []
|
||||||
},
|
},
|
||||||
"commercial": {
|
"commercial": {
|
||||||
"visit_renewal_days": null,
|
"visit_renewal_days": 180,
|
||||||
"refill_window_days": 30,
|
"refill_window_days": 30,
|
||||||
"_note": "Commercial payer rules vary by plan. Refill window is a conservative default.",
|
"pa_required": true,
|
||||||
|
"pecos_required": false,
|
||||||
|
"_note": "Commercial payer rules vary by plan. PA required by default — verify per plan.",
|
||||||
"covered_devices": []
|
"covered_devices": []
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"visit_renewal_days": null,
|
"visit_renewal_days": 180,
|
||||||
"refill_window_days": 30,
|
"refill_window_days": 30,
|
||||||
|
"pa_required": true,
|
||||||
|
"pecos_required": false,
|
||||||
"covered_devices": []
|
"covered_devices": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,24 @@ and payer-specific wear-day rules.
|
||||||
|
|
||||||
PHI CONTRACT:
|
PHI CONTRACT:
|
||||||
This module receives only: patient_id, device_type, shipment_date,
|
This module receives only: patient_id, device_type, shipment_date,
|
||||||
quantity, payer. No names, SSNs, DOBs, or contact fields may be
|
quantity, payer, component, and optional doc status fields.
|
||||||
added to any function signature or data structure in this file.
|
No names, SSNs, DOBs, or contact fields may be added to any function
|
||||||
|
signature or data structure in this file.
|
||||||
|
|
||||||
Flag types emitted:
|
Flag types emitted:
|
||||||
REFILL_WINDOW — patient is within the billable refill window
|
SUPPLY_LAPSED — supply cycle ended, no new shipment
|
||||||
VISIT_DUE — physician visit renewal is approaching or overdue
|
VISIT_REQUIRED — visit date past due, no confirmed new visit
|
||||||
OUT_OF_COVERAGE — coverage has lapsed; outreach required before shipment
|
TRANSFER_PENDING — transferred patient, all docs need verification
|
||||||
OK — no action needed at this time
|
RENEWAL_CRITICAL — <= 45 days to next visit due
|
||||||
|
RENEWAL_ELEVATED — <= 60 days to next visit due
|
||||||
|
RENEWAL_SOON — <= 90 days to next visit due
|
||||||
|
RESUPPLY_READY — within refill window, visit not yet urgent
|
||||||
|
ACTIVE — all clear
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -27,16 +32,20 @@ from typing import Optional
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
RULE_VERSION = "v0.1"
|
RULE_VERSION = "v0.2"
|
||||||
|
|
||||||
PAYER_RULES_PATH = Path(__file__).parent.parent / "config" / "payer_rules.json"
|
PAYER_RULES_PATH = Path(__file__).parent.parent / "config" / "payer_rules.json"
|
||||||
|
|
||||||
|
|
||||||
class CoverageFlag(str, Enum):
|
class CoverageFlag(str, Enum):
|
||||||
REFILL_WINDOW = "REFILL_WINDOW"
|
SUPPLY_LAPSED = "SUPPLY_LAPSED" # supply cycle ended, no new shipment
|
||||||
VISIT_DUE = "VISIT_DUE"
|
VISIT_REQUIRED = "VISIT_REQUIRED" # visit date past due, no confirmed new visit
|
||||||
OUT_OF_COVERAGE = "OUT_OF_COVERAGE"
|
TRANSFER_PENDING = "TRANSFER_PENDING" # transferred patient, all docs need verification
|
||||||
OK = "OK"
|
RENEWAL_CRITICAL = "RENEWAL_CRITICAL" # <= 45 days to next visit due
|
||||||
|
RENEWAL_ELEVATED = "RENEWAL_ELEVATED" # <= 60 days to next visit due
|
||||||
|
RENEWAL_SOON = "RENEWAL_SOON" # <= 90 days to next visit due
|
||||||
|
RESUPPLY_READY = "RESUPPLY_READY" # within refill window, visit not yet urgent
|
||||||
|
ACTIVE = "ACTIVE" # all clear
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
@ -46,13 +55,22 @@ class ShipmentRecord:
|
||||||
|
|
||||||
patient_id: Supplier's internal MRN or account number.
|
patient_id: Supplier's internal MRN or account number.
|
||||||
This is the sole crosswalk key — no real identity data here.
|
This is the sole crosswalk key — no real identity data here.
|
||||||
|
csv_* fields: Optional doc status columns parsed from the CSV.
|
||||||
|
csv_transfer_from: Non-empty string means this is a transferred patient.
|
||||||
"""
|
"""
|
||||||
patient_id: str
|
patient_id: str
|
||||||
device_type: str
|
device_type: str
|
||||||
shipment_date: date
|
shipment_date: date
|
||||||
quantity: int
|
quantity: int
|
||||||
payer: str
|
payer: str
|
||||||
component: str = "sensor" # "sensor" | "transmitter" | "pod"
|
component: str = "sensor"
|
||||||
|
# Optional doc fields — populated when CSV contains these columns
|
||||||
|
csv_visit_date: Optional[date] = None
|
||||||
|
csv_swo_status: Optional[str] = None # "On File" | "Pending" | "Expired"
|
||||||
|
csv_pecos_verified: Optional[str] = None # "Yes" | "No"
|
||||||
|
csv_pa_status: Optional[str] = None # "Approved" | "Pending" | "Denied" | "Not Required"
|
||||||
|
csv_diagnosis_on_file: Optional[str] = None # "Yes" | "No"
|
||||||
|
csv_transfer_from: Optional[str] = None # prior supplier name if transfer
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -67,7 +85,9 @@ class CoverageResult:
|
||||||
flag: CoverageFlag
|
flag: CoverageFlag
|
||||||
days_until_coverage_end: int
|
days_until_coverage_end: int
|
||||||
days_until_visit_due: Optional[int]
|
days_until_visit_due: Optional[int]
|
||||||
priority_score: int # Higher = more urgent; used for worklist sort
|
priority_score: int
|
||||||
|
visit_date_confidence: str = "estimated" # "confirmed" | "estimated"
|
||||||
|
is_transfer: bool = False
|
||||||
rule_version: str = RULE_VERSION
|
rule_version: str = RULE_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -109,13 +129,16 @@ def _normalize_payer(payer: str) -> str:
|
||||||
"""
|
"""
|
||||||
Map raw DME CSV payer strings to canonical payer_rules.json keys.
|
Map raw DME CSV payer strings to canonical payer_rules.json keys.
|
||||||
|
|
||||||
"Medicare Part B" → "medicare"
|
"Medicare Part B" → "medicare"
|
||||||
"Medicaid - GA" → "medicaid"
|
"Medicare Advantage" → "medicare_advantage"
|
||||||
"Aetna PPO" → "commercial"
|
"Medicaid - GA" → "medicaid"
|
||||||
|
"Aetna PPO" → "commercial"
|
||||||
|
|
||||||
Preserves original value in CoverageResult.payer for display.
|
Preserves original value in CoverageResult.payer for display.
|
||||||
"""
|
"""
|
||||||
s = payer.strip().lower()
|
s = payer.strip().lower()
|
||||||
|
if "medicare advantage" in s or "medicare_advantage" in s:
|
||||||
|
return "medicare_advantage"
|
||||||
if "medicare" in s:
|
if "medicare" in s:
|
||||||
return "medicare"
|
return "medicare"
|
||||||
if "tricare for life" in s or s == "tfl":
|
if "tricare for life" in s or s == "tfl":
|
||||||
|
|
@ -136,66 +159,148 @@ def _get_payer_config(rules: dict, payer: str) -> dict:
|
||||||
return payer_rules.get(normalized, payer_rules.get("default", {}))
|
return payer_rules.get(normalized, payer_rules.get("default", {}))
|
||||||
|
|
||||||
|
|
||||||
def _compute_priority(flag: CoverageFlag, days_until_end: int) -> int:
|
def _resolve_visit_date(
|
||||||
|
record: ShipmentRecord,
|
||||||
|
confirmed_visit_date: Optional[date] = None,
|
||||||
|
) -> tuple[Optional[date], str]:
|
||||||
"""
|
"""
|
||||||
Priority score for worklist ordering. Higher = act sooner.
|
Visit date priority chain:
|
||||||
|
1. CSV visit date column (if present + valid)
|
||||||
OUT_OF_COVERAGE patients are highest priority regardless of days.
|
2. Supabase confirmed visit date (stored by staff)
|
||||||
Within REFILL_WINDOW and VISIT_DUE, urgency increases as days decrease.
|
3. Estimated proxy: shipment_date - 30 days
|
||||||
|
Returns (resolved_date, confidence) where confidence is 'confirmed' or 'estimated'.
|
||||||
"""
|
"""
|
||||||
if flag == CoverageFlag.OUT_OF_COVERAGE:
|
if record.csv_visit_date:
|
||||||
return 1000 + abs(days_until_end)
|
return record.csv_visit_date, "confirmed"
|
||||||
if flag == CoverageFlag.VISIT_DUE:
|
if confirmed_visit_date:
|
||||||
return 500 + max(0, 90 - days_until_end)
|
return confirmed_visit_date, "confirmed"
|
||||||
if flag == CoverageFlag.REFILL_WINDOW:
|
estimated = record.shipment_date - timedelta(days=30)
|
||||||
return 200 + max(0, 30 - days_until_end)
|
return estimated, "estimated"
|
||||||
return 0 # OK
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_coverage(record: ShipmentRecord,
|
def _compute_visit_flag(
|
||||||
as_of: Optional[date] = None) -> CoverageResult:
|
next_visit_due: date,
|
||||||
|
today: date,
|
||||||
|
) -> CoverageFlag:
|
||||||
|
"""Map days-to-next-visit to the appropriate tier flag."""
|
||||||
|
days = (next_visit_due - today).days
|
||||||
|
if days < 0:
|
||||||
|
return CoverageFlag.VISIT_REQUIRED
|
||||||
|
if days <= 45:
|
||||||
|
return CoverageFlag.RENEWAL_CRITICAL
|
||||||
|
if days <= 60:
|
||||||
|
return CoverageFlag.RENEWAL_ELEVATED
|
||||||
|
if days <= 90:
|
||||||
|
return CoverageFlag.RENEWAL_SOON
|
||||||
|
return CoverageFlag.ACTIVE
|
||||||
|
|
||||||
|
|
||||||
|
def _compute_priority(flag: CoverageFlag, days_until_visit: Optional[int]) -> int:
|
||||||
|
"""
|
||||||
|
Priority score for worklist sort. Higher = more urgent.
|
||||||
|
Supply Lapsed and Transfer Pending are always highest.
|
||||||
|
Within visit tiers, urgency increases as days decrease.
|
||||||
|
"""
|
||||||
|
urgency_days = abs(days_until_visit) if days_until_visit is not None else 0
|
||||||
|
if flag == CoverageFlag.SUPPLY_LAPSED:
|
||||||
|
return 2000 + urgency_days
|
||||||
|
if flag == CoverageFlag.TRANSFER_PENDING:
|
||||||
|
return 1800
|
||||||
|
if flag == CoverageFlag.VISIT_REQUIRED:
|
||||||
|
return 1500 + urgency_days
|
||||||
|
if flag == CoverageFlag.RENEWAL_CRITICAL:
|
||||||
|
return 1000 + (45 - max(0, urgency_days))
|
||||||
|
if flag == CoverageFlag.RENEWAL_ELEVATED:
|
||||||
|
return 700 + (60 - max(0, urgency_days))
|
||||||
|
if flag == CoverageFlag.RENEWAL_SOON:
|
||||||
|
return 400 + (90 - max(0, urgency_days))
|
||||||
|
if flag == CoverageFlag.RESUPPLY_READY:
|
||||||
|
return 200
|
||||||
|
return 0 # ACTIVE
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_coverage(
|
||||||
|
record: ShipmentRecord,
|
||||||
|
as_of: Optional[date] = None,
|
||||||
|
confirmed_visit_date: Optional[date] = None,
|
||||||
|
) -> CoverageResult:
|
||||||
"""
|
"""
|
||||||
Calculate coverage status for a single shipment record.
|
Calculate coverage status for a single shipment record.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
record: ShipmentRecord with non-PHI fields only.
|
record: ShipmentRecord with non-PHI fields only.
|
||||||
as_of: Date to evaluate against. Defaults to today.
|
as_of: Date to evaluate against. Defaults to today.
|
||||||
|
confirmed_visit_date: Confirmed visit date from Supabase (staff-entered).
|
||||||
Returns:
|
|
||||||
CoverageResult with flag, dates, and priority score.
|
|
||||||
"""
|
"""
|
||||||
rules = _load_payer_rules()
|
rules = _load_payer_rules()
|
||||||
today = as_of or date.today()
|
today = as_of or date.today()
|
||||||
|
|
||||||
|
# Transferred patients: all docs need verification before anything else
|
||||||
|
if record.csv_transfer_from:
|
||||||
|
return CoverageResult(
|
||||||
|
patient_id=record.patient_id,
|
||||||
|
device_type=record.device_type,
|
||||||
|
payer=record.payer,
|
||||||
|
component=record.component,
|
||||||
|
last_shipment_date=record.shipment_date,
|
||||||
|
coverage_end_date=record.shipment_date,
|
||||||
|
next_visit_due_date=None,
|
||||||
|
flag=CoverageFlag.TRANSFER_PENDING,
|
||||||
|
days_until_coverage_end=0,
|
||||||
|
days_until_visit_due=None,
|
||||||
|
priority_score=1800,
|
||||||
|
visit_date_confidence="estimated",
|
||||||
|
is_transfer=True,
|
||||||
|
rule_version=RULE_VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
wear_days = _get_wear_days(rules, record.device_type, record.component)
|
wear_days = _get_wear_days(rules, record.device_type, record.component)
|
||||||
payer_config = _get_payer_config(rules, record.payer)
|
payer_config = _get_payer_config(rules, record.payer)
|
||||||
|
|
||||||
# Coverage end = last shipment date + (wear days × quantity shipped)
|
|
||||||
total_wear_days = wear_days * record.quantity
|
total_wear_days = wear_days * record.quantity
|
||||||
coverage_end = record.shipment_date + timedelta(days=total_wear_days)
|
coverage_end = record.shipment_date + timedelta(days=total_wear_days)
|
||||||
days_until_end = (coverage_end - today).days
|
days_until_end = (coverage_end - today).days
|
||||||
|
|
||||||
refill_window_days = payer_config.get("refill_window_days", 30)
|
# Supply lapsed — coverage cycle ended
|
||||||
visit_renewal_days = payer_config.get("visit_renewal_days")
|
if days_until_end < 0:
|
||||||
|
visit_date, confidence = _resolve_visit_date(record, confirmed_visit_date)
|
||||||
|
next_visit_due = visit_date + timedelta(days=180) if visit_date else None
|
||||||
|
days_visit = (next_visit_due - today).days if next_visit_due else None
|
||||||
|
return CoverageResult(
|
||||||
|
patient_id=record.patient_id,
|
||||||
|
device_type=record.device_type,
|
||||||
|
payer=record.payer,
|
||||||
|
component=record.component,
|
||||||
|
last_shipment_date=record.shipment_date,
|
||||||
|
coverage_end_date=coverage_end,
|
||||||
|
next_visit_due_date=next_visit_due,
|
||||||
|
flag=CoverageFlag.SUPPLY_LAPSED,
|
||||||
|
days_until_coverage_end=days_until_end,
|
||||||
|
days_until_visit_due=days_visit,
|
||||||
|
priority_score=_compute_priority(CoverageFlag.SUPPLY_LAPSED, days_visit),
|
||||||
|
visit_date_confidence=confidence,
|
||||||
|
rule_version=RULE_VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
# Visit due date (Medicare: every 180 days from shipment)
|
visit_renewal_days = payer_config.get("visit_renewal_days")
|
||||||
|
refill_window_days = payer_config.get("refill_window_days", 30)
|
||||||
|
|
||||||
|
# Resolve visit date using priority chain
|
||||||
|
visit_date, confidence = _resolve_visit_date(record, confirmed_visit_date)
|
||||||
next_visit_due: Optional[date] = None
|
next_visit_due: Optional[date] = None
|
||||||
days_until_visit: Optional[int] = None
|
days_until_visit: Optional[int] = None
|
||||||
if visit_renewal_days:
|
flag = CoverageFlag.ACTIVE
|
||||||
next_visit_due = record.shipment_date + timedelta(days=visit_renewal_days)
|
|
||||||
|
if visit_renewal_days and visit_date:
|
||||||
|
next_visit_due = visit_date + timedelta(days=visit_renewal_days)
|
||||||
days_until_visit = (next_visit_due - today).days
|
days_until_visit = (next_visit_due - today).days
|
||||||
|
flag = _compute_visit_flag(next_visit_due, today)
|
||||||
|
|
||||||
# Determine flag — evaluated in priority order
|
# If no visit urgency but within refill window, mark resupply ready
|
||||||
if days_until_end < 0:
|
if flag == CoverageFlag.ACTIVE and days_until_end <= refill_window_days:
|
||||||
flag = CoverageFlag.OUT_OF_COVERAGE
|
flag = CoverageFlag.RESUPPLY_READY
|
||||||
elif days_until_visit is not None and days_until_visit <= 30:
|
|
||||||
flag = CoverageFlag.VISIT_DUE
|
|
||||||
elif days_until_end <= refill_window_days:
|
|
||||||
flag = CoverageFlag.REFILL_WINDOW
|
|
||||||
else:
|
|
||||||
flag = CoverageFlag.OK
|
|
||||||
|
|
||||||
priority = _compute_priority(flag, days_until_end)
|
priority = _compute_priority(flag, days_until_visit)
|
||||||
|
|
||||||
return CoverageResult(
|
return CoverageResult(
|
||||||
patient_id=record.patient_id,
|
patient_id=record.patient_id,
|
||||||
|
|
@ -209,22 +314,34 @@ def calculate_coverage(record: ShipmentRecord,
|
||||||
days_until_coverage_end=days_until_end,
|
days_until_coverage_end=days_until_end,
|
||||||
days_until_visit_due=days_until_visit,
|
days_until_visit_due=days_until_visit,
|
||||||
priority_score=priority,
|
priority_score=priority,
|
||||||
|
visit_date_confidence=confidence,
|
||||||
|
rule_version=RULE_VERSION,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def calculate_batch(records: list[ShipmentRecord],
|
def calculate_batch(
|
||||||
as_of: Optional[date] = None) -> list[CoverageResult]:
|
records: list[ShipmentRecord],
|
||||||
|
as_of: Optional[date] = None,
|
||||||
|
confirmed_visits: Optional[dict[str, date]] = None,
|
||||||
|
) -> list[CoverageResult]:
|
||||||
"""
|
"""
|
||||||
Calculate coverage for a list of shipment records and return a
|
Calculate coverage for a list of shipment records and return a
|
||||||
worklist sorted by priority (highest first).
|
worklist sorted by priority (highest first).
|
||||||
|
|
||||||
|
confirmed_visits: dict mapping patient_id_hash -> confirmed visit date.
|
||||||
|
Loaded from Supabase by persistence layer before calling this function.
|
||||||
|
|
||||||
Skips records that raise ValueError (unknown device/component) and
|
Skips records that raise ValueError (unknown device/component) and
|
||||||
logs a warning so the batch continues.
|
logs a warning so the batch continues.
|
||||||
"""
|
"""
|
||||||
|
import hashlib
|
||||||
|
confirmed_visits = confirmed_visits or {}
|
||||||
results = []
|
results = []
|
||||||
for record in records:
|
for record in records:
|
||||||
|
patient_hash = hashlib.sha256(record.patient_id.encode()).hexdigest()
|
||||||
|
confirmed_date = confirmed_visits.get(patient_hash)
|
||||||
try:
|
try:
|
||||||
result = calculate_coverage(record, as_of=as_of)
|
result = calculate_coverage(record, as_of=as_of, confirmed_visit_date=confirmed_date)
|
||||||
results.append(result)
|
results.append(result)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
logger.warning("Skipping record for patient_id hash — %s", exc)
|
logger.warning("Skipping record for patient_id hash — %s", exc)
|
||||||
|
|
|
||||||
165
python-backend/core/doc_state_machine.py
Normal file
165
python-backend/core/doc_state_machine.py
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
"""
|
||||||
|
doc_state_machine.py
|
||||||
|
Signal — STTIL Solutions
|
||||||
|
|
||||||
|
Computes per-patient documentation status across 5 items.
|
||||||
|
Requirements are payer-dependent. Produces cascade consequence chain.
|
||||||
|
|
||||||
|
PHI Contract: no patient names, DOBs, or contact info in any parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import date
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
# Payer types that require PECOS enrollment
|
||||||
|
_PECOS_REQUIRED = {"medicare", "medicare_advantage"}
|
||||||
|
|
||||||
|
# Payer types that require Prior Authorization for CGM
|
||||||
|
_PA_REQUIRED = {"medicare_advantage", "medicaid", "commercial"}
|
||||||
|
|
||||||
|
# Medicare FFS: PA not required (CGM coverage expansion 2023 + CMS-1828-F exemption path)
|
||||||
|
_PA_NOT_REQUIRED = {"medicare"}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DocState:
|
||||||
|
swo: str = "Pending" # "On File" | "Pending" | "Expired"
|
||||||
|
visit: str = "Not Confirmed" # "Confirmed YYYY-MM-DD" | "Estimated YYYY-MM-DD" | "Not Confirmed"
|
||||||
|
pecos: str = "Not Verified" # "Verified" | "Not Verified" | "N/A" | "Pending — Verify"
|
||||||
|
pa: str = "Required — Not Started" # "Not Required" | "Approved" | "Pending" | "Denied" | "Required — Not Started" | "Pending — Verify"
|
||||||
|
diagnosis: str = "Missing" # "On File" | "Missing" | "Pending — Verify"
|
||||||
|
cascade: list = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_visit_display(
|
||||||
|
csv_visit_date: Optional[date],
|
||||||
|
confirmed_visit_date: Optional[date],
|
||||||
|
is_transfer: bool,
|
||||||
|
) -> tuple[str, bool]:
|
||||||
|
"""
|
||||||
|
Returns (display_string, is_confirmed).
|
||||||
|
is_confirmed=True means the date is reliable (from CSV or staff-confirmed).
|
||||||
|
"""
|
||||||
|
if is_transfer:
|
||||||
|
return "Pending — Verify", False
|
||||||
|
if csv_visit_date:
|
||||||
|
return f"Confirmed {csv_visit_date.isoformat()}", True
|
||||||
|
if confirmed_visit_date:
|
||||||
|
return f"Confirmed {confirmed_visit_date.isoformat()}", True
|
||||||
|
return "Not Confirmed — Estimated Only", False
|
||||||
|
|
||||||
|
|
||||||
|
def _build_cascade(
|
||||||
|
swo: str,
|
||||||
|
visit_confirmed: bool,
|
||||||
|
pecos: str,
|
||||||
|
pa: str,
|
||||||
|
diagnosis: str,
|
||||||
|
payer_type: str,
|
||||||
|
) -> list:
|
||||||
|
"""
|
||||||
|
Build the downstream consequence chain for missing/at-risk doc items.
|
||||||
|
Only add cascade steps that are actually blocked.
|
||||||
|
"""
|
||||||
|
cascade = []
|
||||||
|
|
||||||
|
if not visit_confirmed:
|
||||||
|
cascade.append("Qualifying visit not confirmed")
|
||||||
|
if swo in ("Pending", "Expired"):
|
||||||
|
cascade.append("SWO validity at risk")
|
||||||
|
if payer_type in _PA_REQUIRED and pa in ("Pending", "Required — Not Started"):
|
||||||
|
cascade.append("PA renewal cannot proceed")
|
||||||
|
cascade.append("Next shipment blocked")
|
||||||
|
return cascade
|
||||||
|
|
||||||
|
if swo in ("Pending", "Expired"):
|
||||||
|
cascade.append("SWO missing or expired")
|
||||||
|
if payer_type in _PECOS_REQUIRED and pecos not in ("Verified",):
|
||||||
|
cascade.append("PECOS enrollment at risk")
|
||||||
|
cascade.append("Next shipment blocked")
|
||||||
|
|
||||||
|
if diagnosis == "Missing":
|
||||||
|
cascade.append("Diagnosis documentation missing — required for all payers")
|
||||||
|
|
||||||
|
return cascade
|
||||||
|
|
||||||
|
|
||||||
|
def compute_doc_state(
|
||||||
|
payer_type: str,
|
||||||
|
csv_swo_status: Optional[str],
|
||||||
|
csv_visit_date: Optional[date],
|
||||||
|
confirmed_visit_date: Optional[date],
|
||||||
|
csv_pecos_verified: Optional[str],
|
||||||
|
csv_pa_status: Optional[str],
|
||||||
|
csv_diagnosis_on_file: Optional[str],
|
||||||
|
is_transfer: bool = False,
|
||||||
|
) -> DocState:
|
||||||
|
"""
|
||||||
|
Compute the full documentation state for one patient.
|
||||||
|
|
||||||
|
payer_type: "medicare" | "medicare_advantage" | "medicaid" | "commercial"
|
||||||
|
All csv_* fields are Optional — None means not present in the CSV.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Transfer patients: everything is pending verification
|
||||||
|
if is_transfer:
|
||||||
|
return DocState(
|
||||||
|
swo="Pending — Verify",
|
||||||
|
visit="Pending — Verify",
|
||||||
|
pecos="Pending — Verify",
|
||||||
|
pa="Pending — Verify",
|
||||||
|
diagnosis="Pending — Verify",
|
||||||
|
cascade=[
|
||||||
|
"Patient transferred from prior supplier",
|
||||||
|
"Verify: SWO from current prescriber, qualifying visit within 6 months, PA status with current payer",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# SWO
|
||||||
|
swo_map = {"on file": "On File", "pending": "Pending", "expired": "Expired"}
|
||||||
|
swo = swo_map.get((csv_swo_status or "").strip().lower(), "Pending")
|
||||||
|
|
||||||
|
# Visit
|
||||||
|
visit_display, visit_confirmed = _resolve_visit_display(
|
||||||
|
csv_visit_date, confirmed_visit_date, is_transfer
|
||||||
|
)
|
||||||
|
|
||||||
|
# PECOS — only required for Medicare
|
||||||
|
if payer_type in _PECOS_REQUIRED:
|
||||||
|
pecos_val = (csv_pecos_verified or "").strip().lower()
|
||||||
|
pecos = "Verified" if pecos_val in ("yes", "verified", "true", "1") else "Not Verified"
|
||||||
|
else:
|
||||||
|
pecos = "N/A"
|
||||||
|
|
||||||
|
# PA — not required for Medicare FFS; required for MA, Medicaid, Commercial
|
||||||
|
if payer_type in _PA_NOT_REQUIRED:
|
||||||
|
pa = "Not Required"
|
||||||
|
elif csv_pa_status:
|
||||||
|
pa_map = {
|
||||||
|
"approved": "Approved",
|
||||||
|
"pending": "Pending",
|
||||||
|
"denied": "Denied",
|
||||||
|
"not required": "Not Required",
|
||||||
|
"not started": "Required — Not Started",
|
||||||
|
}
|
||||||
|
pa_key = csv_pa_status.strip().lower()
|
||||||
|
pa = pa_map.get(pa_key, "Required — Not Started")
|
||||||
|
else:
|
||||||
|
pa = "Required — Not Started"
|
||||||
|
|
||||||
|
# Diagnosis
|
||||||
|
dx_val = (csv_diagnosis_on_file or "").strip().lower()
|
||||||
|
diagnosis = "On File" if dx_val in ("yes", "true", "on file", "1") else "Missing"
|
||||||
|
|
||||||
|
cascade = _build_cascade(swo, visit_confirmed, pecos, pa, diagnosis, payer_type)
|
||||||
|
|
||||||
|
return DocState(
|
||||||
|
swo=swo,
|
||||||
|
visit=visit_display,
|
||||||
|
pecos=pecos,
|
||||||
|
pa=pa,
|
||||||
|
diagnosis=diagnosis,
|
||||||
|
cascade=cascade,
|
||||||
|
)
|
||||||
|
|
@ -150,9 +150,10 @@ def persist_upload(
|
||||||
client.table("mapping_decisions").insert(mapping_rows).execute()
|
client.table("mapping_decisions").insert(mapping_rows).execute()
|
||||||
|
|
||||||
# 5. Report run summary
|
# 5. Report run summary
|
||||||
|
_no_action_flags = {"OK", "ACTIVE", "RESUPPLY_READY"}
|
||||||
flagged = sum(
|
flagged = sum(
|
||||||
1 for r in coverage_results
|
1 for r in coverage_results
|
||||||
if (r.flag.value if hasattr(r.flag, "value") else str(r.flag)) != "OK"
|
if (r.flag.value if hasattr(r.flag, "value") else str(r.flag)) not in _no_action_flags
|
||||||
)
|
)
|
||||||
client.table("report_runs").insert({
|
client.table("report_runs").insert({
|
||||||
"batch_id": batch_id,
|
"batch_id": batch_id,
|
||||||
|
|
@ -170,6 +171,60 @@ def persist_upload(
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def upsert_confirmed_visit(
|
||||||
|
org_id: str,
|
||||||
|
patient_id_hash: str,
|
||||||
|
confirmed_date: date,
|
||||||
|
confirmed_by: str = "staff",
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Insert or update a confirmed visit date for a patient.
|
||||||
|
Returns True on success, False if Supabase unavailable.
|
||||||
|
"""
|
||||||
|
client = get_client()
|
||||||
|
if not client:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
client.table("confirmed_visits").upsert({
|
||||||
|
"org_id": org_id,
|
||||||
|
"patient_id_hash": patient_id_hash,
|
||||||
|
"confirmed_date": confirmed_date.isoformat(),
|
||||||
|
"confirmed_by": confirmed_by,
|
||||||
|
"updated_at": "now()",
|
||||||
|
}, on_conflict="org_id,patient_id_hash").execute()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to upsert confirmed visit: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def load_confirmed_visits_for_org(org_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
Load all confirmed visit dates for an org.
|
||||||
|
Returns dict mapping patient_id_hash -> confirmed_date.
|
||||||
|
"""
|
||||||
|
client = get_client()
|
||||||
|
if not client:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
result = client.table("confirmed_visits") \
|
||||||
|
.select("patient_id_hash,confirmed_date") \
|
||||||
|
.eq("org_id", org_id) \
|
||||||
|
.execute()
|
||||||
|
return {
|
||||||
|
row["patient_id_hash"]: date.fromisoformat(row["confirmed_date"])
|
||||||
|
for row in (result.data or [])
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to load confirmed visits: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_org(clerk_org_id: str | None = None) -> str | None:
|
||||||
|
"""Public wrapper around _get_or_create_org for use by the API."""
|
||||||
|
return _get_or_create_org(clerk_org_id=clerk_org_id)
|
||||||
|
|
||||||
|
|
||||||
def persist_export(batch_id: str | None, filename: str, row_count: int) -> None:
|
def persist_export(batch_id: str | None, filename: str, row_count: int) -> None:
|
||||||
"""Record that a work queue CSV was exported. Best-effort."""
|
"""Record that a work queue CSV was exported. Best-effort."""
|
||||||
if not batch_id:
|
if not batch_id:
|
||||||
|
|
|
||||||
19
python-backend/db/migrations/001_add_confirmed_visits.sql
Normal file
19
python-backend/db/migrations/001_add_confirmed_visits.sql
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
-- Migration 001: Add confirmed_visits table
|
||||||
|
-- Run in Supabase SQL Editor: Dashboard > SQL Editor > New query
|
||||||
|
-- Safe to run multiple times.
|
||||||
|
|
||||||
|
create table if not exists confirmed_visits (
|
||||||
|
id uuid primary key default uuid_generate_v4(),
|
||||||
|
org_id uuid not null references organizations(id) on delete cascade,
|
||||||
|
patient_id_hash text not null, -- SHA-256 of patient_id — no raw PHI
|
||||||
|
confirmed_date date not null,
|
||||||
|
confirmed_by text, -- staff identifier (not PHI — just a label)
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now(),
|
||||||
|
unique(org_id, patient_id_hash)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists idx_confirmed_visits_org on confirmed_visits(org_id);
|
||||||
|
create index if not exists idx_confirmed_visits_hash on confirmed_visits(patient_id_hash);
|
||||||
|
|
||||||
|
alter table confirmed_visits enable row level security;
|
||||||
|
|
@ -166,6 +166,21 @@ do $$ begin
|
||||||
end if;
|
end if;
|
||||||
end $$;
|
end $$;
|
||||||
|
|
||||||
|
-- Confirmed visit dates (staff-entered, persists across CSV imports)
|
||||||
|
create table if not exists confirmed_visits (
|
||||||
|
id uuid primary key default uuid_generate_v4(),
|
||||||
|
org_id uuid not null references organizations(id) on delete cascade,
|
||||||
|
patient_id_hash text not null,
|
||||||
|
confirmed_date date not null,
|
||||||
|
confirmed_by text,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now(),
|
||||||
|
unique(org_id, patient_id_hash)
|
||||||
|
);
|
||||||
|
create index if not exists idx_confirmed_visits_org on confirmed_visits(org_id);
|
||||||
|
create index if not exists idx_confirmed_visits_hash on confirmed_visits(patient_id_hash);
|
||||||
|
alter table confirmed_visits enable row level security;
|
||||||
|
|
||||||
-- Indexes
|
-- Indexes
|
||||||
create index if not exists idx_upload_batches_org on upload_batches(org_id);
|
create index if not exists idx_upload_batches_org on upload_batches(org_id);
|
||||||
create index if not exists idx_normalized_records_batch on normalized_records(batch_id);
|
create index if not exists idx_normalized_records_batch on normalized_records(batch_id);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import CSVImport from "./components/CSVImport";
|
||||||
import CSVExport from "./components/CSVExport";
|
import CSVExport from "./components/CSVExport";
|
||||||
import ThemeToggle from "./components/ThemeToggle";
|
import ThemeToggle from "./components/ThemeToggle";
|
||||||
import Toast from "./components/Toast";
|
import Toast from "./components/Toast";
|
||||||
|
import Privacy from "./Privacy";
|
||||||
import { showToast } from "./lib/toast";
|
import { showToast } from "./lib/toast";
|
||||||
import { uploadToBackend, apiRecordToLocal } from "./lib/api";
|
import { uploadToBackend, apiRecordToLocal } from "./lib/api";
|
||||||
import { parseCSV, processBatch } from "./lib/coverage";
|
import { parseCSV, processBatch } from "./lib/coverage";
|
||||||
|
|
@ -20,11 +21,16 @@ function AppInner() {
|
||||||
const [importLabel, setImportLabel] = useState("No data imported");
|
const [importLabel, setImportLabel] = useState("No data imported");
|
||||||
const csvImportRef = useRef(null);
|
const csvImportRef = useRef(null);
|
||||||
|
|
||||||
const ooc = records.filter((r) => r.flag === "OUT_OF_COVERAGE").length;
|
const supplyLapsed = records.filter((r) => r.flag === "SUPPLY_LAPSED").length;
|
||||||
const visitDue = records.filter((r) => r.flag === "VISIT_DUE").length;
|
const visitRequired = records.filter((r) => r.flag === "VISIT_REQUIRED").length;
|
||||||
const refill = records.filter((r) => r.flag === "REFILL_WINDOW").length;
|
const renewalCritical = records.filter((r) => r.flag === "RENEWAL_CRITICAL").length;
|
||||||
const okCount = records.filter((r) => r.flag === "OK").length;
|
const renewalElevated = records.filter((r) => r.flag === "RENEWAL_ELEVATED").length;
|
||||||
const urgent = ooc + visitDue;
|
const renewalSoon = records.filter((r) => r.flag === "RENEWAL_SOON").length;
|
||||||
|
const transferPending = records.filter((r) => r.flag === "TRANSFER_PENDING").length;
|
||||||
|
const refill = records.filter((r) => r.flag === "RESUPPLY_READY").length;
|
||||||
|
const okCount = records.filter((r) => r.flag === "ACTIVE").length;
|
||||||
|
const escalateCount = visitRequired + renewalCritical;
|
||||||
|
const outreachCount = renewalSoon + renewalElevated;
|
||||||
|
|
||||||
const handleResults = useCallback(async (file) => {
|
const handleResults = useCallback(async (file) => {
|
||||||
const label = new Date().toLocaleDateString("en-US", {
|
const label = new Date().toLocaleDateString("en-US", {
|
||||||
|
|
@ -66,9 +72,9 @@ function AppInner() {
|
||||||
<div className="flex w-full min-h-screen bg-[var(--bg-page)] text-[var(--text-primary)] transition-colors">
|
<div className="flex w-full min-h-screen bg-[var(--bg-page)] text-[var(--text-primary)] transition-colors">
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<Sidebar
|
<Sidebar
|
||||||
oocCount={ooc}
|
supplyLapsedCount={supplyLapsed}
|
||||||
visitDueCount={visitDue}
|
escalateCount={escalateCount}
|
||||||
refillCount={refill}
|
clearToShipCount={refill}
|
||||||
activeFilter={activeFilter}
|
activeFilter={activeFilter}
|
||||||
onFilterChange={setActiveFilter}
|
onFilterChange={setActiveFilter}
|
||||||
onImportClick={() => csvImportRef.current?.trigger()}
|
onImportClick={() => csvImportRef.current?.trigger()}
|
||||||
|
|
@ -106,19 +112,19 @@ function AppInner() {
|
||||||
<div className="grid grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-3 gap-4 mb-6">
|
||||||
<StatCard
|
<StatCard
|
||||||
priority
|
priority
|
||||||
label="⚡ Prescriber Action"
|
label="Needs Attention"
|
||||||
value={urgent}
|
value={supplyLapsed + escalateCount + transferPending}
|
||||||
sub={`${ooc} out of coverage · ${visitDue} visit${visitDue !== 1 ? "s" : ""} due — prescriber contact required`}
|
sub={`${supplyLapsed} docs required · ${escalateCount} escalate · ${transferPending} new patient`}
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
label="⚠ Resupply Ready"
|
label="Outreach in Progress"
|
||||||
|
value={outreachCount}
|
||||||
|
sub={`${renewalElevated} confirm appt. · ${renewalSoon} begin outreach`}
|
||||||
|
/>
|
||||||
|
<StatCard
|
||||||
|
label="Clear to Ship"
|
||||||
value={refill}
|
value={refill}
|
||||||
sub="patients within resupply window — initiate now"
|
sub={`${refill} patients in resupply window · ${okCount} on track`}
|
||||||
/>
|
|
||||||
<StatCard
|
|
||||||
label="✓ Active"
|
|
||||||
value={okCount}
|
|
||||||
sub="patients with supply on track — no action needed"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -139,6 +145,11 @@ function AppInner() {
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { isSignedIn, isLoaded } = useAuth();
|
const { isSignedIn, isLoaded } = useAuth();
|
||||||
|
|
||||||
|
// Public routes — accessible without authentication
|
||||||
|
if (window.location.pathname === "/privacy") {
|
||||||
|
return <Privacy />;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isLoaded) {
|
if (!isLoaded) {
|
||||||
return <div className="flex items-center justify-center min-h-screen bg-[#F0EAE1]" />;
|
return <div className="flex items-center justify-center min-h-screen bg-[#F0EAE1]" />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
158
signal-ui/src/Privacy.jsx
Normal file
158
signal-ui/src/Privacy.jsx
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
export default function Privacy() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-[var(--bg-page,#F4F9F9)]">
|
||||||
|
<div className="max-w-[700px] mx-auto px-6 py-12 font-body text-[var(--text-primary,#1C2C2C)]">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="mb-10">
|
||||||
|
<div className="text-[11px] font-semibold tracking-[0.12em] uppercase text-[var(--brand,#147A7A)] mb-3">
|
||||||
|
STTIL Solutions LLC
|
||||||
|
</div>
|
||||||
|
<h1 className="font-heading font-bold text-[28px] text-[#0A3030] mb-2 leading-tight">
|
||||||
|
Signal Privacy Policy
|
||||||
|
</h1>
|
||||||
|
<p className="text-[13px] text-[var(--text-muted,#7A9E9E)]">
|
||||||
|
Effective Date: June 7, 2026
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Who We Are */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">Who We Are</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)]">
|
||||||
|
STTIL Solutions LLC ("STTIL", "we", "us") operates Signal, a documentation readiness tool for
|
||||||
|
DMEPOS suppliers. Signal helps supplier staff identify patients whose documentation may need
|
||||||
|
attention before supplies are shipped or claims are submitted. This Privacy Policy describes
|
||||||
|
how Signal handles the data uploaded to our platform.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* What We Process */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">What Data Signal Processes</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)] mb-4">
|
||||||
|
When your staff uploads a CSV file to Signal, we process:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-5 text-[14px] leading-[1.85] text-[var(--text-secondary,#5A7E7E)] space-y-1">
|
||||||
|
<li>Your internal patient identifier (MRN or account number) — used as a de-identified tracking key only</li>
|
||||||
|
<li>Device type (e.g., Dexcom G7, FreeStyle Libre 3)</li>
|
||||||
|
<li>Most recent supply shipment date</li>
|
||||||
|
<li>Payer name (Medicare, Medicaid, Commercial)</li>
|
||||||
|
<li>Quantity shipped</li>
|
||||||
|
<li>Documentation status fields if included in your CSV (SWO status, PA status, PECOS flag)</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)] mt-4">
|
||||||
|
Signal produces a prioritized documentation worklist as output.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* What We Do NOT Store */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">What Signal Does NOT Store</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)] mb-4">
|
||||||
|
Signal is designed to operate without storing protected health information (PHI). We never store:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-5 text-[14px] leading-[1.85] text-[var(--text-secondary,#5A7E7E)] space-y-1">
|
||||||
|
<li>Patient names</li>
|
||||||
|
<li>Social Security numbers</li>
|
||||||
|
<li>Dates of birth</li>
|
||||||
|
<li>Home addresses</li>
|
||||||
|
<li>Phone numbers or email addresses</li>
|
||||||
|
<li>Diagnosis codes (ICD codes)</li>
|
||||||
|
<li>Clinical notes or records</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)] mt-4">
|
||||||
|
The connection between a patient identifier and a real patient identity stays with your staff.
|
||||||
|
STTIL never holds that crosswalk.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* How We Store */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">How We Store What We Do Collect</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)]">
|
||||||
|
Patient identifiers are hashed using SHA-256 before storage — we store a cryptographic fingerprint
|
||||||
|
of the identifier, not the identifier itself. The hash cannot be reversed to recover the original
|
||||||
|
value. All stored data is associated with your organization's account and protected by row-level
|
||||||
|
security (RLS) policies. Your data is not accessible to other Signal customers. All data in
|
||||||
|
transit is encrypted via HTTPS. All data at rest is encrypted using AES-256.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Patient Outreach */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">Patient Outreach</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)]">
|
||||||
|
Signal does not contact patients. All outreach and supply decisions remain with your staff.
|
||||||
|
Signal surfaces documentation conditions — your team acts on them. All prescriber outreach
|
||||||
|
flags go to the prescriber office, never to the patient.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Data Retention */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">Data Retention and Deletion</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)]">
|
||||||
|
Your data is retained for the duration of your subscription plus 90 days. Upon written request,
|
||||||
|
all data associated with your account will be deleted within 10 business days, and we will
|
||||||
|
confirm deletion in writing. Audit logs are retained for 7 years for compliance purposes.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* HIPAA */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">HIPAA</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)]">
|
||||||
|
Signal processes de-identified operational data. To the extent that any data processed by Signal
|
||||||
|
could be considered protected health information under HIPAA, STTIL Solutions has executed a
|
||||||
|
Business Associate Agreement (BAA) with its cloud infrastructure providers and operates
|
||||||
|
appropriate administrative, physical, and technical safeguards. If you believe a BAA is required
|
||||||
|
for your engagement with Signal, please contact us before uploading any data.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Security Incidents */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">Security Incidents</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)]">
|
||||||
|
In the event of a security incident affecting your organization's data, STTIL Solutions will
|
||||||
|
notify you within 72 hours of discovery. We will provide a description of the incident, the
|
||||||
|
data affected, and the steps taken to address it.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Contact */}
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="font-heading font-semibold text-[16px] text-[#0A3030] mb-3">Contact</h2>
|
||||||
|
<p className="text-[14px] leading-[1.75] text-[var(--text-secondary,#5A7E7E)]">
|
||||||
|
Questions about this Privacy Policy or your data:
|
||||||
|
<br />
|
||||||
|
<strong className="text-[var(--text-primary,#1C2C2C)]">STTIL Solutions LLC</strong>
|
||||||
|
<br />
|
||||||
|
Email:{" "}
|
||||||
|
<a
|
||||||
|
href="mailto:kisasttil@gmail.com"
|
||||||
|
className="text-[var(--brand,#147A7A)] underline underline-offset-2"
|
||||||
|
>
|
||||||
|
kisasttil@gmail.com
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<div className="border-t border-[var(--border-color,#C8D8D8)] pt-6 mt-10">
|
||||||
|
<p className="text-[11px] text-[var(--text-muted,#7A9E9E)] leading-[1.6]">
|
||||||
|
Signal is operated by STTIL Solutions LLC.{" "}
|
||||||
|
<a
|
||||||
|
href="https://signal.sttilsolutions.com"
|
||||||
|
className="text-[var(--brand,#147A7A)] underline underline-offset-2"
|
||||||
|
>
|
||||||
|
signal.sttilsolutions.com
|
||||||
|
</a>
|
||||||
|
{" "}· We may update this policy from time to time. We will notify active customers of material
|
||||||
|
changes by email. Continued use of Signal following notice constitutes acceptance.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,48 +1,88 @@
|
||||||
import { getFlagLabel, getFlagAction } from "../lib/coverage";
|
const FLAG_CONFIG = {
|
||||||
|
// RED — At Risk: supply may be interrupted, immediate action required
|
||||||
function Badge({ flag, dark }) {
|
SUPPLY_LAPSED: {
|
||||||
const style = badgeStyles(flag, dark);
|
label: "At Risk",
|
||||||
|
bg: "bg-[rgba(176,0,0,0.12)]",
|
||||||
|
text: "text-[#B00000]",
|
||||||
|
border: "border-[#B00000]",
|
||||||
|
weight: "font-bold",
|
||||||
|
icon: "⚠",
|
||||||
|
},
|
||||||
|
VISIT_REQUIRED: {
|
||||||
|
label: "At Risk",
|
||||||
|
bg: "bg-[rgba(204,34,34,0.10)]",
|
||||||
|
text: "text-[#CC2222]",
|
||||||
|
border: "border-[#CC2222]",
|
||||||
|
weight: "font-bold",
|
||||||
|
icon: "⚠",
|
||||||
|
},
|
||||||
|
RENEWAL_CRITICAL: {
|
||||||
|
label: "At Risk",
|
||||||
|
bg: "bg-[rgba(220,60,0,0.10)]",
|
||||||
|
text: "text-[#DC3C00]",
|
||||||
|
border: "border-[#DC3C00]",
|
||||||
|
weight: "font-bold",
|
||||||
|
icon: "⚠",
|
||||||
|
},
|
||||||
|
// AMBER — Action Needed: confirm requirements before patient's specific deadline
|
||||||
|
RENEWAL_ELEVATED: {
|
||||||
|
label: "Action Needed",
|
||||||
|
bg: "bg-[rgba(203,107,32,0.12)]",
|
||||||
|
text: "text-[#CB6B20]",
|
||||||
|
border: "border-[#CB6B20]",
|
||||||
|
weight: "font-semibold",
|
||||||
|
icon: "→",
|
||||||
|
},
|
||||||
|
RENEWAL_SOON: {
|
||||||
|
label: "Action Needed",
|
||||||
|
bg: "bg-[rgba(203,107,32,0.08)]",
|
||||||
|
text: "text-[#CB6B20]",
|
||||||
|
border: "border-[#CB6B20]",
|
||||||
|
weight: "font-normal",
|
||||||
|
icon: "→",
|
||||||
|
},
|
||||||
|
TRANSFER_PENDING: {
|
||||||
|
label: "Action Needed",
|
||||||
|
bg: "bg-[rgba(203,107,32,0.08)]",
|
||||||
|
text: "text-[#CB6B20]",
|
||||||
|
border: "border-[#CB6B20]",
|
||||||
|
weight: "font-semibold",
|
||||||
|
icon: "→",
|
||||||
|
},
|
||||||
|
// GREEN — no action needed
|
||||||
|
RESUPPLY_READY: {
|
||||||
|
label: "Clear to Ship",
|
||||||
|
bg: "bg-[rgba(46,163,163,0.12)]",
|
||||||
|
text: "text-[#1A7070]",
|
||||||
|
border: "border-[#2EA3A3]",
|
||||||
|
weight: "font-semibold",
|
||||||
|
icon: "✓",
|
||||||
|
},
|
||||||
|
ACTIVE: {
|
||||||
|
label: "On Track",
|
||||||
|
bg: "bg-[rgba(40,160,80,0.10)]",
|
||||||
|
text: "text-[#1A8040]",
|
||||||
|
border: "border-[#28A050]",
|
||||||
|
weight: "font-normal",
|
||||||
|
icon: "●",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Badge({ flag }) {
|
||||||
|
const cfg = FLAG_CONFIG[flag] || {
|
||||||
|
label: flag,
|
||||||
|
bg: "bg-gray-100",
|
||||||
|
text: "text-gray-600",
|
||||||
|
border: "border-gray-300",
|
||||||
|
weight: "font-normal",
|
||||||
|
icon: "",
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`inline-flex items-center gap-[5px] px-[9px] py-[3px] rounded-full text-[11.5px] font-medium whitespace-nowrap ${style}`}
|
className={`inline-flex items-center gap-[3px] px-[8px] py-[3px] rounded-full text-[11px] border ${cfg.bg} ${cfg.text} ${cfg.border} ${cfg.weight} whitespace-nowrap`}
|
||||||
title={getFlagAction(flag)}
|
|
||||||
>
|
>
|
||||||
<span>{style.icon}</span>
|
{cfg.icon && <span className="text-[10px]">{cfg.icon}</span>}
|
||||||
{getFlagLabel(flag)}
|
{cfg.label}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function badgeStyles(flag, dark) {
|
|
||||||
if (dark) return darkBadge(flag);
|
|
||||||
return lightBadge(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
function darkBadge(flag) {
|
|
||||||
switch (flag) {
|
|
||||||
case "OUT_OF_COVERAGE":
|
|
||||||
return { icon: "✕", className: "bg-[rgba(200,48,48,0.22)] text-[#FF7070]" };
|
|
||||||
case "VISIT_DUE":
|
|
||||||
return { icon: "⚡", className: "bg-[rgba(240,120,64,0.18)] text-[#FFB070] border border-[rgba(240,120,64,0.3)]" };
|
|
||||||
case "REFILL_WINDOW":
|
|
||||||
return { icon: "⚠", className: "bg-[rgba(168,90,24,0.16)] text-[#F0B464]" };
|
|
||||||
default:
|
|
||||||
return { icon: "✓", className: "bg-[rgba(26,122,78,0.16)] text-[#4AE899]" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function lightBadge(flag) {
|
|
||||||
switch (flag) {
|
|
||||||
case "OUT_OF_COVERAGE":
|
|
||||||
return { icon: "✕", className: "bg-[#F8CCCC] text-[#A02020]" };
|
|
||||||
case "VISIT_DUE":
|
|
||||||
return { icon: "⚡", className: "bg-[#FFE4CC] text-[#903A14] border border-[rgba(144,58,20,0.2)]" };
|
|
||||||
case "REFILL_WINDOW":
|
|
||||||
return { icon: "⚠", className: "bg-[#FDECD5] text-[#A85A18]" };
|
|
||||||
default:
|
|
||||||
return { icon: "✓", className: "bg-[#C8EDD8] text-[#146040]" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Badge;
|
|
||||||
|
|
@ -34,10 +34,19 @@ export default function CSVExport({ records, batchId }) {
|
||||||
const headers = [
|
const headers = [
|
||||||
"Patient ID", "Device", "Payer", "Status", "Priority Score",
|
"Patient ID", "Device", "Payer", "Status", "Priority Score",
|
||||||
"Days Until Resupply End", "Recommended Action", "Resupply End Date", "Reason",
|
"Days Until Resupply End", "Recommended Action", "Resupply End Date", "Reason",
|
||||||
|
"Visit Confidence",
|
||||||
];
|
];
|
||||||
const rows = records.map((r) => [
|
const rows = records.map((r) => [
|
||||||
r.patient_id, getDeviceDisplay(r.device_type), r.payer, getFlagLabel(r.flag),
|
r.patient_id,
|
||||||
r.priority, r.daysUntilEnd, getFlagAction(r.flag), r.coverageEndDate || "", r.reason || "",
|
r.device_display || getDeviceDisplay(r.device_type),
|
||||||
|
r.payer,
|
||||||
|
r.status_label || getFlagLabel(r.flag),
|
||||||
|
r.priority_score ?? r.priority,
|
||||||
|
r.days_until_coverage_end ?? r.daysUntilEnd,
|
||||||
|
r.action || getFlagAction(r.flag),
|
||||||
|
r.coverage_end_date || r.coverageEndDate || "",
|
||||||
|
r.reason || "",
|
||||||
|
r.visit_date_confidence || "estimated",
|
||||||
]);
|
]);
|
||||||
const csv = [headers, ...rows]
|
const csv = [headers, ...rows]
|
||||||
.map((row) => row.map((v) => `"${String(v).replace(/"/g, '""')}"`).join(","))
|
.map((row) => row.map((v) => `"${String(v).replace(/"/g, '""')}"`).join(","))
|
||||||
|
|
|
||||||
105
signal-ui/src/components/ConfirmVisitModal.jsx
Normal file
105
signal-ui/src/components/ConfirmVisitModal.jsx
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { confirmVisit } from "../lib/api";
|
||||||
|
|
||||||
|
export default function ConfirmVisitModal({ record, onClose, onConfirmed }) {
|
||||||
|
const [visitDate, setVisitDate] = useState("");
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
|
const today = new Date().toISOString().split("T")[0];
|
||||||
|
const minDate = "2020-01-01";
|
||||||
|
|
||||||
|
// Compute shipment_date from coverage_end_date and days_until_coverage_end
|
||||||
|
function computeShipmentDate() {
|
||||||
|
if (record.coverage_end_date && record.days_until_coverage_end !== undefined) {
|
||||||
|
const coverageEnd = new Date(record.coverage_end_date);
|
||||||
|
const shipmentMs = coverageEnd.getTime() - record.days_until_coverage_end * 86400000;
|
||||||
|
return new Date(shipmentMs).toISOString().split("T")[0];
|
||||||
|
}
|
||||||
|
return today;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave() {
|
||||||
|
setError("");
|
||||||
|
if (!visitDate) {
|
||||||
|
setError("Please enter a visit date.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (visitDate > today) {
|
||||||
|
setError("Visit date cannot be in the future.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSaving(true);
|
||||||
|
try {
|
||||||
|
const updated = await confirmVisit({
|
||||||
|
patient_id: record.patient_id,
|
||||||
|
confirmed_date: visitDate,
|
||||||
|
shipment_date: computeShipmentDate(),
|
||||||
|
payer: record.payer,
|
||||||
|
device_type: record.device_type,
|
||||||
|
quantity: 1,
|
||||||
|
component: record.component || "sensor",
|
||||||
|
});
|
||||||
|
onConfirmed(record.patient_id, updated);
|
||||||
|
onClose();
|
||||||
|
} catch (e) {
|
||||||
|
setError(e.message || "Failed to save. Please try again.");
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40" onClick={onClose}>
|
||||||
|
<div
|
||||||
|
className="bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[12px] shadow-xl p-6 w-[420px] max-w-[95vw]"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div className="font-heading font-bold text-[15px] text-[var(--text-heading)] mb-1">
|
||||||
|
Confirm Qualifying Visit
|
||||||
|
</div>
|
||||||
|
<div className="text-[12px] text-[var(--text-muted)] mb-4">
|
||||||
|
Patient ID: <span className="font-mono font-semibold text-[var(--text-secondary)]">{record.patient_id}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label className="block text-[12px] font-semibold text-[var(--text-secondary)] mb-1">
|
||||||
|
Qualifying visit date — confirmed with prescriber office or from SWO on file
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={visitDate}
|
||||||
|
max={today}
|
||||||
|
min={minDate}
|
||||||
|
onChange={(e) => setVisitDate(e.target.value)}
|
||||||
|
className="w-full border border-[var(--border-color)] rounded-[6px] px-3 py-2 text-[13px] bg-[var(--bg-elevated)] text-[var(--text-primary)] mb-2 focus:outline-none focus:border-[var(--brand)]"
|
||||||
|
/>
|
||||||
|
<div className="text-[10.5px] text-[var(--text-muted)] mb-4">
|
||||||
|
Contact prescriber office to confirm. Do not use patient-reported date.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="text-[11px] text-[#CC2222] mb-3 px-2 py-1 bg-red-50 rounded border border-red-200">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex gap-3 justify-end">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-4 py-2 text-[12px] rounded-md border border-[var(--border-color)] text-[var(--text-secondary)] hover:border-[var(--brand)] cursor-pointer"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={saving}
|
||||||
|
className="px-4 py-2 text-[12px] rounded-md bg-[var(--brand)] text-white font-semibold hover:opacity-90 cursor-pointer disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{saving ? "Saving..." : "Save Visit Date"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
68
signal-ui/src/components/DocStatusBar.jsx
Normal file
68
signal-ui/src/components/DocStatusBar.jsx
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* DocStatusBar — 5 colored dots showing SWO, Visit, PECOS, PA, Diagnosis status.
|
||||||
|
* Green = complete, Amber = pending/estimated, Red = missing/denied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const DOT_STATUS = {
|
||||||
|
// SWO
|
||||||
|
"Expired": "red",
|
||||||
|
// PECOS
|
||||||
|
"Verified": "green",
|
||||||
|
"Not Verified": "red",
|
||||||
|
"N/A": "gray",
|
||||||
|
// PA
|
||||||
|
"Not Required": "green",
|
||||||
|
"Approved": "green",
|
||||||
|
"Denied": "red",
|
||||||
|
// Diagnosis
|
||||||
|
"Missing": "red",
|
||||||
|
// Transfer / generic pending
|
||||||
|
"Pending — Verify": "amber",
|
||||||
|
"Pending": "amber",
|
||||||
|
"Required — Not Started": "red",
|
||||||
|
"Not Confirmed — Estimated Only": "amber",
|
||||||
|
"Not Confirmed": "amber",
|
||||||
|
};
|
||||||
|
|
||||||
|
function dotColor(status) {
|
||||||
|
if (!status) return "amber";
|
||||||
|
const s = status.trim();
|
||||||
|
if (s.startsWith("Confirmed")) return "green";
|
||||||
|
if (s.startsWith("Approved")) return "green";
|
||||||
|
if (s === "On File") return "green";
|
||||||
|
return DOT_STATUS[s] || "amber";
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLOR_MAP = {
|
||||||
|
green: "bg-[#28A050]",
|
||||||
|
amber: "bg-[#CB6B20]",
|
||||||
|
red: "bg-[#CC2222]",
|
||||||
|
gray: "bg-[#AAAAAA]",
|
||||||
|
};
|
||||||
|
|
||||||
|
const LABELS = ["SWO", "Visit", "PECOS", "PA", "Dx"];
|
||||||
|
|
||||||
|
export default function DocStatusBar({ docState }) {
|
||||||
|
if (!docState) return null;
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
docState.swo,
|
||||||
|
docState.visit,
|
||||||
|
docState.pecos,
|
||||||
|
docState.pa,
|
||||||
|
docState.diagnosis,
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-[5px]" title="Documentation status: SWO · Visit · PECOS · PA · Diagnosis">
|
||||||
|
{items.map((status, i) => (
|
||||||
|
<div key={i} className="flex flex-col items-center gap-[2px]">
|
||||||
|
<div
|
||||||
|
className={`w-[8px] h-[8px] rounded-full ${COLOR_MAP[dotColor(status)]}`}
|
||||||
|
title={`${LABELS[i]}: ${status || "Unknown"}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export default function Sidebar({
|
export default function Sidebar({
|
||||||
oocCount,
|
supplyLapsedCount,
|
||||||
visitDueCount,
|
escalateCount,
|
||||||
refillCount,
|
clearToShipCount,
|
||||||
activeFilter,
|
activeFilter,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onImportClick,
|
onImportClick,
|
||||||
|
|
@ -14,23 +14,24 @@ export default function Sidebar({
|
||||||
badge: null,
|
badge: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Resupply Ready",
|
label: "Docs Required",
|
||||||
key: "REFILL_WINDOW",
|
key: "SUPPLY_LAPSED",
|
||||||
icon: "⚠",
|
icon: "!",
|
||||||
badge: refillCount,
|
badge: supplyLapsedCount,
|
||||||
badgeWarn: true,
|
badgeWarn: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Renewal Due",
|
label: "Escalate",
|
||||||
key: "VISIT_DUE",
|
key: "escalate",
|
||||||
icon: "⚡",
|
icon: "!",
|
||||||
badge: visitDueCount,
|
badge: escalateCount,
|
||||||
|
badgeWarn: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Supply Lapsed",
|
label: "Clear to Ship",
|
||||||
key: "OUT_OF_COVERAGE",
|
key: "RESUPPLY_READY",
|
||||||
icon: "✕",
|
icon: "✓",
|
||||||
badge: oocCount,
|
badge: clearToShipCount,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
import { useTheme } from "../useTheme";
|
import { useTheme } from "../useTheme";
|
||||||
import Badge from "./Badge";
|
import Badge from "./Badge";
|
||||||
import { getDeviceDisplay } from "../lib/coverage";
|
import DocStatusBar from "./DocStatusBar";
|
||||||
|
import ConfirmVisitModal from "./ConfirmVisitModal";
|
||||||
|
|
||||||
function daysLabel(days) {
|
function daysLabel(days) {
|
||||||
|
if (days === undefined || days === null) return <span className="font-mono font-medium text-[var(--text-muted)]">—</span>;
|
||||||
if (days < 0)
|
if (days < 0)
|
||||||
return <span className="font-mono font-semibold text-[#FF7070]">Expired {Math.abs(days)}d ago</span>;
|
return <span className="font-mono font-semibold text-[#FF7070]">Expired {Math.abs(days)}d ago</span>;
|
||||||
if (days <= 7)
|
if (days <= 7)
|
||||||
|
|
@ -13,30 +16,49 @@ function daysLabel(days) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function scoreClass(priority) {
|
function scoreClass(priority) {
|
||||||
|
if (priority >= 1000) return "text-[#B00000]";
|
||||||
if (priority >= 500) return "text-[var(--accent-text)]";
|
if (priority >= 500) return "text-[var(--accent-text)]";
|
||||||
if (priority >= 200) return "text-[var(--text-secondary)]";
|
if (priority >= 200) return "text-[var(--text-secondary)]";
|
||||||
return "text-[var(--text-muted)]";
|
return "text-[var(--text-muted)]";
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHighPriority(flag) {
|
|
||||||
return flag === "OUT_OF_COVERAGE" || flag === "VISIT_DUE";
|
|
||||||
}
|
|
||||||
|
|
||||||
const FILTERS = [
|
const FILTERS = [
|
||||||
{ key: "all", label: "All" },
|
{ key: "all", label: "All" },
|
||||||
{ key: "OUT_OF_COVERAGE", label: "Supply Lapsed" },
|
{ key: "at-risk", label: "At Risk" },
|
||||||
{ key: "VISIT_DUE", label: "Renewal Due" },
|
{ key: "action-needed", label: "Action Needed" },
|
||||||
{ key: "REFILL_WINDOW", label: "Resupply Ready" },
|
{ key: "RESUPPLY_READY", label: "Clear to Ship" },
|
||||||
{ key: "OK", label: "Active" },
|
{ key: "ACTIVE", label: "On Track" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const AT_RISK_FLAGS = ["SUPPLY_LAPSED", "VISIT_REQUIRED", "RENEWAL_CRITICAL"];
|
||||||
|
const ACTION_NEEDED_FLAGS = ["RENEWAL_ELEVATED", "RENEWAL_SOON", "TRANSFER_PENDING"];
|
||||||
|
|
||||||
|
const HIGH_PRIORITY_FLAGS = ["SUPPLY_LAPSED", "VISIT_REQUIRED", "RENEWAL_CRITICAL", "TRANSFER_PENDING"];
|
||||||
|
const CONFIRM_VISIT_FLAGS = ["VISIT_REQUIRED", "RENEWAL_CRITICAL", "RENEWAL_ELEVATED", "RENEWAL_SOON"];
|
||||||
|
|
||||||
export default function WorklistTable({ records, activeFilter, onFilterChange }) {
|
export default function WorklistTable({ records, activeFilter, onFilterChange }) {
|
||||||
const { dark } = useTheme();
|
const { dark } = useTheme();
|
||||||
|
const [expandedRow, setExpandedRow] = useState(null);
|
||||||
|
const [confirmingRecord, setConfirmingRecord] = useState(null);
|
||||||
|
const [localRecords, setLocalRecords] = useState(records);
|
||||||
|
|
||||||
|
useEffect(() => { setLocalRecords(records); }, [records]);
|
||||||
|
|
||||||
|
function handleVisitConfirmed(patientId, updatedRecord) {
|
||||||
|
setLocalRecords(prev =>
|
||||||
|
prev.map(r => r.patient_id === patientId ? { ...r, ...updatedRecord } : r)
|
||||||
|
.sort((a, b) => (b.priority_score || b.priority || 0) - (a.priority_score || a.priority || 0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const filtered =
|
const filtered =
|
||||||
activeFilter === "all"
|
activeFilter === "all"
|
||||||
? records
|
? localRecords
|
||||||
: records.filter((r) => r.flag === activeFilter);
|
: activeFilter === "at-risk"
|
||||||
|
? localRecords.filter((r) => AT_RISK_FLAGS.includes(r.flag))
|
||||||
|
: activeFilter === "action-needed"
|
||||||
|
? localRecords.filter((r) => ACTION_NEEDED_FLAGS.includes(r.flag))
|
||||||
|
: localRecords.filter((r) => r.flag === activeFilter);
|
||||||
|
|
||||||
const todayStr = new Date().toLocaleDateString("en-US", {
|
const todayStr = new Date().toLocaleDateString("en-US", {
|
||||||
month: "short",
|
month: "short",
|
||||||
|
|
@ -53,7 +75,7 @@ export default function WorklistTable({ records, activeFilter, onFilterChange })
|
||||||
Outreach Worklist
|
Outreach Worklist
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-[11px] text-[var(--text-muted)] mt-[2px]">
|
<div className="font-mono text-[11px] text-[var(--text-muted)] mt-[2px]">
|
||||||
{records.length} patients · sorted by priority score · {todayStr}
|
{localRecords.length} patients · sorted by priority score · {todayStr}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-[6px] flex-wrap">
|
<div className="flex gap-[6px] flex-wrap">
|
||||||
|
|
@ -77,97 +99,129 @@ export default function WorklistTable({ records, activeFilter, onFilterChange })
|
||||||
<table className="w-full border-collapse">
|
<table className="w-full border-collapse">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
{["Patient ID", "Device", "Payer", "Days Left", "Status", "Priority", "Action"].map(h => (
|
||||||
Patient ID
|
<th key={h} className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
||||||
</th>
|
{h}
|
||||||
<th className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
</th>
|
||||||
Device
|
))}
|
||||||
</th>
|
|
||||||
<th className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
|
||||||
Payer
|
|
||||||
</th>
|
|
||||||
<th className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
|
||||||
Days Left
|
|
||||||
</th>
|
|
||||||
<th className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
|
||||||
Status
|
|
||||||
</th>
|
|
||||||
<th className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
|
||||||
Priority
|
|
||||||
</th>
|
|
||||||
<th className="px-[22px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-muted)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
|
|
||||||
Action
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{filtered.map((r, i) => {
|
{filtered.map((r, i) => {
|
||||||
const hp = isHighPriority(r.flag);
|
const hp = HIGH_PRIORITY_FLAGS.includes(r.flag);
|
||||||
|
const rowKey = r.patient_id + "-" + i;
|
||||||
|
const isExpanded = expandedRow === rowKey;
|
||||||
|
const showConfirmVisit = CONFIRM_VISIT_FLAGS.includes(r.flag) || r.visit_date_confidence === "estimated";
|
||||||
|
// Support both API shape (days_until_coverage_end) and legacy local shape (daysUntilEnd)
|
||||||
|
const daysLeft = r.days_until_coverage_end ?? r.daysUntilEnd;
|
||||||
|
const priority = r.priority_score ?? r.priority;
|
||||||
|
const deviceDisplay = r.device_display || r.device_type;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<>
|
||||||
key={r.patient_id + "-" + i}
|
<tr
|
||||||
className={`border-b border-[var(--border-subtle)] last:border-b-0 transition-colors hover:bg-[var(--row-hover)] ${
|
key={rowKey}
|
||||||
hp
|
className={`border-b border-[var(--border-subtle)] transition-colors cursor-pointer hover:bg-[var(--row-hover)] ${
|
||||||
? dark
|
hp
|
||||||
? "bg-[rgba(224,104,48,0.09)] hover:bg-[rgba(203,107,32,0.14)]"
|
? dark
|
||||||
: "bg-[rgba(224,96,40,0.05)] hover:bg-[rgba(203,107,32,0.14)]"
|
? "bg-[rgba(224,104,48,0.09)] hover:bg-[rgba(203,107,32,0.14)]"
|
||||||
: ""
|
: "bg-[rgba(224,96,40,0.05)] hover:bg-[rgba(203,107,32,0.14)]"
|
||||||
}`}
|
: ""
|
||||||
>
|
}`}
|
||||||
<td className="px-[22px] py-[13px] align-middle">
|
onClick={() => setExpandedRow(isExpanded ? null : rowKey)}
|
||||||
<div
|
>
|
||||||
className={`font-mono text-[12.5px] font-bold ${
|
<td className="px-[22px] py-[13px] align-middle">
|
||||||
hp
|
<div className={`font-mono text-[12.5px] font-bold ${hp ? "text-[var(--accent-text)]" : "text-[var(--text-secondary)]"}`}>
|
||||||
? "text-[var(--accent-text)]"
|
{r.patient_id}
|
||||||
: "text-[var(--text-secondary)]"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{r.patient_id}
|
|
||||||
</div>
|
|
||||||
{i === 0 && (
|
|
||||||
<div className="font-mono text-[9.5px] font-semibold tracking-[0.06em] text-[var(--accent-text)] mt-[2px]">
|
|
||||||
★ TOP PRIORITY
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
{i === 0 && (
|
||||||
</td>
|
<div className="font-mono text-[9.5px] font-semibold tracking-[0.06em] text-[var(--accent-text)] mt-[2px]">
|
||||||
<td className="px-[22px] py-[13px] text-[13.5px] font-medium text-[var(--text-primary)] align-middle">
|
★ TOP PRIORITY
|
||||||
{getDeviceDisplay(r.device_type)}
|
</div>
|
||||||
</td>
|
)}
|
||||||
<td className="px-[22px] py-[13px] text-[13px] text-[var(--text-secondary)] align-middle">
|
{r.visit_date_confidence === "estimated" && (
|
||||||
{r.payer}
|
<div className="text-[9px] text-[#CB6B20] mt-[2px]">Estimated visit date</div>
|
||||||
</td>
|
)}
|
||||||
<td className="px-[22px] py-[13px] align-middle">
|
</td>
|
||||||
{daysLabel(r.daysUntilEnd)}
|
<td className="px-[22px] py-[13px] text-[13.5px] font-medium text-[var(--text-primary)] align-middle">
|
||||||
</td>
|
{deviceDisplay}
|
||||||
<td className="px-[22px] py-[13px] align-middle">
|
</td>
|
||||||
<Badge flag={r.flag} dark={dark} />
|
<td className="px-[22px] py-[13px] text-[13px] text-[var(--text-secondary)] align-middle">
|
||||||
{r.reason && (
|
{r.payer}
|
||||||
<div className="text-[10.5px] text-[var(--text-muted)] mt-[4px] max-w-[220px] leading-[1.4]">
|
</td>
|
||||||
{r.reason}
|
<td className="px-[22px] py-[13px] align-middle">
|
||||||
|
{daysLabel(daysLeft)}
|
||||||
|
</td>
|
||||||
|
<td className="px-[22px] py-[13px] align-middle">
|
||||||
|
<div className="flex items-center gap-[8px]">
|
||||||
|
<Badge flag={r.flag} />
|
||||||
|
{r.doc_state && <DocStatusBar docState={r.doc_state} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
{r.reason && (
|
||||||
</td>
|
<div className="text-[10.5px] text-[var(--text-muted)] mt-[4px] max-w-[260px] leading-[1.4]">
|
||||||
<td className="px-[22px] py-[13px] align-middle">
|
{r.reason}
|
||||||
<span
|
</div>
|
||||||
className={`font-mono text-[16px] font-medium ${scoreClass(
|
)}
|
||||||
r.priority
|
</td>
|
||||||
)}`}
|
<td className="px-[22px] py-[13px] align-middle">
|
||||||
>
|
<span className={`font-mono text-[16px] font-medium ${scoreClass(priority)}`}>
|
||||||
{r.priority}
|
{priority}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-[22px] py-[13px] align-middle">
|
<td className="px-[22px] py-[13px] align-middle" onClick={e => e.stopPropagation()}>
|
||||||
<ActionButton flag={r.flag} />
|
{showConfirmVisit ? (
|
||||||
</td>
|
<button
|
||||||
</tr>
|
onClick={() => setConfirmingRecord(r)}
|
||||||
|
className="bg-transparent border border-[var(--accent)] text-[var(--accent-text)] px-[13px] py-[5px] rounded-md text-xs cursor-pointer font-body whitespace-nowrap transition-all hover:bg-[rgba(203,107,32,0.1)]"
|
||||||
|
>
|
||||||
|
Confirm Visit →
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<ActionButton flag={r.flag} />
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Expanded row — cascade + doc checklist */}
|
||||||
|
{isExpanded && (
|
||||||
|
<tr key={rowKey + "-expanded"} className="bg-[var(--bg-elevated)]">
|
||||||
|
<td colSpan={7} className="px-[32px] py-[16px]">
|
||||||
|
{r.cascade && r.cascade.length > 0 && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<div className="text-[11px] font-semibold text-[#CC2222] uppercase tracking-[0.06em] mb-2">
|
||||||
|
Documentation Cascade
|
||||||
|
</div>
|
||||||
|
<div className="text-[12px] text-[var(--text-secondary)] font-mono">
|
||||||
|
{r.cascade.join(" → ")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{r.doc_state && (
|
||||||
|
<div>
|
||||||
|
<div className="text-[11px] font-semibold text-[var(--text-muted)] uppercase tracking-[0.06em] mb-2">
|
||||||
|
Documentation Checklist
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-y-[6px] gap-x-[20px] text-[12px]">
|
||||||
|
<DocItem label="SWO" value={r.doc_state.swo} />
|
||||||
|
<DocItem label="Qualifying Visit" value={r.doc_state.visit} />
|
||||||
|
<DocItem label="PECOS Enrollment" value={r.doc_state.pecos} />
|
||||||
|
<DocItem label="Prior Authorization" value={r.doc_state.pa} />
|
||||||
|
<DocItem label="Diagnosis on File" value={r.doc_state.diagnosis} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!r.doc_state && !r.cascade?.length && (
|
||||||
|
<div className="text-[12px] text-[var(--text-muted)]">No documentation details available. Upload a CSV with doc columns to see the full checklist.</div>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{filtered.length === 0 && (
|
{filtered.length === 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td colSpan={7} className="px-[22px] py-8 text-center text-[var(--text-muted)]">
|
||||||
colSpan={7}
|
|
||||||
className="px-[22px] py-8 text-center text-[var(--text-muted)]"
|
|
||||||
>
|
|
||||||
No patients match this filter.
|
No patients match this filter.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -178,14 +232,23 @@ export default function WorklistTable({ records, activeFilter, onFilterChange })
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex items-center justify-between px-[22px] py-3 border-t border-[var(--border-subtle)] text-[11px] text-[var(--text-muted)] bg-[var(--bg-elevated)]">
|
<div className="flex items-center justify-between px-[22px] py-3 border-t border-[var(--border-subtle)] text-[11px] text-[var(--text-muted)] bg-[var(--bg-elevated)]">
|
||||||
<span className="flex items-center gap-[6px] text-[var(--text-secondary)]">
|
<span className="flex items-center gap-[6px] text-[var(--text-secondary)]">
|
||||||
🔒 PHI-safe — patient names and DOBs never stored. Crosswalk: patient_id only.
|
PHI-safe — patient names and DOBs never stored. Crosswalk: patient_id only.
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{activeFilter === "all"
|
{activeFilter === "all"
|
||||||
? `${records.length} patients · all results shown`
|
? `${localRecords.length} patients · all results shown`
|
||||||
: `${filtered.length} of ${records.length} · filtered`}
|
: `${filtered.length} of ${localRecords.length} · filtered`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Confirm Visit Modal */}
|
||||||
|
{confirmingRecord && (
|
||||||
|
<ConfirmVisitModal
|
||||||
|
record={confirmingRecord}
|
||||||
|
onClose={() => setConfirmingRecord(null)}
|
||||||
|
onConfirmed={handleVisitConfirmed}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -194,13 +257,33 @@ function ActionButton({ flag }) {
|
||||||
const base =
|
const base =
|
||||||
"bg-transparent border border-[var(--border-color)] text-[var(--text-secondary)] px-[13px] py-[5px] rounded-md text-xs cursor-pointer font-body whitespace-nowrap transition-all hover:border-[var(--brand)] hover:text-[var(--brand)]";
|
"bg-transparent border border-[var(--border-color)] text-[var(--text-secondary)] px-[13px] py-[5px] rounded-md text-xs cursor-pointer font-body whitespace-nowrap transition-all hover:border-[var(--brand)] hover:text-[var(--brand)]";
|
||||||
|
|
||||||
if (flag === "OUT_OF_COVERAGE" || flag === "VISIT_DUE") {
|
if (flag === "SUPPLY_LAPSED" || flag === "VISIT_REQUIRED") {
|
||||||
const label =
|
return <button className={`${base} !border-[var(--accent)] !text-[var(--accent-text)] hover:bg-[rgba(203,107,32,0.1)]`}>Contact Prescriber</button>;
|
||||||
flag === "OUT_OF_COVERAGE" ? "Contact Prescriber" : "Request Renewal →";
|
|
||||||
return <button className={`${base} !border-[var(--accent)] !text-[var(--accent-text)] hover:bg-[rgba(203,107,32,0.1)]`}>{label}</button>;
|
|
||||||
}
|
}
|
||||||
if (flag === "REFILL_WINDOW") {
|
if (flag === "TRANSFER_PENDING") {
|
||||||
|
return <button className={`${base} !border-[var(--accent)] !text-[var(--accent-text)] hover:bg-[rgba(203,107,32,0.1)]`}>Verify Docs</button>;
|
||||||
|
}
|
||||||
|
if (flag === "RESUPPLY_READY") {
|
||||||
return <button className={base}>Initiate Resupply</button>;
|
return <button className={base}>Initiate Resupply</button>;
|
||||||
}
|
}
|
||||||
return <button className={base}>View</button>;
|
return <button className={base}>View</button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DocItem({ label, value }) {
|
||||||
|
const isGood = value && (
|
||||||
|
value === "On File" || value === "Verified" || value === "Not Required" ||
|
||||||
|
value.startsWith("Confirmed") || value.startsWith("Approved") || value === "N/A"
|
||||||
|
);
|
||||||
|
const isBad = value && (
|
||||||
|
value === "Missing" || value === "Not Verified" || value === "Expired" ||
|
||||||
|
value === "Denied" || value === "Required — Not Started"
|
||||||
|
);
|
||||||
|
const color = isGood ? "text-[#1A8040]" : isBad ? "text-[#CC2222]" : "text-[#CB6B20]";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-start gap-[6px]">
|
||||||
|
<span className="text-[var(--text-muted)] w-[140px] shrink-0">{label}:</span>
|
||||||
|
<span className={`font-medium ${color}`}>{value || "Unknown"}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,14 +63,55 @@ export function apiRecordToLocal(r) {
|
||||||
return {
|
return {
|
||||||
patient_id: r.patient_id,
|
patient_id: r.patient_id,
|
||||||
device_type: r.device_type,
|
device_type: r.device_type,
|
||||||
|
device_display: r.device_display,
|
||||||
payer: r.payer,
|
payer: r.payer,
|
||||||
component: r.component,
|
component: r.component,
|
||||||
flag: r.flag,
|
flag: r.flag,
|
||||||
daysUntilEnd: r.days_until_coverage_end,
|
daysUntilEnd: r.days_until_coverage_end,
|
||||||
|
days_until_coverage_end: r.days_until_coverage_end,
|
||||||
daysUntilVisit: r.days_until_visit_due,
|
daysUntilVisit: r.days_until_visit_due,
|
||||||
|
days_until_visit_due: r.days_until_visit_due,
|
||||||
priority: r.priority_score,
|
priority: r.priority_score,
|
||||||
|
priority_score: r.priority_score,
|
||||||
coverageEndDate: r.coverage_end_date,
|
coverageEndDate: r.coverage_end_date,
|
||||||
|
coverage_end_date: r.coverage_end_date,
|
||||||
nextVisitDueDate: r.next_visit_due_date,
|
nextVisitDueDate: r.next_visit_due_date,
|
||||||
|
next_visit_due_date: r.next_visit_due_date,
|
||||||
reason: r.reason || "",
|
reason: r.reason || "",
|
||||||
|
status_label: r.status_label,
|
||||||
|
action: r.action,
|
||||||
|
rule_version: r.rule_version,
|
||||||
|
visit_date_confidence: r.visit_date_confidence || "estimated",
|
||||||
|
is_transfer: r.is_transfer || false,
|
||||||
|
doc_state: r.doc_state || null,
|
||||||
|
cascade: r.cascade || [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm a qualifying visit date for a patient.
|
||||||
|
* Returns the updated RecordOut from the backend.
|
||||||
|
*/
|
||||||
|
export async function confirmVisit(payload) {
|
||||||
|
const authHeader = API_KEY ? { "X-API-Key": API_KEY } : {};
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${BACKEND_URL}/api/confirm-visit`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...authHeader,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const err = await res.json().catch(() => ({}));
|
||||||
|
throw new Error(err.detail || `HTTP ${res.status}`);
|
||||||
|
}
|
||||||
|
const raw = await res.json();
|
||||||
|
// Normalize to local shape
|
||||||
|
return apiRecordToLocal(raw);
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
test-data/.gitignore
vendored
Normal file
4
test-data/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Override root *.csv exclusion so synthetic test sets are tracked
|
||||||
|
!*.csv
|
||||||
|
!pa-set/*.csv
|
||||||
|
!nj-set/*.csv
|
||||||
19
test-data/generate_nj_set.py
Normal file
19
test-data/generate_nj_set.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""Generate 25 NJ-market CSV files (Northern NJ / Metro NY)."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
from generate_supplier_set import generate_set
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_set(
|
||||||
|
state="NJ",
|
||||||
|
supplier="Gaboro DME",
|
||||||
|
patients=25,
|
||||||
|
challenge_level="high",
|
||||||
|
output_dir=os.path.join(SCRIPT_DIR, "nj-set"),
|
||||||
|
num_files=25,
|
||||||
|
)
|
||||||
|
print("NJ set complete.")
|
||||||
19
test-data/generate_pa_set.py
Normal file
19
test-data/generate_pa_set.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""Generate 25 PA-market CSV files (Philadelphia/Southeastern PA)."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
from generate_supplier_set import generate_set
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_set(
|
||||||
|
state="PA",
|
||||||
|
supplier="Gaboro DME",
|
||||||
|
patients=30,
|
||||||
|
challenge_level="high",
|
||||||
|
output_dir=os.path.join(SCRIPT_DIR, "pa-set"),
|
||||||
|
num_files=25,
|
||||||
|
)
|
||||||
|
print("PA set complete.")
|
||||||
547
test-data/generate_supplier_set.py
Normal file
547
test-data/generate_supplier_set.py
Normal file
|
|
@ -0,0 +1,547 @@
|
||||||
|
"""
|
||||||
|
generate_supplier_set.py — Parameterized Signal CSV generator.
|
||||||
|
Produces N files for a given state/supplier/challenge-level combination.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python generate_supplier_set.py --state PA --supplier "Gaboro DME" --patients 30 --challenge-level high --output pa-set
|
||||||
|
python generate_supplier_set.py --state NJ --supplier "Gaboro DME" --patients 25 --challenge-level high --output nj-set
|
||||||
|
|
||||||
|
Challenge levels:
|
||||||
|
low: canonical headers, clean dates, valid payers
|
||||||
|
medium: header variants, mixed date formats, some nulls
|
||||||
|
high: all variants + nulls + malformed dates + transfer patients + state-change patients
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
random.seed(42)
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
MARKET_DATA_PATH = os.path.join(SCRIPT_DIR, "market_data.json")
|
||||||
|
|
||||||
|
DEVICE_TYPES = ["dexcom_g7", "dexcom_g6", "freestyle_libre_3", "freestyle_libre_2"]
|
||||||
|
COMPONENTS = {
|
||||||
|
"dexcom_g7": "sensor",
|
||||||
|
"dexcom_g6": "sensor",
|
||||||
|
"freestyle_libre_3": "sensor",
|
||||||
|
"freestyle_libre_2": "sensor",
|
||||||
|
}
|
||||||
|
|
||||||
|
TODAY = date.today()
|
||||||
|
|
||||||
|
DATE_BUCKETS = {
|
||||||
|
"active": (TODAY - timedelta(days=10), TODAY - timedelta(days=1)),
|
||||||
|
"renewal_soon": (TODAY - timedelta(days=270), TODAY - timedelta(days=260)),
|
||||||
|
"renewal_elevated": (TODAY - timedelta(days=300), TODAY - timedelta(days=290)),
|
||||||
|
"renewal_critical": (TODAY - timedelta(days=315), TODAY - timedelta(days=310)),
|
||||||
|
"visit_required": (TODAY - timedelta(days=400), TODAY - timedelta(days=380)),
|
||||||
|
"supply_lapsed": (TODAY - timedelta(days=600), TODAY - timedelta(days=500)),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Header variants — each entry is a tuple:
|
||||||
|
# (patient_id_col, device_col, date_col, qty_col, payer_col, component_col, date_fmt, extras_factory)
|
||||||
|
# extras_factory(state_data) -> dict of extra static columns to include
|
||||||
|
BASE_HEADER_VARIANTS = [
|
||||||
|
# 1 — canonical
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 2 — Brightree-style
|
||||||
|
("Patient ID", "Item Description", "Service Date", "Qty", "Insurance Name", "Item Type",
|
||||||
|
"%m/%d/%Y", lambda sd: {"Prescriber NPI": "1234567890", "Branch": sd["branch_id"]}),
|
||||||
|
# 3 — all caps
|
||||||
|
("PT_ID", "DEVICE", "SHIP DATE", "UNITS", "CARRIER", "TYPE",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 4 — MRN + text date
|
||||||
|
("MRN", "Product Name", "Dispense Date", "Qty Dispensed", "Plan Name", "Supply Type",
|
||||||
|
"%d-%b-%Y", lambda sd: {"Supplier": sd["supplier_name"], "State": sd["state"]}),
|
||||||
|
# 5 — account_number
|
||||||
|
("Account Number", "Product", "Fill Date", "Count", "Primary Payer", "component",
|
||||||
|
"%m/%d/%y", lambda sd: {}),
|
||||||
|
# 6 — external ref + ISO datetime
|
||||||
|
("External Patient Ref", "Item", "Date of Service", "Qty Shipped", "Insurance", "item_type",
|
||||||
|
"%Y-%m-%dT%H:%M:%S", lambda sd: {"Notes": "batch export", "Region": sd["geographies"][0]}),
|
||||||
|
# 7 — Acct # + YYYYMMDD
|
||||||
|
("Acct #", "DME", "Order Date", "quantity", "plan", "component_type",
|
||||||
|
"%Y%m%d", lambda sd: {}),
|
||||||
|
# 8 — patientid no space + dispense date
|
||||||
|
("patientid", "devicetype", "dispensedate", "qty", "payername", "supplytype",
|
||||||
|
"%m/%d/%Y", lambda sd: {}),
|
||||||
|
# 9 — pt_id + dos
|
||||||
|
("pt_id", "product_type", "dos", "qty", "ins_name", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 10 — account_no + hcpcs
|
||||||
|
("account_no", "hcpcs_description", "service_date", "units", "primary_payer", "supply_type",
|
||||||
|
"%m-%d-%Y", lambda sd: {"HCPCS Code": "A9277"}),
|
||||||
|
# 11 — patient_account + commercial
|
||||||
|
("patient_account", "description", "ship_date", "quantity_dispensed", "carrier", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Account Manager": "Staff"}),
|
||||||
|
# 12 — id + product
|
||||||
|
("id", "product", "fill_date", "qty_shipped", "payer", "item_type",
|
||||||
|
"%m/%d/%Y", lambda sd: {}),
|
||||||
|
# 13 — PT ID spaces
|
||||||
|
("PT ID", "Device Type", "Shipment Date", "Quantity", "Insurance", "Component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Region": sd["geographies"][1] if len(sd["geographies"]) > 1 else ""}),
|
||||||
|
# 14 — lowercase no space (same as 8 — patientid/devicetype variants)
|
||||||
|
("patientid", "devicetype", "dispensedate", "qty", "payername", "supplytype",
|
||||||
|
"%m/%d/%Y", lambda sd: {}),
|
||||||
|
# 15 — account_number + d/m/y
|
||||||
|
("account_number", "item_description", "order_date", "units_dispensed", "plan_name", "component",
|
||||||
|
"%d/%m/%Y", lambda sd: {"Facility": f"{sd['supplier_name']} Main"}),
|
||||||
|
# 16 — member_id
|
||||||
|
("member_id", "Product Type", "Service Date", "Qty", "Insurance Name", "Component Type",
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 17 — acct no
|
||||||
|
("acct no", "dme description", "last ship date", "qty dispensed", "payer name", "type",
|
||||||
|
"%m/%d/%Y", lambda sd: {"Billing Cycle": "Monthly"}),
|
||||||
|
# 18 — external_patient_ref
|
||||||
|
("external_patient_ref", "equipment description", "service_date", "count", "primary_payer", "supply_type",
|
||||||
|
"%Y-%m-%dT%H:%M:%S", lambda sd: {}),
|
||||||
|
# 19 — with doc columns: swo_status, pa_status
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"swo_status": "On File", "pa_status": "Not Required", "diagnosis_on_file": "Yes"}),
|
||||||
|
# 20 — with visit_date column
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"visit_date": "", "pecos_verified": "Yes", "swo_status": "On File"}),
|
||||||
|
# 21 — PA status column variants (Brightree-style + auth_status)
|
||||||
|
("Patient ID", "Item Description", "Service Date", "Qty", "Insurance Name", "Item Type",
|
||||||
|
"%m/%d/%Y", lambda sd: {"auth_status": "Approved", "swo_status": "On File"}),
|
||||||
|
# 22 — Prior Auth column
|
||||||
|
("account_no", "device_type", "shipment_date", "qty", "insurance", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Prior Auth": "Pending", "diagnosis_on_file": "Yes"}),
|
||||||
|
# 23 — with notes + auth numbers (noise columns)
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Notes": "see chart", "Auth Number": "A12345", "Staff": "K.F."}),
|
||||||
|
# 24 — missing optional columns (no component, no quantity)
|
||||||
|
("patient_id", "device_type", "shipment_date", None, "payer", None,
|
||||||
|
"%Y-%m-%d", lambda sd: {}),
|
||||||
|
# 25 — transfer patient column
|
||||||
|
("patient_id", "device_type", "shipment_date", "quantity", "payer", "component",
|
||||||
|
"%Y-%m-%d", lambda sd: {"Transfer_From": "Prior Supplier LLC", "swo_status": "Pending"}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _pick_payer(state_data: dict, payer_type: str) -> str:
|
||||||
|
payers = state_data["payers"].get(payer_type, [])
|
||||||
|
return random.choice(payers) if payers else "Medicare Part B"
|
||||||
|
|
||||||
|
|
||||||
|
def _format_date(d: date, fmt: str) -> str:
|
||||||
|
return d.strftime(fmt)
|
||||||
|
|
||||||
|
|
||||||
|
def _random_date_in_bucket(bucket: tuple) -> date:
|
||||||
|
start, end = bucket
|
||||||
|
days = (end - start).days
|
||||||
|
return start + timedelta(days=random.randint(0, max(days, 0)))
|
||||||
|
|
||||||
|
|
||||||
|
def _device_display(device: str, header_style: str) -> str:
|
||||||
|
"""Map device type to display string matching the header style."""
|
||||||
|
display_map = {
|
||||||
|
"dexcom_g7": {
|
||||||
|
"canonical": "dexcom_g7",
|
||||||
|
"display": "Dexcom G7",
|
||||||
|
"short": "G7",
|
||||||
|
},
|
||||||
|
"dexcom_g6": {
|
||||||
|
"canonical": "dexcom_g6",
|
||||||
|
"display": "Dexcom G6",
|
||||||
|
"short": "G6",
|
||||||
|
},
|
||||||
|
"freestyle_libre_3": {
|
||||||
|
"canonical": "freestyle_libre_3",
|
||||||
|
"display": "FreeStyle Libre 3",
|
||||||
|
"short": "FSL3",
|
||||||
|
},
|
||||||
|
"freestyle_libre_2": {
|
||||||
|
"canonical": "freestyle_libre_2",
|
||||||
|
"display": "FreeStyle Libre 2",
|
||||||
|
"short": "FSL2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dm = display_map.get(device, {"canonical": device, "display": device, "short": device})
|
||||||
|
if header_style in (
|
||||||
|
"Item Description", "Product Name", "product", "description",
|
||||||
|
"dme description", "equipment description", "item_description",
|
||||||
|
"hcpcs_description", "Product Type",
|
||||||
|
):
|
||||||
|
return dm["display"]
|
||||||
|
if header_style in ("DEVICE", "DME"):
|
||||||
|
return dm["short"]
|
||||||
|
return dm["canonical"]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_state_change_patients(state_data: dict, file_index: int, num_files: int) -> list:
|
||||||
|
"""
|
||||||
|
Generate 6 state-change patients whose status evolves across the file sequence.
|
||||||
|
Returns patient records that change based on file_index.
|
||||||
|
"""
|
||||||
|
patients = []
|
||||||
|
progress = file_index / max(num_files - 1, 1) # 0.0 to 1.0
|
||||||
|
|
||||||
|
# Patient A: starts RENEWAL_SOON, confirmed in file 10, ACTIVE by file 20
|
||||||
|
if file_index < 10:
|
||||||
|
shipment_date_a = TODAY - timedelta(days=270)
|
||||||
|
visit_date_a = ""
|
||||||
|
swo_status_a = "On File"
|
||||||
|
elif file_index < 20:
|
||||||
|
shipment_date_a = TODAY - timedelta(days=10)
|
||||||
|
visit_date_a = (TODAY - timedelta(days=5)).strftime("%Y-%m-%d")
|
||||||
|
swo_status_a = "On File"
|
||||||
|
else:
|
||||||
|
shipment_date_a = TODAY - timedelta(days=5)
|
||||||
|
visit_date_a = (TODAY - timedelta(days=5)).strftime("%Y-%m-%d")
|
||||||
|
swo_status_a = "On File"
|
||||||
|
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-A-001",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": shipment_date_a,
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
"visit_date": visit_date_a,
|
||||||
|
"swo_status": swo_status_a,
|
||||||
|
"pecos_verified": "Yes",
|
||||||
|
"pa_status": "Not Required",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patient B: starts ACTIVE, slides to RENEWAL_SOON by file 15
|
||||||
|
base_visit_offset = 170 - int(progress * 90) # 170 to 80 days ago
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-B-002",
|
||||||
|
"device_type": "freestyle_libre_3",
|
||||||
|
"shipment_date": TODAY - timedelta(days=5),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicaid"),
|
||||||
|
"component": "sensor",
|
||||||
|
"visit_date": (TODAY - timedelta(days=base_visit_offset)).strftime("%Y-%m-%d"),
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pecos_verified": "Yes",
|
||||||
|
"pa_status": "Approved",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patient C: transfer patient appearing mid-sequence, clears by file 18
|
||||||
|
if file_index < 12:
|
||||||
|
transfer_from_c = "Prior Supplier LLC"
|
||||||
|
swo_status_c = "Pending"
|
||||||
|
pa_status_c = "Pending"
|
||||||
|
diagnosis_c = "No"
|
||||||
|
else:
|
||||||
|
transfer_from_c = ""
|
||||||
|
swo_status_c = "On File"
|
||||||
|
pa_status_c = "Approved"
|
||||||
|
diagnosis_c = "Yes"
|
||||||
|
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-C-003",
|
||||||
|
"device_type": "dexcom_g6",
|
||||||
|
"shipment_date": TODAY - timedelta(days=30),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
"Transfer_From": transfer_from_c,
|
||||||
|
"swo_status": swo_status_c,
|
||||||
|
"pa_status": pa_status_c,
|
||||||
|
"diagnosis_on_file": diagnosis_c,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patient D: SUPPLY_LAPSED -> new shipment after file 8 -> ACTIVE
|
||||||
|
shipment_d = TODAY - timedelta(days=600) if file_index < 8 else TODAY - timedelta(days=5)
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-D-004",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": shipment_d,
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_advantage"),
|
||||||
|
"component": "sensor",
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pa_status": "Approved" if file_index >= 8 else "Pending",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Patients E and F: stable control group
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-E-005",
|
||||||
|
"device_type": "freestyle_libre_2",
|
||||||
|
"shipment_date": TODAY - timedelta(days=3),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
"visit_date": (TODAY - timedelta(days=30)).strftime("%Y-%m-%d"),
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pecos_verified": "Yes",
|
||||||
|
"pa_status": "Not Required",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
patients.append({
|
||||||
|
"patient_id": "SC-F-006",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY - timedelta(days=7),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
"swo_status": "On File",
|
||||||
|
"pa_status": "Approved",
|
||||||
|
"diagnosis_on_file": "Yes",
|
||||||
|
})
|
||||||
|
|
||||||
|
return patients
|
||||||
|
|
||||||
|
|
||||||
|
def generate_challenge_rows(state_data: dict, challenge_level: str, file_index: int) -> list:
|
||||||
|
"""Generate challenge-type rows based on challenge_level."""
|
||||||
|
if challenge_level == "low":
|
||||||
|
return []
|
||||||
|
|
||||||
|
challenges = []
|
||||||
|
if challenge_level in ("medium", "high"):
|
||||||
|
# Null patient_id (should be skipped by normalizer)
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": "",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY - timedelta(days=10),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
|
||||||
|
if challenge_level == "high":
|
||||||
|
# Future shipment date (edge case)
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"FUTURE-{file_index:03d}",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY + timedelta(days=30),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
# Zero quantity
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"ZERQTY-{file_index:03d}",
|
||||||
|
"device_type": "freestyle_libre_3",
|
||||||
|
"shipment_date": TODAY - timedelta(days=5),
|
||||||
|
"quantity": 0,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
# Duplicate patient_id (second entry should also be processed)
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"DUP-PT-{file_index:03d}",
|
||||||
|
"device_type": "dexcom_g7",
|
||||||
|
"shipment_date": TODAY - timedelta(days=5),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "medicare_ffs"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
challenges.append({
|
||||||
|
"patient_id": f"DUP-PT-{file_index:03d}", # intentional duplicate
|
||||||
|
"device_type": "freestyle_libre_3",
|
||||||
|
"shipment_date": TODAY - timedelta(days=3),
|
||||||
|
"quantity": 1,
|
||||||
|
"payer": _pick_payer(state_data, "commercial"),
|
||||||
|
"component": "sensor",
|
||||||
|
})
|
||||||
|
|
||||||
|
return challenges
|
||||||
|
|
||||||
|
|
||||||
|
def generate_patient_batch(
|
||||||
|
state_data: dict,
|
||||||
|
patient_count: int,
|
||||||
|
file_index: int,
|
||||||
|
num_files: int,
|
||||||
|
challenge_level: str,
|
||||||
|
) -> list:
|
||||||
|
"""Generate the main patient batch for one file."""
|
||||||
|
patients = []
|
||||||
|
payer_types = ["medicare_ffs", "medicare_ffs", "medicare_advantage", "medicaid", "commercial"]
|
||||||
|
status_buckets = [
|
||||||
|
"active", "active", "active", "active",
|
||||||
|
"renewal_soon", "renewal_elevated", "renewal_critical",
|
||||||
|
"visit_required", "supply_lapsed",
|
||||||
|
]
|
||||||
|
|
||||||
|
for i in range(patient_count):
|
||||||
|
device = random.choice(DEVICE_TYPES)
|
||||||
|
payer_type = random.choice(payer_types)
|
||||||
|
payer_str = _pick_payer(state_data, payer_type)
|
||||||
|
bucket_name = status_buckets[i % len(status_buckets)]
|
||||||
|
shipment_date = _random_date_in_bucket(DATE_BUCKETS[bucket_name])
|
||||||
|
qty = random.choice([1, 1, 1, 2, 3])
|
||||||
|
|
||||||
|
patient = {
|
||||||
|
"patient_id": f"{state_data['branch_prefix']}-{file_index:02d}-{i+1:04d}",
|
||||||
|
"device_type": device,
|
||||||
|
"shipment_date": shipment_date,
|
||||||
|
"quantity": qty,
|
||||||
|
"payer": payer_str,
|
||||||
|
"component": COMPONENTS.get(device, "sensor"),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add doc fields based on payer type
|
||||||
|
if payer_type == "medicare_ffs":
|
||||||
|
patient.update({
|
||||||
|
"swo_status": random.choice(["On File", "On File", "Pending"]),
|
||||||
|
"pecos_verified": random.choice(["Yes", "Yes", "No"]),
|
||||||
|
"pa_status": "Not Required",
|
||||||
|
"diagnosis_on_file": random.choice(["Yes", "Yes", "No"]),
|
||||||
|
})
|
||||||
|
elif payer_type == "medicare_advantage":
|
||||||
|
patient.update({
|
||||||
|
"swo_status": random.choice(["On File", "Pending"]),
|
||||||
|
"pecos_verified": random.choice(["Yes", "No"]),
|
||||||
|
"pa_status": random.choice(["Approved", "Pending", "Denied"]),
|
||||||
|
"diagnosis_on_file": random.choice(["Yes", "No"]),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
patient.update({
|
||||||
|
"swo_status": random.choice(["On File", "Pending", "Expired"]),
|
||||||
|
"pa_status": random.choice(["Approved", "Pending", "Not Required"]),
|
||||||
|
"diagnosis_on_file": random.choice(["Yes", "No"]),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add visit date for some patients
|
||||||
|
if random.random() > 0.4:
|
||||||
|
visit_offset = random.randint(30, 200)
|
||||||
|
patient["visit_date"] = (TODAY - timedelta(days=visit_offset)).strftime("%Y-%m-%d")
|
||||||
|
else:
|
||||||
|
patient["visit_date"] = ""
|
||||||
|
|
||||||
|
patients.append(patient)
|
||||||
|
|
||||||
|
return patients
|
||||||
|
|
||||||
|
|
||||||
|
def write_csv_file(
|
||||||
|
output_path: str,
|
||||||
|
patients: list,
|
||||||
|
variant: tuple,
|
||||||
|
state_data: dict,
|
||||||
|
challenge_level: str,
|
||||||
|
file_index: int,
|
||||||
|
) -> None:
|
||||||
|
"""Write one CSV file using the given header variant."""
|
||||||
|
pid_col, dev_col, date_col, qty_col, payer_col, comp_col, date_fmt, extras_factory = variant
|
||||||
|
extras = extras_factory(state_data)
|
||||||
|
|
||||||
|
# Build the ordered list of headers
|
||||||
|
core_headers = [h for h in [pid_col, dev_col, date_col, qty_col, payer_col, comp_col] if h is not None]
|
||||||
|
extra_headers = list(extras.keys())
|
||||||
|
all_headers = core_headers + extra_headers
|
||||||
|
|
||||||
|
# Malformed date files: indices 7 and 14 get some intentionally broken dates
|
||||||
|
malformed_indices = set()
|
||||||
|
if challenge_level == "high" and file_index in (7, 14):
|
||||||
|
malformed_indices = {0, 1, 2}
|
||||||
|
|
||||||
|
with open(output_path, "w", newline="") as f:
|
||||||
|
writer = csv.DictWriter(f, fieldnames=all_headers, extrasaction="ignore")
|
||||||
|
writer.writeheader()
|
||||||
|
|
||||||
|
for row_idx, p in enumerate(patients):
|
||||||
|
row = {}
|
||||||
|
|
||||||
|
# Map patient dict fields to this variant's column names
|
||||||
|
if pid_col:
|
||||||
|
row[pid_col] = p.get("patient_id", "")
|
||||||
|
if dev_col:
|
||||||
|
row[dev_col] = _device_display(p.get("device_type", "dexcom_g7"), dev_col)
|
||||||
|
if date_col:
|
||||||
|
sd = p.get("shipment_date", TODAY)
|
||||||
|
if isinstance(sd, date):
|
||||||
|
if row_idx in malformed_indices:
|
||||||
|
row[date_col] = random.choice(["not on file", "pending", "TBD"])
|
||||||
|
else:
|
||||||
|
row[date_col] = _format_date(sd, date_fmt)
|
||||||
|
else:
|
||||||
|
row[date_col] = str(sd)
|
||||||
|
if qty_col:
|
||||||
|
row[qty_col] = p.get("quantity", 1)
|
||||||
|
if payer_col:
|
||||||
|
row[payer_col] = p.get("payer", "Medicare Part B")
|
||||||
|
if comp_col:
|
||||||
|
row[comp_col] = p.get("component", "sensor")
|
||||||
|
|
||||||
|
# Static extras (branch, prescriber NPI, region, etc.)
|
||||||
|
for k, v in extras.items():
|
||||||
|
row[k] = v
|
||||||
|
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_set(
|
||||||
|
state: str,
|
||||||
|
supplier: str,
|
||||||
|
patients: int,
|
||||||
|
challenge_level: str,
|
||||||
|
output_dir: str,
|
||||||
|
num_files: int = 25,
|
||||||
|
) -> None:
|
||||||
|
"""Generate num_files CSV files for the given state."""
|
||||||
|
with open(MARKET_DATA_PATH) as f:
|
||||||
|
market = json.load(f)
|
||||||
|
|
||||||
|
state_data = market.get(state.upper())
|
||||||
|
if not state_data:
|
||||||
|
raise ValueError(
|
||||||
|
f"State '{state}' not found in market_data.json. Available: {list(market.keys())}"
|
||||||
|
)
|
||||||
|
|
||||||
|
state_data = dict(state_data) # avoid mutating the original
|
||||||
|
state_data["supplier_name"] = supplier
|
||||||
|
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
for i in range(num_files):
|
||||||
|
variant = BASE_HEADER_VARIANTS[i % len(BASE_HEADER_VARIANTS)]
|
||||||
|
|
||||||
|
# Core patient batch
|
||||||
|
patient_rows = generate_patient_batch(state_data, patients, i, num_files, challenge_level)
|
||||||
|
|
||||||
|
# State-change patients (appear in every file, evolve across the sequence)
|
||||||
|
sc_patients = generate_state_change_patients(state_data, i, num_files)
|
||||||
|
all_patients = sc_patients + patient_rows
|
||||||
|
|
||||||
|
# Challenge rows appended at end
|
||||||
|
if challenge_level in ("medium", "high"):
|
||||||
|
challenge_rows = generate_challenge_rows(state_data, challenge_level, i)
|
||||||
|
all_patients = all_patients + challenge_rows
|
||||||
|
|
||||||
|
filename = f"batch-{i+1:02d}-{state.lower()}-{challenge_level}.csv"
|
||||||
|
output_path = os.path.join(output_dir, filename)
|
||||||
|
|
||||||
|
write_csv_file(output_path, all_patients, variant, state_data, challenge_level, i)
|
||||||
|
print(f" Written: {filename} ({len(all_patients)} rows)")
|
||||||
|
|
||||||
|
print(f"\nGenerated {num_files} files in {output_dir}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Generate Signal CSV test files")
|
||||||
|
parser.add_argument("--state", default="PA")
|
||||||
|
parser.add_argument("--supplier", default="Gaboro DME")
|
||||||
|
parser.add_argument("--patients", type=int, default=30)
|
||||||
|
parser.add_argument("--challenge-level", choices=["low", "medium", "high"], default="high")
|
||||||
|
# Accept both --output and --output-dir for compatibility
|
||||||
|
parser.add_argument("--output", "--output-dir", dest="output", default="pa-set")
|
||||||
|
# Accept both --files and --count for compatibility
|
||||||
|
parser.add_argument("--files", "--count", dest="files", type=int, default=25)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
output_path = os.path.join(SCRIPT_DIR, args.output)
|
||||||
|
generate_set(
|
||||||
|
state=args.state,
|
||||||
|
supplier=args.supplier,
|
||||||
|
patients=args.patients,
|
||||||
|
challenge_level=args.challenge_level,
|
||||||
|
output_dir=output_path,
|
||||||
|
num_files=args.files,
|
||||||
|
)
|
||||||
30
test-data/market_data.json
Normal file
30
test-data/market_data.json
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"PA": {
|
||||||
|
"supplier_name": "Gaboro DME",
|
||||||
|
"branch_prefix": "PA",
|
||||||
|
"branch_id": "PA-001",
|
||||||
|
"state": "PA",
|
||||||
|
"payers": {
|
||||||
|
"medicare_ffs": ["Medicare Part B", "Medicare", "CMS"],
|
||||||
|
"medicare_advantage": ["UPMC Health Plan", "Highmark BlueCross BlueShield", "Aetna PA Medicare"],
|
||||||
|
"medicaid": ["PA Medicaid", "Keystone First", "AmeriHealth Caritas PA", "UPMC for You"],
|
||||||
|
"commercial": ["Independence Blue Cross", "Highmark BCBS PA", "Aetna PA", "Cigna PA", "Geisinger"]
|
||||||
|
},
|
||||||
|
"geographies": ["Philadelphia", "Bucks County", "Montgomery County", "Chester County"],
|
||||||
|
"prescribers": ["Penn Medicine", "Jefferson Health", "Temple Health", "Drexel Medicine"]
|
||||||
|
},
|
||||||
|
"NJ": {
|
||||||
|
"supplier_name": "Gaboro DME",
|
||||||
|
"branch_prefix": "NJ",
|
||||||
|
"branch_id": "NJ-002",
|
||||||
|
"state": "NJ",
|
||||||
|
"payers": {
|
||||||
|
"medicare_ffs": ["Medicare Part B", "Medicare", "Medicare FFS NJ"],
|
||||||
|
"medicare_advantage": ["Aetna NJ Medicare", "UnitedHealthcare NJ Medicare", "Horizon BCBS NJ Medicare"],
|
||||||
|
"medicaid": ["NJ FamilyCare", "WellCare NJ", "AmeriHealth NJ"],
|
||||||
|
"commercial": ["Horizon BCBS NJ", "Aetna NJ", "Cigna NJ", "Oxford Health", "UnitedHealthcare NJ"]
|
||||||
|
},
|
||||||
|
"geographies": ["Bergen County", "Essex County", "Hudson County", "Passaic County"],
|
||||||
|
"prescribers": ["Hackensack Meridian", "RWJBarnabas", "Atlantic Health", "Valley Health"]
|
||||||
|
}
|
||||||
|
}
|
||||||
37
test-data/nj-set/batch-01-nj-high.csv
Normal file
37
test-data/nj-set/batch-01-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component
|
||||||
|
SC-A-001,dexcom_g7,2025-09-10,1,Medicare Part B,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,NJ FamilyCare,sensor
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Oxford Health,sensor
|
||||||
|
SC-D-004,dexcom_g7,2024-10-15,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-00-0001,dexcom_g7,2026-06-01,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-00-0002,dexcom_g7,2026-05-28,1,Oxford Health,sensor
|
||||||
|
NJ-00-0003,dexcom_g6,2026-05-31,2,Oxford Health,sensor
|
||||||
|
NJ-00-0004,freestyle_libre_2,2026-05-30,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-00-0005,freestyle_libre_3,2025-09-10,2,Cigna NJ,sensor
|
||||||
|
NJ-00-0006,freestyle_libre_3,2025-08-20,1,Cigna NJ,sensor
|
||||||
|
NJ-00-0007,dexcom_g7,2025-07-30,1,Medicare Part B,sensor
|
||||||
|
NJ-00-0008,freestyle_libre_3,2025-05-11,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-00-0009,dexcom_g6,2024-11-18,3,WellCare NJ,sensor
|
||||||
|
NJ-00-0010,dexcom_g6,2026-06-03,1,Medicare,sensor
|
||||||
|
NJ-00-0011,dexcom_g6,2026-06-04,1,WellCare NJ,sensor
|
||||||
|
NJ-00-0012,freestyle_libre_3,2026-06-06,2,Oxford Health,sensor
|
||||||
|
NJ-00-0013,dexcom_g7,2026-05-30,2,Medicare Part B,sensor
|
||||||
|
NJ-00-0014,freestyle_libre_2,2025-09-18,1,Cigna NJ,sensor
|
||||||
|
NJ-00-0015,freestyle_libre_3,2025-08-15,2,Aetna NJ Medicare,sensor
|
||||||
|
NJ-00-0016,dexcom_g7,2025-07-31,3,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-00-0017,dexcom_g7,2025-05-18,1,Cigna NJ,sensor
|
||||||
|
NJ-00-0018,dexcom_g6,2024-10-25,2,Horizon BCBS NJ,sensor
|
||||||
|
NJ-00-0019,freestyle_libre_2,2026-06-01,3,Aetna NJ,sensor
|
||||||
|
NJ-00-0020,freestyle_libre_3,2026-06-02,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-00-0021,dexcom_g7,2026-06-06,3,Aetna NJ Medicare,sensor
|
||||||
|
NJ-00-0022,dexcom_g6,2026-06-02,1,Medicare Part B,sensor
|
||||||
|
NJ-00-0023,dexcom_g6,2025-09-17,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-00-0024,freestyle_libre_2,2025-08-17,2,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-00-0025,dexcom_g6,2025-07-31,2,Medicare Part B,sensor
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare FFS NJ,sensor
|
||||||
|
FUTURE-000,dexcom_g7,2026-07-07,1,Medicare FFS NJ,sensor
|
||||||
|
ZERQTY-000,freestyle_libre_3,2026-06-02,0,Horizon BCBS NJ,sensor
|
||||||
|
DUP-PT-000,dexcom_g7,2026-06-02,1,Medicare Part B,sensor
|
||||||
|
DUP-PT-000,freestyle_libre_3,2026-06-04,1,Aetna NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-02-nj-high.csv
Normal file
37
test-data/nj-set/batch-02-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
Patient ID,Item Description,Service Date,Qty,Insurance Name,Item Type,Prescriber NPI,Branch
|
||||||
|
SC-A-001,Dexcom G7,09/10/2025,1,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,WellCare NJ,sensor,1234567890,NJ-002
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Horizon BCBS NJ,sensor,1234567890,NJ-002
|
||||||
|
SC-D-004,Dexcom G7,10/15/2024,1,Aetna NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,Cigna NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0001,Dexcom G6,06/04/2026,1,WellCare NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0002,FreeStyle Libre 2,06/01/2026,2,UnitedHealthcare NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0003,Dexcom G7,05/28/2026,1,UnitedHealthcare NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0004,Dexcom G6,05/29/2026,1,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0005,FreeStyle Libre 2,09/13/2025,3,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0006,FreeStyle Libre 3,08/21/2025,1,Aetna NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0007,FreeStyle Libre 2,07/27/2025,2,Aetna NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0008,FreeStyle Libre 3,05/14/2025,1,Medicare Part B,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0009,Dexcom G7,01/07/2025,1,Cigna NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0010,Dexcom G6,06/06/2026,1,UnitedHealthcare NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0011,Dexcom G7,06/03/2026,1,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0012,FreeStyle Libre 3,06/04/2026,3,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0013,Dexcom G7,05/28/2026,1,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0014,FreeStyle Libre 3,09/15/2025,1,Aetna NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0015,Dexcom G6,08/17/2025,1,Medicare Part B,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0016,Dexcom G6,08/01/2025,1,Aetna NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0017,FreeStyle Libre 2,05/10/2025,1,UnitedHealthcare NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0018,Dexcom G7,01/05/2025,3,UnitedHealthcare NJ Medicare,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0019,Dexcom G6,05/28/2026,1,Cigna NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0020,FreeStyle Libre 2,05/29/2026,2,UnitedHealthcare NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0021,FreeStyle Libre 2,06/05/2026,1,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0022,FreeStyle Libre 3,06/01/2026,3,Medicare FFS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0023,FreeStyle Libre 3,09/13/2025,2,Aetna NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0024,FreeStyle Libre 2,08/15/2025,1,Horizon BCBS NJ,sensor,1234567890,NJ-002
|
||||||
|
NJ-01-0025,Dexcom G6,08/01/2025,1,Oxford Health,sensor,1234567890,NJ-002
|
||||||
|
,Dexcom G7,05/28/2026,1,Medicare Part B,sensor,1234567890,NJ-002
|
||||||
|
FUTURE-001,Dexcom G7,07/07/2026,1,Medicare Part B,sensor,1234567890,NJ-002
|
||||||
|
ZERQTY-001,FreeStyle Libre 3,06/02/2026,0,Aetna NJ,sensor,1234567890,NJ-002
|
||||||
|
DUP-PT-001,Dexcom G7,06/02/2026,1,Medicare Part B,sensor,1234567890,NJ-002
|
||||||
|
DUP-PT-001,FreeStyle Libre 3,06/04/2026,1,Horizon BCBS NJ,sensor,1234567890,NJ-002
|
||||||
|
37
test-data/nj-set/batch-03-nj-high.csv
Normal file
37
test-data/nj-set/batch-03-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
PT_ID,DEVICE,SHIP DATE,UNITS,CARRIER,TYPE
|
||||||
|
SC-A-001,G7,2025-09-10,1,Medicare,sensor
|
||||||
|
SC-B-002,FSL3,2026-06-02,1,WellCare NJ,sensor
|
||||||
|
SC-C-003,G6,2026-05-08,1,Cigna NJ,sensor
|
||||||
|
SC-D-004,G7,2024-10-15,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
SC-E-005,FSL2,2026-06-04,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,G7,2026-05-31,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-02-0001,G6,2026-05-29,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-02-0002,FSL2,2026-05-30,1,NJ FamilyCare,sensor
|
||||||
|
NJ-02-0003,FSL2,2026-05-31,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-02-0004,FSL3,2026-06-05,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-02-0005,FSL2,2025-09-20,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-02-0006,FSL3,2025-08-16,1,Medicare,sensor
|
||||||
|
NJ-02-0007,FSL2,2025-07-28,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-02-0008,G7,2025-05-15,3,Medicare,sensor
|
||||||
|
NJ-02-0009,G7,2025-01-19,2,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-02-0010,FSL2,2026-06-02,2,Medicare,sensor
|
||||||
|
NJ-02-0011,G7,2026-06-06,1,AmeriHealth NJ,sensor
|
||||||
|
NJ-02-0012,FSL2,2026-06-01,2,Medicare Part B,sensor
|
||||||
|
NJ-02-0013,FSL2,2026-06-01,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-02-0014,G6,2025-09-10,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-02-0015,G6,2025-08-12,3,AmeriHealth NJ,sensor
|
||||||
|
NJ-02-0016,G7,2025-07-27,3,Medicare,sensor
|
||||||
|
NJ-02-0017,FSL2,2025-05-21,1,Medicare Part B,sensor
|
||||||
|
NJ-02-0018,G7,2024-11-28,3,Horizon BCBS NJ,sensor
|
||||||
|
NJ-02-0019,FSL3,2026-06-04,1,Medicare,sensor
|
||||||
|
NJ-02-0020,G6,2026-06-05,1,NJ FamilyCare,sensor
|
||||||
|
NJ-02-0021,FSL3,2026-05-29,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-02-0022,FSL2,2026-06-04,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-02-0023,G7,2025-09-11,1,Aetna NJ,sensor
|
||||||
|
NJ-02-0024,FSL2,2025-08-11,1,WellCare NJ,sensor
|
||||||
|
NJ-02-0025,FSL3,2025-07-31,1,AmeriHealth NJ,sensor
|
||||||
|
,G7,2026-05-28,1,Medicare FFS NJ,sensor
|
||||||
|
FUTURE-002,G7,2026-07-07,1,Medicare Part B,sensor
|
||||||
|
ZERQTY-002,FSL3,2026-06-02,0,Cigna NJ,sensor
|
||||||
|
DUP-PT-002,G7,2026-06-02,1,Medicare Part B,sensor
|
||||||
|
DUP-PT-002,FSL3,2026-06-04,1,UnitedHealthcare NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-04-nj-high.csv
Normal file
37
test-data/nj-set/batch-04-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
MRN,Product Name,Dispense Date,Qty Dispensed,Plan Name,Supply Type,Supplier,State
|
||||||
|
SC-A-001,Dexcom G7,10-Sep-2025,1,Medicare FFS NJ,sensor,Gaboro DME,NJ
|
||||||
|
SC-B-002,FreeStyle Libre 3,02-Jun-2026,1,WellCare NJ,sensor,Gaboro DME,NJ
|
||||||
|
SC-C-003,Dexcom G6,08-May-2026,1,Horizon BCBS NJ,sensor,Gaboro DME,NJ
|
||||||
|
SC-D-004,Dexcom G7,15-Oct-2024,1,UnitedHealthcare NJ Medicare,sensor,Gaboro DME,NJ
|
||||||
|
SC-E-005,FreeStyle Libre 2,04-Jun-2026,1,Medicare Part B,sensor,Gaboro DME,NJ
|
||||||
|
SC-F-006,Dexcom G7,31-May-2026,1,UnitedHealthcare NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0001,Dexcom G6,04-Jun-2026,1,Medicare Part B,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0002,Dexcom G6,02-Jun-2026,1,Aetna NJ Medicare,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0003,Dexcom G7,30-May-2026,2,Cigna NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0004,FreeStyle Libre 3,01-Jun-2026,2,Medicare Part B,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0005,Dexcom G7,19-Sep-2025,1,Medicare Part B,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0006,Dexcom G6,18-Aug-2025,2,Oxford Health,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0007,Dexcom G7,28-Jul-2025,1,Horizon BCBS NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0008,Dexcom G6,08-May-2025,1,Horizon BCBS NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0009,Dexcom G6,20-Nov-2024,2,Horizon BCBS NJ Medicare,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0010,Dexcom G6,30-May-2026,1,Medicare,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0011,FreeStyle Libre 3,04-Jun-2026,1,NJ FamilyCare,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0012,FreeStyle Libre 2,28-May-2026,2,Medicare FFS NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0013,Dexcom G7,06-Jun-2026,3,Medicare FFS NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0014,FreeStyle Libre 2,14-Sep-2025,1,Oxford Health,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0015,FreeStyle Libre 3,16-Aug-2025,1,WellCare NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0016,FreeStyle Libre 2,30-Jul-2025,1,Horizon BCBS NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0017,FreeStyle Libre 2,20-May-2025,2,Horizon BCBS NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0018,FreeStyle Libre 2,10-Nov-2024,1,NJ FamilyCare,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0019,Dexcom G7,06-Jun-2026,1,Medicare FFS NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0020,FreeStyle Libre 2,29-May-2026,2,Medicare Part B,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0021,FreeStyle Libre 3,04-Jun-2026,1,WellCare NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0022,Dexcom G6,03-Jun-2026,1,Medicare,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0023,FreeStyle Libre 3,20-Sep-2025,2,UnitedHealthcare NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0024,FreeStyle Libre 2,14-Aug-2025,1,WellCare NJ,sensor,Gaboro DME,NJ
|
||||||
|
NJ-03-0025,Dexcom G7,30-Jul-2025,2,Horizon BCBS NJ Medicare,sensor,Gaboro DME,NJ
|
||||||
|
,Dexcom G7,28-May-2026,1,Medicare,sensor,Gaboro DME,NJ
|
||||||
|
FUTURE-003,Dexcom G7,07-Jul-2026,1,Medicare Part B,sensor,Gaboro DME,NJ
|
||||||
|
ZERQTY-003,FreeStyle Libre 3,02-Jun-2026,0,Aetna NJ,sensor,Gaboro DME,NJ
|
||||||
|
DUP-PT-003,Dexcom G7,02-Jun-2026,1,Medicare,sensor,Gaboro DME,NJ
|
||||||
|
DUP-PT-003,FreeStyle Libre 3,04-Jun-2026,1,Aetna NJ,sensor,Gaboro DME,NJ
|
||||||
|
37
test-data/nj-set/batch-05-nj-high.csv
Normal file
37
test-data/nj-set/batch-05-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
Account Number,Product,Fill Date,Count,Primary Payer,component
|
||||||
|
SC-A-001,dexcom_g7,09/10/25,1,Medicare,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,06/02/26,1,AmeriHealth NJ,sensor
|
||||||
|
SC-C-003,dexcom_g6,05/08/26,1,Aetna NJ,sensor
|
||||||
|
SC-D-004,dexcom_g7,10/15/24,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,06/04/26,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,dexcom_g7,05/31/26,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-04-0001,dexcom_g7,06/02/26,3,WellCare NJ,sensor
|
||||||
|
NJ-04-0002,freestyle_libre_2,06/04/26,3,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-04-0003,freestyle_libre_2,05/29/26,2,Medicare FFS NJ,sensor
|
||||||
|
NJ-04-0004,freestyle_libre_3,06/02/26,1,Medicare,sensor
|
||||||
|
NJ-04-0005,dexcom_g6,09/11/25,3,Medicare Part B,sensor
|
||||||
|
NJ-04-0006,dexcom_g6,08/21/25,1,Medicare,sensor
|
||||||
|
NJ-04-0007,freestyle_libre_2,07/31/25,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-04-0008,freestyle_libre_3,05/09/25,2,WellCare NJ,sensor
|
||||||
|
NJ-04-0009,freestyle_libre_3,01/12/25,1,Cigna NJ,sensor
|
||||||
|
NJ-04-0010,dexcom_g7,06/01/26,1,Oxford Health,sensor
|
||||||
|
NJ-04-0011,freestyle_libre_3,05/29/26,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-04-0012,dexcom_g7,06/03/26,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-04-0013,dexcom_g6,06/01/26,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-04-0014,freestyle_libre_2,09/11/25,2,Cigna NJ,sensor
|
||||||
|
NJ-04-0015,freestyle_libre_2,08/21/25,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-04-0016,dexcom_g7,07/29/25,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-04-0017,freestyle_libre_2,05/10/25,3,Medicare Part B,sensor
|
||||||
|
NJ-04-0018,freestyle_libre_2,12/07/24,1,Aetna NJ,sensor
|
||||||
|
NJ-04-0019,dexcom_g7,06/04/26,2,Aetna NJ Medicare,sensor
|
||||||
|
NJ-04-0020,freestyle_libre_3,06/01/26,1,Medicare,sensor
|
||||||
|
NJ-04-0021,dexcom_g7,05/31/26,1,Medicare,sensor
|
||||||
|
NJ-04-0022,dexcom_g6,05/31/26,1,Aetna NJ,sensor
|
||||||
|
NJ-04-0023,dexcom_g6,09/14/25,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-04-0024,freestyle_libre_3,08/16/25,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-04-0025,freestyle_libre_2,08/01/25,1,Horizon BCBS NJ,sensor
|
||||||
|
,dexcom_g7,05/28/26,1,Medicare,sensor
|
||||||
|
FUTURE-004,dexcom_g7,07/07/26,1,Medicare,sensor
|
||||||
|
ZERQTY-004,freestyle_libre_3,06/02/26,0,Horizon BCBS NJ,sensor
|
||||||
|
DUP-PT-004,dexcom_g7,06/02/26,1,Medicare Part B,sensor
|
||||||
|
DUP-PT-004,freestyle_libre_3,06/04/26,1,Oxford Health,sensor
|
||||||
|
37
test-data/nj-set/batch-06-nj-high.csv
Normal file
37
test-data/nj-set/batch-06-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
External Patient Ref,Item,Date of Service,Qty Shipped,Insurance,item_type,Notes,Region
|
||||||
|
SC-A-001,dexcom_g7,2025-09-10T00:00:00,1,Medicare,sensor,batch export,Bergen County
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02T00:00:00,1,NJ FamilyCare,sensor,batch export,Bergen County
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08T00:00:00,1,Horizon BCBS NJ,sensor,batch export,Bergen County
|
||||||
|
SC-D-004,dexcom_g7,2024-10-15T00:00:00,1,Horizon BCBS NJ Medicare,sensor,batch export,Bergen County
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04T00:00:00,1,Medicare FFS NJ,sensor,batch export,Bergen County
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31T00:00:00,1,UnitedHealthcare NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0001,freestyle_libre_2,2026-05-29T00:00:00,2,AmeriHealth NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0002,dexcom_g6,2026-06-01T00:00:00,3,Medicare Part B,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0003,freestyle_libre_3,2026-06-06T00:00:00,2,AmeriHealth NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0004,dexcom_g6,2026-05-31T00:00:00,2,WellCare NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0005,freestyle_libre_3,2025-09-20T00:00:00,1,WellCare NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0006,dexcom_g7,2025-08-12T00:00:00,1,Medicare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0007,freestyle_libre_3,2025-07-29T00:00:00,2,Medicare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0008,dexcom_g7,2025-05-09T00:00:00,1,UnitedHealthcare NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0009,freestyle_libre_3,2024-12-27T00:00:00,1,Medicare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0010,dexcom_g7,2026-05-28T00:00:00,1,Cigna NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0011,dexcom_g7,2026-06-06T00:00:00,2,Medicare Part B,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0012,dexcom_g7,2026-06-03T00:00:00,2,Aetna NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0013,freestyle_libre_3,2026-05-29T00:00:00,1,UnitedHealthcare NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0014,freestyle_libre_2,2025-09-19T00:00:00,2,WellCare NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0015,freestyle_libre_3,2025-08-15T00:00:00,3,NJ FamilyCare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0016,freestyle_libre_2,2025-07-27T00:00:00,1,Horizon BCBS NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0017,freestyle_libre_3,2025-05-18T00:00:00,2,Medicare Part B,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0018,freestyle_libre_3,2025-01-17T00:00:00,1,Medicare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0019,freestyle_libre_2,2026-06-03T00:00:00,1,Oxford Health,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0020,freestyle_libre_2,2026-06-03T00:00:00,1,Aetna NJ Medicare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0021,freestyle_libre_3,2026-06-02T00:00:00,1,Aetna NJ Medicare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0022,freestyle_libre_3,2026-05-29T00:00:00,1,Medicare Part B,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0023,dexcom_g7,2025-09-17T00:00:00,2,Medicare FFS NJ,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0024,freestyle_libre_3,2025-08-18T00:00:00,1,Aetna NJ Medicare,sensor,batch export,Bergen County
|
||||||
|
NJ-05-0025,dexcom_g7,2025-07-27T00:00:00,1,Horizon BCBS NJ Medicare,sensor,batch export,Bergen County
|
||||||
|
,dexcom_g7,2026-05-28T00:00:00,1,Medicare,sensor,batch export,Bergen County
|
||||||
|
FUTURE-005,dexcom_g7,2026-07-07T00:00:00,1,Medicare,sensor,batch export,Bergen County
|
||||||
|
ZERQTY-005,freestyle_libre_3,2026-06-02T00:00:00,0,UnitedHealthcare NJ,sensor,batch export,Bergen County
|
||||||
|
DUP-PT-005,dexcom_g7,2026-06-02T00:00:00,1,Medicare FFS NJ,sensor,batch export,Bergen County
|
||||||
|
DUP-PT-005,freestyle_libre_3,2026-06-04T00:00:00,1,Horizon BCBS NJ,sensor,batch export,Bergen County
|
||||||
|
37
test-data/nj-set/batch-07-nj-high.csv
Normal file
37
test-data/nj-set/batch-07-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
Acct #,DME,Order Date,quantity,plan,component_type
|
||||||
|
SC-A-001,G7,20250910,1,Medicare,sensor
|
||||||
|
SC-B-002,FSL3,20260602,1,WellCare NJ,sensor
|
||||||
|
SC-C-003,G6,20260508,1,Horizon BCBS NJ,sensor
|
||||||
|
SC-D-004,G7,20241015,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,FSL2,20260604,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,G7,20260531,1,Cigna NJ,sensor
|
||||||
|
NJ-06-0001,FSL2,20260602,3,Aetna NJ,sensor
|
||||||
|
NJ-06-0002,G7,20260529,3,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-06-0003,G6,20260601,2,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-06-0004,FSL3,20260529,3,Medicare,sensor
|
||||||
|
NJ-06-0005,G6,20250919,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-06-0006,G7,20250817,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-06-0007,FSL3,20250728,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-06-0008,FSL3,20250520,3,Medicare FFS NJ,sensor
|
||||||
|
NJ-06-0009,G6,20241103,1,Oxford Health,sensor
|
||||||
|
NJ-06-0010,FSL2,20260531,2,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-06-0011,FSL2,20260606,2,Medicare,sensor
|
||||||
|
NJ-06-0012,G6,20260530,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-06-0013,G7,20260601,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-06-0014,FSL2,20250917,1,Medicare Part B,sensor
|
||||||
|
NJ-06-0015,FSL3,20250812,1,Medicare,sensor
|
||||||
|
NJ-06-0016,FSL3,20250727,1,Medicare Part B,sensor
|
||||||
|
NJ-06-0017,FSL2,20250505,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-06-0018,FSL2,20241114,1,Medicare,sensor
|
||||||
|
NJ-06-0019,FSL2,20260604,3,Medicare Part B,sensor
|
||||||
|
NJ-06-0020,G6,20260528,3,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-06-0021,G7,20260529,3,Cigna NJ,sensor
|
||||||
|
NJ-06-0022,FSL2,20260603,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-06-0023,G7,20250920,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-06-0024,G6,20250818,1,AmeriHealth NJ,sensor
|
||||||
|
NJ-06-0025,G6,20250727,1,Medicare Part B,sensor
|
||||||
|
,G7,20260528,1,Medicare,sensor
|
||||||
|
FUTURE-006,G7,20260707,1,Medicare Part B,sensor
|
||||||
|
ZERQTY-006,FSL3,20260602,0,Cigna NJ,sensor
|
||||||
|
DUP-PT-006,G7,20260602,1,Medicare,sensor
|
||||||
|
DUP-PT-006,FSL3,20260604,1,Horizon BCBS NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-08-nj-high.csv
Normal file
37
test-data/nj-set/batch-08-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patientid,devicetype,dispensedate,qty,payername,supplytype
|
||||||
|
SC-A-001,dexcom_g7,pending,1,Medicare,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,pending,1,AmeriHealth NJ,sensor
|
||||||
|
SC-C-003,dexcom_g6,not on file,1,Oxford Health,sensor
|
||||||
|
SC-D-004,dexcom_g7,10/15/2024,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,06/04/2026,1,Medicare FFS NJ,sensor
|
||||||
|
SC-F-006,dexcom_g7,05/31/2026,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-07-0001,freestyle_libre_3,06/04/2026,2,WellCare NJ,sensor
|
||||||
|
NJ-07-0002,freestyle_libre_3,06/03/2026,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-07-0003,dexcom_g7,06/01/2026,1,Medicare,sensor
|
||||||
|
NJ-07-0004,freestyle_libre_3,06/02/2026,3,NJ FamilyCare,sensor
|
||||||
|
NJ-07-0005,freestyle_libre_2,09/20/2025,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-07-0006,dexcom_g7,08/16/2025,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-07-0007,freestyle_libre_3,07/27/2025,3,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-07-0008,dexcom_g7,05/08/2025,2,Medicare FFS NJ,sensor
|
||||||
|
NJ-07-0009,freestyle_libre_3,01/01/2025,1,NJ FamilyCare,sensor
|
||||||
|
NJ-07-0010,dexcom_g7,06/04/2026,1,NJ FamilyCare,sensor
|
||||||
|
NJ-07-0011,freestyle_libre_3,05/30/2026,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-07-0012,freestyle_libre_2,06/05/2026,3,Medicare,sensor
|
||||||
|
NJ-07-0013,dexcom_g7,06/03/2026,2,Medicare,sensor
|
||||||
|
NJ-07-0014,dexcom_g6,09/11/2025,2,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-07-0015,freestyle_libre_2,08/12/2025,3,Horizon BCBS NJ,sensor
|
||||||
|
NJ-07-0016,dexcom_g6,07/29/2025,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-07-0017,freestyle_libre_3,05/05/2025,3,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-07-0018,freestyle_libre_3,10/18/2024,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-07-0019,freestyle_libre_2,06/04/2026,3,Horizon BCBS NJ,sensor
|
||||||
|
NJ-07-0020,freestyle_libre_3,05/28/2026,1,NJ FamilyCare,sensor
|
||||||
|
NJ-07-0021,dexcom_g6,06/02/2026,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-07-0022,freestyle_libre_2,06/04/2026,1,Medicare,sensor
|
||||||
|
NJ-07-0023,freestyle_libre_2,09/13/2025,1,Oxford Health,sensor
|
||||||
|
NJ-07-0024,dexcom_g6,08/18/2025,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-07-0025,freestyle_libre_2,07/28/2025,1,Oxford Health,sensor
|
||||||
|
,dexcom_g7,05/28/2026,1,Medicare Part B,sensor
|
||||||
|
FUTURE-007,dexcom_g7,07/07/2026,1,Medicare,sensor
|
||||||
|
ZERQTY-007,freestyle_libre_3,06/02/2026,0,Aetna NJ,sensor
|
||||||
|
DUP-PT-007,dexcom_g7,06/02/2026,1,Medicare,sensor
|
||||||
|
DUP-PT-007,freestyle_libre_3,06/04/2026,1,Cigna NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-09-nj-high.csv
Normal file
37
test-data/nj-set/batch-09-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
pt_id,product_type,dos,qty,ins_name,component
|
||||||
|
SC-A-001,dexcom_g7,2025-09-10,1,Medicare,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,WellCare NJ,sensor
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,UnitedHealthcare NJ,sensor
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare FFS NJ,sensor
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-08-0001,freestyle_libre_2,2026-06-05,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-08-0002,freestyle_libre_2,2026-06-06,3,Cigna NJ,sensor
|
||||||
|
NJ-08-0003,freestyle_libre_3,2026-05-30,1,Cigna NJ,sensor
|
||||||
|
NJ-08-0004,dexcom_g7,2026-06-02,2,Medicare FFS NJ,sensor
|
||||||
|
NJ-08-0005,dexcom_g6,2025-09-15,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-08-0006,freestyle_libre_2,2025-08-18,1,Cigna NJ,sensor
|
||||||
|
NJ-08-0007,freestyle_libre_3,2025-07-30,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-08-0008,dexcom_g7,2025-05-08,3,Oxford Health,sensor
|
||||||
|
NJ-08-0009,dexcom_g6,2025-01-12,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-08-0010,dexcom_g6,2026-05-31,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-08-0011,dexcom_g6,2026-06-05,1,Aetna NJ,sensor
|
||||||
|
NJ-08-0012,dexcom_g6,2026-06-02,3,AmeriHealth NJ,sensor
|
||||||
|
NJ-08-0013,dexcom_g6,2026-06-01,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-08-0014,freestyle_libre_3,2025-09-18,1,WellCare NJ,sensor
|
||||||
|
NJ-08-0015,freestyle_libre_3,2025-08-11,1,Medicare,sensor
|
||||||
|
NJ-08-0016,freestyle_libre_3,2025-07-28,3,NJ FamilyCare,sensor
|
||||||
|
NJ-08-0017,dexcom_g6,2025-05-19,1,Medicare Part B,sensor
|
||||||
|
NJ-08-0018,freestyle_libre_3,2025-01-10,1,NJ FamilyCare,sensor
|
||||||
|
NJ-08-0019,freestyle_libre_2,2026-06-02,1,Medicare,sensor
|
||||||
|
NJ-08-0020,dexcom_g7,2026-05-30,1,Medicare,sensor
|
||||||
|
NJ-08-0021,dexcom_g7,2026-06-06,3,Medicare,sensor
|
||||||
|
NJ-08-0022,freestyle_libre_2,2026-06-04,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-08-0023,freestyle_libre_2,2025-09-12,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-08-0024,freestyle_libre_2,2025-08-16,1,AmeriHealth NJ,sensor
|
||||||
|
NJ-08-0025,dexcom_g6,2025-07-28,1,WellCare NJ,sensor
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare,sensor
|
||||||
|
FUTURE-008,dexcom_g7,2026-07-07,1,Medicare FFS NJ,sensor
|
||||||
|
ZERQTY-008,freestyle_libre_3,2026-06-02,0,Cigna NJ,sensor
|
||||||
|
DUP-PT-008,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor
|
||||||
|
DUP-PT-008,freestyle_libre_3,2026-06-04,1,Aetna NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-10-nj-high.csv
Normal file
37
test-data/nj-set/batch-10-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
account_no,hcpcs_description,service_date,units,primary_payer,supply_type,HCPCS Code
|
||||||
|
SC-A-001,Dexcom G7,09-10-2025,1,Medicare,sensor,A9277
|
||||||
|
SC-B-002,FreeStyle Libre 3,06-02-2026,1,NJ FamilyCare,sensor,A9277
|
||||||
|
SC-C-003,Dexcom G6,05-08-2026,1,UnitedHealthcare NJ,sensor,A9277
|
||||||
|
SC-D-004,Dexcom G7,06-02-2026,1,Aetna NJ Medicare,sensor,A9277
|
||||||
|
SC-E-005,FreeStyle Libre 2,06-04-2026,1,Medicare FFS NJ,sensor,A9277
|
||||||
|
SC-F-006,Dexcom G7,05-31-2026,1,Aetna NJ,sensor,A9277
|
||||||
|
NJ-09-0001,Dexcom G7,06-01-2026,1,Medicare,sensor,A9277
|
||||||
|
NJ-09-0002,Dexcom G7,06-06-2026,1,UnitedHealthcare NJ Medicare,sensor,A9277
|
||||||
|
NJ-09-0003,FreeStyle Libre 3,06-05-2026,2,AmeriHealth NJ,sensor,A9277
|
||||||
|
NJ-09-0004,Dexcom G7,06-05-2026,1,Oxford Health,sensor,A9277
|
||||||
|
NJ-09-0005,Dexcom G6,09-12-2025,1,UnitedHealthcare NJ,sensor,A9277
|
||||||
|
NJ-09-0006,Dexcom G6,08-12-2025,3,Oxford Health,sensor,A9277
|
||||||
|
NJ-09-0007,FreeStyle Libre 2,07-27-2025,3,AmeriHealth NJ,sensor,A9277
|
||||||
|
NJ-09-0008,FreeStyle Libre 3,05-09-2025,3,Medicare FFS NJ,sensor,A9277
|
||||||
|
NJ-09-0009,Dexcom G7,10-23-2024,2,Horizon BCBS NJ,sensor,A9277
|
||||||
|
NJ-09-0010,Dexcom G6,06-03-2026,2,WellCare NJ,sensor,A9277
|
||||||
|
NJ-09-0011,Dexcom G6,05-30-2026,3,Aetna NJ Medicare,sensor,A9277
|
||||||
|
NJ-09-0012,FreeStyle Libre 3,06-05-2026,2,AmeriHealth NJ,sensor,A9277
|
||||||
|
NJ-09-0013,FreeStyle Libre 3,05-31-2026,1,Medicare,sensor,A9277
|
||||||
|
NJ-09-0014,Dexcom G6,09-11-2025,3,Medicare Part B,sensor,A9277
|
||||||
|
NJ-09-0015,Dexcom G6,08-18-2025,1,Medicare,sensor,A9277
|
||||||
|
NJ-09-0016,FreeStyle Libre 3,07-31-2025,1,Medicare FFS NJ,sensor,A9277
|
||||||
|
NJ-09-0017,FreeStyle Libre 3,05-19-2025,1,Medicare FFS NJ,sensor,A9277
|
||||||
|
NJ-09-0018,Dexcom G6,12-17-2024,1,AmeriHealth NJ,sensor,A9277
|
||||||
|
NJ-09-0019,FreeStyle Libre 2,06-01-2026,2,NJ FamilyCare,sensor,A9277
|
||||||
|
NJ-09-0020,Dexcom G6,05-30-2026,2,Horizon BCBS NJ Medicare,sensor,A9277
|
||||||
|
NJ-09-0021,FreeStyle Libre 3,05-29-2026,2,Medicare,sensor,A9277
|
||||||
|
NJ-09-0022,Dexcom G7,06-06-2026,3,UnitedHealthcare NJ Medicare,sensor,A9277
|
||||||
|
NJ-09-0023,FreeStyle Libre 3,09-13-2025,1,Medicare Part B,sensor,A9277
|
||||||
|
NJ-09-0024,FreeStyle Libre 2,08-16-2025,1,Medicare FFS NJ,sensor,A9277
|
||||||
|
NJ-09-0025,FreeStyle Libre 3,07-30-2025,2,Horizon BCBS NJ Medicare,sensor,A9277
|
||||||
|
,Dexcom G7,05-28-2026,1,Medicare Part B,sensor,A9277
|
||||||
|
FUTURE-009,Dexcom G7,07-07-2026,1,Medicare,sensor,A9277
|
||||||
|
ZERQTY-009,FreeStyle Libre 3,06-02-2026,0,UnitedHealthcare NJ,sensor,A9277
|
||||||
|
DUP-PT-009,Dexcom G7,06-02-2026,1,Medicare FFS NJ,sensor,A9277
|
||||||
|
DUP-PT-009,FreeStyle Libre 3,06-04-2026,1,UnitedHealthcare NJ,sensor,A9277
|
||||||
|
37
test-data/nj-set/batch-11-nj-high.csv
Normal file
37
test-data/nj-set/batch-11-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patient_account,description,ship_date,quantity_dispensed,carrier,component,Account Manager
|
||||||
|
SC-A-001,Dexcom G7,2026-05-28,1,Medicare,sensor,Staff
|
||||||
|
SC-B-002,FreeStyle Libre 3,2026-06-02,1,WellCare NJ,sensor,Staff
|
||||||
|
SC-C-003,Dexcom G6,2026-05-08,1,Cigna NJ,sensor,Staff
|
||||||
|
SC-D-004,Dexcom G7,2026-06-02,1,UnitedHealthcare NJ Medicare,sensor,Staff
|
||||||
|
SC-E-005,FreeStyle Libre 2,2026-06-04,1,Medicare,sensor,Staff
|
||||||
|
SC-F-006,Dexcom G7,2026-05-31,1,Horizon BCBS NJ,sensor,Staff
|
||||||
|
NJ-10-0001,FreeStyle Libre 2,2026-05-31,1,Aetna NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0002,FreeStyle Libre 3,2026-06-04,2,UnitedHealthcare NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0003,Dexcom G6,2026-06-05,1,Horizon BCBS NJ,sensor,Staff
|
||||||
|
NJ-10-0004,Dexcom G6,2026-05-30,1,Medicare Part B,sensor,Staff
|
||||||
|
NJ-10-0005,FreeStyle Libre 2,2025-09-10,3,NJ FamilyCare,sensor,Staff
|
||||||
|
NJ-10-0006,FreeStyle Libre 3,2025-08-11,3,Cigna NJ,sensor,Staff
|
||||||
|
NJ-10-0007,Dexcom G7,2025-07-28,1,Medicare FFS NJ,sensor,Staff
|
||||||
|
NJ-10-0008,FreeStyle Libre 3,2025-05-05,1,UnitedHealthcare NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0009,FreeStyle Libre 3,2025-01-06,1,Medicare FFS NJ,sensor,Staff
|
||||||
|
NJ-10-0010,FreeStyle Libre 2,2026-05-28,1,Medicare FFS NJ,sensor,Staff
|
||||||
|
NJ-10-0011,Dexcom G7,2026-05-31,1,Horizon BCBS NJ,sensor,Staff
|
||||||
|
NJ-10-0012,Dexcom G7,2026-06-06,1,Horizon BCBS NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0013,Dexcom G7,2026-06-06,1,Aetna NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0014,FreeStyle Libre 3,2025-09-16,1,Medicare Part B,sensor,Staff
|
||||||
|
NJ-10-0015,FreeStyle Libre 2,2025-08-21,1,Aetna NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0016,FreeStyle Libre 2,2025-07-31,2,Medicare FFS NJ,sensor,Staff
|
||||||
|
NJ-10-0017,Dexcom G7,2025-05-15,1,Aetna NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0018,FreeStyle Libre 3,2025-01-20,1,WellCare NJ,sensor,Staff
|
||||||
|
NJ-10-0019,FreeStyle Libre 2,2026-06-05,2,UnitedHealthcare NJ,sensor,Staff
|
||||||
|
NJ-10-0020,Dexcom G7,2026-06-02,2,UnitedHealthcare NJ,sensor,Staff
|
||||||
|
NJ-10-0021,Dexcom G7,2026-06-06,3,Aetna NJ,sensor,Staff
|
||||||
|
NJ-10-0022,FreeStyle Libre 3,2026-06-04,1,UnitedHealthcare NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0023,Dexcom G7,2025-09-13,2,UnitedHealthcare NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0024,FreeStyle Libre 2,2025-08-17,1,Horizon BCBS NJ Medicare,sensor,Staff
|
||||||
|
NJ-10-0025,Dexcom G6,2025-07-28,2,WellCare NJ,sensor,Staff
|
||||||
|
,Dexcom G7,2026-05-28,1,Medicare FFS NJ,sensor,Staff
|
||||||
|
FUTURE-010,Dexcom G7,2026-07-07,1,Medicare Part B,sensor,Staff
|
||||||
|
ZERQTY-010,FreeStyle Libre 3,2026-06-02,0,UnitedHealthcare NJ,sensor,Staff
|
||||||
|
DUP-PT-010,Dexcom G7,2026-06-02,1,Medicare FFS NJ,sensor,Staff
|
||||||
|
DUP-PT-010,FreeStyle Libre 3,2026-06-04,1,Aetna NJ,sensor,Staff
|
||||||
|
37
test-data/nj-set/batch-12-nj-high.csv
Normal file
37
test-data/nj-set/batch-12-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
id,product,fill_date,qty_shipped,payer,item_type
|
||||||
|
SC-A-001,Dexcom G7,05/28/2026,1,Medicare FFS NJ,sensor
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,WellCare NJ,sensor
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Cigna NJ,sensor
|
||||||
|
SC-D-004,Dexcom G7,06/02/2026,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,Medicare,sensor
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-11-0001,Dexcom G7,05/31/2026,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-11-0002,FreeStyle Libre 2,06/01/2026,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-11-0003,Dexcom G7,06/04/2026,3,AmeriHealth NJ,sensor
|
||||||
|
NJ-11-0004,Dexcom G7,06/02/2026,1,Medicare Part B,sensor
|
||||||
|
NJ-11-0005,FreeStyle Libre 2,09/11/2025,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-11-0006,FreeStyle Libre 2,08/13/2025,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-11-0007,Dexcom G7,07/28/2025,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-11-0008,FreeStyle Libre 2,05/06/2025,2,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-11-0009,Dexcom G7,01/05/2025,1,Medicare,sensor
|
||||||
|
NJ-11-0010,Dexcom G6,06/02/2026,3,Aetna NJ,sensor
|
||||||
|
NJ-11-0011,Dexcom G6,06/03/2026,2,Medicare FFS NJ,sensor
|
||||||
|
NJ-11-0012,FreeStyle Libre 2,05/31/2026,1,NJ FamilyCare,sensor
|
||||||
|
NJ-11-0013,Dexcom G6,05/31/2026,2,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-11-0014,FreeStyle Libre 3,09/19/2025,2,Oxford Health,sensor
|
||||||
|
NJ-11-0015,FreeStyle Libre 2,08/21/2025,1,Aetna NJ,sensor
|
||||||
|
NJ-11-0016,Dexcom G7,07/27/2025,1,Medicare,sensor
|
||||||
|
NJ-11-0017,Dexcom G6,05/16/2025,1,Oxford Health,sensor
|
||||||
|
NJ-11-0018,FreeStyle Libre 3,11/24/2024,2,Medicare Part B,sensor
|
||||||
|
NJ-11-0019,Dexcom G6,06/05/2026,3,Medicare,sensor
|
||||||
|
NJ-11-0020,FreeStyle Libre 2,05/29/2026,3,NJ FamilyCare,sensor
|
||||||
|
NJ-11-0021,Dexcom G6,05/30/2026,2,Cigna NJ,sensor
|
||||||
|
NJ-11-0022,Dexcom G7,06/02/2026,3,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-11-0023,Dexcom G6,09/17/2025,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-11-0024,Dexcom G6,08/20/2025,2,Cigna NJ,sensor
|
||||||
|
NJ-11-0025,Dexcom G7,07/31/2025,1,Cigna NJ,sensor
|
||||||
|
,Dexcom G7,05/28/2026,1,Medicare FFS NJ,sensor
|
||||||
|
FUTURE-011,Dexcom G7,07/07/2026,1,Medicare Part B,sensor
|
||||||
|
ZERQTY-011,FreeStyle Libre 3,06/02/2026,0,Aetna NJ,sensor
|
||||||
|
DUP-PT-011,Dexcom G7,06/02/2026,1,Medicare,sensor
|
||||||
|
DUP-PT-011,FreeStyle Libre 3,06/04/2026,1,Horizon BCBS NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-13-nj-high.csv
Normal file
37
test-data/nj-set/batch-13-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
PT ID,Device Type,Shipment Date,Quantity,Insurance,Component,Region
|
||||||
|
SC-A-001,dexcom_g7,2026-05-28,1,Medicare,sensor,Essex County
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,NJ FamilyCare,sensor,Essex County
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Oxford Health,sensor,Essex County
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,Horizon BCBS NJ Medicare,sensor,Essex County
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare Part B,sensor,Essex County
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Cigna NJ,sensor,Essex County
|
||||||
|
NJ-12-0001,freestyle_libre_3,2026-05-30,1,Medicare FFS NJ,sensor,Essex County
|
||||||
|
NJ-12-0002,freestyle_libre_2,2026-06-01,2,Medicare FFS NJ,sensor,Essex County
|
||||||
|
NJ-12-0003,freestyle_libre_2,2026-06-02,2,Cigna NJ,sensor,Essex County
|
||||||
|
NJ-12-0004,freestyle_libre_2,2026-06-05,2,Aetna NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0005,freestyle_libre_3,2025-09-19,1,Horizon BCBS NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0006,freestyle_libre_2,2025-08-13,2,Medicare Part B,sensor,Essex County
|
||||||
|
NJ-12-0007,dexcom_g6,2025-07-29,1,Aetna NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0008,dexcom_g6,2025-05-23,1,Medicare,sensor,Essex County
|
||||||
|
NJ-12-0009,dexcom_g7,2024-12-15,1,Medicare Part B,sensor,Essex County
|
||||||
|
NJ-12-0010,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor,Essex County
|
||||||
|
NJ-12-0011,freestyle_libre_2,2026-05-29,1,Medicare FFS NJ,sensor,Essex County
|
||||||
|
NJ-12-0012,dexcom_g6,2026-06-05,3,Medicare Part B,sensor,Essex County
|
||||||
|
NJ-12-0013,freestyle_libre_3,2026-05-30,2,Medicare FFS NJ,sensor,Essex County
|
||||||
|
NJ-12-0014,dexcom_g7,2025-09-19,2,Medicare Part B,sensor,Essex County
|
||||||
|
NJ-12-0015,freestyle_libre_3,2025-08-19,3,Medicare Part B,sensor,Essex County
|
||||||
|
NJ-12-0016,dexcom_g7,2025-07-31,3,Aetna NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0017,freestyle_libre_2,2025-05-10,2,NJ FamilyCare,sensor,Essex County
|
||||||
|
NJ-12-0018,freestyle_libre_3,2024-12-03,3,Aetna NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0019,dexcom_g7,2026-05-28,1,Horizon BCBS NJ,sensor,Essex County
|
||||||
|
NJ-12-0020,freestyle_libre_3,2026-06-03,2,Oxford Health,sensor,Essex County
|
||||||
|
NJ-12-0021,freestyle_libre_3,2026-05-31,1,Medicare FFS NJ,sensor,Essex County
|
||||||
|
NJ-12-0022,dexcom_g7,2026-06-03,1,Horizon BCBS NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0023,dexcom_g7,2025-09-20,1,Horizon BCBS NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0024,freestyle_libre_2,2025-08-19,1,Horizon BCBS NJ Medicare,sensor,Essex County
|
||||||
|
NJ-12-0025,dexcom_g7,2025-07-29,3,UnitedHealthcare NJ Medicare,sensor,Essex County
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare Part B,sensor,Essex County
|
||||||
|
FUTURE-012,dexcom_g7,2026-07-07,1,Medicare,sensor,Essex County
|
||||||
|
ZERQTY-012,freestyle_libre_3,2026-06-02,0,Horizon BCBS NJ,sensor,Essex County
|
||||||
|
DUP-PT-012,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor,Essex County
|
||||||
|
DUP-PT-012,freestyle_libre_3,2026-06-04,1,Horizon BCBS NJ,sensor,Essex County
|
||||||
|
37
test-data/nj-set/batch-14-nj-high.csv
Normal file
37
test-data/nj-set/batch-14-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patientid,devicetype,dispensedate,qty,payername,supplytype
|
||||||
|
SC-A-001,dexcom_g7,05/28/2026,1,Medicare FFS NJ,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,06/02/2026,1,WellCare NJ,sensor
|
||||||
|
SC-C-003,dexcom_g6,05/08/2026,1,Cigna NJ,sensor
|
||||||
|
SC-D-004,dexcom_g7,06/02/2026,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,06/04/2026,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,dexcom_g7,05/31/2026,1,Cigna NJ,sensor
|
||||||
|
NJ-13-0001,dexcom_g6,05/28/2026,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-13-0002,dexcom_g6,06/05/2026,2,Medicare Part B,sensor
|
||||||
|
NJ-13-0003,freestyle_libre_3,06/02/2026,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-13-0004,dexcom_g7,06/04/2026,1,NJ FamilyCare,sensor
|
||||||
|
NJ-13-0005,freestyle_libre_3,09/19/2025,1,Medicare,sensor
|
||||||
|
NJ-13-0006,freestyle_libre_2,08/19/2025,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-13-0007,freestyle_libre_3,07/27/2025,2,Medicare,sensor
|
||||||
|
NJ-13-0008,dexcom_g6,05/04/2025,1,WellCare NJ,sensor
|
||||||
|
NJ-13-0009,dexcom_g6,01/12/2025,1,Oxford Health,sensor
|
||||||
|
NJ-13-0010,dexcom_g6,06/06/2026,3,NJ FamilyCare,sensor
|
||||||
|
NJ-13-0011,freestyle_libre_3,06/02/2026,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-13-0012,dexcom_g7,05/30/2026,2,Medicare Part B,sensor
|
||||||
|
NJ-13-0013,freestyle_libre_3,06/01/2026,2,Medicare,sensor
|
||||||
|
NJ-13-0014,freestyle_libre_3,09/13/2025,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-13-0015,dexcom_g7,08/13/2025,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-13-0016,dexcom_g7,07/27/2025,1,Medicare Part B,sensor
|
||||||
|
NJ-13-0017,dexcom_g7,05/11/2025,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-13-0018,dexcom_g6,10/30/2024,1,Medicare,sensor
|
||||||
|
NJ-13-0019,freestyle_libre_2,06/03/2026,2,NJ FamilyCare,sensor
|
||||||
|
NJ-13-0020,freestyle_libre_3,05/28/2026,3,NJ FamilyCare,sensor
|
||||||
|
NJ-13-0021,freestyle_libre_2,05/28/2026,1,Oxford Health,sensor
|
||||||
|
NJ-13-0022,dexcom_g6,06/03/2026,1,Horizon BCBS NJ,sensor
|
||||||
|
NJ-13-0023,freestyle_libre_3,09/12/2025,1,WellCare NJ,sensor
|
||||||
|
NJ-13-0024,freestyle_libre_3,08/19/2025,1,Medicare,sensor
|
||||||
|
NJ-13-0025,dexcom_g6,08/01/2025,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
,dexcom_g7,05/28/2026,1,Medicare FFS NJ,sensor
|
||||||
|
FUTURE-013,dexcom_g7,07/07/2026,1,Medicare FFS NJ,sensor
|
||||||
|
ZERQTY-013,freestyle_libre_3,06/02/2026,0,Aetna NJ,sensor
|
||||||
|
DUP-PT-013,dexcom_g7,06/02/2026,1,Medicare FFS NJ,sensor
|
||||||
|
DUP-PT-013,freestyle_libre_3,06/04/2026,1,Aetna NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-15-nj-high.csv
Normal file
37
test-data/nj-set/batch-15-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
account_number,item_description,order_date,units_dispensed,plan_name,component,Facility
|
||||||
|
SC-A-001,Dexcom G7,pending,1,Medicare FFS NJ,sensor,Gaboro DME Main
|
||||||
|
SC-B-002,FreeStyle Libre 3,pending,1,AmeriHealth NJ,sensor,Gaboro DME Main
|
||||||
|
SC-C-003,Dexcom G6,TBD,1,UnitedHealthcare NJ,sensor,Gaboro DME Main
|
||||||
|
SC-D-004,Dexcom G7,02/06/2026,1,Aetna NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
SC-E-005,FreeStyle Libre 2,04/06/2026,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
SC-F-006,Dexcom G7,31/05/2026,1,UnitedHealthcare NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0001,FreeStyle Libre 2,30/05/2026,2,Cigna NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0002,Dexcom G6,04/06/2026,1,Medicare FFS NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0003,Dexcom G6,28/05/2026,1,Aetna NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0004,FreeStyle Libre 2,02/06/2026,1,Horizon BCBS NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0005,FreeStyle Libre 2,18/09/2025,3,Aetna NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0006,FreeStyle Libre 3,11/08/2025,3,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0007,FreeStyle Libre 3,01/08/2025,3,Aetna NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0008,Dexcom G7,13/05/2025,1,Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0009,FreeStyle Libre 2,10/11/2024,1,Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0010,Dexcom G7,03/06/2026,2,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0011,FreeStyle Libre 2,03/06/2026,1,Horizon BCBS NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0012,Dexcom G7,02/06/2026,1,Aetna NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0013,Dexcom G7,02/06/2026,3,Horizon BCBS NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0014,Dexcom G6,12/09/2025,1,Oxford Health,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0015,FreeStyle Libre 3,14/08/2025,1,UnitedHealthcare NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0016,FreeStyle Libre 3,27/07/2025,1,Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0017,Dexcom G7,23/05/2025,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0018,FreeStyle Libre 2,07/01/2025,1,WellCare NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0019,FreeStyle Libre 2,04/06/2026,1,Aetna NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0020,FreeStyle Libre 3,30/05/2026,1,Horizon BCBS NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0021,Dexcom G6,03/06/2026,3,Horizon BCBS NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0022,FreeStyle Libre 2,03/06/2026,1,Aetna NJ Medicare,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0023,Dexcom G6,10/09/2025,2,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0024,Dexcom G6,16/08/2025,1,Cigna NJ,sensor,Gaboro DME Main
|
||||||
|
NJ-14-0025,FreeStyle Libre 2,31/07/2025,3,Medicare FFS NJ,sensor,Gaboro DME Main
|
||||||
|
,Dexcom G7,28/05/2026,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
FUTURE-014,Dexcom G7,07/07/2026,1,Medicare,sensor,Gaboro DME Main
|
||||||
|
ZERQTY-014,FreeStyle Libre 3,02/06/2026,0,UnitedHealthcare NJ,sensor,Gaboro DME Main
|
||||||
|
DUP-PT-014,Dexcom G7,02/06/2026,1,Medicare,sensor,Gaboro DME Main
|
||||||
|
DUP-PT-014,FreeStyle Libre 3,04/06/2026,1,Oxford Health,sensor,Gaboro DME Main
|
||||||
|
37
test-data/nj-set/batch-16-nj-high.csv
Normal file
37
test-data/nj-set/batch-16-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
member_id,Product Type,Service Date,Qty,Insurance Name,Component Type
|
||||||
|
SC-A-001,Dexcom G7,2026-05-28,1,Medicare Part B,sensor
|
||||||
|
SC-B-002,FreeStyle Libre 3,2026-06-02,1,NJ FamilyCare,sensor
|
||||||
|
SC-C-003,Dexcom G6,2026-05-08,1,Horizon BCBS NJ,sensor
|
||||||
|
SC-D-004,Dexcom G7,2026-06-02,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,FreeStyle Libre 2,2026-06-04,1,Medicare FFS NJ,sensor
|
||||||
|
SC-F-006,Dexcom G7,2026-05-31,1,Aetna NJ,sensor
|
||||||
|
NJ-15-0001,FreeStyle Libre 2,2026-06-04,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-15-0002,FreeStyle Libre 2,2026-06-02,1,Medicare,sensor
|
||||||
|
NJ-15-0003,Dexcom G7,2026-06-01,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-15-0004,FreeStyle Libre 2,2026-06-03,3,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-15-0005,FreeStyle Libre 2,2025-09-10,3,Cigna NJ,sensor
|
||||||
|
NJ-15-0006,Dexcom G6,2025-08-11,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-15-0007,FreeStyle Libre 2,2025-08-01,1,Medicare,sensor
|
||||||
|
NJ-15-0008,Dexcom G7,2025-05-14,2,Medicare Part B,sensor
|
||||||
|
NJ-15-0009,FreeStyle Libre 3,2024-11-17,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-15-0010,Dexcom G6,2026-05-31,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-15-0011,FreeStyle Libre 2,2026-05-30,3,Medicare FFS NJ,sensor
|
||||||
|
NJ-15-0012,FreeStyle Libre 3,2026-06-02,2,WellCare NJ,sensor
|
||||||
|
NJ-15-0013,FreeStyle Libre 2,2026-05-28,3,Horizon BCBS NJ,sensor
|
||||||
|
NJ-15-0014,Dexcom G7,2025-09-15,1,NJ FamilyCare,sensor
|
||||||
|
NJ-15-0015,FreeStyle Libre 3,2025-08-19,1,Medicare Part B,sensor
|
||||||
|
NJ-15-0016,Dexcom G7,2025-07-29,2,WellCare NJ,sensor
|
||||||
|
NJ-15-0017,Dexcom G6,2025-05-10,3,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-15-0018,Dexcom G6,2024-12-14,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-15-0019,FreeStyle Libre 3,2026-06-01,2,Medicare FFS NJ,sensor
|
||||||
|
NJ-15-0020,Dexcom G7,2026-06-06,3,NJ FamilyCare,sensor
|
||||||
|
NJ-15-0021,Dexcom G7,2026-06-01,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-15-0022,FreeStyle Libre 2,2026-05-30,1,Oxford Health,sensor
|
||||||
|
NJ-15-0023,Dexcom G6,2025-09-10,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-15-0024,FreeStyle Libre 2,2025-08-20,2,Medicare Part B,sensor
|
||||||
|
NJ-15-0025,FreeStyle Libre 2,2025-07-31,1,Medicare Part B,sensor
|
||||||
|
,Dexcom G7,2026-05-28,1,Medicare Part B,sensor
|
||||||
|
FUTURE-015,Dexcom G7,2026-07-07,1,Medicare FFS NJ,sensor
|
||||||
|
ZERQTY-015,FreeStyle Libre 3,2026-06-02,0,Cigna NJ,sensor
|
||||||
|
DUP-PT-015,Dexcom G7,2026-06-02,1,Medicare,sensor
|
||||||
|
DUP-PT-015,FreeStyle Libre 3,2026-06-04,1,Aetna NJ,sensor
|
||||||
|
37
test-data/nj-set/batch-17-nj-high.csv
Normal file
37
test-data/nj-set/batch-17-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
acct no,dme description,last ship date,qty dispensed,payer name,type,Billing Cycle
|
||||||
|
SC-A-001,Dexcom G7,05/28/2026,1,Medicare FFS NJ,sensor,Monthly
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,WellCare NJ,sensor,Monthly
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Cigna NJ,sensor,Monthly
|
||||||
|
SC-D-004,Dexcom G7,06/02/2026,1,Horizon BCBS NJ Medicare,sensor,Monthly
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,Medicare,sensor,Monthly
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,Horizon BCBS NJ,sensor,Monthly
|
||||||
|
NJ-16-0001,Dexcom G7,06/06/2026,1,AmeriHealth NJ,sensor,Monthly
|
||||||
|
NJ-16-0002,FreeStyle Libre 3,06/05/2026,3,AmeriHealth NJ,sensor,Monthly
|
||||||
|
NJ-16-0003,FreeStyle Libre 3,05/31/2026,1,WellCare NJ,sensor,Monthly
|
||||||
|
NJ-16-0004,Dexcom G6,05/28/2026,3,Medicare FFS NJ,sensor,Monthly
|
||||||
|
NJ-16-0005,Dexcom G7,09/14/2025,3,AmeriHealth NJ,sensor,Monthly
|
||||||
|
NJ-16-0006,Dexcom G7,08/12/2025,2,Medicare FFS NJ,sensor,Monthly
|
||||||
|
NJ-16-0007,Dexcom G6,08/01/2025,1,Medicare FFS NJ,sensor,Monthly
|
||||||
|
NJ-16-0008,FreeStyle Libre 3,05/07/2025,2,UnitedHealthcare NJ,sensor,Monthly
|
||||||
|
NJ-16-0009,Dexcom G7,12/31/2024,1,NJ FamilyCare,sensor,Monthly
|
||||||
|
NJ-16-0010,FreeStyle Libre 2,05/28/2026,3,Aetna NJ Medicare,sensor,Monthly
|
||||||
|
NJ-16-0011,Dexcom G7,05/29/2026,1,Medicare FFS NJ,sensor,Monthly
|
||||||
|
NJ-16-0012,Dexcom G7,05/29/2026,1,Horizon BCBS NJ,sensor,Monthly
|
||||||
|
NJ-16-0013,FreeStyle Libre 3,05/29/2026,1,NJ FamilyCare,sensor,Monthly
|
||||||
|
NJ-16-0014,FreeStyle Libre 2,09/10/2025,1,Medicare Part B,sensor,Monthly
|
||||||
|
NJ-16-0015,FreeStyle Libre 3,08/15/2025,1,AmeriHealth NJ,sensor,Monthly
|
||||||
|
NJ-16-0016,FreeStyle Libre 2,07/30/2025,2,WellCare NJ,sensor,Monthly
|
||||||
|
NJ-16-0017,Dexcom G7,05/16/2025,3,Medicare Part B,sensor,Monthly
|
||||||
|
NJ-16-0018,FreeStyle Libre 2,10/18/2024,1,Horizon BCBS NJ Medicare,sensor,Monthly
|
||||||
|
NJ-16-0019,FreeStyle Libre 3,06/05/2026,3,Aetna NJ Medicare,sensor,Monthly
|
||||||
|
NJ-16-0020,FreeStyle Libre 2,06/03/2026,1,Horizon BCBS NJ Medicare,sensor,Monthly
|
||||||
|
NJ-16-0021,FreeStyle Libre 2,06/04/2026,1,Medicare Part B,sensor,Monthly
|
||||||
|
NJ-16-0022,Dexcom G6,06/03/2026,1,Oxford Health,sensor,Monthly
|
||||||
|
NJ-16-0023,Dexcom G7,09/15/2025,1,WellCare NJ,sensor,Monthly
|
||||||
|
NJ-16-0024,FreeStyle Libre 2,08/19/2025,1,Medicare FFS NJ,sensor,Monthly
|
||||||
|
NJ-16-0025,Dexcom G6,07/29/2025,1,WellCare NJ,sensor,Monthly
|
||||||
|
,Dexcom G7,05/28/2026,1,Medicare Part B,sensor,Monthly
|
||||||
|
FUTURE-016,Dexcom G7,07/07/2026,1,Medicare FFS NJ,sensor,Monthly
|
||||||
|
ZERQTY-016,FreeStyle Libre 3,06/02/2026,0,Aetna NJ,sensor,Monthly
|
||||||
|
DUP-PT-016,Dexcom G7,06/02/2026,1,Medicare Part B,sensor,Monthly
|
||||||
|
DUP-PT-016,FreeStyle Libre 3,06/04/2026,1,Cigna NJ,sensor,Monthly
|
||||||
|
37
test-data/nj-set/batch-18-nj-high.csv
Normal file
37
test-data/nj-set/batch-18-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
external_patient_ref,equipment description,service_date,count,primary_payer,supply_type
|
||||||
|
SC-A-001,Dexcom G7,2026-05-28T00:00:00,1,Medicare,sensor
|
||||||
|
SC-B-002,FreeStyle Libre 3,2026-06-02T00:00:00,1,AmeriHealth NJ,sensor
|
||||||
|
SC-C-003,Dexcom G6,2026-05-08T00:00:00,1,Horizon BCBS NJ,sensor
|
||||||
|
SC-D-004,Dexcom G7,2026-06-02T00:00:00,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
SC-E-005,FreeStyle Libre 2,2026-06-04T00:00:00,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,Dexcom G7,2026-05-31T00:00:00,1,Cigna NJ,sensor
|
||||||
|
NJ-17-0001,FreeStyle Libre 3,2026-06-05T00:00:00,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-17-0002,FreeStyle Libre 2,2026-06-06T00:00:00,1,Medicare,sensor
|
||||||
|
NJ-17-0003,Dexcom G6,2026-06-03T00:00:00,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-17-0004,Dexcom G7,2026-06-06T00:00:00,2,Oxford Health,sensor
|
||||||
|
NJ-17-0005,Dexcom G7,2025-09-19T00:00:00,3,AmeriHealth NJ,sensor
|
||||||
|
NJ-17-0006,Dexcom G6,2025-08-16T00:00:00,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-17-0007,FreeStyle Libre 3,2025-08-01T00:00:00,3,Cigna NJ,sensor
|
||||||
|
NJ-17-0008,FreeStyle Libre 3,2025-05-07T00:00:00,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-17-0009,FreeStyle Libre 2,2024-12-08T00:00:00,1,Cigna NJ,sensor
|
||||||
|
NJ-17-0010,Dexcom G7,2026-06-01T00:00:00,1,Medicare Part B,sensor
|
||||||
|
NJ-17-0011,Dexcom G7,2026-06-05T00:00:00,1,Aetna NJ Medicare,sensor
|
||||||
|
NJ-17-0012,FreeStyle Libre 2,2026-06-01T00:00:00,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-17-0013,Dexcom G7,2026-06-01T00:00:00,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-17-0014,FreeStyle Libre 3,2025-09-19T00:00:00,1,UnitedHealthcare NJ,sensor
|
||||||
|
NJ-17-0015,Dexcom G6,2025-08-19T00:00:00,1,Medicare FFS NJ,sensor
|
||||||
|
NJ-17-0016,FreeStyle Libre 3,2025-07-31T00:00:00,1,Medicare Part B,sensor
|
||||||
|
NJ-17-0017,Dexcom G7,2025-05-22T00:00:00,1,Medicare,sensor
|
||||||
|
NJ-17-0018,FreeStyle Libre 3,2024-11-24T00:00:00,3,Medicare FFS NJ,sensor
|
||||||
|
NJ-17-0019,Dexcom G6,2026-06-05T00:00:00,2,AmeriHealth NJ,sensor
|
||||||
|
NJ-17-0020,FreeStyle Libre 3,2026-06-01T00:00:00,1,UnitedHealthcare NJ Medicare,sensor
|
||||||
|
NJ-17-0021,Dexcom G6,2026-06-02T00:00:00,2,Medicare FFS NJ,sensor
|
||||||
|
NJ-17-0022,Dexcom G7,2026-06-05T00:00:00,3,Cigna NJ,sensor
|
||||||
|
NJ-17-0023,Dexcom G6,2025-09-11T00:00:00,1,Horizon BCBS NJ Medicare,sensor
|
||||||
|
NJ-17-0024,Dexcom G6,2025-08-14T00:00:00,3,NJ FamilyCare,sensor
|
||||||
|
NJ-17-0025,Dexcom G6,2025-08-01T00:00:00,1,Medicare,sensor
|
||||||
|
,Dexcom G7,2026-05-28T00:00:00,1,Medicare,sensor
|
||||||
|
FUTURE-017,Dexcom G7,2026-07-07T00:00:00,1,Medicare,sensor
|
||||||
|
ZERQTY-017,FreeStyle Libre 3,2026-06-02T00:00:00,0,Oxford Health,sensor
|
||||||
|
DUP-PT-017,Dexcom G7,2026-06-02T00:00:00,1,Medicare FFS NJ,sensor
|
||||||
|
DUP-PT-017,FreeStyle Libre 3,2026-06-04T00:00:00,1,Oxford Health,sensor
|
||||||
|
37
test-data/nj-set/batch-19-nj-high.csv
Normal file
37
test-data/nj-set/batch-19-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,swo_status,pa_status,diagnosis_on_file
|
||||||
|
SC-A-001,dexcom_g7,2026-05-28,1,Medicare FFS NJ,sensor,On File,Not Required,Yes
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,NJ FamilyCare,sensor,On File,Not Required,Yes
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,UnitedHealthcare NJ,sensor,On File,Not Required,Yes
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,Aetna NJ Medicare,sensor,On File,Not Required,Yes
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Aetna NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0001,freestyle_libre_2,2026-06-03,1,Aetna NJ Medicare,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0002,freestyle_libre_3,2026-06-01,1,Horizon BCBS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0003,freestyle_libre_2,2026-05-28,2,AmeriHealth NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0004,freestyle_libre_2,2026-05-30,1,Cigna NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0005,dexcom_g6,2025-09-10,3,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0006,freestyle_libre_3,2025-08-16,1,Medicare FFS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0007,dexcom_g6,2025-07-29,1,Medicare FFS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0008,freestyle_libre_2,2025-05-12,1,Horizon BCBS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0009,freestyle_libre_3,2025-01-08,1,Aetna NJ Medicare,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0010,freestyle_libre_2,2026-06-06,1,WellCare NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0011,freestyle_libre_3,2026-05-28,1,NJ FamilyCare,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0012,freestyle_libre_3,2026-06-02,2,Medicare FFS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0013,dexcom_g7,2026-06-06,1,Medicare FFS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0014,dexcom_g7,2025-09-18,1,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0015,dexcom_g7,2025-08-19,3,Cigna NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0016,dexcom_g6,2025-07-28,2,Cigna NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0017,dexcom_g7,2025-05-08,3,Medicare FFS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0018,dexcom_g6,2024-10-20,1,WellCare NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0019,dexcom_g6,2026-05-31,3,Medicare FFS NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0020,dexcom_g7,2026-06-05,1,Aetna NJ Medicare,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0021,dexcom_g7,2026-06-03,1,Cigna NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0022,freestyle_libre_2,2026-05-28,3,AmeriHealth NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0023,freestyle_libre_3,2025-09-12,1,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0024,dexcom_g6,2025-08-12,2,WellCare NJ,sensor,On File,Not Required,Yes
|
||||||
|
NJ-18-0025,dexcom_g6,2025-07-28,1,NJ FamilyCare,sensor,On File,Not Required,Yes
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare,sensor,On File,Not Required,Yes
|
||||||
|
FUTURE-018,dexcom_g7,2026-07-07,1,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
ZERQTY-018,freestyle_libre_3,2026-06-02,0,UnitedHealthcare NJ,sensor,On File,Not Required,Yes
|
||||||
|
DUP-PT-018,dexcom_g7,2026-06-02,1,Medicare,sensor,On File,Not Required,Yes
|
||||||
|
DUP-PT-018,freestyle_libre_3,2026-06-04,1,UnitedHealthcare NJ,sensor,On File,Not Required,Yes
|
||||||
|
37
test-data/nj-set/batch-20-nj-high.csv
Normal file
37
test-data/nj-set/batch-20-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,visit_date,pecos_verified,swo_status
|
||||||
|
SC-A-001,dexcom_g7,2026-05-28,1,Medicare Part B,sensor,,Yes,On File
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,WellCare NJ,sensor,,Yes,On File
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Cigna NJ,sensor,,Yes,On File
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UnitedHealthcare NJ Medicare,sensor,,Yes,On File
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare Part B,sensor,,Yes,On File
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,UnitedHealthcare NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0001,dexcom_g7,2026-05-31,2,Aetna NJ Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0002,dexcom_g6,2026-06-06,3,Aetna NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0003,dexcom_g7,2026-06-03,1,Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0004,freestyle_libre_2,2026-05-29,1,Medicare FFS NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0005,dexcom_g7,2025-09-20,1,Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0006,freestyle_libre_2,2025-08-16,1,Oxford Health,sensor,,Yes,On File
|
||||||
|
NJ-19-0007,freestyle_libre_2,2025-07-31,3,WellCare NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0008,dexcom_g6,2025-05-22,3,Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0009,dexcom_g7,2024-10-30,3,UnitedHealthcare NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0010,freestyle_libre_2,2026-05-30,2,Aetna NJ Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0011,freestyle_libre_3,2026-06-03,3,NJ FamilyCare,sensor,,Yes,On File
|
||||||
|
NJ-19-0012,freestyle_libre_2,2026-06-06,1,UnitedHealthcare NJ Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0013,dexcom_g6,2026-06-05,2,Medicare Part B,sensor,,Yes,On File
|
||||||
|
NJ-19-0014,dexcom_g6,2025-09-10,1,AmeriHealth NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0015,freestyle_libre_3,2025-08-12,3,Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0016,freestyle_libre_3,2025-07-28,2,Horizon BCBS NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0017,freestyle_libre_3,2025-05-22,3,Medicare Part B,sensor,,Yes,On File
|
||||||
|
NJ-19-0018,freestyle_libre_3,2024-10-15,1,Medicare FFS NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0019,freestyle_libre_2,2026-05-31,3,AmeriHealth NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0020,freestyle_libre_3,2026-06-02,1,UnitedHealthcare NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0021,dexcom_g7,2026-06-03,3,Aetna NJ Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0022,dexcom_g7,2026-06-05,3,Cigna NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0023,freestyle_libre_2,2025-09-17,2,Medicare,sensor,,Yes,On File
|
||||||
|
NJ-19-0024,dexcom_g7,2025-08-14,1,AmeriHealth NJ,sensor,,Yes,On File
|
||||||
|
NJ-19-0025,freestyle_libre_3,2025-07-27,2,Medicare,sensor,,Yes,On File
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare,sensor,,Yes,On File
|
||||||
|
FUTURE-019,dexcom_g7,2026-07-07,1,Medicare Part B,sensor,,Yes,On File
|
||||||
|
ZERQTY-019,freestyle_libre_3,2026-06-02,0,Aetna NJ,sensor,,Yes,On File
|
||||||
|
DUP-PT-019,dexcom_g7,2026-06-02,1,Medicare,sensor,,Yes,On File
|
||||||
|
DUP-PT-019,freestyle_libre_3,2026-06-04,1,Aetna NJ,sensor,,Yes,On File
|
||||||
|
37
test-data/nj-set/batch-21-nj-high.csv
Normal file
37
test-data/nj-set/batch-21-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
Patient ID,Item Description,Service Date,Qty,Insurance Name,Item Type,auth_status,swo_status
|
||||||
|
SC-A-001,Dexcom G7,06/02/2026,1,Medicare FFS NJ,sensor,Approved,On File
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,NJ FamilyCare,sensor,Approved,On File
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Horizon BCBS NJ,sensor,Approved,On File
|
||||||
|
SC-D-004,Dexcom G7,06/02/2026,1,Horizon BCBS NJ Medicare,sensor,Approved,On File
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,Aetna NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0001,Dexcom G6,05/31/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0002,Dexcom G7,06/04/2026,3,Medicare Part B,sensor,Approved,On File
|
||||||
|
NJ-20-0003,FreeStyle Libre 3,06/01/2026,2,AmeriHealth NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0004,Dexcom G6,06/03/2026,2,Medicare FFS NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0005,Dexcom G6,09/18/2025,2,NJ FamilyCare,sensor,Approved,On File
|
||||||
|
NJ-20-0006,Dexcom G6,08/12/2025,1,AmeriHealth NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0007,FreeStyle Libre 3,07/30/2025,3,Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0008,FreeStyle Libre 2,05/15/2025,1,UnitedHealthcare NJ Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0009,Dexcom G7,12/25/2024,2,Aetna NJ Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0010,Dexcom G6,05/30/2026,3,WellCare NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0011,FreeStyle Libre 3,05/28/2026,2,UnitedHealthcare NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0012,FreeStyle Libre 3,05/29/2026,2,AmeriHealth NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0013,FreeStyle Libre 3,06/03/2026,1,Aetna NJ Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0014,FreeStyle Libre 3,09/18/2025,2,Horizon BCBS NJ Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0015,Dexcom G6,08/21/2025,3,UnitedHealthcare NJ Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0016,FreeStyle Libre 3,07/29/2025,1,Horizon BCBS NJ Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0017,FreeStyle Libre 2,05/08/2025,1,NJ FamilyCare,sensor,Approved,On File
|
||||||
|
NJ-20-0018,Dexcom G6,10/28/2024,1,Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0019,FreeStyle Libre 2,05/29/2026,1,Horizon BCBS NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0020,FreeStyle Libre 3,06/01/2026,2,Medicare Part B,sensor,Approved,On File
|
||||||
|
NJ-20-0021,Dexcom G7,06/02/2026,1,Medicare FFS NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0022,FreeStyle Libre 2,06/05/2026,1,Medicare FFS NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0023,FreeStyle Libre 2,09/14/2025,1,UnitedHealthcare NJ,sensor,Approved,On File
|
||||||
|
NJ-20-0024,FreeStyle Libre 3,08/15/2025,1,UnitedHealthcare NJ Medicare,sensor,Approved,On File
|
||||||
|
NJ-20-0025,FreeStyle Libre 2,08/01/2025,2,Horizon BCBS NJ,sensor,Approved,On File
|
||||||
|
,Dexcom G7,05/28/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
FUTURE-020,Dexcom G7,07/07/2026,1,Medicare FFS NJ,sensor,Approved,On File
|
||||||
|
ZERQTY-020,FreeStyle Libre 3,06/02/2026,0,Oxford Health,sensor,Approved,On File
|
||||||
|
DUP-PT-020,Dexcom G7,06/02/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
DUP-PT-020,FreeStyle Libre 3,06/04/2026,1,Aetna NJ,sensor,Approved,On File
|
||||||
|
37
test-data/nj-set/batch-22-nj-high.csv
Normal file
37
test-data/nj-set/batch-22-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
account_no,device_type,shipment_date,qty,insurance,component,Prior Auth,diagnosis_on_file
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor,Pending,Yes
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,WellCare NJ,sensor,Pending,Yes
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Oxford Health,sensor,Pending,Yes
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,Horizon BCBS NJ Medicare,sensor,Pending,Yes
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare FFS NJ,sensor,Pending,Yes
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Oxford Health,sensor,Pending,Yes
|
||||||
|
NJ-21-0001,dexcom_g6,2026-05-31,1,WellCare NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0002,freestyle_libre_3,2026-06-05,1,Horizon BCBS NJ Medicare,sensor,Pending,Yes
|
||||||
|
NJ-21-0003,dexcom_g7,2026-06-04,1,AmeriHealth NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0004,dexcom_g7,2026-06-05,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
NJ-21-0005,freestyle_libre_2,2025-09-11,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
NJ-21-0006,freestyle_libre_3,2025-08-18,1,WellCare NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0007,freestyle_libre_2,2025-07-30,1,Medicare FFS NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0008,dexcom_g6,2025-05-10,1,WellCare NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0009,freestyle_libre_2,2024-11-23,1,AmeriHealth NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0010,dexcom_g7,2026-06-01,1,UnitedHealthcare NJ Medicare,sensor,Pending,Yes
|
||||||
|
NJ-21-0011,freestyle_libre_2,2026-05-30,1,Aetna NJ Medicare,sensor,Pending,Yes
|
||||||
|
NJ-21-0012,freestyle_libre_2,2026-06-02,1,Aetna NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0013,freestyle_libre_3,2026-06-02,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
NJ-21-0014,dexcom_g6,2025-09-20,1,Horizon BCBS NJ Medicare,sensor,Pending,Yes
|
||||||
|
NJ-21-0015,dexcom_g6,2025-08-18,2,Aetna NJ Medicare,sensor,Pending,Yes
|
||||||
|
NJ-21-0016,freestyle_libre_2,2025-07-31,1,Horizon BCBS NJ Medicare,sensor,Pending,Yes
|
||||||
|
NJ-21-0017,freestyle_libre_3,2025-05-23,3,NJ FamilyCare,sensor,Pending,Yes
|
||||||
|
NJ-21-0018,freestyle_libre_3,2024-11-25,1,Aetna NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0019,freestyle_libre_2,2026-06-06,1,Aetna NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0020,dexcom_g7,2026-05-30,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
NJ-21-0021,dexcom_g7,2026-06-04,3,Oxford Health,sensor,Pending,Yes
|
||||||
|
NJ-21-0022,dexcom_g6,2026-05-28,1,Horizon BCBS NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0023,dexcom_g7,2025-09-16,1,Horizon BCBS NJ Medicare,sensor,Pending,Yes
|
||||||
|
NJ-21-0024,dexcom_g7,2025-08-12,1,Medicare FFS NJ,sensor,Pending,Yes
|
||||||
|
NJ-21-0025,dexcom_g6,2025-07-31,1,Aetna NJ,sensor,Pending,Yes
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
FUTURE-021,dexcom_g7,2026-07-07,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
ZERQTY-021,freestyle_libre_3,2026-06-02,0,Aetna NJ,sensor,Pending,Yes
|
||||||
|
DUP-PT-021,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor,Pending,Yes
|
||||||
|
DUP-PT-021,freestyle_libre_3,2026-06-04,1,Oxford Health,sensor,Pending,Yes
|
||||||
|
37
test-data/nj-set/batch-23-nj-high.csv
Normal file
37
test-data/nj-set/batch-23-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,Notes,Auth Number,Staff
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor,see chart,A12345,K.F.
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,AmeriHealth NJ,sensor,see chart,A12345,K.F.
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Aetna NJ,sensor,see chart,A12345,K.F.
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UnitedHealthcare NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,UnitedHealthcare NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0001,freestyle_libre_3,2026-06-06,2,Medicare FFS NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0002,freestyle_libre_3,2026-06-03,1,AmeriHealth NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0003,freestyle_libre_3,2026-06-04,2,UnitedHealthcare NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0004,dexcom_g7,2026-05-31,1,Aetna NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0005,freestyle_libre_2,2025-09-12,1,Aetna NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0006,freestyle_libre_2,2025-08-12,1,Aetna NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0007,dexcom_g7,2025-07-27,3,Cigna NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0008,freestyle_libre_3,2025-05-06,1,WellCare NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0009,freestyle_libre_2,2024-12-21,1,Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0010,dexcom_g6,2026-06-03,2,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0011,freestyle_libre_2,2026-05-30,1,UnitedHealthcare NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0012,dexcom_g7,2026-06-03,1,AmeriHealth NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0013,dexcom_g6,2026-06-05,1,UnitedHealthcare NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0014,dexcom_g7,2025-09-16,2,Medicare FFS NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0015,dexcom_g7,2025-08-16,3,Aetna NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0016,dexcom_g6,2025-08-01,1,Aetna NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0017,dexcom_g7,2025-05-06,1,Horizon BCBS NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0018,dexcom_g6,2024-12-20,1,Medicare FFS NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0019,dexcom_g7,2026-06-05,1,AmeriHealth NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0020,freestyle_libre_3,2026-05-28,1,Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0021,freestyle_libre_3,2026-06-01,2,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0022,freestyle_libre_3,2026-06-05,1,AmeriHealth NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0023,dexcom_g7,2025-09-14,1,Medicare FFS NJ,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0024,freestyle_libre_3,2025-08-21,1,UnitedHealthcare NJ Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
NJ-22-0025,freestyle_libre_3,2025-07-29,2,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
FUTURE-022,dexcom_g7,2026-07-07,1,Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
ZERQTY-022,freestyle_libre_3,2026-06-02,0,Cigna NJ,sensor,see chart,A12345,K.F.
|
||||||
|
DUP-PT-022,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor,see chart,A12345,K.F.
|
||||||
|
DUP-PT-022,freestyle_libre_3,2026-06-04,1,Cigna NJ,sensor,see chart,A12345,K.F.
|
||||||
|
37
test-data/nj-set/batch-24-nj-high.csv
Normal file
37
test-data/nj-set/batch-24-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patient_id,device_type,shipment_date,payer
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,Medicare
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,AmeriHealth NJ
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,UnitedHealthcare NJ
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,Horizon BCBS NJ Medicare
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,Medicare
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,Aetna NJ
|
||||||
|
NJ-23-0001,freestyle_libre_2,2026-05-31,Aetna NJ
|
||||||
|
NJ-23-0002,dexcom_g6,2026-06-06,NJ FamilyCare
|
||||||
|
NJ-23-0003,dexcom_g6,2026-06-03,Medicare
|
||||||
|
NJ-23-0004,freestyle_libre_3,2026-05-29,Medicare Part B
|
||||||
|
NJ-23-0005,dexcom_g6,2025-09-13,Aetna NJ
|
||||||
|
NJ-23-0006,freestyle_libre_2,2025-08-17,Aetna NJ Medicare
|
||||||
|
NJ-23-0007,freestyle_libre_2,2025-07-30,Medicare
|
||||||
|
NJ-23-0008,dexcom_g6,2025-05-18,AmeriHealth NJ
|
||||||
|
NJ-23-0009,freestyle_libre_2,2025-01-01,UnitedHealthcare NJ Medicare
|
||||||
|
NJ-23-0010,dexcom_g6,2026-06-03,UnitedHealthcare NJ
|
||||||
|
NJ-23-0011,freestyle_libre_2,2026-06-01,WellCare NJ
|
||||||
|
NJ-23-0012,dexcom_g6,2026-06-04,AmeriHealth NJ
|
||||||
|
NJ-23-0013,dexcom_g6,2026-06-02,WellCare NJ
|
||||||
|
NJ-23-0014,freestyle_libre_2,2025-09-15,Medicare
|
||||||
|
NJ-23-0015,freestyle_libre_2,2025-08-19,Aetna NJ Medicare
|
||||||
|
NJ-23-0016,dexcom_g6,2025-08-01,Horizon BCBS NJ Medicare
|
||||||
|
NJ-23-0017,freestyle_libre_2,2025-05-08,Horizon BCBS NJ Medicare
|
||||||
|
NJ-23-0018,freestyle_libre_2,2024-10-15,AmeriHealth NJ
|
||||||
|
NJ-23-0019,freestyle_libre_2,2026-05-29,WellCare NJ
|
||||||
|
NJ-23-0020,dexcom_g6,2026-05-30,Medicare Part B
|
||||||
|
NJ-23-0021,dexcom_g7,2026-06-01,UnitedHealthcare NJ
|
||||||
|
NJ-23-0022,dexcom_g6,2026-06-06,Horizon BCBS NJ Medicare
|
||||||
|
NJ-23-0023,freestyle_libre_3,2025-09-16,UnitedHealthcare NJ Medicare
|
||||||
|
NJ-23-0024,freestyle_libre_3,2025-08-17,Horizon BCBS NJ Medicare
|
||||||
|
NJ-23-0025,dexcom_g7,2025-08-01,AmeriHealth NJ
|
||||||
|
,dexcom_g7,2026-05-28,Medicare FFS NJ
|
||||||
|
FUTURE-023,dexcom_g7,2026-07-07,Medicare FFS NJ
|
||||||
|
ZERQTY-023,freestyle_libre_3,2026-06-02,Oxford Health
|
||||||
|
DUP-PT-023,dexcom_g7,2026-06-02,Medicare FFS NJ
|
||||||
|
DUP-PT-023,freestyle_libre_3,2026-06-04,Oxford Health
|
||||||
|
37
test-data/nj-set/batch-25-nj-high.csv
Normal file
37
test-data/nj-set/batch-25-nj-high.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,Transfer_From,swo_status
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,1,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,WellCare NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,UnitedHealthcare NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UnitedHealthcare NJ Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Aetna NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0001,freestyle_libre_2,2026-05-31,1,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0002,dexcom_g7,2026-06-03,2,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0003,dexcom_g6,2026-05-31,1,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0004,freestyle_libre_3,2026-06-02,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0005,dexcom_g6,2025-09-15,2,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0006,dexcom_g6,2025-08-16,2,AmeriHealth NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0007,freestyle_libre_3,2025-07-27,1,UnitedHealthcare NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0008,dexcom_g7,2025-05-03,1,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0009,freestyle_libre_2,2024-10-29,1,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0010,freestyle_libre_2,2026-05-31,1,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0011,dexcom_g7,2026-05-31,1,Aetna NJ Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0012,freestyle_libre_2,2026-06-02,1,Cigna NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0013,dexcom_g7,2026-06-05,1,WellCare NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0014,dexcom_g7,2025-09-10,1,UnitedHealthcare NJ Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0015,freestyle_libre_3,2025-08-15,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0016,dexcom_g6,2025-07-27,1,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0017,freestyle_libre_3,2025-05-11,1,Cigna NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0018,freestyle_libre_3,2024-12-23,2,Cigna NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0019,dexcom_g7,2026-05-29,2,NJ FamilyCare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0020,freestyle_libre_3,2026-06-05,1,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0021,freestyle_libre_2,2026-06-01,2,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0022,freestyle_libre_3,2026-05-30,1,UnitedHealthcare NJ Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0023,dexcom_g7,2025-09-17,1,UnitedHealthcare NJ Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0024,dexcom_g7,2025-08-21,1,Horizon BCBS NJ Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
NJ-24-0025,dexcom_g6,2025-07-30,2,Aetna NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
FUTURE-024,dexcom_g7,2026-07-07,1,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
ZERQTY-024,freestyle_libre_3,2026-06-02,0,Horizon BCBS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
DUP-PT-024,dexcom_g7,2026-06-02,1,Medicare FFS NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
DUP-PT-024,freestyle_libre_3,2026-06-04,1,UnitedHealthcare NJ,sensor,Prior Supplier LLC,Pending
|
||||||
|
42
test-data/pa-set/batch-01-pa-high.csv
Normal file
42
test-data/pa-set/batch-01-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component
|
||||||
|
SC-A-001,dexcom_g7,2025-09-10,1,CMS,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,PA Medicaid,sensor
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Highmark BCBS PA,sensor
|
||||||
|
SC-D-004,dexcom_g7,2024-10-15,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Geisinger,sensor
|
||||||
|
PA-00-0001,dexcom_g7,2026-06-01,1,CMS,sensor
|
||||||
|
PA-00-0002,dexcom_g7,2026-05-28,1,Cigna PA,sensor
|
||||||
|
PA-00-0003,dexcom_g6,2026-05-31,2,Cigna PA,sensor
|
||||||
|
PA-00-0004,freestyle_libre_2,2026-05-30,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-00-0005,freestyle_libre_3,2025-09-10,2,Aetna PA,sensor
|
||||||
|
PA-00-0006,freestyle_libre_3,2025-08-20,1,Aetna PA,sensor
|
||||||
|
PA-00-0007,dexcom_g7,2025-07-30,1,Medicare Part B,sensor
|
||||||
|
PA-00-0008,freestyle_libre_3,2025-05-11,1,CMS,sensor
|
||||||
|
PA-00-0009,dexcom_g6,2024-11-18,3,UPMC for You,sensor
|
||||||
|
PA-00-0010,dexcom_g6,2026-06-03,1,Medicare,sensor
|
||||||
|
PA-00-0011,dexcom_g6,2026-06-04,1,UPMC for You,sensor
|
||||||
|
PA-00-0012,freestyle_libre_3,2026-06-06,2,Cigna PA,sensor
|
||||||
|
PA-00-0013,dexcom_g7,2026-05-30,2,Medicare Part B,sensor
|
||||||
|
PA-00-0014,freestyle_libre_2,2025-09-18,1,Aetna PA,sensor
|
||||||
|
PA-00-0015,freestyle_libre_3,2025-08-15,2,UPMC Health Plan,sensor
|
||||||
|
PA-00-0016,dexcom_g7,2025-07-31,3,Aetna PA Medicare,sensor
|
||||||
|
PA-00-0017,dexcom_g7,2025-05-18,1,Aetna PA,sensor
|
||||||
|
PA-00-0018,dexcom_g6,2024-10-25,2,Independence Blue Cross,sensor
|
||||||
|
PA-00-0019,freestyle_libre_2,2026-06-01,3,Highmark BCBS PA,sensor
|
||||||
|
PA-00-0020,freestyle_libre_3,2026-06-04,3,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-00-0021,freestyle_libre_3,2026-06-05,1,CMS,sensor
|
||||||
|
PA-00-0022,dexcom_g7,2026-05-28,1,Medicare Part B,sensor
|
||||||
|
PA-00-0023,freestyle_libre_2,2025-09-12,3,CMS,sensor
|
||||||
|
PA-00-0024,dexcom_g6,2025-08-21,2,Medicare Part B,sensor
|
||||||
|
PA-00-0025,dexcom_g7,2025-08-01,1,Medicare,sensor
|
||||||
|
PA-00-0026,freestyle_libre_2,2025-05-08,1,Medicare,sensor
|
||||||
|
PA-00-0027,dexcom_g7,2024-12-23,1,CMS,sensor
|
||||||
|
PA-00-0028,dexcom_g6,2026-05-30,2,PA Medicaid,sensor
|
||||||
|
PA-00-0029,freestyle_libre_3,2026-05-30,1,UPMC for You,sensor
|
||||||
|
PA-00-0030,dexcom_g7,2026-05-28,3,UPMC Health Plan,sensor
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare Part B,sensor
|
||||||
|
FUTURE-000,dexcom_g7,2026-07-07,1,CMS,sensor
|
||||||
|
ZERQTY-000,freestyle_libre_3,2026-06-02,0,Geisinger,sensor
|
||||||
|
DUP-PT-000,dexcom_g7,2026-06-02,1,Medicare Part B,sensor
|
||||||
|
DUP-PT-000,freestyle_libre_3,2026-06-04,1,Geisinger,sensor
|
||||||
|
42
test-data/pa-set/batch-02-pa-high.csv
Normal file
42
test-data/pa-set/batch-02-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
Patient ID,Item Description,Service Date,Qty,Insurance Name,Item Type,Prescriber NPI,Branch
|
||||||
|
SC-A-001,Dexcom G7,09/10/2025,1,Medicare Part B,sensor,1234567890,PA-001
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,PA Medicaid,sensor,1234567890,PA-001
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Cigna PA,sensor,1234567890,PA-001
|
||||||
|
SC-D-004,Dexcom G7,10/15/2024,1,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,Medicare,sensor,1234567890,PA-001
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,Highmark BCBS PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0001,Dexcom G7,06/01/2026,1,AmeriHealth Caritas PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0002,FreeStyle Libre 2,06/01/2026,2,CMS,sensor,1234567890,PA-001
|
||||||
|
PA-01-0003,Dexcom G7,05/31/2026,3,CMS,sensor,1234567890,PA-001
|
||||||
|
PA-01-0004,FreeStyle Libre 3,06/04/2026,3,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
PA-01-0005,Dexcom G6,09/11/2025,3,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
PA-01-0006,FreeStyle Libre 3,08/15/2025,1,Cigna PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0007,Dexcom G7,08/01/2025,1,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
PA-01-0008,Dexcom G7,05/04/2025,1,CMS,sensor,1234567890,PA-001
|
||||||
|
PA-01-0009,FreeStyle Libre 3,11/29/2024,1,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
PA-01-0010,Dexcom G6,06/03/2026,1,Medicare Part B,sensor,1234567890,PA-001
|
||||||
|
PA-01-0011,Dexcom G6,05/29/2026,2,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
PA-01-0012,FreeStyle Libre 3,05/31/2026,1,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
PA-01-0013,FreeStyle Libre 3,06/05/2026,1,Cigna PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0014,FreeStyle Libre 3,09/19/2025,2,Medicare Part B,sensor,1234567890,PA-001
|
||||||
|
PA-01-0015,Dexcom G7,08/15/2025,1,Keystone First,sensor,1234567890,PA-001
|
||||||
|
PA-01-0016,Dexcom G6,07/27/2025,1,Highmark BlueCross BlueShield,sensor,1234567890,PA-001
|
||||||
|
PA-01-0017,FreeStyle Libre 2,05/12/2025,3,Highmark BlueCross BlueShield,sensor,1234567890,PA-001
|
||||||
|
PA-01-0018,FreeStyle Libre 3,11/22/2024,1,PA Medicaid,sensor,1234567890,PA-001
|
||||||
|
PA-01-0019,Dexcom G6,05/30/2026,1,Cigna PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0020,Dexcom G6,05/31/2026,1,UPMC Health Plan,sensor,1234567890,PA-001
|
||||||
|
PA-01-0021,FreeStyle Libre 2,06/03/2026,2,Keystone First,sensor,1234567890,PA-001
|
||||||
|
PA-01-0022,Dexcom G7,05/30/2026,3,Keystone First,sensor,1234567890,PA-001
|
||||||
|
PA-01-0023,FreeStyle Libre 2,09/20/2025,3,Medicare,sensor,1234567890,PA-001
|
||||||
|
PA-01-0024,FreeStyle Libre 2,08/13/2025,2,Cigna PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0025,FreeStyle Libre 2,07/30/2025,1,Medicare,sensor,1234567890,PA-001
|
||||||
|
PA-01-0026,FreeStyle Libre 3,05/07/2025,1,Independence Blue Cross,sensor,1234567890,PA-001
|
||||||
|
PA-01-0027,FreeStyle Libre 2,12/23/2024,2,AmeriHealth Caritas PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0028,Dexcom G7,06/04/2026,1,Cigna PA,sensor,1234567890,PA-001
|
||||||
|
PA-01-0029,Dexcom G6,06/01/2026,2,Keystone First,sensor,1234567890,PA-001
|
||||||
|
PA-01-0030,FreeStyle Libre 2,05/30/2026,3,Medicare,sensor,1234567890,PA-001
|
||||||
|
,Dexcom G7,05/28/2026,1,Medicare Part B,sensor,1234567890,PA-001
|
||||||
|
FUTURE-001,Dexcom G7,07/07/2026,1,Medicare,sensor,1234567890,PA-001
|
||||||
|
ZERQTY-001,FreeStyle Libre 3,06/02/2026,0,Cigna PA,sensor,1234567890,PA-001
|
||||||
|
DUP-PT-001,Dexcom G7,06/02/2026,1,Medicare,sensor,1234567890,PA-001
|
||||||
|
DUP-PT-001,FreeStyle Libre 3,06/04/2026,1,Highmark BCBS PA,sensor,1234567890,PA-001
|
||||||
|
42
test-data/pa-set/batch-03-pa-high.csv
Normal file
42
test-data/pa-set/batch-03-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
PT_ID,DEVICE,SHIP DATE,UNITS,CARRIER,TYPE
|
||||||
|
SC-A-001,G7,2025-09-10,1,Medicare,sensor
|
||||||
|
SC-B-002,FSL3,2026-06-02,1,PA Medicaid,sensor
|
||||||
|
SC-C-003,G6,2026-05-08,1,Geisinger,sensor
|
||||||
|
SC-D-004,G7,2024-10-15,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
SC-E-005,FSL2,2026-06-04,1,Medicare,sensor
|
||||||
|
SC-F-006,G7,2026-05-31,1,Independence Blue Cross,sensor
|
||||||
|
PA-02-0001,FSL2,2026-06-03,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-02-0002,G7,2026-05-29,1,UPMC Health Plan,sensor
|
||||||
|
PA-02-0003,G6,2026-06-06,1,PA Medicaid,sensor
|
||||||
|
PA-02-0004,G7,2026-05-29,3,Medicare,sensor
|
||||||
|
PA-02-0005,FSL2,2025-09-19,1,Medicare Part B,sensor
|
||||||
|
PA-02-0006,G7,2025-08-16,3,Independence Blue Cross,sensor
|
||||||
|
PA-02-0007,FSL3,2025-07-30,1,Medicare,sensor
|
||||||
|
PA-02-0008,G6,2025-05-19,1,Keystone First,sensor
|
||||||
|
PA-02-0009,FSL3,2024-10-26,1,UPMC Health Plan,sensor
|
||||||
|
PA-02-0010,FSL2,2026-06-04,1,UPMC Health Plan,sensor
|
||||||
|
PA-02-0011,G7,2026-05-29,1,Highmark BCBS PA,sensor
|
||||||
|
PA-02-0012,FSL2,2026-05-28,1,UPMC for You,sensor
|
||||||
|
PA-02-0013,FSL3,2026-06-03,3,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-02-0014,FSL3,2025-09-13,1,Medicare Part B,sensor
|
||||||
|
PA-02-0015,G6,2025-08-18,1,CMS,sensor
|
||||||
|
PA-02-0016,G6,2025-07-29,1,UPMC Health Plan,sensor
|
||||||
|
PA-02-0017,G7,2025-05-07,2,Aetna PA,sensor
|
||||||
|
PA-02-0018,FSL3,2024-11-16,2,Medicare Part B,sensor
|
||||||
|
PA-02-0019,G7,2026-06-06,1,Medicare Part B,sensor
|
||||||
|
PA-02-0020,G6,2026-06-04,2,Cigna PA,sensor
|
||||||
|
PA-02-0021,G7,2026-05-31,1,Independence Blue Cross,sensor
|
||||||
|
PA-02-0022,G6,2026-05-30,1,Independence Blue Cross,sensor
|
||||||
|
PA-02-0023,G6,2025-09-14,2,Aetna PA Medicare,sensor
|
||||||
|
PA-02-0024,G6,2025-08-13,1,Medicare,sensor
|
||||||
|
PA-02-0025,FSL3,2025-07-30,1,PA Medicaid,sensor
|
||||||
|
PA-02-0026,FSL2,2025-05-04,2,CMS,sensor
|
||||||
|
PA-02-0027,G7,2024-12-27,3,CMS,sensor
|
||||||
|
PA-02-0028,FSL2,2026-06-01,1,Cigna PA,sensor
|
||||||
|
PA-02-0029,FSL3,2026-06-02,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-02-0030,FSL2,2026-06-04,1,Independence Blue Cross,sensor
|
||||||
|
,G7,2026-05-28,1,Medicare Part B,sensor
|
||||||
|
FUTURE-002,G7,2026-07-07,1,CMS,sensor
|
||||||
|
ZERQTY-002,FSL3,2026-06-02,0,Aetna PA,sensor
|
||||||
|
DUP-PT-002,G7,2026-06-02,1,CMS,sensor
|
||||||
|
DUP-PT-002,FSL3,2026-06-04,1,Cigna PA,sensor
|
||||||
|
42
test-data/pa-set/batch-04-pa-high.csv
Normal file
42
test-data/pa-set/batch-04-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
MRN,Product Name,Dispense Date,Qty Dispensed,Plan Name,Supply Type,Supplier,State
|
||||||
|
SC-A-001,Dexcom G7,10-Sep-2025,1,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
SC-B-002,FreeStyle Libre 3,02-Jun-2026,1,Keystone First,sensor,Gaboro DME,PA
|
||||||
|
SC-C-003,Dexcom G6,08-May-2026,1,Highmark BCBS PA,sensor,Gaboro DME,PA
|
||||||
|
SC-D-004,Dexcom G7,15-Oct-2024,1,Highmark BlueCross BlueShield,sensor,Gaboro DME,PA
|
||||||
|
SC-E-005,FreeStyle Libre 2,04-Jun-2026,1,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
SC-F-006,Dexcom G7,31-May-2026,1,Geisinger,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0001,FreeStyle Libre 2,01-Jun-2026,3,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0002,Dexcom G7,06-Jun-2026,1,CMS,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0003,FreeStyle Libre 2,29-May-2026,2,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0004,FreeStyle Libre 3,04-Jun-2026,1,UPMC for You,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0005,Dexcom G6,16-Sep-2025,1,Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0006,FreeStyle Libre 3,21-Aug-2025,2,Geisinger,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0007,FreeStyle Libre 2,28-Jul-2025,1,AmeriHealth Caritas PA,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0008,Dexcom G7,18-May-2025,2,Aetna PA Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0009,FreeStyle Libre 3,27-Oct-2024,3,Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0010,Dexcom G6,01-Jun-2026,1,Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0011,FreeStyle Libre 2,04-Jun-2026,3,Aetna PA Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0012,FreeStyle Libre 2,29-May-2026,2,CMS,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0013,FreeStyle Libre 3,02-Jun-2026,1,Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0014,Dexcom G6,11-Sep-2025,3,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0015,Dexcom G6,21-Aug-2025,1,Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0016,FreeStyle Libre 2,31-Jul-2025,1,Aetna PA Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0017,FreeStyle Libre 3,09-May-2025,2,AmeriHealth Caritas PA,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0018,FreeStyle Libre 3,12-Jan-2025,1,Aetna PA,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0019,Dexcom G7,01-Jun-2026,1,Cigna PA,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0020,FreeStyle Libre 3,29-May-2026,1,CMS,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0021,Dexcom G7,03-Jun-2026,1,Highmark BlueCross BlueShield,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0022,Dexcom G6,01-Jun-2026,1,Highmark BlueCross BlueShield,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0023,FreeStyle Libre 2,11-Sep-2025,2,Aetna PA,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0024,FreeStyle Libre 2,21-Aug-2025,1,UPMC Health Plan,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0025,Dexcom G7,01-Aug-2025,2,AmeriHealth Caritas PA,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0026,FreeStyle Libre 2,10-May-2025,3,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0027,FreeStyle Libre 2,07-Dec-2024,1,Highmark BCBS PA,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0028,Dexcom G7,04-Jun-2026,2,UPMC Health Plan,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0029,FreeStyle Libre 3,01-Jun-2026,1,Medicare,sensor,Gaboro DME,PA
|
||||||
|
PA-03-0030,Dexcom G7,31-May-2026,1,Medicare,sensor,Gaboro DME,PA
|
||||||
|
,Dexcom G7,28-May-2026,1,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
FUTURE-003,Dexcom G7,07-Jul-2026,1,Medicare,sensor,Gaboro DME,PA
|
||||||
|
ZERQTY-003,FreeStyle Libre 3,02-Jun-2026,0,Highmark BCBS PA,sensor,Gaboro DME,PA
|
||||||
|
DUP-PT-003,Dexcom G7,02-Jun-2026,1,Medicare Part B,sensor,Gaboro DME,PA
|
||||||
|
DUP-PT-003,FreeStyle Libre 3,04-Jun-2026,1,Geisinger,sensor,Gaboro DME,PA
|
||||||
|
42
test-data/pa-set/batch-05-pa-high.csv
Normal file
42
test-data/pa-set/batch-05-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
Account Number,Product,Fill Date,Count,Primary Payer,component
|
||||||
|
SC-A-001,dexcom_g7,09/10/25,1,CMS,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,06/02/26,1,Keystone First,sensor
|
||||||
|
SC-C-003,dexcom_g6,05/08/26,1,Aetna PA,sensor
|
||||||
|
SC-D-004,dexcom_g7,10/15/24,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,06/04/26,1,Medicare,sensor
|
||||||
|
SC-F-006,dexcom_g7,05/31/26,1,Cigna PA,sensor
|
||||||
|
PA-04-0001,freestyle_libre_3,05/28/26,1,Medicare Part B,sensor
|
||||||
|
PA-04-0002,dexcom_g6,05/30/26,2,UPMC Health Plan,sensor
|
||||||
|
PA-04-0003,dexcom_g7,06/06/26,1,Keystone First,sensor
|
||||||
|
PA-04-0004,dexcom_g7,06/03/26,1,UPMC for You,sensor
|
||||||
|
PA-04-0005,dexcom_g7,09/12/25,1,Aetna PA Medicare,sensor
|
||||||
|
PA-04-0006,freestyle_libre_3,08/12/25,1,UPMC for You,sensor
|
||||||
|
PA-04-0007,freestyle_libre_2,07/30/25,2,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-04-0008,dexcom_g6,05/05/25,1,PA Medicaid,sensor
|
||||||
|
PA-04-0009,dexcom_g6,12/29/24,3,Independence Blue Cross,sensor
|
||||||
|
PA-04-0010,freestyle_libre_2,06/06/26,1,Medicare,sensor
|
||||||
|
PA-04-0011,freestyle_libre_2,06/02/26,3,Medicare Part B,sensor
|
||||||
|
PA-04-0012,dexcom_g7,05/28/26,1,Aetna PA,sensor
|
||||||
|
PA-04-0013,dexcom_g7,06/06/26,2,Medicare Part B,sensor
|
||||||
|
PA-04-0014,dexcom_g7,09/16/25,2,Highmark BCBS PA,sensor
|
||||||
|
PA-04-0015,freestyle_libre_3,08/12/25,1,Geisinger,sensor
|
||||||
|
PA-04-0016,freestyle_libre_2,07/31/25,2,UPMC for You,sensor
|
||||||
|
PA-04-0017,freestyle_libre_3,05/12/25,3,PA Medicaid,sensor
|
||||||
|
PA-04-0018,freestyle_libre_2,10/15/24,1,Independence Blue Cross,sensor
|
||||||
|
PA-04-0019,freestyle_libre_3,06/04/26,2,Medicare Part B,sensor
|
||||||
|
PA-04-0020,freestyle_libre_3,05/28/26,2,Medicare,sensor
|
||||||
|
PA-04-0021,freestyle_libre_2,06/03/26,1,Cigna PA,sensor
|
||||||
|
PA-04-0022,freestyle_libre_2,06/03/26,1,UPMC Health Plan,sensor
|
||||||
|
PA-04-0023,freestyle_libre_3,09/15/25,1,UPMC Health Plan,sensor
|
||||||
|
PA-04-0024,freestyle_libre_3,08/12/25,1,Medicare Part B,sensor
|
||||||
|
PA-04-0025,dexcom_g7,07/30/25,2,CMS,sensor
|
||||||
|
PA-04-0026,freestyle_libre_3,05/17/25,1,UPMC Health Plan,sensor
|
||||||
|
PA-04-0027,dexcom_g7,10/24/24,1,Aetna PA Medicare,sensor
|
||||||
|
PA-04-0028,freestyle_libre_3,05/29/26,3,CMS,sensor
|
||||||
|
PA-04-0029,dexcom_g7,06/02/26,3,Keystone First,sensor
|
||||||
|
PA-04-0030,dexcom_g7,05/29/26,3,Aetna PA Medicare,sensor
|
||||||
|
,dexcom_g7,05/28/26,1,Medicare,sensor
|
||||||
|
FUTURE-004,dexcom_g7,07/07/26,1,Medicare,sensor
|
||||||
|
ZERQTY-004,freestyle_libre_3,06/02/26,0,Geisinger,sensor
|
||||||
|
DUP-PT-004,dexcom_g7,06/02/26,1,Medicare Part B,sensor
|
||||||
|
DUP-PT-004,freestyle_libre_3,06/04/26,1,Aetna PA,sensor
|
||||||
|
42
test-data/pa-set/batch-06-pa-high.csv
Normal file
42
test-data/pa-set/batch-06-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
External Patient Ref,Item,Date of Service,Qty Shipped,Insurance,item_type,Notes,Region
|
||||||
|
SC-A-001,dexcom_g7,2025-09-10T00:00:00,1,Medicare,sensor,batch export,Philadelphia
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02T00:00:00,1,UPMC for You,sensor,batch export,Philadelphia
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08T00:00:00,1,Highmark BCBS PA,sensor,batch export,Philadelphia
|
||||||
|
SC-D-004,dexcom_g7,2024-10-15T00:00:00,1,Aetna PA Medicare,sensor,batch export,Philadelphia
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04T00:00:00,1,Medicare,sensor,batch export,Philadelphia
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31T00:00:00,1,Cigna PA,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0001,dexcom_g7,2026-06-05T00:00:00,2,UPMC Health Plan,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0002,dexcom_g6,2026-06-03T00:00:00,1,Highmark BlueCross BlueShield,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0003,dexcom_g6,2026-06-03T00:00:00,3,Independence Blue Cross,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0004,dexcom_g6,2026-05-31T00:00:00,1,Highmark BlueCross BlueShield,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0005,dexcom_g7,2025-09-12T00:00:00,3,Aetna PA Medicare,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0006,dexcom_g6,2025-08-17T00:00:00,1,PA Medicaid,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0007,freestyle_libre_3,2025-07-31T00:00:00,1,Keystone First,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0008,freestyle_libre_2,2025-05-15T00:00:00,3,AmeriHealth Caritas PA,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0009,dexcom_g6,2025-01-05T00:00:00,2,UPMC Health Plan,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0010,dexcom_g6,2026-06-05T00:00:00,1,Highmark BlueCross BlueShield,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0011,freestyle_libre_2,2026-06-05T00:00:00,1,Medicare,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0012,dexcom_g7,2026-06-02T00:00:00,1,UPMC Health Plan,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0013,freestyle_libre_2,2026-05-29T00:00:00,1,PA Medicaid,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0014,freestyle_libre_2,2025-09-13T00:00:00,1,Medicare,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0015,freestyle_libre_2,2025-08-18T00:00:00,3,Medicare Part B,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0016,dexcom_g6,2025-07-27T00:00:00,3,Highmark BlueCross BlueShield,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0017,dexcom_g7,2025-05-05T00:00:00,3,Aetna PA,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0018,freestyle_libre_2,2024-12-03T00:00:00,1,CMS,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0019,dexcom_g7,2026-05-29T00:00:00,3,UPMC Health Plan,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0020,dexcom_g6,2026-05-30T00:00:00,1,UPMC for You,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0021,dexcom_g6,2026-05-28T00:00:00,1,Medicare Part B,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0022,freestyle_libre_2,2026-05-31T00:00:00,1,UPMC Health Plan,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0023,freestyle_libre_3,2025-09-17T00:00:00,2,UPMC for You,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0024,freestyle_libre_3,2025-08-17T00:00:00,1,CMS,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0025,dexcom_g7,2025-08-01T00:00:00,1,Medicare,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0026,freestyle_libre_2,2025-05-04T00:00:00,1,Highmark BlueCross BlueShield,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0027,freestyle_libre_2,2025-01-18T00:00:00,1,Aetna PA Medicare,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0028,dexcom_g7,2026-06-02T00:00:00,1,CMS,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0029,freestyle_libre_3,2026-05-29T00:00:00,3,Highmark BlueCross BlueShield,sensor,batch export,Philadelphia
|
||||||
|
PA-05-0030,dexcom_g7,2026-05-30T00:00:00,2,CMS,sensor,batch export,Philadelphia
|
||||||
|
,dexcom_g7,2026-05-28T00:00:00,1,Medicare Part B,sensor,batch export,Philadelphia
|
||||||
|
FUTURE-005,dexcom_g7,2026-07-07T00:00:00,1,Medicare Part B,sensor,batch export,Philadelphia
|
||||||
|
ZERQTY-005,freestyle_libre_3,2026-06-02T00:00:00,0,Highmark BCBS PA,sensor,batch export,Philadelphia
|
||||||
|
DUP-PT-005,dexcom_g7,2026-06-02T00:00:00,1,Medicare Part B,sensor,batch export,Philadelphia
|
||||||
|
DUP-PT-005,freestyle_libre_3,2026-06-04T00:00:00,1,Cigna PA,sensor,batch export,Philadelphia
|
||||||
|
42
test-data/pa-set/batch-07-pa-high.csv
Normal file
42
test-data/pa-set/batch-07-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
Acct #,DME,Order Date,quantity,plan,component_type
|
||||||
|
SC-A-001,G7,20250910,1,CMS,sensor
|
||||||
|
SC-B-002,FSL3,20260602,1,UPMC for You,sensor
|
||||||
|
SC-C-003,G6,20260508,1,Cigna PA,sensor
|
||||||
|
SC-D-004,G7,20241015,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
SC-E-005,FSL2,20260604,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,G7,20260531,1,Aetna PA,sensor
|
||||||
|
PA-06-0001,G6,20260602,1,PA Medicaid,sensor
|
||||||
|
PA-06-0002,G6,20260531,1,Aetna PA Medicare,sensor
|
||||||
|
PA-06-0003,FSL3,20260606,1,Keystone First,sensor
|
||||||
|
PA-06-0004,FSL2,20260530,1,PA Medicaid,sensor
|
||||||
|
PA-06-0005,FSL3,20250920,3,CMS,sensor
|
||||||
|
PA-06-0006,G7,20250815,1,Highmark BCBS PA,sensor
|
||||||
|
PA-06-0007,FSL3,20250728,3,Medicare,sensor
|
||||||
|
PA-06-0008,G7,20250521,3,Highmark BCBS PA,sensor
|
||||||
|
PA-06-0009,G7,20241020,3,Medicare Part B,sensor
|
||||||
|
PA-06-0010,G7,20260601,2,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-06-0011,G7,20260604,2,Medicare Part B,sensor
|
||||||
|
PA-06-0012,G6,20260602,1,Aetna PA Medicare,sensor
|
||||||
|
PA-06-0013,FSL2,20260604,1,Medicare,sensor
|
||||||
|
PA-06-0014,FSL2,20250913,1,Cigna PA,sensor
|
||||||
|
PA-06-0015,G6,20250818,1,UPMC for You,sensor
|
||||||
|
PA-06-0016,FSL2,20250731,1,Medicare Part B,sensor
|
||||||
|
PA-06-0017,FSL2,20250503,1,Aetna PA Medicare,sensor
|
||||||
|
PA-06-0018,G7,20241226,3,PA Medicaid,sensor
|
||||||
|
PA-06-0019,FSL2,20260606,3,Aetna PA,sensor
|
||||||
|
PA-06-0020,FSL3,20260530,1,Aetna PA,sensor
|
||||||
|
PA-06-0021,G7,20260602,2,CMS,sensor
|
||||||
|
PA-06-0022,G6,20260602,1,Aetna PA Medicare,sensor
|
||||||
|
PA-06-0023,FSL2,20250917,1,Aetna PA,sensor
|
||||||
|
PA-06-0024,FSL3,20250818,1,CMS,sensor
|
||||||
|
PA-06-0025,G7,20250728,3,Cigna PA,sensor
|
||||||
|
PA-06-0026,G6,20250506,1,CMS,sensor
|
||||||
|
PA-06-0027,G6,20250117,1,UPMC for You,sensor
|
||||||
|
PA-06-0028,FSL2,20260602,1,Cigna PA,sensor
|
||||||
|
PA-06-0029,FSL3,20260531,1,Cigna PA,sensor
|
||||||
|
PA-06-0030,FSL3,20260531,2,Aetna PA Medicare,sensor
|
||||||
|
,G7,20260528,1,Medicare Part B,sensor
|
||||||
|
FUTURE-006,G7,20260707,1,Medicare,sensor
|
||||||
|
ZERQTY-006,FSL3,20260602,0,Independence Blue Cross,sensor
|
||||||
|
DUP-PT-006,G7,20260602,1,Medicare,sensor
|
||||||
|
DUP-PT-006,FSL3,20260604,1,Cigna PA,sensor
|
||||||
|
42
test-data/pa-set/batch-08-pa-high.csv
Normal file
42
test-data/pa-set/batch-08-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patientid,devicetype,dispensedate,qty,payername,supplytype
|
||||||
|
SC-A-001,dexcom_g7,not on file,1,Medicare,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,pending,1,Keystone First,sensor
|
||||||
|
SC-C-003,dexcom_g6,pending,1,Aetna PA,sensor
|
||||||
|
SC-D-004,dexcom_g7,10/15/2024,1,Aetna PA Medicare,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,06/04/2026,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,dexcom_g7,05/31/2026,1,Cigna PA,sensor
|
||||||
|
PA-07-0001,freestyle_libre_3,05/31/2026,3,Keystone First,sensor
|
||||||
|
PA-07-0002,dexcom_g6,06/05/2026,1,Medicare Part B,sensor
|
||||||
|
PA-07-0003,freestyle_libre_3,05/30/2026,1,PA Medicaid,sensor
|
||||||
|
PA-07-0004,freestyle_libre_2,06/02/2026,1,Medicare,sensor
|
||||||
|
PA-07-0005,dexcom_g7,09/12/2025,1,Medicare,sensor
|
||||||
|
PA-07-0006,dexcom_g7,08/20/2025,3,Medicare,sensor
|
||||||
|
PA-07-0007,freestyle_libre_2,07/30/2025,1,CMS,sensor
|
||||||
|
PA-07-0008,freestyle_libre_2,05/08/2025,1,Aetna PA Medicare,sensor
|
||||||
|
PA-07-0009,freestyle_libre_2,10/26/2024,2,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-07-0010,freestyle_libre_3,06/01/2026,2,Medicare Part B,sensor
|
||||||
|
PA-07-0011,dexcom_g7,06/02/2026,1,Aetna PA Medicare,sensor
|
||||||
|
PA-07-0012,freestyle_libre_3,06/01/2026,1,CMS,sensor
|
||||||
|
PA-07-0013,dexcom_g6,06/03/2026,3,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-07-0014,dexcom_g7,09/18/2025,1,Cigna PA,sensor
|
||||||
|
PA-07-0015,dexcom_g6,08/13/2025,1,Geisinger,sensor
|
||||||
|
PA-07-0016,dexcom_g6,07/27/2025,3,Cigna PA,sensor
|
||||||
|
PA-07-0017,freestyle_libre_2,05/20/2025,2,PA Medicaid,sensor
|
||||||
|
PA-07-0018,dexcom_g7,10/24/2024,1,CMS,sensor
|
||||||
|
PA-07-0019,dexcom_g7,05/28/2026,1,Medicare,sensor
|
||||||
|
PA-07-0020,freestyle_libre_2,06/04/2026,2,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-07-0021,dexcom_g7,06/03/2026,3,CMS,sensor
|
||||||
|
PA-07-0022,freestyle_libre_3,06/05/2026,2,UPMC for You,sensor
|
||||||
|
PA-07-0023,freestyle_libre_3,09/11/2025,1,CMS,sensor
|
||||||
|
PA-07-0024,dexcom_g6,08/12/2025,3,Medicare Part B,sensor
|
||||||
|
PA-07-0025,dexcom_g6,07/30/2025,1,Medicare,sensor
|
||||||
|
PA-07-0026,freestyle_libre_3,05/21/2025,1,CMS,sensor
|
||||||
|
PA-07-0027,freestyle_libre_3,12/20/2024,1,CMS,sensor
|
||||||
|
PA-07-0028,dexcom_g6,05/28/2026,1,UPMC for You,sensor
|
||||||
|
PA-07-0029,freestyle_libre_2,06/01/2026,2,Keystone First,sensor
|
||||||
|
PA-07-0030,dexcom_g6,05/30/2026,2,Aetna PA Medicare,sensor
|
||||||
|
,dexcom_g7,05/28/2026,1,Medicare,sensor
|
||||||
|
FUTURE-007,dexcom_g7,07/07/2026,1,Medicare Part B,sensor
|
||||||
|
ZERQTY-007,freestyle_libre_3,06/02/2026,0,Geisinger,sensor
|
||||||
|
DUP-PT-007,dexcom_g7,06/02/2026,1,CMS,sensor
|
||||||
|
DUP-PT-007,freestyle_libre_3,06/04/2026,1,Highmark BCBS PA,sensor
|
||||||
|
42
test-data/pa-set/batch-09-pa-high.csv
Normal file
42
test-data/pa-set/batch-09-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
pt_id,product_type,dos,qty,ins_name,component
|
||||||
|
SC-A-001,dexcom_g7,2025-09-10,1,Medicare,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,PA Medicaid,sensor
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Geisinger,sensor
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UPMC Health Plan,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,CMS,sensor
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Geisinger,sensor
|
||||||
|
PA-08-0001,dexcom_g7,2026-06-06,3,Medicare Part B,sensor
|
||||||
|
PA-08-0002,freestyle_libre_3,2026-05-31,1,Medicare Part B,sensor
|
||||||
|
PA-08-0003,freestyle_libre_2,2026-06-02,1,CMS,sensor
|
||||||
|
PA-08-0004,freestyle_libre_3,2026-06-04,2,Aetna PA Medicare,sensor
|
||||||
|
PA-08-0005,freestyle_libre_2,2025-09-13,1,CMS,sensor
|
||||||
|
PA-08-0006,freestyle_libre_2,2025-08-14,1,UPMC Health Plan,sensor
|
||||||
|
PA-08-0007,freestyle_libre_3,2025-08-01,2,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-08-0008,freestyle_libre_2,2025-05-23,1,CMS,sensor
|
||||||
|
PA-08-0009,dexcom_g7,2025-01-03,1,Keystone First,sensor
|
||||||
|
PA-08-0010,freestyle_libre_2,2026-06-06,3,UPMC Health Plan,sensor
|
||||||
|
PA-08-0011,dexcom_g7,2026-06-01,3,CMS,sensor
|
||||||
|
PA-08-0012,dexcom_g6,2026-06-05,1,Medicare Part B,sensor
|
||||||
|
PA-08-0013,freestyle_libre_3,2026-06-02,2,PA Medicaid,sensor
|
||||||
|
PA-08-0014,freestyle_libre_3,2025-09-20,1,CMS,sensor
|
||||||
|
PA-08-0015,freestyle_libre_2,2025-08-11,1,CMS,sensor
|
||||||
|
PA-08-0016,dexcom_g7,2025-07-28,1,Independence Blue Cross,sensor
|
||||||
|
PA-08-0017,dexcom_g7,2025-05-21,1,Aetna PA Medicare,sensor
|
||||||
|
PA-08-0018,dexcom_g7,2024-12-26,1,UPMC Health Plan,sensor
|
||||||
|
PA-08-0019,freestyle_libre_3,2026-06-03,1,Medicare Part B,sensor
|
||||||
|
PA-08-0020,freestyle_libre_2,2026-06-01,2,UPMC Health Plan,sensor
|
||||||
|
PA-08-0021,freestyle_libre_2,2026-06-01,1,CMS,sensor
|
||||||
|
PA-08-0022,freestyle_libre_2,2026-06-03,1,CMS,sensor
|
||||||
|
PA-08-0023,freestyle_libre_3,2025-09-14,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-08-0024,freestyle_libre_2,2025-08-12,1,Medicare Part B,sensor
|
||||||
|
PA-08-0025,freestyle_libre_2,2025-07-30,2,Medicare,sensor
|
||||||
|
PA-08-0026,dexcom_g6,2025-05-07,1,Keystone First,sensor
|
||||||
|
PA-08-0027,dexcom_g6,2024-12-07,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-08-0028,dexcom_g6,2026-06-04,1,Keystone First,sensor
|
||||||
|
PA-08-0029,freestyle_libre_2,2026-06-02,2,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-08-0030,freestyle_libre_2,2026-06-04,3,Highmark BlueCross BlueShield,sensor
|
||||||
|
,dexcom_g7,2026-05-28,1,Medicare Part B,sensor
|
||||||
|
FUTURE-008,dexcom_g7,2026-07-07,1,CMS,sensor
|
||||||
|
ZERQTY-008,freestyle_libre_3,2026-06-02,0,Geisinger,sensor
|
||||||
|
DUP-PT-008,dexcom_g7,2026-06-02,1,Medicare Part B,sensor
|
||||||
|
DUP-PT-008,freestyle_libre_3,2026-06-04,1,Cigna PA,sensor
|
||||||
|
42
test-data/pa-set/batch-10-pa-high.csv
Normal file
42
test-data/pa-set/batch-10-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
account_no,hcpcs_description,service_date,units,primary_payer,supply_type,HCPCS Code
|
||||||
|
SC-A-001,Dexcom G7,09-10-2025,1,Medicare,sensor,A9277
|
||||||
|
SC-B-002,FreeStyle Libre 3,06-02-2026,1,AmeriHealth Caritas PA,sensor,A9277
|
||||||
|
SC-C-003,Dexcom G6,05-08-2026,1,Geisinger,sensor,A9277
|
||||||
|
SC-D-004,Dexcom G7,06-02-2026,1,Aetna PA Medicare,sensor,A9277
|
||||||
|
SC-E-005,FreeStyle Libre 2,06-04-2026,1,Medicare Part B,sensor,A9277
|
||||||
|
SC-F-006,Dexcom G7,05-31-2026,1,Geisinger,sensor,A9277
|
||||||
|
PA-09-0001,Dexcom G6,05-29-2026,2,AmeriHealth Caritas PA,sensor,A9277
|
||||||
|
PA-09-0002,FreeStyle Libre 3,06-03-2026,2,CMS,sensor,A9277
|
||||||
|
PA-09-0003,FreeStyle Libre 2,06-02-2026,3,Independence Blue Cross,sensor,A9277
|
||||||
|
PA-09-0004,Dexcom G6,06-06-2026,1,UPMC Health Plan,sensor,A9277
|
||||||
|
PA-09-0005,FreeStyle Libre 3,09-18-2025,3,CMS,sensor,A9277
|
||||||
|
PA-09-0006,FreeStyle Libre 3,08-21-2025,3,UPMC for You,sensor,A9277
|
||||||
|
PA-09-0007,Dexcom G7,07-29-2025,2,PA Medicaid,sensor,A9277
|
||||||
|
PA-09-0008,FreeStyle Libre 3,05-19-2025,2,Keystone First,sensor,A9277
|
||||||
|
PA-09-0009,FreeStyle Libre 3,10-20-2024,1,Medicare,sensor,A9277
|
||||||
|
PA-09-0010,Dexcom G6,05-30-2026,3,CMS,sensor,A9277
|
||||||
|
PA-09-0011,Dexcom G6,05-30-2026,2,Keystone First,sensor,A9277
|
||||||
|
PA-09-0012,FreeStyle Libre 2,05-31-2026,1,CMS,sensor,A9277
|
||||||
|
PA-09-0013,Dexcom G6,05-31-2026,2,Highmark BlueCross BlueShield,sensor,A9277
|
||||||
|
PA-09-0014,FreeStyle Libre 3,09-19-2025,2,Cigna PA,sensor,A9277
|
||||||
|
PA-09-0015,FreeStyle Libre 2,08-21-2025,1,Highmark BCBS PA,sensor,A9277
|
||||||
|
PA-09-0016,Dexcom G7,07-27-2025,1,Medicare,sensor,A9277
|
||||||
|
PA-09-0017,Dexcom G6,05-16-2025,1,Cigna PA,sensor,A9277
|
||||||
|
PA-09-0018,FreeStyle Libre 3,11-24-2024,2,Medicare Part B,sensor,A9277
|
||||||
|
PA-09-0019,Dexcom G6,06-05-2026,3,Medicare,sensor,A9277
|
||||||
|
PA-09-0020,FreeStyle Libre 2,05-29-2026,3,Keystone First,sensor,A9277
|
||||||
|
PA-09-0021,Dexcom G6,05-30-2026,2,Aetna PA,sensor,A9277
|
||||||
|
PA-09-0022,Dexcom G7,06-02-2026,3,Aetna PA Medicare,sensor,A9277
|
||||||
|
PA-09-0023,Dexcom G6,09-17-2025,1,UPMC Health Plan,sensor,A9277
|
||||||
|
PA-09-0024,Dexcom G6,08-20-2025,2,Aetna PA,sensor,A9277
|
||||||
|
PA-09-0025,Dexcom G7,07-31-2025,1,Aetna PA,sensor,A9277
|
||||||
|
PA-09-0026,FreeStyle Libre 3,05-11-2025,3,Aetna PA Medicare,sensor,A9277
|
||||||
|
PA-09-0027,Dexcom G6,11-02-2024,1,Medicare Part B,sensor,A9277
|
||||||
|
PA-09-0028,FreeStyle Libre 3,06-04-2026,1,UPMC for You,sensor,A9277
|
||||||
|
PA-09-0029,FreeStyle Libre 3,06-02-2026,3,UPMC for You,sensor,A9277
|
||||||
|
PA-09-0030,FreeStyle Libre 3,06-03-2026,1,CMS,sensor,A9277
|
||||||
|
,Dexcom G7,05-28-2026,1,Medicare,sensor,A9277
|
||||||
|
FUTURE-009,Dexcom G7,07-07-2026,1,Medicare,sensor,A9277
|
||||||
|
ZERQTY-009,FreeStyle Libre 3,06-02-2026,0,Independence Blue Cross,sensor,A9277
|
||||||
|
DUP-PT-009,Dexcom G7,06-02-2026,1,Medicare Part B,sensor,A9277
|
||||||
|
DUP-PT-009,FreeStyle Libre 3,06-04-2026,1,Independence Blue Cross,sensor,A9277
|
||||||
|
42
test-data/pa-set/batch-11-pa-high.csv
Normal file
42
test-data/pa-set/batch-11-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patient_account,description,ship_date,quantity_dispensed,carrier,component,Account Manager
|
||||||
|
SC-A-001,Dexcom G7,2026-05-28,1,CMS,sensor,Staff
|
||||||
|
SC-B-002,FreeStyle Libre 3,2026-06-02,1,PA Medicaid,sensor,Staff
|
||||||
|
SC-C-003,Dexcom G6,2026-05-08,1,Geisinger,sensor,Staff
|
||||||
|
SC-D-004,Dexcom G7,2026-06-02,1,UPMC Health Plan,sensor,Staff
|
||||||
|
SC-E-005,FreeStyle Libre 2,2026-06-04,1,CMS,sensor,Staff
|
||||||
|
SC-F-006,Dexcom G7,2026-05-31,1,Cigna PA,sensor,Staff
|
||||||
|
PA-10-0001,FreeStyle Libre 2,2026-05-30,2,Medicare Part B,sensor,Staff
|
||||||
|
PA-10-0002,Dexcom G6,2026-06-01,1,UPMC Health Plan,sensor,Staff
|
||||||
|
PA-10-0003,Dexcom G6,2026-06-01,1,Medicare,sensor,Staff
|
||||||
|
PA-10-0004,Dexcom G6,2026-05-30,3,PA Medicaid,sensor,Staff
|
||||||
|
PA-10-0005,FreeStyle Libre 3,2025-09-12,2,Medicare,sensor,Staff
|
||||||
|
PA-10-0006,Dexcom G7,2025-08-16,1,Medicare,sensor,Staff
|
||||||
|
PA-10-0007,Dexcom G7,2025-07-27,3,Geisinger,sensor,Staff
|
||||||
|
PA-10-0008,Dexcom G6,2025-05-22,2,Medicare,sensor,Staff
|
||||||
|
PA-10-0009,Dexcom G7,2024-11-28,2,Cigna PA,sensor,Staff
|
||||||
|
PA-10-0010,Dexcom G7,2026-06-06,3,CMS,sensor,Staff
|
||||||
|
PA-10-0011,FreeStyle Libre 3,2026-06-06,3,CMS,sensor,Staff
|
||||||
|
PA-10-0012,Dexcom G7,2026-06-03,1,Medicare,sensor,Staff
|
||||||
|
PA-10-0013,Dexcom G7,2026-05-30,2,Highmark BlueCross BlueShield,sensor,Staff
|
||||||
|
PA-10-0014,Dexcom G7,2025-09-10,1,Independence Blue Cross,sensor,Staff
|
||||||
|
PA-10-0015,FreeStyle Libre 3,2025-08-17,2,Cigna PA,sensor,Staff
|
||||||
|
PA-10-0016,FreeStyle Libre 3,2025-07-28,1,CMS,sensor,Staff
|
||||||
|
PA-10-0017,Dexcom G7,2025-05-16,1,Aetna PA Medicare,sensor,Staff
|
||||||
|
PA-10-0018,Dexcom G7,2025-01-17,1,Aetna PA Medicare,sensor,Staff
|
||||||
|
PA-10-0019,FreeStyle Libre 2,2026-06-05,1,Aetna PA Medicare,sensor,Staff
|
||||||
|
PA-10-0020,Dexcom G7,2026-06-01,3,Highmark BlueCross BlueShield,sensor,Staff
|
||||||
|
PA-10-0021,FreeStyle Libre 3,2026-06-06,1,Medicare,sensor,Staff
|
||||||
|
PA-10-0022,Dexcom G7,2026-06-05,1,CMS,sensor,Staff
|
||||||
|
PA-10-0023,FreeStyle Libre 3,2025-09-12,1,Keystone First,sensor,Staff
|
||||||
|
PA-10-0024,Dexcom G6,2025-08-15,1,AmeriHealth Caritas PA,sensor,Staff
|
||||||
|
PA-10-0025,Dexcom G6,2025-07-27,2,CMS,sensor,Staff
|
||||||
|
PA-10-0026,FreeStyle Libre 3,2025-05-17,1,CMS,sensor,Staff
|
||||||
|
PA-10-0027,Dexcom G6,2024-12-29,2,AmeriHealth Caritas PA,sensor,Staff
|
||||||
|
PA-10-0028,FreeStyle Libre 2,2026-06-05,1,Medicare Part B,sensor,Staff
|
||||||
|
PA-10-0029,FreeStyle Libre 2,2026-05-31,3,UPMC Health Plan,sensor,Staff
|
||||||
|
PA-10-0030,FreeStyle Libre 3,2026-05-31,3,Medicare Part B,sensor,Staff
|
||||||
|
,Dexcom G7,2026-05-28,1,Medicare Part B,sensor,Staff
|
||||||
|
FUTURE-010,Dexcom G7,2026-07-07,1,CMS,sensor,Staff
|
||||||
|
ZERQTY-010,FreeStyle Libre 3,2026-06-02,0,Geisinger,sensor,Staff
|
||||||
|
DUP-PT-010,Dexcom G7,2026-06-02,1,Medicare Part B,sensor,Staff
|
||||||
|
DUP-PT-010,FreeStyle Libre 3,2026-06-04,1,Cigna PA,sensor,Staff
|
||||||
|
42
test-data/pa-set/batch-12-pa-high.csv
Normal file
42
test-data/pa-set/batch-12-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
id,product,fill_date,qty_shipped,payer,item_type
|
||||||
|
SC-A-001,Dexcom G7,05/28/2026,1,CMS,sensor
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,AmeriHealth Caritas PA,sensor
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Aetna PA,sensor
|
||||||
|
SC-D-004,Dexcom G7,06/02/2026,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,Independence Blue Cross,sensor
|
||||||
|
PA-11-0001,Dexcom G6,06/01/2026,1,Medicare,sensor
|
||||||
|
PA-11-0002,FreeStyle Libre 2,05/29/2026,1,UPMC Health Plan,sensor
|
||||||
|
PA-11-0003,FreeStyle Libre 2,05/29/2026,3,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-11-0004,Dexcom G7,06/01/2026,3,CMS,sensor
|
||||||
|
PA-11-0005,Dexcom G6,09/18/2025,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-11-0006,Dexcom G6,08/12/2025,1,Medicare,sensor
|
||||||
|
PA-11-0007,Dexcom G6,07/29/2025,1,Medicare,sensor
|
||||||
|
PA-11-0008,Dexcom G7,05/23/2025,2,Medicare Part B,sensor
|
||||||
|
PA-11-0009,FreeStyle Libre 2,12/27/2024,2,Medicare Part B,sensor
|
||||||
|
PA-11-0010,FreeStyle Libre 2,05/31/2026,1,Medicare,sensor
|
||||||
|
PA-11-0011,Dexcom G6,06/03/2026,3,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-11-0012,FreeStyle Libre 2,06/05/2026,1,CMS,sensor
|
||||||
|
PA-11-0013,FreeStyle Libre 2,06/02/2026,3,Cigna PA,sensor
|
||||||
|
PA-11-0014,Dexcom G7,09/13/2025,1,Aetna PA Medicare,sensor
|
||||||
|
PA-11-0015,Dexcom G6,08/21/2025,1,Medicare Part B,sensor
|
||||||
|
PA-11-0016,Dexcom G6,07/29/2025,1,Cigna PA,sensor
|
||||||
|
PA-11-0017,Dexcom G7,05/21/2025,1,Aetna PA Medicare,sensor
|
||||||
|
PA-11-0018,Dexcom G7,11/13/2024,3,Medicare Part B,sensor
|
||||||
|
PA-11-0019,Dexcom G6,05/30/2026,1,Medicare,sensor
|
||||||
|
PA-11-0020,Dexcom G7,05/29/2026,1,Aetna PA,sensor
|
||||||
|
PA-11-0021,FreeStyle Libre 3,05/30/2026,2,UPMC Health Plan,sensor
|
||||||
|
PA-11-0022,Dexcom G6,06/01/2026,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-11-0023,FreeStyle Libre 2,09/12/2025,3,Independence Blue Cross,sensor
|
||||||
|
PA-11-0024,FreeStyle Libre 2,08/12/2025,1,Medicare,sensor
|
||||||
|
PA-11-0025,FreeStyle Libre 3,07/29/2025,1,PA Medicaid,sensor
|
||||||
|
PA-11-0026,Dexcom G7,05/15/2025,2,Medicare Part B,sensor
|
||||||
|
PA-11-0027,FreeStyle Libre 2,12/02/2024,1,Aetna PA Medicare,sensor
|
||||||
|
PA-11-0028,Dexcom G7,06/02/2026,1,UPMC Health Plan,sensor
|
||||||
|
PA-11-0029,Dexcom G7,06/02/2026,3,Independence Blue Cross,sensor
|
||||||
|
PA-11-0030,Dexcom G6,05/30/2026,1,Cigna PA,sensor
|
||||||
|
,Dexcom G7,05/28/2026,1,Medicare Part B,sensor
|
||||||
|
FUTURE-011,Dexcom G7,07/07/2026,1,Medicare,sensor
|
||||||
|
ZERQTY-011,FreeStyle Libre 3,06/02/2026,0,Geisinger,sensor
|
||||||
|
DUP-PT-011,Dexcom G7,06/02/2026,1,CMS,sensor
|
||||||
|
DUP-PT-011,FreeStyle Libre 3,06/04/2026,1,Cigna PA,sensor
|
||||||
|
42
test-data/pa-set/batch-13-pa-high.csv
Normal file
42
test-data/pa-set/batch-13-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
PT ID,Device Type,Shipment Date,Quantity,Insurance,Component,Region
|
||||||
|
SC-A-001,dexcom_g7,2026-05-28,1,Medicare Part B,sensor,Bucks County
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,UPMC for You,sensor,Bucks County
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Independence Blue Cross,sensor,Bucks County
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,Aetna PA Medicare,sensor,Bucks County
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,CMS,sensor,Bucks County
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Geisinger,sensor,Bucks County
|
||||||
|
PA-12-0001,dexcom_g6,2026-05-28,2,Medicare,sensor,Bucks County
|
||||||
|
PA-12-0002,dexcom_g6,2026-05-31,1,Medicare Part B,sensor,Bucks County
|
||||||
|
PA-12-0003,freestyle_libre_3,2026-06-04,1,UPMC for You,sensor,Bucks County
|
||||||
|
PA-12-0004,freestyle_libre_2,2026-06-04,1,UPMC Health Plan,sensor,Bucks County
|
||||||
|
PA-12-0005,freestyle_libre_3,2025-09-12,1,Independence Blue Cross,sensor,Bucks County
|
||||||
|
PA-12-0006,dexcom_g6,2025-08-17,3,Independence Blue Cross,sensor,Bucks County
|
||||||
|
PA-12-0007,freestyle_libre_2,2025-07-30,1,UPMC Health Plan,sensor,Bucks County
|
||||||
|
PA-12-0008,dexcom_g6,2025-05-04,2,Medicare Part B,sensor,Bucks County
|
||||||
|
PA-12-0009,dexcom_g6,2024-11-24,1,Aetna PA,sensor,Bucks County
|
||||||
|
PA-12-0010,freestyle_libre_2,2026-06-06,3,CMS,sensor,Bucks County
|
||||||
|
PA-12-0011,dexcom_g7,2026-05-29,1,CMS,sensor,Bucks County
|
||||||
|
PA-12-0012,freestyle_libre_2,2026-06-04,1,Geisinger,sensor,Bucks County
|
||||||
|
PA-12-0013,freestyle_libre_2,2026-06-02,1,Medicare,sensor,Bucks County
|
||||||
|
PA-12-0014,dexcom_g7,2025-09-14,1,Highmark BlueCross BlueShield,sensor,Bucks County
|
||||||
|
PA-12-0015,freestyle_libre_2,2025-08-17,3,Highmark BlueCross BlueShield,sensor,Bucks County
|
||||||
|
PA-12-0016,freestyle_libre_2,2025-07-27,3,Aetna PA,sensor,Bucks County
|
||||||
|
PA-12-0017,dexcom_g6,2025-05-03,1,Geisinger,sensor,Bucks County
|
||||||
|
PA-12-0018,freestyle_libre_2,2025-01-13,1,Medicare,sensor,Bucks County
|
||||||
|
PA-12-0019,dexcom_g7,2026-06-02,2,Medicare Part B,sensor,Bucks County
|
||||||
|
PA-12-0020,freestyle_libre_3,2026-06-01,1,UPMC Health Plan,sensor,Bucks County
|
||||||
|
PA-12-0021,dexcom_g6,2026-05-31,1,CMS,sensor,Bucks County
|
||||||
|
PA-12-0022,freestyle_libre_2,2026-05-30,3,CMS,sensor,Bucks County
|
||||||
|
PA-12-0023,freestyle_libre_3,2025-09-15,2,UPMC for You,sensor,Bucks County
|
||||||
|
PA-12-0024,freestyle_libre_2,2025-08-11,3,Independence Blue Cross,sensor,Bucks County
|
||||||
|
PA-12-0025,dexcom_g7,2025-07-29,1,PA Medicaid,sensor,Bucks County
|
||||||
|
PA-12-0026,freestyle_libre_3,2025-05-19,1,Medicare Part B,sensor,Bucks County
|
||||||
|
PA-12-0027,dexcom_g7,2024-11-17,2,AmeriHealth Caritas PA,sensor,Bucks County
|
||||||
|
PA-12-0028,dexcom_g6,2026-05-31,3,Aetna PA Medicare,sensor,Bucks County
|
||||||
|
PA-12-0029,dexcom_g6,2026-06-04,1,UPMC Health Plan,sensor,Bucks County
|
||||||
|
PA-12-0030,freestyle_libre_3,2026-06-01,2,CMS,sensor,Bucks County
|
||||||
|
,dexcom_g7,2026-05-28,1,CMS,sensor,Bucks County
|
||||||
|
FUTURE-012,dexcom_g7,2026-07-07,1,Medicare Part B,sensor,Bucks County
|
||||||
|
ZERQTY-012,freestyle_libre_3,2026-06-02,0,Cigna PA,sensor,Bucks County
|
||||||
|
DUP-PT-012,dexcom_g7,2026-06-02,1,Medicare Part B,sensor,Bucks County
|
||||||
|
DUP-PT-012,freestyle_libre_3,2026-06-04,1,Geisinger,sensor,Bucks County
|
||||||
|
42
test-data/pa-set/batch-14-pa-high.csv
Normal file
42
test-data/pa-set/batch-14-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patientid,devicetype,dispensedate,qty,payername,supplytype
|
||||||
|
SC-A-001,dexcom_g7,05/28/2026,1,CMS,sensor
|
||||||
|
SC-B-002,freestyle_libre_3,06/02/2026,1,AmeriHealth Caritas PA,sensor
|
||||||
|
SC-C-003,dexcom_g6,05/08/2026,1,Geisinger,sensor
|
||||||
|
SC-D-004,dexcom_g7,06/02/2026,1,Aetna PA Medicare,sensor
|
||||||
|
SC-E-005,freestyle_libre_2,06/04/2026,1,Medicare,sensor
|
||||||
|
SC-F-006,dexcom_g7,05/31/2026,1,Independence Blue Cross,sensor
|
||||||
|
PA-13-0001,dexcom_g7,06/03/2026,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-13-0002,freestyle_libre_2,05/30/2026,1,Medicare,sensor
|
||||||
|
PA-13-0003,dexcom_g7,06/01/2026,1,Medicare,sensor
|
||||||
|
PA-13-0004,dexcom_g6,06/03/2026,2,Cigna PA,sensor
|
||||||
|
PA-13-0005,dexcom_g6,09/15/2025,3,UPMC Health Plan,sensor
|
||||||
|
PA-13-0006,dexcom_g6,08/13/2025,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-13-0007,freestyle_libre_3,07/30/2025,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-13-0008,freestyle_libre_2,05/10/2025,1,Medicare Part B,sensor
|
||||||
|
PA-13-0009,dexcom_g7,01/06/2025,1,Aetna PA Medicare,sensor
|
||||||
|
PA-13-0010,dexcom_g7,05/29/2026,2,Medicare Part B,sensor
|
||||||
|
PA-13-0011,dexcom_g6,05/28/2026,1,CMS,sensor
|
||||||
|
PA-13-0012,dexcom_g6,06/05/2026,3,PA Medicaid,sensor
|
||||||
|
PA-13-0013,dexcom_g6,06/01/2026,3,Highmark BCBS PA,sensor
|
||||||
|
PA-13-0014,dexcom_g7,09/13/2025,2,Cigna PA,sensor
|
||||||
|
PA-13-0015,dexcom_g7,08/21/2025,2,CMS,sensor
|
||||||
|
PA-13-0016,dexcom_g7,07/28/2025,2,Aetna PA Medicare,sensor
|
||||||
|
PA-13-0017,dexcom_g6,05/14/2025,1,Medicare,sensor
|
||||||
|
PA-13-0018,dexcom_g7,01/07/2025,1,Medicare Part B,sensor
|
||||||
|
PA-13-0019,dexcom_g7,05/30/2026,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-13-0020,freestyle_libre_2,05/30/2026,1,Medicare Part B,sensor
|
||||||
|
PA-13-0021,freestyle_libre_2,06/03/2026,1,CMS,sensor
|
||||||
|
PA-13-0022,freestyle_libre_2,06/03/2026,2,PA Medicaid,sensor
|
||||||
|
PA-13-0023,dexcom_g6,09/18/2025,3,Aetna PA Medicare,sensor
|
||||||
|
PA-13-0024,freestyle_libre_2,08/20/2025,1,CMS,sensor
|
||||||
|
PA-13-0025,freestyle_libre_2,07/30/2025,1,Medicare Part B,sensor
|
||||||
|
PA-13-0026,dexcom_g6,05/15/2025,1,Cigna PA,sensor
|
||||||
|
PA-13-0027,dexcom_g7,11/24/2024,1,UPMC for You,sensor
|
||||||
|
PA-13-0028,freestyle_libre_2,06/05/2026,1,CMS,sensor
|
||||||
|
PA-13-0029,dexcom_g6,06/02/2026,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-13-0030,freestyle_libre_2,06/04/2026,1,Aetna PA Medicare,sensor
|
||||||
|
,dexcom_g7,05/28/2026,1,Medicare Part B,sensor
|
||||||
|
FUTURE-013,dexcom_g7,07/07/2026,1,Medicare Part B,sensor
|
||||||
|
ZERQTY-013,freestyle_libre_3,06/02/2026,0,Cigna PA,sensor
|
||||||
|
DUP-PT-013,dexcom_g7,06/02/2026,1,Medicare Part B,sensor
|
||||||
|
DUP-PT-013,freestyle_libre_3,06/04/2026,1,Cigna PA,sensor
|
||||||
|
42
test-data/pa-set/batch-15-pa-high.csv
Normal file
42
test-data/pa-set/batch-15-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
account_number,item_description,order_date,units_dispensed,plan_name,component,Facility
|
||||||
|
SC-A-001,Dexcom G7,TBD,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
SC-B-002,FreeStyle Libre 3,TBD,1,UPMC for You,sensor,Gaboro DME Main
|
||||||
|
SC-C-003,Dexcom G6,not on file,1,Geisinger,sensor,Gaboro DME Main
|
||||||
|
SC-D-004,Dexcom G7,02/06/2026,1,Aetna PA Medicare,sensor,Gaboro DME Main
|
||||||
|
SC-E-005,FreeStyle Libre 2,04/06/2026,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
SC-F-006,Dexcom G7,31/05/2026,1,Aetna PA,sensor,Gaboro DME Main
|
||||||
|
PA-14-0001,Dexcom G7,31/05/2026,1,Aetna PA Medicare,sensor,Gaboro DME Main
|
||||||
|
PA-14-0002,FreeStyle Libre 3,28/05/2026,3,Medicare,sensor,Gaboro DME Main
|
||||||
|
PA-14-0003,Dexcom G6,05/06/2026,1,PA Medicaid,sensor,Gaboro DME Main
|
||||||
|
PA-14-0004,Dexcom G6,03/06/2026,3,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
PA-14-0005,Dexcom G6,15/09/2025,1,Highmark BlueCross BlueShield,sensor,Gaboro DME Main
|
||||||
|
PA-14-0006,FreeStyle Libre 3,19/08/2025,1,Aetna PA,sensor,Gaboro DME Main
|
||||||
|
PA-14-0007,Dexcom G6,01/08/2025,1,Geisinger,sensor,Gaboro DME Main
|
||||||
|
PA-14-0008,FreeStyle Libre 2,16/05/2025,1,Aetna PA,sensor,Gaboro DME Main
|
||||||
|
PA-14-0009,Dexcom G7,18/11/2024,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
PA-14-0010,Dexcom G7,05/06/2026,1,UPMC Health Plan,sensor,Gaboro DME Main
|
||||||
|
PA-14-0011,FreeStyle Libre 2,01/06/2026,1,CMS,sensor,Gaboro DME Main
|
||||||
|
PA-14-0012,Dexcom G7,01/06/2026,1,CMS,sensor,Gaboro DME Main
|
||||||
|
PA-14-0013,FreeStyle Libre 3,06/06/2026,1,Geisinger,sensor,Gaboro DME Main
|
||||||
|
PA-14-0014,Dexcom G6,18/09/2025,1,CMS,sensor,Gaboro DME Main
|
||||||
|
PA-14-0015,FreeStyle Libre 3,20/08/2025,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
PA-14-0016,Dexcom G7,31/07/2025,1,Medicare,sensor,Gaboro DME Main
|
||||||
|
PA-14-0017,FreeStyle Libre 3,13/05/2025,3,CMS,sensor,Gaboro DME Main
|
||||||
|
PA-14-0018,Dexcom G6,26/10/2024,1,UPMC for You,sensor,Gaboro DME Main
|
||||||
|
PA-14-0019,FreeStyle Libre 3,05/06/2026,2,CMS,sensor,Gaboro DME Main
|
||||||
|
PA-14-0020,FreeStyle Libre 3,06/06/2026,3,Keystone First,sensor,Gaboro DME Main
|
||||||
|
PA-14-0021,Dexcom G7,05/06/2026,1,AmeriHealth Caritas PA,sensor,Gaboro DME Main
|
||||||
|
PA-14-0022,FreeStyle Libre 2,01/06/2026,1,PA Medicaid,sensor,Gaboro DME Main
|
||||||
|
PA-14-0023,Dexcom G7,20/09/2025,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
PA-14-0024,FreeStyle Libre 2,21/08/2025,1,CMS,sensor,Gaboro DME Main
|
||||||
|
PA-14-0025,Dexcom G7,28/07/2025,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
PA-14-0026,FreeStyle Libre 2,23/05/2025,2,UPMC for You,sensor,Gaboro DME Main
|
||||||
|
PA-14-0027,Dexcom G7,05/01/2025,3,Independence Blue Cross,sensor,Gaboro DME Main
|
||||||
|
PA-14-0028,FreeStyle Libre 3,01/06/2026,1,Independence Blue Cross,sensor,Gaboro DME Main
|
||||||
|
PA-14-0029,FreeStyle Libre 2,04/06/2026,1,PA Medicaid,sensor,Gaboro DME Main
|
||||||
|
PA-14-0030,FreeStyle Libre 3,02/06/2026,1,Medicare,sensor,Gaboro DME Main
|
||||||
|
,Dexcom G7,28/05/2026,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
FUTURE-014,Dexcom G7,07/07/2026,1,CMS,sensor,Gaboro DME Main
|
||||||
|
ZERQTY-014,FreeStyle Libre 3,02/06/2026,0,Aetna PA,sensor,Gaboro DME Main
|
||||||
|
DUP-PT-014,Dexcom G7,02/06/2026,1,Medicare Part B,sensor,Gaboro DME Main
|
||||||
|
DUP-PT-014,FreeStyle Libre 3,04/06/2026,1,Cigna PA,sensor,Gaboro DME Main
|
||||||
|
42
test-data/pa-set/batch-16-pa-high.csv
Normal file
42
test-data/pa-set/batch-16-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
member_id,Product Type,Service Date,Qty,Insurance Name,Component Type
|
||||||
|
SC-A-001,Dexcom G7,2026-05-28,1,Medicare,sensor
|
||||||
|
SC-B-002,FreeStyle Libre 3,2026-06-02,1,UPMC for You,sensor
|
||||||
|
SC-C-003,Dexcom G6,2026-05-08,1,Highmark BCBS PA,sensor
|
||||||
|
SC-D-004,Dexcom G7,2026-06-02,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
SC-E-005,FreeStyle Libre 2,2026-06-04,1,CMS,sensor
|
||||||
|
SC-F-006,Dexcom G7,2026-05-31,1,Geisinger,sensor
|
||||||
|
PA-15-0001,Dexcom G6,2026-06-06,1,Medicare Part B,sensor
|
||||||
|
PA-15-0002,Dexcom G7,2026-06-05,1,Cigna PA,sensor
|
||||||
|
PA-15-0003,Dexcom G6,2026-06-02,1,Cigna PA,sensor
|
||||||
|
PA-15-0004,Dexcom G7,2026-06-04,2,Medicare,sensor
|
||||||
|
PA-15-0005,Dexcom G6,2025-09-15,1,Geisinger,sensor
|
||||||
|
PA-15-0006,Dexcom G6,2025-08-16,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-15-0007,FreeStyle Libre 2,2025-07-30,3,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-15-0008,Dexcom G6,2025-05-23,1,Aetna PA Medicare,sensor
|
||||||
|
PA-15-0009,FreeStyle Libre 2,2024-10-19,3,Highmark BCBS PA,sensor
|
||||||
|
PA-15-0010,FreeStyle Libre 3,2026-06-06,1,Keystone First,sensor
|
||||||
|
PA-15-0011,Dexcom G7,2026-05-30,3,CMS,sensor
|
||||||
|
PA-15-0012,Dexcom G6,2026-05-28,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-15-0013,Dexcom G6,2026-05-31,3,CMS,sensor
|
||||||
|
PA-15-0014,Dexcom G7,2025-09-18,1,UPMC Health Plan,sensor
|
||||||
|
PA-15-0015,Dexcom G7,2025-08-17,1,Aetna PA,sensor
|
||||||
|
PA-15-0016,FreeStyle Libre 2,2025-07-31,1,PA Medicaid,sensor
|
||||||
|
PA-15-0017,FreeStyle Libre 3,2025-05-08,1,Medicare Part B,sensor
|
||||||
|
PA-15-0018,Dexcom G6,2025-01-15,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-15-0019,Dexcom G6,2026-05-31,1,PA Medicaid,sensor
|
||||||
|
PA-15-0020,Dexcom G7,2026-05-28,1,Highmark BCBS PA,sensor
|
||||||
|
PA-15-0021,Dexcom G7,2026-05-31,2,UPMC Health Plan,sensor
|
||||||
|
PA-15-0022,Dexcom G6,2026-06-06,3,Highmark BCBS PA,sensor
|
||||||
|
PA-15-0023,Dexcom G7,2025-09-16,1,Medicare,sensor
|
||||||
|
PA-15-0024,FreeStyle Libre 2,2025-08-12,1,CMS,sensor
|
||||||
|
PA-15-0025,Dexcom G7,2025-08-01,1,Medicare,sensor
|
||||||
|
PA-15-0026,FreeStyle Libre 2,2025-05-14,1,Cigna PA,sensor
|
||||||
|
PA-15-0027,FreeStyle Libre 2,2024-12-29,3,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-15-0028,Dexcom G6,2026-06-06,3,Medicare,sensor
|
||||||
|
PA-15-0029,Dexcom G7,2026-05-29,3,Geisinger,sensor
|
||||||
|
PA-15-0030,FreeStyle Libre 2,2026-05-30,2,UPMC Health Plan,sensor
|
||||||
|
,Dexcom G7,2026-05-28,1,Medicare,sensor
|
||||||
|
FUTURE-015,Dexcom G7,2026-07-07,1,Medicare Part B,sensor
|
||||||
|
ZERQTY-015,FreeStyle Libre 3,2026-06-02,0,Aetna PA,sensor
|
||||||
|
DUP-PT-015,Dexcom G7,2026-06-02,1,CMS,sensor
|
||||||
|
DUP-PT-015,FreeStyle Libre 3,2026-06-04,1,Geisinger,sensor
|
||||||
|
42
test-data/pa-set/batch-17-pa-high.csv
Normal file
42
test-data/pa-set/batch-17-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
acct no,dme description,last ship date,qty dispensed,payer name,type,Billing Cycle
|
||||||
|
SC-A-001,Dexcom G7,05/28/2026,1,Medicare Part B,sensor,Monthly
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,Keystone First,sensor,Monthly
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Aetna PA,sensor,Monthly
|
||||||
|
SC-D-004,Dexcom G7,06/02/2026,1,UPMC Health Plan,sensor,Monthly
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,Medicare,sensor,Monthly
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,Aetna PA,sensor,Monthly
|
||||||
|
PA-16-0001,FreeStyle Libre 2,06/06/2026,1,Highmark BlueCross BlueShield,sensor,Monthly
|
||||||
|
PA-16-0002,Dexcom G6,06/05/2026,2,Medicare Part B,sensor,Monthly
|
||||||
|
PA-16-0003,Dexcom G6,05/29/2026,1,PA Medicaid,sensor,Monthly
|
||||||
|
PA-16-0004,FreeStyle Libre 3,05/29/2026,3,Medicare,sensor,Monthly
|
||||||
|
PA-16-0005,FreeStyle Libre 3,09/12/2025,2,Independence Blue Cross,sensor,Monthly
|
||||||
|
PA-16-0006,FreeStyle Libre 3,08/20/2025,3,Medicare Part B,sensor,Monthly
|
||||||
|
PA-16-0007,FreeStyle Libre 3,07/27/2025,1,CMS,sensor,Monthly
|
||||||
|
PA-16-0008,FreeStyle Libre 2,05/19/2025,1,Keystone First,sensor,Monthly
|
||||||
|
PA-16-0009,FreeStyle Libre 3,11/28/2024,1,Geisinger,sensor,Monthly
|
||||||
|
PA-16-0010,Dexcom G7,06/03/2026,3,UPMC Health Plan,sensor,Monthly
|
||||||
|
PA-16-0011,Dexcom G7,06/05/2026,3,Aetna PA,sensor,Monthly
|
||||||
|
PA-16-0012,FreeStyle Libre 2,06/04/2026,2,Medicare,sensor,Monthly
|
||||||
|
PA-16-0013,Dexcom G7,05/31/2026,1,Keystone First,sensor,Monthly
|
||||||
|
PA-16-0014,Dexcom G6,09/16/2025,1,UPMC Health Plan,sensor,Monthly
|
||||||
|
PA-16-0015,FreeStyle Libre 3,08/21/2025,1,CMS,sensor,Monthly
|
||||||
|
PA-16-0016,Dexcom G6,07/28/2025,1,UPMC Health Plan,sensor,Monthly
|
||||||
|
PA-16-0017,Dexcom G7,05/20/2025,1,PA Medicaid,sensor,Monthly
|
||||||
|
PA-16-0018,FreeStyle Libre 3,01/20/2025,3,Keystone First,sensor,Monthly
|
||||||
|
PA-16-0019,Dexcom G7,06/04/2026,3,Cigna PA,sensor,Monthly
|
||||||
|
PA-16-0020,Dexcom G6,06/05/2026,2,PA Medicaid,sensor,Monthly
|
||||||
|
PA-16-0021,Dexcom G6,06/02/2026,1,PA Medicaid,sensor,Monthly
|
||||||
|
PA-16-0022,FreeStyle Libre 3,06/02/2026,1,AmeriHealth Caritas PA,sensor,Monthly
|
||||||
|
PA-16-0023,FreeStyle Libre 3,09/17/2025,1,AmeriHealth Caritas PA,sensor,Monthly
|
||||||
|
PA-16-0024,Dexcom G6,08/17/2025,1,Cigna PA,sensor,Monthly
|
||||||
|
PA-16-0025,Dexcom G6,07/28/2025,3,UPMC for You,sensor,Monthly
|
||||||
|
PA-16-0026,FreeStyle Libre 3,05/04/2025,2,Geisinger,sensor,Monthly
|
||||||
|
PA-16-0027,FreeStyle Libre 3,12/11/2024,1,PA Medicaid,sensor,Monthly
|
||||||
|
PA-16-0028,FreeStyle Libre 3,05/29/2026,1,Medicare,sensor,Monthly
|
||||||
|
PA-16-0029,Dexcom G6,06/02/2026,3,Highmark BlueCross BlueShield,sensor,Monthly
|
||||||
|
PA-16-0030,Dexcom G6,06/02/2026,2,CMS,sensor,Monthly
|
||||||
|
,Dexcom G7,05/28/2026,1,CMS,sensor,Monthly
|
||||||
|
FUTURE-016,Dexcom G7,07/07/2026,1,Medicare,sensor,Monthly
|
||||||
|
ZERQTY-016,FreeStyle Libre 3,06/02/2026,0,Aetna PA,sensor,Monthly
|
||||||
|
DUP-PT-016,Dexcom G7,06/02/2026,1,CMS,sensor,Monthly
|
||||||
|
DUP-PT-016,FreeStyle Libre 3,06/04/2026,1,Cigna PA,sensor,Monthly
|
||||||
|
42
test-data/pa-set/batch-18-pa-high.csv
Normal file
42
test-data/pa-set/batch-18-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
external_patient_ref,equipment description,service_date,count,primary_payer,supply_type
|
||||||
|
SC-A-001,Dexcom G7,2026-05-28T00:00:00,1,Medicare Part B,sensor
|
||||||
|
SC-B-002,FreeStyle Libre 3,2026-06-02T00:00:00,1,Keystone First,sensor
|
||||||
|
SC-C-003,Dexcom G6,2026-05-08T00:00:00,1,Independence Blue Cross,sensor
|
||||||
|
SC-D-004,Dexcom G7,2026-06-02T00:00:00,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
SC-E-005,FreeStyle Libre 2,2026-06-04T00:00:00,1,Medicare Part B,sensor
|
||||||
|
SC-F-006,Dexcom G7,2026-05-31T00:00:00,1,Independence Blue Cross,sensor
|
||||||
|
PA-17-0001,FreeStyle Libre 3,2026-06-06T00:00:00,3,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-17-0002,Dexcom G7,2026-05-31T00:00:00,1,CMS,sensor
|
||||||
|
PA-17-0003,Dexcom G6,2026-05-29T00:00:00,1,PA Medicaid,sensor
|
||||||
|
PA-17-0004,FreeStyle Libre 2,2026-05-29T00:00:00,1,Independence Blue Cross,sensor
|
||||||
|
PA-17-0005,FreeStyle Libre 3,2025-09-14T00:00:00,2,Medicare Part B,sensor
|
||||||
|
PA-17-0006,Dexcom G7,2025-08-16T00:00:00,1,CMS,sensor
|
||||||
|
PA-17-0007,FreeStyle Libre 2,2025-07-31T00:00:00,1,CMS,sensor
|
||||||
|
PA-17-0008,FreeStyle Libre 2,2025-05-12T00:00:00,1,Geisinger,sensor
|
||||||
|
PA-17-0009,FreeStyle Libre 3,2024-11-17T00:00:00,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-17-0010,FreeStyle Libre 2,2026-06-04T00:00:00,2,Independence Blue Cross,sensor
|
||||||
|
PA-17-0011,Dexcom G6,2026-06-02T00:00:00,1,CMS,sensor
|
||||||
|
PA-17-0012,Dexcom G6,2026-05-31T00:00:00,1,UPMC for You,sensor
|
||||||
|
PA-17-0013,FreeStyle Libre 3,2026-06-05T00:00:00,1,Aetna PA Medicare,sensor
|
||||||
|
PA-17-0014,Dexcom G7,2025-09-14T00:00:00,3,UPMC for You,sensor
|
||||||
|
PA-17-0015,Dexcom G7,2025-08-13T00:00:00,1,Independence Blue Cross,sensor
|
||||||
|
PA-17-0016,Dexcom G6,2025-07-27T00:00:00,2,Medicare Part B,sensor
|
||||||
|
PA-17-0017,FreeStyle Libre 2,2025-05-04T00:00:00,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-17-0018,FreeStyle Libre 2,2024-11-16T00:00:00,2,Medicare Part B,sensor
|
||||||
|
PA-17-0019,FreeStyle Libre 2,2026-05-28T00:00:00,1,CMS,sensor
|
||||||
|
PA-17-0020,FreeStyle Libre 2,2026-06-02T00:00:00,1,AmeriHealth Caritas PA,sensor
|
||||||
|
PA-17-0021,Dexcom G7,2026-06-01T00:00:00,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
PA-17-0022,FreeStyle Libre 2,2026-05-30T00:00:00,1,UPMC Health Plan,sensor
|
||||||
|
PA-17-0023,FreeStyle Libre 2,2025-09-15T00:00:00,1,Highmark BCBS PA,sensor
|
||||||
|
PA-17-0024,FreeStyle Libre 3,2025-08-16T00:00:00,1,Medicare Part B,sensor
|
||||||
|
PA-17-0025,Dexcom G6,2025-08-01T00:00:00,1,Aetna PA Medicare,sensor
|
||||||
|
PA-17-0026,Dexcom G6,2025-05-18T00:00:00,2,UPMC Health Plan,sensor
|
||||||
|
PA-17-0027,FreeStyle Libre 2,2024-12-24T00:00:00,1,Aetna PA Medicare,sensor
|
||||||
|
PA-17-0028,FreeStyle Libre 3,2026-06-05T00:00:00,2,PA Medicaid,sensor
|
||||||
|
PA-17-0029,Dexcom G6,2026-05-29T00:00:00,3,Aetna PA Medicare,sensor
|
||||||
|
PA-17-0030,FreeStyle Libre 3,2026-06-02T00:00:00,1,Highmark BlueCross BlueShield,sensor
|
||||||
|
,Dexcom G7,2026-05-28T00:00:00,1,Medicare Part B,sensor
|
||||||
|
FUTURE-017,Dexcom G7,2026-07-07T00:00:00,1,CMS,sensor
|
||||||
|
ZERQTY-017,FreeStyle Libre 3,2026-06-02T00:00:00,0,Cigna PA,sensor
|
||||||
|
DUP-PT-017,Dexcom G7,2026-06-02T00:00:00,1,CMS,sensor
|
||||||
|
DUP-PT-017,FreeStyle Libre 3,2026-06-04T00:00:00,1,Cigna PA,sensor
|
||||||
|
42
test-data/pa-set/batch-19-pa-high.csv
Normal file
42
test-data/pa-set/batch-19-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,swo_status,pa_status,diagnosis_on_file
|
||||||
|
SC-A-001,dexcom_g7,2026-05-28,1,CMS,sensor,On File,Not Required,Yes
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,AmeriHealth Caritas PA,sensor,On File,Not Required,Yes
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Highmark BCBS PA,sensor,On File,Not Required,Yes
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UPMC Health Plan,sensor,On File,Not Required,Yes
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare,sensor,On File,Not Required,Yes
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Cigna PA,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0001,freestyle_libre_3,2026-05-31,3,Medicare,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0002,freestyle_libre_2,2026-05-28,1,Medicare,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0003,freestyle_libre_3,2026-05-29,1,Independence Blue Cross,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0004,dexcom_g6,2026-05-31,1,Cigna PA,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0005,freestyle_libre_2,2025-09-19,1,CMS,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0006,dexcom_g6,2025-08-17,3,CMS,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0007,freestyle_libre_2,2025-07-31,1,Highmark BlueCross BlueShield,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0008,dexcom_g6,2025-05-09,2,Geisinger,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0009,freestyle_libre_2,2024-10-15,1,UPMC for You,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0010,dexcom_g7,2026-05-31,1,UPMC Health Plan,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0011,freestyle_libre_2,2026-05-30,1,UPMC Health Plan,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0012,freestyle_libre_2,2026-05-29,1,Highmark BCBS PA,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0013,dexcom_g7,2026-05-28,3,Aetna PA,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0014,freestyle_libre_3,2025-09-11,1,UPMC for You,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0015,freestyle_libre_2,2025-08-19,1,Medicare,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0016,dexcom_g6,2025-08-01,2,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0017,freestyle_libre_2,2025-05-07,1,Highmark BlueCross BlueShield,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0018,dexcom_g7,2025-01-22,1,UPMC for You,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0019,dexcom_g6,2026-06-05,1,Geisinger,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0020,dexcom_g7,2026-06-03,2,CMS,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0021,dexcom_g7,2026-06-02,3,UPMC Health Plan,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0022,dexcom_g6,2026-05-29,1,UPMC Health Plan,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0023,dexcom_g7,2025-09-11,1,Aetna PA Medicare,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0024,dexcom_g6,2025-08-19,1,CMS,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0025,dexcom_g7,2025-07-29,1,PA Medicaid,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0026,freestyle_libre_3,2025-05-23,1,Medicare,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0027,freestyle_libre_3,2024-11-21,2,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0028,freestyle_libre_3,2026-05-30,1,PA Medicaid,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0029,freestyle_libre_3,2026-06-03,1,Highmark BlueCross BlueShield,sensor,On File,Not Required,Yes
|
||||||
|
PA-18-0030,freestyle_libre_3,2026-06-01,1,CMS,sensor,On File,Not Required,Yes
|
||||||
|
,dexcom_g7,2026-05-28,1,CMS,sensor,On File,Not Required,Yes
|
||||||
|
FUTURE-018,dexcom_g7,2026-07-07,1,Medicare Part B,sensor,On File,Not Required,Yes
|
||||||
|
ZERQTY-018,freestyle_libre_3,2026-06-02,0,Independence Blue Cross,sensor,On File,Not Required,Yes
|
||||||
|
DUP-PT-018,dexcom_g7,2026-06-02,1,CMS,sensor,On File,Not Required,Yes
|
||||||
|
DUP-PT-018,freestyle_libre_3,2026-06-04,1,Cigna PA,sensor,On File,Not Required,Yes
|
||||||
|
42
test-data/pa-set/batch-20-pa-high.csv
Normal file
42
test-data/pa-set/batch-20-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,visit_date,pecos_verified,swo_status
|
||||||
|
SC-A-001,dexcom_g7,2026-05-28,1,CMS,sensor,,Yes,On File
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,PA Medicaid,sensor,,Yes,On File
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Highmark BCBS PA,sensor,,Yes,On File
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UPMC Health Plan,sensor,,Yes,On File
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare,sensor,,Yes,On File
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Aetna PA,sensor,,Yes,On File
|
||||||
|
PA-19-0001,dexcom_g6,2026-06-06,1,Keystone First,sensor,,Yes,On File
|
||||||
|
PA-19-0002,dexcom_g6,2026-06-05,1,CMS,sensor,,Yes,On File
|
||||||
|
PA-19-0003,dexcom_g6,2026-06-06,1,Keystone First,sensor,,Yes,On File
|
||||||
|
PA-19-0004,dexcom_g6,2026-06-03,2,Medicare,sensor,,Yes,On File
|
||||||
|
PA-19-0005,freestyle_libre_3,2025-09-11,1,Medicare Part B,sensor,,Yes,On File
|
||||||
|
PA-19-0006,dexcom_g6,2025-08-14,1,Highmark BCBS PA,sensor,,Yes,On File
|
||||||
|
PA-19-0007,freestyle_libre_2,2025-07-30,2,UPMC Health Plan,sensor,,Yes,On File
|
||||||
|
PA-19-0008,freestyle_libre_2,2025-05-16,1,Medicare,sensor,,Yes,On File
|
||||||
|
PA-19-0009,dexcom_g6,2025-01-11,1,UPMC for You,sensor,,Yes,On File
|
||||||
|
PA-19-0010,freestyle_libre_2,2026-06-06,3,Highmark BlueCross BlueShield,sensor,,Yes,On File
|
||||||
|
PA-19-0011,dexcom_g6,2026-06-03,1,Geisinger,sensor,,Yes,On File
|
||||||
|
PA-19-0012,freestyle_libre_2,2026-06-01,2,AmeriHealth Caritas PA,sensor,,Yes,On File
|
||||||
|
PA-19-0013,dexcom_g6,2026-05-29,3,UPMC for You,sensor,,Yes,On File
|
||||||
|
PA-19-0014,freestyle_libre_3,2025-09-12,2,PA Medicaid,sensor,,Yes,On File
|
||||||
|
PA-19-0015,freestyle_libre_3,2025-08-21,1,Aetna PA Medicare,sensor,,Yes,On File
|
||||||
|
PA-19-0016,freestyle_libre_2,2025-07-28,2,Geisinger,sensor,,Yes,On File
|
||||||
|
PA-19-0017,dexcom_g6,2025-05-08,1,Medicare Part B,sensor,,Yes,On File
|
||||||
|
PA-19-0018,freestyle_libre_3,2024-10-31,1,Medicare Part B,sensor,,Yes,On File
|
||||||
|
PA-19-0019,freestyle_libre_2,2026-06-06,2,PA Medicaid,sensor,,Yes,On File
|
||||||
|
PA-19-0020,dexcom_g7,2026-05-28,1,Geisinger,sensor,,Yes,On File
|
||||||
|
PA-19-0021,dexcom_g6,2026-06-02,1,CMS,sensor,,Yes,On File
|
||||||
|
PA-19-0022,freestyle_libre_3,2026-05-28,1,UPMC Health Plan,sensor,,Yes,On File
|
||||||
|
PA-19-0023,dexcom_g6,2025-09-10,1,AmeriHealth Caritas PA,sensor,,Yes,On File
|
||||||
|
PA-19-0024,freestyle_libre_2,2025-08-15,2,Independence Blue Cross,sensor,,Yes,On File
|
||||||
|
PA-19-0025,freestyle_libre_2,2025-08-01,1,Highmark BCBS PA,sensor,,Yes,On File
|
||||||
|
PA-19-0026,dexcom_g7,2025-05-16,3,AmeriHealth Caritas PA,sensor,,Yes,On File
|
||||||
|
PA-19-0027,freestyle_libre_2,2024-12-17,1,Cigna PA,sensor,,Yes,On File
|
||||||
|
PA-19-0028,dexcom_g7,2026-05-29,1,Medicare,sensor,,Yes,On File
|
||||||
|
PA-19-0029,freestyle_libre_3,2026-05-28,1,Highmark BCBS PA,sensor,,Yes,On File
|
||||||
|
PA-19-0030,freestyle_libre_3,2026-06-02,1,Geisinger,sensor,,Yes,On File
|
||||||
|
,dexcom_g7,2026-05-28,1,CMS,sensor,,Yes,On File
|
||||||
|
FUTURE-019,dexcom_g7,2026-07-07,1,Medicare,sensor,,Yes,On File
|
||||||
|
ZERQTY-019,freestyle_libre_3,2026-06-02,0,Cigna PA,sensor,,Yes,On File
|
||||||
|
DUP-PT-019,dexcom_g7,2026-06-02,1,Medicare Part B,sensor,,Yes,On File
|
||||||
|
DUP-PT-019,freestyle_libre_3,2026-06-04,1,Highmark BCBS PA,sensor,,Yes,On File
|
||||||
|
42
test-data/pa-set/batch-21-pa-high.csv
Normal file
42
test-data/pa-set/batch-21-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
Patient ID,Item Description,Service Date,Qty,Insurance Name,Item Type,auth_status,swo_status
|
||||||
|
SC-A-001,Dexcom G7,06/02/2026,1,Medicare Part B,sensor,Approved,On File
|
||||||
|
SC-B-002,FreeStyle Libre 3,06/02/2026,1,Keystone First,sensor,Approved,On File
|
||||||
|
SC-C-003,Dexcom G6,05/08/2026,1,Cigna PA,sensor,Approved,On File
|
||||||
|
SC-D-004,Dexcom G7,06/02/2026,1,Highmark BlueCross BlueShield,sensor,Approved,On File
|
||||||
|
SC-E-005,FreeStyle Libre 2,06/04/2026,1,CMS,sensor,Approved,On File
|
||||||
|
SC-F-006,Dexcom G7,05/31/2026,1,Geisinger,sensor,Approved,On File
|
||||||
|
PA-20-0001,FreeStyle Libre 2,06/04/2026,3,CMS,sensor,Approved,On File
|
||||||
|
PA-20-0002,FreeStyle Libre 3,06/05/2026,3,Aetna PA,sensor,Approved,On File
|
||||||
|
PA-20-0003,Dexcom G7,05/28/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
PA-20-0004,FreeStyle Libre 2,05/29/2026,1,CMS,sensor,Approved,On File
|
||||||
|
PA-20-0005,FreeStyle Libre 2,09/13/2025,1,Medicare,sensor,Approved,On File
|
||||||
|
PA-20-0006,Dexcom G7,08/14/2025,1,UPMC Health Plan,sensor,Approved,On File
|
||||||
|
PA-20-0007,FreeStyle Libre 2,07/29/2025,1,Aetna PA,sensor,Approved,On File
|
||||||
|
PA-20-0008,Dexcom G7,05/19/2025,1,UPMC for You,sensor,Approved,On File
|
||||||
|
PA-20-0009,Dexcom G7,10/20/2024,1,Highmark BlueCross BlueShield,sensor,Approved,On File
|
||||||
|
PA-20-0010,FreeStyle Libre 3,06/01/2026,1,Medicare Part B,sensor,Approved,On File
|
||||||
|
PA-20-0011,Dexcom G6,05/29/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
PA-20-0012,FreeStyle Libre 3,06/01/2026,1,Aetna PA,sensor,Approved,On File
|
||||||
|
PA-20-0013,FreeStyle Libre 3,06/05/2026,2,Aetna PA,sensor,Approved,On File
|
||||||
|
PA-20-0014,Dexcom G7,09/11/2025,2,PA Medicaid,sensor,Approved,On File
|
||||||
|
PA-20-0015,FreeStyle Libre 3,08/21/2025,3,CMS,sensor,Approved,On File
|
||||||
|
PA-20-0016,FreeStyle Libre 2,07/29/2025,2,Medicare,sensor,Approved,On File
|
||||||
|
PA-20-0017,FreeStyle Libre 3,05/08/2025,1,Highmark BlueCross BlueShield,sensor,Approved,On File
|
||||||
|
PA-20-0018,Dexcom G7,12/13/2024,1,Highmark BlueCross BlueShield,sensor,Approved,On File
|
||||||
|
PA-20-0019,Dexcom G7,05/29/2026,1,Aetna PA Medicare,sensor,Approved,On File
|
||||||
|
PA-20-0020,Dexcom G6,06/04/2026,2,Highmark BCBS PA,sensor,Approved,On File
|
||||||
|
PA-20-0021,FreeStyle Libre 2,06/05/2026,1,AmeriHealth Caritas PA,sensor,Approved,On File
|
||||||
|
PA-20-0022,FreeStyle Libre 2,06/06/2026,2,CMS,sensor,Approved,On File
|
||||||
|
PA-20-0023,FreeStyle Libre 3,09/19/2025,1,PA Medicaid,sensor,Approved,On File
|
||||||
|
PA-20-0024,Dexcom G7,08/13/2025,3,Medicare Part B,sensor,Approved,On File
|
||||||
|
PA-20-0025,FreeStyle Libre 3,07/28/2025,1,UPMC for You,sensor,Approved,On File
|
||||||
|
PA-20-0026,FreeStyle Libre 2,05/21/2025,1,UPMC Health Plan,sensor,Approved,On File
|
||||||
|
PA-20-0027,Dexcom G7,12/25/2024,1,CMS,sensor,Approved,On File
|
||||||
|
PA-20-0028,FreeStyle Libre 2,06/03/2026,2,Aetna PA Medicare,sensor,Approved,On File
|
||||||
|
PA-20-0029,Dexcom G6,06/03/2026,1,CMS,sensor,Approved,On File
|
||||||
|
PA-20-0030,Dexcom G6,05/28/2026,1,Medicare Part B,sensor,Approved,On File
|
||||||
|
,Dexcom G7,05/28/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
FUTURE-020,Dexcom G7,07/07/2026,1,CMS,sensor,Approved,On File
|
||||||
|
ZERQTY-020,FreeStyle Libre 3,06/02/2026,0,Highmark BCBS PA,sensor,Approved,On File
|
||||||
|
DUP-PT-020,Dexcom G7,06/02/2026,1,Medicare,sensor,Approved,On File
|
||||||
|
DUP-PT-020,FreeStyle Libre 3,06/04/2026,1,Geisinger,sensor,Approved,On File
|
||||||
|
42
test-data/pa-set/batch-22-pa-high.csv
Normal file
42
test-data/pa-set/batch-22-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
account_no,device_type,shipment_date,qty,insurance,component,Prior Auth,diagnosis_on_file
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,1,Medicare,sensor,Pending,Yes
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,Keystone First,sensor,Pending,Yes
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Highmark BCBS PA,sensor,Pending,Yes
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UPMC Health Plan,sensor,Pending,Yes
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Independence Blue Cross,sensor,Pending,Yes
|
||||||
|
PA-21-0001,freestyle_libre_2,2026-06-03,1,Cigna PA,sensor,Pending,Yes
|
||||||
|
PA-21-0002,freestyle_libre_2,2026-06-05,1,CMS,sensor,Pending,Yes
|
||||||
|
PA-21-0003,dexcom_g7,2026-06-02,1,Cigna PA,sensor,Pending,Yes
|
||||||
|
PA-21-0004,freestyle_libre_3,2026-06-05,3,PA Medicaid,sensor,Pending,Yes
|
||||||
|
PA-21-0005,dexcom_g7,2025-09-14,3,Geisinger,sensor,Pending,Yes
|
||||||
|
PA-21-0006,freestyle_libre_2,2025-08-17,1,Aetna PA,sensor,Pending,Yes
|
||||||
|
PA-21-0007,freestyle_libre_3,2025-07-27,2,Aetna PA,sensor,Pending,Yes
|
||||||
|
PA-21-0008,dexcom_g7,2025-05-05,1,Highmark BlueCross BlueShield,sensor,Pending,Yes
|
||||||
|
PA-21-0009,freestyle_libre_3,2024-10-29,3,CMS,sensor,Pending,Yes
|
||||||
|
PA-21-0010,freestyle_libre_2,2026-06-01,1,UPMC Health Plan,sensor,Pending,Yes
|
||||||
|
PA-21-0011,dexcom_g7,2026-05-30,2,CMS,sensor,Pending,Yes
|
||||||
|
PA-21-0012,dexcom_g6,2026-06-02,1,CMS,sensor,Pending,Yes
|
||||||
|
PA-21-0013,freestyle_libre_2,2026-06-01,2,UPMC for You,sensor,Pending,Yes
|
||||||
|
PA-21-0014,freestyle_libre_2,2025-09-12,3,AmeriHealth Caritas PA,sensor,Pending,Yes
|
||||||
|
PA-21-0015,freestyle_libre_2,2025-08-21,1,Aetna PA Medicare,sensor,Pending,Yes
|
||||||
|
PA-21-0016,freestyle_libre_3,2025-08-01,3,Medicare,sensor,Pending,Yes
|
||||||
|
PA-21-0017,dexcom_g6,2025-05-08,1,Aetna PA,sensor,Pending,Yes
|
||||||
|
PA-21-0018,freestyle_libre_2,2024-10-28,2,CMS,sensor,Pending,Yes
|
||||||
|
PA-21-0019,dexcom_g6,2026-06-06,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
PA-21-0020,dexcom_g7,2026-06-06,1,Medicare Part B,sensor,Pending,Yes
|
||||||
|
PA-21-0021,dexcom_g7,2026-06-01,2,Medicare,sensor,Pending,Yes
|
||||||
|
PA-21-0022,freestyle_libre_2,2026-05-30,1,Highmark BlueCross BlueShield,sensor,Pending,Yes
|
||||||
|
PA-21-0023,dexcom_g7,2025-09-12,2,Highmark BlueCross BlueShield,sensor,Pending,Yes
|
||||||
|
PA-21-0024,freestyle_libre_2,2025-08-19,2,Medicare Part B,sensor,Pending,Yes
|
||||||
|
PA-21-0025,freestyle_libre_2,2025-07-27,1,UPMC Health Plan,sensor,Pending,Yes
|
||||||
|
PA-21-0026,dexcom_g7,2025-05-15,1,CMS,sensor,Pending,Yes
|
||||||
|
PA-21-0027,dexcom_g7,2024-12-15,2,Cigna PA,sensor,Pending,Yes
|
||||||
|
PA-21-0028,freestyle_libre_2,2026-06-06,3,PA Medicaid,sensor,Pending,Yes
|
||||||
|
PA-21-0029,dexcom_g7,2026-06-02,1,Aetna PA Medicare,sensor,Pending,Yes
|
||||||
|
PA-21-0030,dexcom_g7,2026-06-01,2,Keystone First,sensor,Pending,Yes
|
||||||
|
,dexcom_g7,2026-05-28,1,CMS,sensor,Pending,Yes
|
||||||
|
FUTURE-021,dexcom_g7,2026-07-07,1,CMS,sensor,Pending,Yes
|
||||||
|
ZERQTY-021,freestyle_libre_3,2026-06-02,0,Highmark BCBS PA,sensor,Pending,Yes
|
||||||
|
DUP-PT-021,dexcom_g7,2026-06-02,1,Medicare,sensor,Pending,Yes
|
||||||
|
DUP-PT-021,freestyle_libre_3,2026-06-04,1,Cigna PA,sensor,Pending,Yes
|
||||||
|
42
test-data/pa-set/batch-23-pa-high.csv
Normal file
42
test-data/pa-set/batch-23-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,Notes,Auth Number,Staff
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,1,Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,AmeriHealth Caritas PA,sensor,see chart,A12345,K.F.
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Highmark BCBS PA,sensor,see chart,A12345,K.F.
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,Aetna PA Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Independence Blue Cross,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0001,dexcom_g7,2026-06-04,3,Aetna PA,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0002,freestyle_libre_3,2026-05-29,1,Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0003,dexcom_g6,2026-05-29,1,Highmark BCBS PA,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0004,dexcom_g7,2026-05-30,1,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0005,freestyle_libre_2,2025-09-19,1,Highmark BCBS PA,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0006,dexcom_g6,2025-08-17,1,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0007,dexcom_g6,2025-07-27,3,UPMC Health Plan,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0008,freestyle_libre_2,2025-05-14,3,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0009,dexcom_g6,2025-01-01,2,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0010,dexcom_g7,2026-06-06,1,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0011,dexcom_g6,2026-06-04,2,Aetna PA Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0012,dexcom_g7,2026-06-03,1,Highmark BlueCross BlueShield,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0013,dexcom_g6,2026-06-01,1,Keystone First,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0014,freestyle_libre_2,2025-09-20,1,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0015,freestyle_libre_2,2025-08-20,2,Highmark BlueCross BlueShield,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0016,dexcom_g6,2025-07-31,1,Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0017,freestyle_libre_3,2025-05-13,1,Keystone First,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0018,freestyle_libre_2,2024-11-12,1,Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0019,freestyle_libre_2,2026-06-06,2,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0020,dexcom_g6,2026-05-31,1,Highmark BlueCross BlueShield,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0021,dexcom_g7,2026-06-03,1,UPMC for You,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0022,dexcom_g6,2026-05-31,1,Geisinger,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0023,freestyle_libre_2,2025-09-10,2,AmeriHealth Caritas PA,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0024,dexcom_g6,2025-08-20,1,Highmark BCBS PA,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0025,dexcom_g7,2025-07-30,1,UPMC for You,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0026,dexcom_g7,2025-05-07,1,Keystone First,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0027,freestyle_libre_3,2024-12-12,3,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0028,dexcom_g6,2026-06-02,2,Keystone First,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0029,freestyle_libre_2,2026-05-28,1,Aetna PA Medicare,sensor,see chart,A12345,K.F.
|
||||||
|
PA-22-0030,freestyle_libre_3,2026-06-02,1,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
,dexcom_g7,2026-05-28,1,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
FUTURE-022,dexcom_g7,2026-07-07,1,CMS,sensor,see chart,A12345,K.F.
|
||||||
|
ZERQTY-022,freestyle_libre_3,2026-06-02,0,Highmark BCBS PA,sensor,see chart,A12345,K.F.
|
||||||
|
DUP-PT-022,dexcom_g7,2026-06-02,1,Medicare Part B,sensor,see chart,A12345,K.F.
|
||||||
|
DUP-PT-022,freestyle_libre_3,2026-06-04,1,Aetna PA,sensor,see chart,A12345,K.F.
|
||||||
|
42
test-data/pa-set/batch-24-pa-high.csv
Normal file
42
test-data/pa-set/batch-24-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patient_id,device_type,shipment_date,payer
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,CMS
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,AmeriHealth Caritas PA
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,Independence Blue Cross
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,Aetna PA Medicare
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,CMS
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,Aetna PA
|
||||||
|
PA-23-0001,dexcom_g6,2026-06-04,Independence Blue Cross
|
||||||
|
PA-23-0002,freestyle_libre_3,2026-06-01,Cigna PA
|
||||||
|
PA-23-0003,dexcom_g7,2026-06-04,AmeriHealth Caritas PA
|
||||||
|
PA-23-0004,freestyle_libre_2,2026-06-03,Medicare Part B
|
||||||
|
PA-23-0005,dexcom_g7,2025-09-12,Medicare
|
||||||
|
PA-23-0006,dexcom_g6,2025-08-17,AmeriHealth Caritas PA
|
||||||
|
PA-23-0007,dexcom_g7,2025-07-31,Medicare Part B
|
||||||
|
PA-23-0008,freestyle_libre_2,2025-05-20,Geisinger
|
||||||
|
PA-23-0009,dexcom_g6,2024-12-15,UPMC Health Plan
|
||||||
|
PA-23-0010,dexcom_g6,2026-06-05,Highmark BCBS PA
|
||||||
|
PA-23-0011,dexcom_g7,2026-06-06,Medicare
|
||||||
|
PA-23-0012,dexcom_g7,2026-05-30,Highmark BlueCross BlueShield
|
||||||
|
PA-23-0013,dexcom_g7,2026-05-30,UPMC for You
|
||||||
|
PA-23-0014,freestyle_libre_3,2025-09-14,Highmark BlueCross BlueShield
|
||||||
|
PA-23-0015,dexcom_g6,2025-08-15,Independence Blue Cross
|
||||||
|
PA-23-0016,freestyle_libre_3,2025-08-01,CMS
|
||||||
|
PA-23-0017,dexcom_g6,2025-05-20,Medicare Part B
|
||||||
|
PA-23-0018,dexcom_g6,2024-12-31,AmeriHealth Caritas PA
|
||||||
|
PA-23-0019,freestyle_libre_2,2026-06-02,Highmark BlueCross BlueShield
|
||||||
|
PA-23-0020,dexcom_g7,2026-06-04,CMS
|
||||||
|
PA-23-0021,freestyle_libre_2,2026-06-06,CMS
|
||||||
|
PA-23-0022,freestyle_libre_3,2026-06-03,CMS
|
||||||
|
PA-23-0023,dexcom_g6,2025-09-19,Cigna PA
|
||||||
|
PA-23-0024,freestyle_libre_2,2025-08-20,Medicare
|
||||||
|
PA-23-0025,dexcom_g7,2025-07-29,Medicare
|
||||||
|
PA-23-0026,dexcom_g6,2025-05-03,Keystone First
|
||||||
|
PA-23-0027,freestyle_libre_2,2024-11-17,Cigna PA
|
||||||
|
PA-23-0028,dexcom_g7,2026-06-06,UPMC Health Plan
|
||||||
|
PA-23-0029,dexcom_g7,2026-06-03,Highmark BlueCross BlueShield
|
||||||
|
PA-23-0030,dexcom_g6,2026-05-30,CMS
|
||||||
|
,dexcom_g7,2026-05-28,Medicare Part B
|
||||||
|
FUTURE-023,dexcom_g7,2026-07-07,Medicare Part B
|
||||||
|
ZERQTY-023,freestyle_libre_3,2026-06-02,Highmark BCBS PA
|
||||||
|
DUP-PT-023,dexcom_g7,2026-06-02,CMS
|
||||||
|
DUP-PT-023,freestyle_libre_3,2026-06-04,Aetna PA
|
||||||
|
42
test-data/pa-set/batch-25-pa-high.csv
Normal file
42
test-data/pa-set/batch-25-pa-high.csv
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
patient_id,device_type,shipment_date,quantity,payer,component,Transfer_From,swo_status
|
||||||
|
SC-A-001,dexcom_g7,2026-06-02,1,CMS,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-B-002,freestyle_libre_3,2026-06-02,1,UPMC for You,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-C-003,dexcom_g6,2026-05-08,1,Aetna PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-D-004,dexcom_g7,2026-06-02,1,UPMC Health Plan,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-E-005,freestyle_libre_2,2026-06-04,1,CMS,sensor,Prior Supplier LLC,Pending
|
||||||
|
SC-F-006,dexcom_g7,2026-05-31,1,Cigna PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0001,freestyle_libre_3,2026-06-02,1,UPMC for You,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0002,dexcom_g7,2026-05-29,1,UPMC for You,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0003,freestyle_libre_3,2026-06-06,3,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0004,dexcom_g7,2026-05-30,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0005,dexcom_g7,2025-09-11,1,PA Medicaid,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0006,dexcom_g7,2025-08-11,2,Geisinger,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0007,dexcom_g7,2025-07-31,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0008,dexcom_g7,2025-05-11,3,CMS,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0009,dexcom_g6,2024-10-23,1,Geisinger,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0010,dexcom_g6,2026-05-29,1,Independence Blue Cross,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0011,dexcom_g6,2026-05-30,1,PA Medicaid,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0012,dexcom_g7,2026-06-04,1,UPMC Health Plan,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0013,dexcom_g7,2026-05-29,2,Highmark BlueCross BlueShield,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0014,dexcom_g6,2025-09-14,2,Cigna PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0015,dexcom_g6,2025-08-18,1,CMS,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0016,dexcom_g7,2025-07-30,3,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0017,dexcom_g6,2025-05-22,2,UPMC Health Plan,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0018,freestyle_libre_3,2024-12-14,3,AmeriHealth Caritas PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0019,freestyle_libre_2,2026-06-01,2,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0020,dexcom_g7,2026-06-04,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0021,freestyle_libre_3,2026-05-29,1,Geisinger,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0022,freestyle_libre_3,2026-06-05,1,UPMC for You,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0023,freestyle_libre_2,2025-09-17,1,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0024,dexcom_g6,2025-08-13,2,Aetna PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0025,dexcom_g7,2025-08-01,3,Cigna PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0026,dexcom_g7,2025-05-18,3,AmeriHealth Caritas PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0027,freestyle_libre_3,2024-12-29,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0028,dexcom_g7,2026-05-29,2,Highmark BlueCross BlueShield,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0029,dexcom_g6,2026-06-01,3,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
PA-24-0030,freestyle_libre_3,2026-06-05,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
,dexcom_g7,2026-05-28,1,CMS,sensor,Prior Supplier LLC,Pending
|
||||||
|
FUTURE-024,dexcom_g7,2026-07-07,1,Medicare Part B,sensor,Prior Supplier LLC,Pending
|
||||||
|
ZERQTY-024,freestyle_libre_3,2026-06-02,0,Aetna PA,sensor,Prior Supplier LLC,Pending
|
||||||
|
DUP-PT-024,dexcom_g7,2026-06-02,1,Medicare,sensor,Prior Supplier LLC,Pending
|
||||||
|
DUP-PT-024,freestyle_libre_3,2026-06-04,1,Geisinger,sensor,Prior Supplier LLC,Pending
|
||||||
|
103
tests/test_coverage_flags.py
Normal file
103
tests/test_coverage_flags.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent / "python-backend"))
|
||||||
|
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from core.coverage_calculator import ShipmentRecord, calculate_coverage, CoverageFlag
|
||||||
|
|
||||||
|
TODAY = date.today()
|
||||||
|
|
||||||
|
def make_record(**kwargs):
|
||||||
|
defaults = dict(
|
||||||
|
patient_id="PT001",
|
||||||
|
device_type="dexcom_g7",
|
||||||
|
shipment_date=TODAY - timedelta(days=10),
|
||||||
|
quantity=1,
|
||||||
|
payer="medicare",
|
||||||
|
component="sensor",
|
||||||
|
)
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return ShipmentRecord(**defaults)
|
||||||
|
|
||||||
|
def test_shipment_record_accepts_csv_visit_date():
|
||||||
|
r = make_record(csv_visit_date=TODAY - timedelta(days=30))
|
||||||
|
assert r.csv_visit_date == TODAY - timedelta(days=30)
|
||||||
|
|
||||||
|
def test_shipment_record_accepts_doc_fields():
|
||||||
|
r = make_record(
|
||||||
|
csv_swo_status="On File",
|
||||||
|
csv_pecos_verified="Yes",
|
||||||
|
csv_pa_status="Not Required",
|
||||||
|
csv_diagnosis_on_file="Yes",
|
||||||
|
)
|
||||||
|
assert r.csv_swo_status == "On File"
|
||||||
|
assert r.csv_pecos_verified == "Yes"
|
||||||
|
|
||||||
|
def test_shipment_record_doc_fields_default_none():
|
||||||
|
r = make_record()
|
||||||
|
assert r.csv_visit_date is None
|
||||||
|
assert r.csv_swo_status is None
|
||||||
|
assert r.csv_transfer_from is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_supply_lapsed_flag_when_coverage_ended():
|
||||||
|
# Shipment 200 days ago, 1 sensor (10 wear days) — coverage ended long ago
|
||||||
|
r = make_record(shipment_date=TODAY - timedelta(days=200), quantity=1)
|
||||||
|
result = calculate_coverage(r)
|
||||||
|
assert result.flag == CoverageFlag.SUPPLY_LAPSED
|
||||||
|
|
||||||
|
def test_renewal_critical_at_45_days():
|
||||||
|
# Last confirmed visit was 135 days ago — next visit due in 45 days
|
||||||
|
r = make_record(
|
||||||
|
shipment_date=TODAY - timedelta(days=10),
|
||||||
|
quantity=1,
|
||||||
|
csv_visit_date=TODAY - timedelta(days=135),
|
||||||
|
)
|
||||||
|
result = calculate_coverage(r)
|
||||||
|
assert result.flag == CoverageFlag.RENEWAL_CRITICAL
|
||||||
|
|
||||||
|
def test_renewal_elevated_at_60_days():
|
||||||
|
r = make_record(
|
||||||
|
shipment_date=TODAY - timedelta(days=10),
|
||||||
|
quantity=1,
|
||||||
|
csv_visit_date=TODAY - timedelta(days=120),
|
||||||
|
)
|
||||||
|
result = calculate_coverage(r)
|
||||||
|
assert result.flag == CoverageFlag.RENEWAL_ELEVATED
|
||||||
|
|
||||||
|
def test_renewal_soon_at_90_days():
|
||||||
|
r = make_record(
|
||||||
|
shipment_date=TODAY - timedelta(days=10),
|
||||||
|
quantity=1,
|
||||||
|
csv_visit_date=TODAY - timedelta(days=90),
|
||||||
|
)
|
||||||
|
result = calculate_coverage(r)
|
||||||
|
assert result.flag == CoverageFlag.RENEWAL_SOON
|
||||||
|
|
||||||
|
def test_active_when_visit_far_away():
|
||||||
|
# 6 sensors × 10 days = 60-day supply, shipped 10 days ago → 50 days left (outside refill window)
|
||||||
|
# visit 10 days ago → 170 days until next due → not in any RENEWAL tier
|
||||||
|
r = make_record(
|
||||||
|
shipment_date=TODAY - timedelta(days=10),
|
||||||
|
quantity=6,
|
||||||
|
csv_visit_date=TODAY - timedelta(days=10),
|
||||||
|
)
|
||||||
|
result = calculate_coverage(r)
|
||||||
|
assert result.flag == CoverageFlag.ACTIVE
|
||||||
|
|
||||||
|
def test_transfer_pending_flag():
|
||||||
|
r = make_record(csv_transfer_from="Prior Supplier LLC")
|
||||||
|
result = calculate_coverage(r)
|
||||||
|
assert result.flag == CoverageFlag.TRANSFER_PENDING
|
||||||
|
|
||||||
|
def test_priority_sort_order():
|
||||||
|
lapsed = make_record(shipment_date=TODAY - timedelta(days=200))
|
||||||
|
critical = make_record(patient_id="PT002", shipment_date=TODAY - timedelta(days=10),
|
||||||
|
csv_visit_date=TODAY - timedelta(days=135))
|
||||||
|
active = make_record(patient_id="PT003", shipment_date=TODAY - timedelta(days=10),
|
||||||
|
quantity=6, csv_visit_date=TODAY - timedelta(days=10))
|
||||||
|
from core.coverage_calculator import calculate_batch
|
||||||
|
results = calculate_batch([active, critical, lapsed])
|
||||||
|
assert results[0].flag == CoverageFlag.SUPPLY_LAPSED
|
||||||
|
assert results[1].flag == CoverageFlag.RENEWAL_CRITICAL
|
||||||
|
assert results[2].flag == CoverageFlag.ACTIVE
|
||||||
80
tests/test_doc_state_machine.py
Normal file
80
tests/test_doc_state_machine.py
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent / "python-backend"))
|
||||||
|
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from core.doc_state_machine import compute_doc_state, DocState
|
||||||
|
|
||||||
|
TODAY = date.today()
|
||||||
|
|
||||||
|
def test_medicare_ffs_pa_not_required():
|
||||||
|
state = compute_doc_state(
|
||||||
|
payer_type="medicare",
|
||||||
|
csv_swo_status="On File",
|
||||||
|
csv_visit_date=TODAY - timedelta(days=30),
|
||||||
|
confirmed_visit_date=None,
|
||||||
|
csv_pecos_verified="Yes",
|
||||||
|
csv_pa_status=None,
|
||||||
|
csv_diagnosis_on_file="Yes",
|
||||||
|
is_transfer=False,
|
||||||
|
)
|
||||||
|
assert state.pa == "Not Required"
|
||||||
|
assert state.pecos == "Verified"
|
||||||
|
|
||||||
|
def test_medicaid_pecos_not_applicable():
|
||||||
|
state = compute_doc_state(
|
||||||
|
payer_type="medicaid",
|
||||||
|
csv_swo_status="On File",
|
||||||
|
csv_visit_date=TODAY - timedelta(days=30),
|
||||||
|
confirmed_visit_date=None,
|
||||||
|
csv_pecos_verified=None,
|
||||||
|
csv_pa_status="Approved",
|
||||||
|
csv_diagnosis_on_file="Yes",
|
||||||
|
is_transfer=False,
|
||||||
|
)
|
||||||
|
assert state.pecos == "N/A"
|
||||||
|
assert state.pa == "Approved"
|
||||||
|
|
||||||
|
def test_transfer_patient_all_pending():
|
||||||
|
state = compute_doc_state(
|
||||||
|
payer_type="medicare",
|
||||||
|
csv_swo_status=None,
|
||||||
|
csv_visit_date=None,
|
||||||
|
confirmed_visit_date=None,
|
||||||
|
csv_pecos_verified=None,
|
||||||
|
csv_pa_status=None,
|
||||||
|
csv_diagnosis_on_file=None,
|
||||||
|
is_transfer=True,
|
||||||
|
)
|
||||||
|
assert state.swo == "Pending — Verify"
|
||||||
|
assert state.visit == "Pending — Verify"
|
||||||
|
assert state.pecos == "Pending — Verify"
|
||||||
|
assert state.pa == "Pending — Verify"
|
||||||
|
assert state.diagnosis == "Pending — Verify"
|
||||||
|
|
||||||
|
def test_cascade_visit_not_confirmed():
|
||||||
|
state = compute_doc_state(
|
||||||
|
payer_type="medicare",
|
||||||
|
csv_swo_status="On File",
|
||||||
|
csv_visit_date=None,
|
||||||
|
confirmed_visit_date=None,
|
||||||
|
csv_pecos_verified="Yes",
|
||||||
|
csv_pa_status=None,
|
||||||
|
csv_diagnosis_on_file="Yes",
|
||||||
|
is_transfer=False,
|
||||||
|
)
|
||||||
|
assert len(state.cascade) > 0
|
||||||
|
assert any("visit" in c.lower() for c in state.cascade)
|
||||||
|
|
||||||
|
def test_no_cascade_when_all_clear():
|
||||||
|
state = compute_doc_state(
|
||||||
|
payer_type="medicare",
|
||||||
|
csv_swo_status="On File",
|
||||||
|
csv_visit_date=TODAY - timedelta(days=30),
|
||||||
|
confirmed_visit_date=None,
|
||||||
|
csv_pecos_verified="Yes",
|
||||||
|
csv_pa_status=None,
|
||||||
|
csv_diagnosis_on_file="Yes",
|
||||||
|
is_transfer=False,
|
||||||
|
)
|
||||||
|
assert state.cascade == []
|
||||||
Loading…
Reference in a new issue