-
Notifications
You must be signed in to change notification settings - Fork 0
adding pdf script to fill out prior auth info #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # Filling PDF Forms - Jane Doe Example | ||
|
|
||
| This directory contains scripts to fill out the Colorado Prescription Drug Prior Authorization Request Form with mock patient data for Jane Doe. | ||
|
|
||
| ## Jane Doe's Profile | ||
|
|
||
| **Patient:** Jane Doe, Age 42 | ||
| **Occupation:** Marketing Director | ||
| **Location:** Denver, Colorado | ||
| **Personality:** Professional, detail-oriented, health-conscious, active lifestyle | ||
| **Condition:** Chronic migraines with aura, diagnosed 3 years ago | ||
| **Medication Needed:** Aimovig (erenumab-aooe) - CGRP inhibitor for migraine prevention | ||
|
|
||
| ## Files | ||
|
|
||
| 1. **`fill_pdf_form.py`** - Python script to automatically fill the PDF form | ||
| 2. **`../assets/pdf-templates/jane-doe-filled-form-reference.txt`** - Text reference showing all filled information | ||
|
|
||
| ## Installation | ||
|
|
||
| To run the PDF filling script, you need to install PyMuPDF: | ||
|
|
||
| ```bash | ||
| pip install pymupdf | ||
| ``` | ||
|
|
||
| Or if you prefer pypdf: | ||
|
|
||
| ```bash | ||
| pip install pypdf | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| Run the script: | ||
|
|
||
| ```bash | ||
| python3 scripts/fill_pdf_form.py | ||
| ``` | ||
|
|
||
| This will: | ||
| - Read the original PDF template from `assets/pdf-templates/co-prescription-drug-prior-authorization-request-form.pdf` | ||
| - Fill it with Jane Doe's information | ||
| - Save the filled PDF to `assets/pdf-templates/co-prescription-drug-prior-authorization-request-form-filled-jane-doe.pdf` | ||
|
|
||
| ## Manual Filling | ||
|
|
||
| If you prefer to fill the form manually or the script doesn't work perfectly, refer to: | ||
| - `assets/pdf-templates/jane-doe-filled-form-reference.txt` for all the information organized by field | ||
|
|
||
| ## Alternative Methods | ||
|
|
||
| If Python libraries aren't available, you can: | ||
| 1. Open the PDF in Adobe Acrobat or another PDF editor | ||
| 2. Use the reference document to fill each field manually | ||
| 3. Or use online PDF fillers like PDFescape, PDFfiller, etc. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,329 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| Fill out the Colorado Prescription Drug Prior Authorization Request Form | ||
| with mock patient data for Jane Doe. | ||
|
|
||
| This script requires pypdf to be installed: | ||
| pip install pypdf | ||
|
|
||
| Or install PyMuPDF for better text positioning: | ||
| pip install pymupdf | ||
| """ | ||
|
|
||
| import sys | ||
| from pathlib import Path | ||
| from datetime import datetime | ||
|
|
||
| USE_PYMUPDF = False | ||
| USE_PYPDF = False | ||
|
|
||
| # Try to import PDF libraries | ||
| try: | ||
| import fitz # PyMuPDF | ||
| USE_PYMUPDF = True | ||
| except ImportError: | ||
| try: | ||
| from pypdf import PdfReader, PdfWriter | ||
| USE_PYPDF = True | ||
| except ImportError: | ||
| print("Error: No PDF library found. Please install one of:") | ||
| print(" pip install pymupdf (recommended)") | ||
| print(" pip install pypdf") | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| # Jane Doe's Profile | ||
| # Age: 42, Marketing Director, lives in Denver, CO | ||
| # Condition: Chronic migraines with aura, diagnosed 3 years ago | ||
| # Medication: Aimovig (erenumab-aooe) - a CGRP inhibitor for migraine prevention | ||
| # Personality: Professional, detail-oriented, health-conscious, active lifestyle | ||
|
|
||
| PATIENT_DATA = { | ||
| # Request Type | ||
| "urgent": False, # Non-urgent | ||
|
|
||
| # Drug Information | ||
| "drug_name": "Aimovig (erenumab-aooe)", | ||
| "drug_scientific": "erenumab-aooe", | ||
| "opioid_dependence": False, | ||
|
|
||
| # Patient Information | ||
| "patient_name": "Jane Doe", | ||
| "member_number": "AET123456789", | ||
| "policy_group_number": "GRP-2024-001", | ||
| "date_of_birth": "1982-03-15", # Age 42 | ||
| "patient_address": "2847 Elm Street, Apt 4B, Denver, CO 80202", | ||
| "patient_phone": "(303) 555-0147", | ||
| "patient_email": "jane.doe.email@example.com", | ||
|
|
||
| # Prescription Date | ||
| "prescription_date": "2026-02-20", | ||
|
|
||
| # Prescriber Information | ||
| "prescriber_name": "Dr. Sarah Chen, MD", | ||
| "prescriber_fax": "(303) 555-0198", | ||
| "prescriber_phone": "(303) 555-0197", | ||
| "prescriber_pager": "", | ||
| "prescriber_address": "1234 Medical Center Drive, Suite 200, Denver, CO 80218", | ||
| "prescriber_office_contact": "Maria Rodriguez", | ||
| "prescriber_npi": "1234567890", | ||
| "prescriber_dea": "BC1234567", | ||
| "prescriber_tax_id": "12-3456789", | ||
| "specialty_facility": "Denver Neurology Associates", | ||
| "prescriber_email": "schen@denverneuro.com", | ||
|
|
||
| # Prior Authorization Type | ||
| "pa_type": "New Request", # New Request or Reauthorization | ||
|
|
||
| # Diagnosis | ||
| "diagnosis": "Chronic migraine with aura, intractable, with status migrainosus", | ||
| "icd_code": "G43.109", | ||
|
|
||
| # Drug Details | ||
| "drug_requested": "Aimovig (erenumab-aooe)", | ||
| "strength_route_frequency": "70 mg subcutaneous injection monthly", | ||
| "unit_volume": "1 autoinjector pen (70 mg/1 mL)", | ||
| "start_date": "2026-03-01", | ||
| "length_of_therapy": "12 months", | ||
|
|
||
| # Treatment Location | ||
| "treatment_location": "Patient's Home - Self-administered subcutaneous injection", | ||
| "location_npi": "", | ||
| "location_address": "2847 Elm Street, Apt 4B, Denver, CO 80202", | ||
| "location_tax_id": "", | ||
|
|
||
| # Clinical Justification | ||
| "clinical_criteria": """Patient is a 42-year-old female with a 3-year history of chronic migraines | ||
| (15-18 headache days per month). Patient has failed multiple preventive therapies including: | ||
| - Topiramate 100mg BID for 6 months (discontinued due to cognitive side effects) | ||
| - Propranolol ER 120mg daily for 8 months (ineffective, minimal reduction in frequency) | ||
| - Amitriptyline 50mg at bedtime for 4 months (discontinued due to excessive sedation) | ||
|
|
||
| Patient has tried acute treatments including: | ||
| - Sumatriptan 100mg tablets (partial relief, frequent use) | ||
| - Rizatriptan 10mg (inconsistent response) | ||
|
|
||
| Current headache diary shows average of 16 migraine days per month over the past 3 months. | ||
| Migraines significantly impact patient's ability to work and maintain daily activities. | ||
| Patient is a marketing director and headaches cause frequent work absences. | ||
|
|
||
| Aimovig (erenumab-aooe) is a CGRP monoclonal antibody indicated for the preventive treatment | ||
| of migraine in adults. This medication is appropriate given patient's treatment-resistant chronic | ||
| migraine and need for a well-tolerated preventive option that does not require daily dosing. | ||
|
|
||
| Patient has been counseled on proper injection technique and storage requirements.""", | ||
|
|
||
| "additional_info": "Patient has no contraindications to CGRP inhibitors. No history of cardiovascular disease.", | ||
| "clinical_trial": False, | ||
|
|
||
| # Prescription Details | ||
| "dose": "70 mg", | ||
| "route": "Subcutaneous", | ||
| "frequency": "Monthly (once every 28 days)", | ||
| "quantity": "1 autoinjector pen", | ||
| "refills": "11", | ||
|
|
||
| # Delivery | ||
| "delivery_location": "Patient's Home", | ||
|
|
||
| # Signature | ||
| "signature_date": "2026-02-20", | ||
|
|
||
| # Pharmacy | ||
| "pharmacy_name": "HealthMart Pharmacy", | ||
| "pharmacy_phone": "(303) 555-0200", | ||
| } | ||
|
|
||
|
|
||
| def format_date(date_str: str) -> str: | ||
| """Convert YYYY-MM-DD to MM/DD/YYYY""" | ||
| dt = datetime.strptime(date_str, "%Y-%m-%d") | ||
| return dt.strftime("%m/%d/%Y") | ||
|
|
||
|
|
||
| def fill_pdf_with_pymupdf(input_pdf_path: str, output_pdf_path: str) -> None: | ||
| """Fill PDF form fields using PyMuPDF.""" | ||
| with fitz.open(input_pdf_path) as doc: | ||
| page = doc[0] | ||
| widgets = list(page.widgets()) | ||
|
|
||
| # Track widgets that need to be updated | ||
| widgets_to_update = [] | ||
|
|
||
| def set_widget_value(widget, value): | ||
| """Set widget value and track it for update.""" | ||
| if widget: | ||
| widget.field_value = value | ||
| widgets_to_update.append(widget) | ||
| return True | ||
| return False | ||
|
|
||
| def set_checkbox(widgets_list, index, checked=True): | ||
| """Set checkbox value and track it.""" | ||
| if widgets_list and len(widgets_list) > index: | ||
| widgets_list[index].field_value = checked | ||
| widgets_to_update.append(widgets_list[index]) | ||
| return True | ||
| return False | ||
|
|
||
| # Urgent/Non-Urgent checkboxes (cb1) - Y=128 | ||
| # First checkbox is Urgent, second is Non-Urgent | ||
| urgent_widgets = [w for w in widgets if w.field_name == "cb1" and abs(w.rect.y0 - 128) < 5] | ||
| if len(urgent_widgets) >= 2: | ||
| if not PATIENT_DATA["urgent"]: | ||
| urgent_widgets[1].field_value = True # Non-Urgent | ||
| widgets_to_update.append(urgent_widgets[1]) | ||
| else: | ||
| urgent_widgets[0].field_value = True # Urgent | ||
| widgets_to_update.append(urgent_widgets[0]) | ||
|
|
||
| # Requested Drug Name (T3) - Y=146 | ||
| drug_name_widget = next((w for w in widgets if w.field_name == "T3"), None) | ||
| if drug_name_widget: | ||
| drug_name_widget.field_value = PATIENT_DATA["drug_name"] | ||
| widgets_to_update.append(drug_name_widget) | ||
|
|
||
| # Opioid dependence checkboxes (cb2) - Y=165 | ||
| # First is Yes, second is No | ||
| opioid_widgets = [w for w in widgets if w.field_name == "cb2" and abs(w.rect.y0 - 165) < 5] | ||
| if len(opioid_widgets) >= 2: | ||
| if not PATIENT_DATA["opioid_dependence"]: | ||
| opioid_widgets[1].field_value = True # No | ||
| widgets_to_update.append(opioid_widgets[1]) | ||
| else: | ||
| opioid_widgets[0].field_value = True # Yes | ||
| widgets_to_update.append(opioid_widgets[0]) | ||
|
|
||
| # Patient Information fields (left column) - T4 through T11 | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T4"), None), PATIENT_DATA["patient_name"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T5"), None), PATIENT_DATA["member_number"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T6"), None), PATIENT_DATA["policy_group_number"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T7"), None), format_date(PATIENT_DATA["date_of_birth"])) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T8"), None), PATIENT_DATA["patient_address"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T9"), None), PATIENT_DATA["patient_phone"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T10"), None), PATIENT_DATA["patient_email"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T11"), None), format_date(PATIENT_DATA["prescription_date"])) | ||
|
|
||
| # Prescriber Information fields (right column) - T12 through T22 | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T12"), None), PATIENT_DATA["prescriber_name"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T13"), None), PATIENT_DATA["prescriber_fax"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T14"), None), PATIENT_DATA["prescriber_phone"]) | ||
| if PATIENT_DATA["prescriber_pager"]: | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T15"), None), PATIENT_DATA["prescriber_pager"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T16"), None), PATIENT_DATA["prescriber_address"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T17"), None), PATIENT_DATA["prescriber_office_contact"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T18"), None), PATIENT_DATA["prescriber_npi"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T19"), None), PATIENT_DATA["prescriber_dea"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T20"), None), PATIENT_DATA["prescriber_tax_id"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T21"), None), PATIENT_DATA["specialty_facility"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T22"), None), PATIENT_DATA["prescriber_email"]) | ||
|
|
||
| # Prior Authorization Type checkboxes (cb23) - Y=437 | ||
| pa_widgets = [w for w in widgets if w.field_name == "cb23" and abs(w.rect.y0 - 437) < 5] | ||
| if len(pa_widgets) >= 2: | ||
| if PATIENT_DATA["pa_type"] == "New Request": | ||
| set_checkbox(pa_widgets, 0) | ||
| else: | ||
| set_checkbox(pa_widgets, 1) | ||
|
|
||
| # Clinical information fields | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T25"), None), | ||
| f"{PATIENT_DATA['diagnosis']} (ICD-10: {PATIENT_DATA['icd_code']})") | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T26"), None), PATIENT_DATA["drug_requested"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T27"), None), PATIENT_DATA["strength_route_frequency"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T28"), None), PATIENT_DATA["unit_volume"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T30"), None), | ||
| f"Start: {format_date(PATIENT_DATA['start_date'])}, Duration: {PATIENT_DATA['length_of_therapy']}") | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T29"), None), PATIENT_DATA["treatment_location"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T31"), None), PATIENT_DATA["clinical_criteria"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T32"), None), PATIENT_DATA["additional_info"]) | ||
|
|
||
| # Drug details | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T33.0"), None), | ||
| f"{PATIENT_DATA['drug_name']} {PATIENT_DATA['dose']}") | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T34"), None), PATIENT_DATA["dose"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T35"), None), PATIENT_DATA["route"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T36"), None), PATIENT_DATA["frequency"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T37"), None), PATIENT_DATA["quantity"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T38"), None), PATIENT_DATA["refills"]) | ||
|
|
||
| # Delivery location checkboxes (cb39, cb40, cb41) - Y=645.7 | ||
| delivery_widgets = [w for w in widgets if w.field_name in ["cb39", "cb40", "cb41"] and abs(w.rect.y0 - 645.7) < 5] | ||
| if delivery_widgets: | ||
| if PATIENT_DATA["delivery_location"] == "Patient's Home": | ||
| cb39 = next((w for w in delivery_widgets if w.field_name == "cb39"), None) | ||
| if cb39: | ||
| set_checkbox([cb39], 0) | ||
| elif PATIENT_DATA["delivery_location"] == "Physician Office": | ||
| cb40 = next((w for w in delivery_widgets if w.field_name == "cb40"), None) | ||
| if cb40: | ||
| set_checkbox([cb40], 0) | ||
|
|
||
| # Signature and pharmacy | ||
| set_widget_value(next((w for w in widgets if w.field_name == "Signature3"), None), PATIENT_DATA["prescriber_name"]) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T43"), None), format_date(PATIENT_DATA["signature_date"])) | ||
| set_widget_value(next((w for w in widgets if w.field_name == "T44"), None), | ||
| f"{PATIENT_DATA['pharmacy_name']}, {PATIENT_DATA['pharmacy_phone']}") | ||
|
|
||
| # Update all modified widgets | ||
| for widget in widgets_to_update: | ||
| try: | ||
| widget.update() | ||
| except Exception as e: | ||
| print(f"Warning: Could not update widget {widget.field_name}: {e}") | ||
|
|
||
| # Save the filled PDF | ||
| doc.save(output_pdf_path, incremental=False, encryption=fitz.PDF_ENCRYPT_KEEP) | ||
|
|
||
|
|
||
| def fill_pdf_with_pypdf(input_pdf_path: str, output_pdf_path: str) -> None: | ||
| """Fill PDF using pypdf (limited - field mapping not yet implemented).""" | ||
| reader = PdfReader(input_pdf_path) | ||
| writer = PdfWriter() | ||
|
|
||
| # Copy pages | ||
| for page in reader.pages: | ||
| writer.add_page(page) | ||
|
|
||
| print("Warning: pypdf field mapping is not yet implemented. Output will be a copy of the original.") | ||
|
|
||
| # Save | ||
| with open(output_pdf_path, "wb") as output_file: | ||
| writer.write(output_file) | ||
|
Comment on lines
+279
to
+292
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pypdf fallback fills zero fields — it's effectively a no-op.
Option A: stub out with a clear warning def fill_pdf_with_pypdf(input_pdf_path: str, output_pdf_path: str) -> None:
- """Fill PDF using pypdf (limited - mainly for form fields)."""
- # pypdf is better for form fields, but this PDF may not have fillable fields
- # So we'll create a text summary instead
+ """Fill PDF using pypdf (limited - field mapping not yet implemented)."""
reader = PdfReader(input_pdf_path)
writer = PdfWriter()
# Copy pages
for page in reader.pages:
writer.add_page(page)
- # Try to fill form fields if they exist
- if reader.get_form_text_fields():
- # PDF has form fields - fill them
- writer.update_page_form_field_values(
- writer.pages[0],
- {
- # Map field names if they exist
- }
- )
+ print("Warning: pypdf field mapping is not yet implemented. Output will be a copy of the original.")
# Save
with open(output_pdf_path, "wb") as output_file:
writer.write(output_file)🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| def main(): | ||
| """Main function.""" | ||
| script_dir = Path(__file__).parent | ||
| project_root = script_dir.parent | ||
| input_pdf = project_root / "assets" / "pdf-templates" / "co-prescription-drug-prior-authorization-request-form.pdf" | ||
| output_pdf = project_root / "assets" / "pdf-templates" / "co-prescription-drug-prior-authorization-request-form-filled-jane-doe.pdf" | ||
|
|
||
| if not input_pdf.exists(): | ||
| print(f"Error: Input PDF not found at {input_pdf}") | ||
| sys.exit(1) | ||
|
|
||
| print(f"Filling PDF form for Jane Doe...") | ||
| print(f"Patient: {PATIENT_DATA['patient_name']}") | ||
| print(f"Drug: {PATIENT_DATA['drug_name']}") | ||
| print(f"Diagnosis: {PATIENT_DATA['diagnosis']}") | ||
| print() | ||
|
|
||
| try: | ||
| if USE_PYMUPDF: | ||
| print("Using PyMuPDF...") | ||
| fill_pdf_with_pymupdf(str(input_pdf), str(output_pdf)) | ||
| elif USE_PYPDF: | ||
| print("Using pypdf...") | ||
| fill_pdf_with_pypdf(str(input_pdf), str(output_pdf)) | ||
| except Exception as e: | ||
| print(f"\n✗ Failed to fill PDF form: {e}") | ||
| sys.exit(1) | ||
|
|
||
| print(f"\n✓ Successfully filled PDF form!") | ||
| print(f" Input: {input_pdf}") | ||
| print(f" Output: {output_pdf}") | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
Uh oh!
There was an error while loading. Please reload this page.