Metadata-Version: 2.4
Name: pyrx-nlt
Version: 1.1.0
Summary: Natural Language Template engine — plain-English templates for email, notifications, and dynamic content
Project-URL: Documentation, https://nlt.pyrx.tech
Author-email: PYRX <dev@pyrx.tech>
License-Expression: MIT
License-File: LICENSE
Keywords: email,natural-language,nlt,pyrx,template,template-engine
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# pyrx-nlt

**Natural Language Template (NLT) Engine** — Plain-English templates that non-technical teams can read, write, and maintain for email, notifications, and dynamic content.

## Installation

```bash
pip install pyrx-nlt
```

Requires Python 3.10+. Zero third-party dependencies.

## Quick Start

```python
from pyrx_nlt import NLTRenderer

renderer = NLTRenderer(
    contact={"first_name": "Alice", "properties": {"Plan": "premium", "Points": 1250}},
    trigger_event={"event_name": "purchase", "attributes": {"Item": "Widget", "Amount": "49.99"}},
)

result = renderer.render(
    subject_template='Order confirmation for {the user\'s First Name, or "Customer"}',
    body_template="""
        <h1>Thank you, {the user's First Name}!</h1>
        <p>You purchased: {the Item from the trigger event}</p>
        <p>Amount: {the Amount from the trigger event, as "currency"}</p>
        {if the user's Plan is premium}
            <p>You earned {the user's Points, as "currency"} in rewards!</p>
        {else}
            <p>Upgrade to Premium for rewards on every purchase.</p>
        {end if}
    """,
)

print(result.html)      # Rendered HTML
print(result.subject)   # "Order confirmation for Alice"
print(result.suppressed) # False
```

## Features

- **3 data sources**: `{the user's X}`, `{the X from the trigger event}`, `{the X from the Y event}`
- **Modifiers**: `or "fallback"`, `required`, `as "format"`, `uppercase/lowercase/titlecase`
- **Math**: `plus`, `minus`, `times`, `divided by`, `modulo`
- **Conditionals**: `{if}...{else if}...{else}...{end if}` with 20 comparison operators, AND/OR/NOT
- **Loops**: `{for item in source}...{end for}` with `loop.index`, `loop.first`, `loop.last`
- **Lists**: `{list source, as comma/ordered/unordered list}`
- **Tables**: `{table source with columns: A, B, C}`
- **Aggregates**: `{sum of}`, `{average of}`, `{count of}`
- **19 chainable filters**: `truncate`, `replace`, `url_encode`, `round`, `default`, `strip_html`, `trim`, `newline_to_br`, `split`, `join`, `first`, `last`, `length`, `sort`, `reverse`, `unique`, `where`, `map`, `slice`
- **Formatting**: 17 currency codes, date presets, custom date tokens, percentages
- **Variable assignment**: `{set x to expression}`
- **Ternary**: `{condition ? "true" : "false"}`
- **Comments**: `{# ignored #}`
- **Raw blocks**: `{raw}...{end raw}`
- **Macros**: `{define name(params)}...{end define}` / `{use name(args)}`
- **Template inheritance**: `{extends "base.html"}` / `{block name}...{end block}` / `{parent}`
- **Built-in variables**: `{today}`, `{now}`
- **XSS-safe**: All output HTML-escaped by default
- **Graceful degradation**: Unparseable expressions preserved as literal text

## Extension Points

```python
from pyrx_nlt import NLTRenderer, EventStore, TemplateLoader

class MyEventStore:
    """Implement to look up historical events for named event resolution."""
    async def get_latest(self, contact_id: str, event_name: str) -> dict | None:
        ...

class MyTemplateLoader:
    """Implement for template inheritance ({extends "base.html"})."""
    def load(self, template_name: str) -> str | None:
        ...

renderer = NLTRenderer(
    contact=contact_data,
    event_store=MyEventStore(),
    template_loader=MyTemplateLoader(),
)
```

## Jinja2 Migration

Built-in bidirectional converter for migrating from Jinja2/MoEngage templates:

```python
from pyrx_nlt.converter import convert_jinja2_to_nlt, convert_nlt_to_jinja2, detect_template_language

nlt_template, result = convert_jinja2_to_nlt(jinja2_template)
print(f"Converted {result.count} expressions")

language = detect_template_language(template)  # "nlt", "jinja2", "mixed", or "none"
```

## License

MIT
