Metadata-Version: 2.4
Name: play-parser
Version: 1.2.0
Summary: Parse dramatic play text into ordered dramatic events.
Author-email: Stergios Poularakis <stpoular@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/stpoular/play-parser
Project-URL: Documentation, https://github.com/stpoular/play-parser/tree/main/docs
Project-URL: Repository, https://github.com/stpoular/play-parser
Project-URL: Issues, https://github.com/stpoular/play-parser/issues
Project-URL: Changelog, https://github.com/stpoular/play-parser/blob/main/CHANGELOG.md
Keywords: theatre,drama,plays,parser,json
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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: Operating System :: OS Independent
Classifier: Topic :: Text Processing
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: build>=1; extra == "dev"
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: jsonschema>=4; extra == "dev"
Requires-Dist: ruff>=0.8; extra == "dev"
Requires-Dist: twine>=5; extra == "dev"
Dynamic: license-file

# play-parser

`play-parser` parses English theatrical play text into a canonical JSON document and assembles canonical documents back into normalised play text.

Play Parser currently supports English structural headings only, such as `ACT I` and `SCENE II`.

Canonical text uses a stable output format. For example, speech labels are emitted in colon form such as `Hamlet: ...`, even when the source text used another supported layout.

## Features

- Parse raw `.txt` play files into structured JSON.
- Assemble canonical JSON documents into normalised `.txt` output.
- Read and validate existing canonical `.json` documents.
- Keep ingestion, parsing, and domain access separate.
- Use the production rule-based parser by default, with an experimental weighted FSM/Viterbi parser available for comparison and research.
- Preserve speeches, stage directions, acts, scenes, metadata, characters, and document statistics.
- Use the package from Python or through the `play-parser` command line interface.

## Supported inputs

- Raw `.txt` play files.
- Canonical `.json` documents produced by this package.

The package does not parse PDFs, DOCX files, HTML pages, scans, images, or audio directly. Convert those sources to text first.

## Installation

```bash
pip install play-parser
```

## Python quick start

Recommended explicit pipeline:

```python
from play_parser import PlayIngestor, PlayParser

ingestor = PlayIngestor("Hamlet.txt")
parser = PlayParser.create()
play = parser.parse(ingestor.data, profile="colon_inline", source_name=ingestor.source_name)

print(play.title)
print(play.author)
print(len(play.acts))
print(len(play.scenes))
print(len(play.characters))
print(len(play.speeches))

play.save_json("Hamlet.json")
play.save_text("Hamlet.canonical.txt")
```

Equivalent direct parser usage:

```python
from play_parser import PlayParser

text = "ACT I\n\nSCENE I.\n\nHAMLET: Who's there?"
play = PlayParser.create("rule").parse(text, profile="colon_inline", source_name="Hamlet.txt")
```

Convenience domain usage:

```python
from play_parser import Play

text = "ACT I\n\nSCENE I.\n\nHAMLET: Who's there?"
play = Play(text, profile="colon_inline", source_name="Hamlet.txt")
```

Experimental weighted FSM parser:

```python
from play_parser import FSMPlayParser

play = FSMPlayParser().parse(text, profile="colon_inline", source_name="Hamlet.txt")

# The default beam is greedy; use a wider beam for delayed decisions.
play = FSMPlayParser(beam_width=2).parse(text, profile="colon_inline")
```

The rule-based parser is the default and recommended parser. The FSM parser is experimental: it is a real separate parser entry point, not a wrapper around the rule parser, and uses a play-specific weighted finite-state model backed by a standalone Viterbi/beam decoder. Both parser implementations are tested against the same bundled 234-play English-focused regression corpus.

Assemble a canonical document:

```python
from play_parser import assemble_play_text

canonical_text = assemble_play_text(play.as_dict())
```

## Command line usage

Show help and version information:

```bash
play-parser --help
play-parser --version
```

Parse one file with the production parser:

```bash
play-parser parse Hamlet.txt \
  --profile colon_inline \
  --json-output Hamlet.json \
  --text-output Hamlet.canonical.txt
```

Run the experimental weighted FSM parser:

```bash
play-parser parse Hamlet.txt --method fsm --beam-width 2 --json-output Hamlet.fsm.json
```

Parse a folder recursively:

```bash
play-parser parse \
  --input-root data/<play name> \
  --recursive \
  --profile colon_inline \
  --json-output-root data/<play name>/optimal.json/generated
```

Assemble canonical JSON files into text:

```bash
play-parser assemble \
  --input-root data/<play name>/optimal.json/generated \
  --recursive \
  --output-root data/<play name>/canonical
```

## Regression corpus

The repository includes 234 sample plays with checked `optimal.json` snapshots. The corpus covers colon-inline dialogue, dot-inline dialogue, dot-block dialogue, bare speaker blocks, screenplay sluglines, radio/SFX cues, cast-list preambles, English act/scene numbering with digits, words and Roman numerals, lowercase speaker labels, false-positive `ACT`/`SCENE` dialogue, page-header artefacts, no-dialogue physical scenes, mixed parenthetical cues, fixed-width and tabular dialogue, dash-separated dialogue, screenplay character blocks, line-numbered dialogue, verse continuations, PDF-style wrapping artefacts, OCR noise, cast-list ambiguity, subtitle/WebVTT/SRT fragments, markdown/web extraction artefacts, and classical/verse conventions. Both the default rule-based parser and the experimental FSM parser are tested against the same English-focused corpus.

## Public API

Stable top-level imports:

```python
from play_parser import (
    Play,
    PlayIngestor,
    PlayParser,
    SimplePlayParser,
    RuleBasedPlayParser,
    FSMPlayParser,
    assemble_play_text,
    get_format_profile,
    list_format_profiles,
    load_format_profile_config,
    load_format_profile_file,
    validate_play_document,
)
```

Domain classes such as `Act`, `Scene`, `Speech`, `Character`, `Monologue`, and `Dialogue` are also available from the top-level package.

## Format profiles

Built-in profiles are available through `list_format_profiles()` and can be passed to a parser or the CLI by name.

```python
from play_parser import list_format_profiles

print(list_format_profiles())
```

See [`docs/FORMAT_PROFILES.md`](docs/FORMAT_PROFILES.md) for the profile schema and examples.

## Documentation

- [`docs/API.md`](docs/API.md): Python API and CLI profile usage.
- [`docs/JSON_SCHEMA.md`](docs/JSON_SCHEMA.md): canonical JSON document format.
- [`docs/FORMAT_PROFILES.md`](docs/FORMAT_PROFILES.md): built-in and custom format profiles.

## Development

Install development dependencies:

```bash
python -m pip install -e .[dev]
```

Run local checks:

```bash
python -m ruff check .
python -m ruff format --check .
python -m pytest
python -m build
python -m twine check dist/*
```

Release steps are documented in [`RELEASE.md`](RELEASE.md).

## Licence

MIT
