Metadata-Version: 2.5
Name: omop-constructs
Version: 0.11.0
Summary: For building complex constructs on top of the omop-alchemy library.
Author-email: Georgie Kennedy <georgina.kennedy@unsw.edu.au>
Requires-Python: >=3.12
Requires-Dist: oa-configurator<3,>=2.0
Requires-Dist: omop-alchemy[postgres]<3,>=2.0
Requires-Dist: omop-semantics>=0.6.2
Requires-Dist: orm-loader<3,>=2.0
Requires-Dist: sqlalchemy>=2.0.45
Provides-Extra: dev
Requires-Dist: ipython>=9.8.0; extra == 'dev'
Requires-Dist: mkdocs-macros-plugin; extra == 'dev'
Requires-Dist: mkdocs-material; extra == 'dev'
Requires-Dist: mkdocs-mermaid2-plugin; extra == 'dev'
Requires-Dist: mkdocs-table-reader-plugin; extra == 'dev'
Requires-Dist: mkdocs<2.0; extra == 'dev'
Requires-Dist: mkdocstrings[python]; extra == 'dev'
Requires-Dist: pygments>=2.20.0; extra == 'dev'
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
Requires-Dist: pytest>=9.0.3; extra == 'dev'
Requires-Dist: ruff<0.16,>=0.15; extra == 'dev'
Requires-Dist: tornado>=6.5.5; extra == 'dev'
Requires-Dist: ty==0.0.61; extra == 'dev'
Description-Content-Type: text/markdown

# omop-constructs

**omop-constructs** is a small library for building **reusable, composable analytical constructs on top of OMOP CDM** using SQLAlchemy.

It sits “above” low-level OMOP table models (from **omop-alchemy**) and semantic definitions (from **omop-semantics**), and provides a way to package up **common query patterns, mappings, and derived views** into reusable units.

In practice, this means you can define things like:

- tumour staging logic (T/N/M, group stage),
- clinical modifiers (grade, laterality, size),
- condition + modifier joins,
- episode-linked phenotypes,
- reusable materialized views or query fragments,

and then reuse them consistently across:

- analytics code,
- cohort definitions,
- ETL / feature engineering,
- dashboards and reports.

---

## What problem does this solve?

When working with OMOP in real research settings, you often end up re-implementing the same patterns:

- joining measurements or observations back to conditions,  
- resolving multiple staging systems (clinical vs pathological),  
- preferring “best available” records (earliest, latest, ranked),  
- materialising complex derived tables for performance,  
- wiring together multiple OMOP tables into analysis-ready shapes.

These patterns are:

- **non-trivial SQL**,  
- **project-specific but reusable**,  
- and easy to drift or fork across notebooks, pipelines, and services.

**omop-constructs gives you a place to define these patterns once**, as explicit, testable Python objects built on SQLAlchemy, and then reuse them anywhere you use OMOP.

Think of it as a library of **semantic query building blocks** for OMOP.

---

## Relationship to other libraries

- **omop-alchemy**  
  Provides the canonical, typed SQLAlchemy models for OMOP CDM tables.

- **omop-semantics**  
  Defines which concepts, groups, and roles mean what in your domain (e.g. staging, modifiers).

- **omop-constructs**  
  Uses both of the above to build **higher-level analytical constructs**, such as:

  - derived views,
  - reusable joins,
  - staged phenotype tables,
  - canonical query fragments.

In short:

> omop-alchemy defines *the schema*  
> omop-semantics defines *the meaning*  
> omop-constructs defines *the reusable analytical shapes*

---

## Core ideas

- **Composable query building blocks**  
  Encapsulate common SQL patterns as reusable Python objects.

- **SQLAlchemy-first**  
  Constructs are normal SQLAlchemy `select()` expressions, subqueries, and ORM-mapped views.

- **Reusable analytical units**  
  Package complex mappings (e.g. staging logic) once and reuse them across contexts.

- **Materialized view support**  
  Support for defining derived tables and materialized views for performance and stability.

- **Semantics-aware**  
  Integrates with omop-semantics concept registries and lookups, rather than hard-coding concept IDs.

---

## Example use case (high level)

A typical construct might:

- take OMOP `Measurement` rows representing staging concepts,  
- classify them into T, N, M, and group stage using semantic lookups,  
- rank multiple records per condition to select the “best” stage,  
- expose the result as a materialized view that can be joined back to <code>Condition_Occurrence</code>.

This allows downstream code to work with a clean, analysis-ready table like:

> “conditions with resolved TNM stage and modifiers”

without re-implementing the logic every time.


---

## Typical workflow

1. **Define semantic lookups**  
   Use <code>omop-semantics</code> to define which concepts represent staging, grading, laterality, etc.

2. **Build constructs**  
   Use SQLAlchemy and <code>omop-alchemy</code> models to define reusable queries and derived views.

3. **Materialize or compose**  
   Optionally materialize complex constructs into views or tables for performance.

4. **Reuse everywhere**  
   Import the same construct into analytics notebooks, ETL jobs, or services.

---

## Configuration and CLI

`omop-constructs` uses `oa-configurator` for runtime logging and CDM resource
resolution.

```bash
omop-config init
omop-config configure omop_alchemy
omop-config configure omop_constructs
```

`omop-config configure omop_constructs` validates that a shared `cdm_db`
resource is available and can record a package-specific `default_resource`
override when `omop-constructs` should use a different CDM resource than
`omop-alchemy`.

## Hosting Requirements

Clinical and vocabulary tables must be reachable through the same database connection. They may use separate schemas on that connection.

The package CLI exposes operational helpers such as registry schema export:

```bash
omop-constructs schema-snapshot tests/artifacts/construct_registry_schema.csv
```

---

## When should you use this?

Use **omop-constructs** if you:

- repeatedly write and share complex OMOP joins and mappings,  
- need consistent, reusable phenotype or feature definitions,  
- want complex logic to live in one place that is versionable and extensible,  
- are building analytics pipelines or research platforms on OMOP,  
- care about making your analytical layer explicit and testable.


---

## Design goals

- Declarative, explicit constructs  
- SQLAlchemy-native  
- No hidden execution or side effects  
- Easy to test in isolation  
- Compatible with materialized views and derived tables  
- SQLAlchemy query definitions are portable across backends
- Materialized view lifecycle management targets PostgreSQL
