# AthenaIDX — Selenium Page Object Model helpers

This repository contains the `athenaidx` Python package: Selenium-based helpers, Page Object Model (POM) pages, small tooling, and a few predefined workflows for working with AthenaIDX's web UI.

## Install

1. Create and activate a virtual environment (Windows CMD):

```bat
python -m venv .venv
.venv\Scripts\activate
python -m pip install --upgrade pip
```

2. Install runtime/dev dependencies (recommended):

```bat
pip install -r requirements-dev.txt
# For editable install (so local edits are importable):
pip install -e .
```

Notes:
- This project uses Selenium. Ensure you have a compatible browser driver on PATH (or let your environment manage it).
- The `athenaidx.config.Config` helper will create a local Chrome driver by default via Selenium.

## Minimal example (programmatic)

```py
from athenaidx.config import Config

# Create a config which will create a Chrome driver by default
cfg = Config()
driver = cfg.get_driver()

# ... use page objects, workflows, etc.

# When done
driver.quit()
```

## Repository layout (high-level)

- `src/athenaidx/` — package root
    - `config.py` — small wrapper around Selenium driver lifecycle (create_driver / get_driver)
    - `model_objects/` — lightweight data objects (e.g., `rejections.py`)
        - `rejections.py` — `Rejections` and `RejectionGrouped` dataclasses and helpers
    - `pages/` — page objects for different screens and modals
        - `login_page.py` — login helper
        - `modals/` — modal helpers (payment codes, reset modal, ...)
        - `payment_posting/` — payment-posting related page objects (e.g., `pp_main.py`, `pp_lipp.py`, `pp_batch.py`)
    - `toolbars/` — toolbar helpers (HOG Screen, VTB, etc.)
    - `workflows/` — ready-to-run workflow orchestrations (subclasses of `BaseWorkflow`)

Files in `workflows/` are small orchestrators implementing steps (see `BaseWorkflow` in `src/athenaidx/workflows/base_workflow.py`).

## Rejection objects — `Rejections` and `RejectionGrouped`

The library provides a simple, typed representation for rejections in `src/athenaidx/model_objects/rejections.py`.

- `Rejections` (dataclass) — fields include:
    - `InvoiceNumber: int`
    - `Group: int` — the numeric group for the invoice
    - `RejCode1`, `RejCode2`, `RejCode3`, `RejCode4` — rejection codes
    - `Carrier`, `Paycode`, `LineItemPost` (bool), `Remark1..4`, `BatchNumber`, `Completed`, `Comments`

- `RejectionGrouped` — holds a `group_id` and `rejections: list[Rejections]`.

Helpers:
- `Rejections.from_dataframe(df)` — convert a pandas DataFrame to `list[Rejections]` (expects DataFrame columns matching the field names).
- `Rejections.from_dataframe_row(row)` — create a single `Rejections` from a DataFrame row.
- `RejectionGrouped.from_rejections(list_of_rejections)` — convert list of Rejections belonging to the same group into a single `RejectionGrouped`.
- `RejectionGrouped.group_by_group_num(list_of_rejections)` — group a list of Rejections by their `Group` field into `list[RejectionGrouped]`.

Example:

```py
from athenaidx.model_objects.rejections import Rejections, RejectionGrouped

# Create one by hand
r = Rejections(InvoiceNumber=12345, Group=5, RejCode1='R01', Carrier='ABC', LineItemPost=True)

# Group from a list
grouped = RejectionGrouped.from_rejections([r])

# Or group many
many = [r, Rejections(InvoiceNumber=12346, Group=5, RejCode1='R02')]
groups = RejectionGrouped.group_by_group_num(many)

# If you have a pandas DataFrame `df` with the expected columns:
# rejections = Rejections.from_dataframe(df)
# groups = RejectionGrouped.group_by_group_num(rejections)
```

Notes:
- The classes are thin dataclasses — they do not perform I/O. They are intended as a data contract passed into workflows.

## Using individual page/module steps

All page objects accept a `Config` instance (or at least expect a `config` object exposing `get_driver()` that returns a Selenium `webdriver`). Example usage pattern:

```py
from athenaidx.config import Config
from athenaidx.pages.payment_posting.pp_main import PICScreen_Main
from athenaidx.pages.payment_posting.pp_select_patient import PP_SelectPatient

cfg = Config()
pic = PICScreen_Main(cfg)
select = PP_SelectPatient(cfg)

# Example: select a patient by invoice number
ok = select.select_patient('12345')  # returns True or modal text

# Example: check which group is currently selected in the PIC header
current_group = pic.get_current_batch_group()
print('Current group:', current_group)

# When finished
cfg.get_driver().quit()
```

Common individual modules and what they expose (summary):
- `PICScreen_Main` (pp_main) — enter paycodes, set line-item-post checkbox (`enter_paycode`, `set_line_item_post_checkbox`, `open_paycode_modal`), and helpers like `get_current_batch_group()`.
- `PP_SelectPatient` — `select_patient(invoice_number)` and `reset_patient()`.
- `PaymentPostingBatch` — open/create batch (`open_batch()`), check batch state (`is_batch_open()`), and `batch_number` property.
- `PP_LIPP` and `PP_LIPP_Rejections` — helpers for line-item payment posting flows: `populate_row`, `finalize_posting`, `enter_carrier`, `post_li_rejections()`.
- `PostDropdown` — small helper to read/set the per-row post dropdown value.

Because the page objects use Selenium, they act directly against the active driver. Use `Config()` to construct the driver, or set your own driver with `cfg.set_driver(your_driver)`.

## Predefined workflows

Workflows are small orchestrators in `src/athenaidx/workflows/` built on top of `BaseWorkflow`.

Example: `PostLineItemRejections` (posts groups of rejections read from a file / list)

```py
from athenaidx.config import Config
from athenaidx.workflows.base_workflow import WorkflowContext
from athenaidx.workflows.post_line_item_rejections import PostLineItemRejections
from athenaidx.model_objects.rejections import RejectionGrouped

cfg = Config()

# ctx.data can be one RejectionGrouped, many RejectionGrouped, or a flat list[Rejections]
ctx = WorkflowContext(config=cfg, data=[/* your RejectionGrouped objects or list of Rejections */])
wf = PostLineItemRejections(ctx)

# Run normally
wf.run(dry_run=False)

# Or perform a dry run which will iterate the workflow steps but not execute them
wf.run(dry_run=True)

# At the end, clean up or inspect ctx.meta/logs for details
cfg.get_driver().quit()
```

Notes for `PostLineItemRejections`:
- The workflow expects `ctx.config` to be an instance of `athenaidx.config.Config`.
- The workflow will attempt to log in interactively if credentials are not present. You can provide credentials via environment variables `IDX_USERNAME` and `IDX_PASSWORD` (these are read by the workflow's login step).
- The workflow performs a small sequence of steps: setup, login_step, process_groups, finalize. See `src/athenaidx/workflows/post_line_item_rejections.py` for the exact logic.

## Tips and troubleshooting

- Use `requirements-dev.txt` to replicate the environment used in development and testing.
- For CI, run the browser in headless mode and ensure a compatible ChromeDriver is available.
- If a page object raises `TimeoutException`, increase implicit waits or investigate the page structure (some elements are lazy-loaded).

## Files changed / touched

- README updated to document installation, structure, Rejection objects, individual steps and workflows.

## Where to look next

- `src/athenaidx/model_objects/rejections.py` — canonical data shapes and grouping helpers
- `src/athenaidx/workflows/post_line_item_rejections.py` — a practical example of wiring model objects through the page objects and workflows
- `docs` directory for sample usages