Metadata-Version: 2.4
Name: cim10-claml
Version: 0.1.0
Summary: Parser for the French CIM-10 (ICD-10-FR) classification in ClaML format
Author-email: Arnaud Fabre <arnaud.fabre@luxcem.fr>
License-Expression: MIT
License-File: LICENSE
Keywords: atih,cim-10,cim10,claml,classification,health,icd-10,icd10
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Healthcare Industry
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# cim10-claml

Parser for the French CIM-10 (ICD-10-FR) classification distributed by the
[ATIH](https://www.atih.sante.fr/) in ClaML format. No dependencies outside
the standard library.

Beyond reading the raw XML, the parser applies the CIM-10-specific ClaML
semantics needed to get a usable code table:

- **Hierarchy** — chapters, blocks, categories and subcategories with parent
  links, materialized paths (`IX/I20-I25/I21/I21.0`) and depths.
- **Chapter letter ranges** — chapter `IX` gets `I00-I99`, so WHO-style
  cross-references (which cite a letter range like "I00-I99" rather than the
  chapter number `IX`) can be resolved to chapters.
- **Composed labels** — ClaML subcategory labels are contextual (D48.6 is
  just "Sein"); the parent label is prepended so every code is
  self-descriptive ("Tumeur…, Sein").
- **Modifier semantics** — `ModifiedBy` inheritance down the parent chain,
  `ExcludeModifier` subtraction, and materialization of the modified codes:
  dotted sub-codes create a new level (`E11` + `.0` → `E11.0`), non-dotted
  sub-codes extend it (`E11.0` + `0` → `E11.00`), chained when a leaf has
  both. Composed codes like `I21.38` come out as regular entries with
  `kind="modified"`.
- **Rubric rendering** — inclusion / exclusion / note rubrics parsed from
  the fragment structure into HTML blocks: shared prefixes become nested
  lists, item runs become brace-groups (the typographic `{` layout of the
  printed CIM-10), with dagger/aster references preserved. Each block
  carries its French title ("Comprend :", "À l'exclusion de :", …).

## Installation

```bash
pip install cim10-claml
```

## Usage

```python
from cim10_claml import parse_cim10

result = parse_cim10("cim10.xml")  # ClaML file from the ATIH

for cls in result.classes:
    print(cls.code, cls.kind, cls.label)
    # cls.parent_code, cls.path, cls.depth
    # cls.modifier_codes  — effective modifiers (inherited, minus exclusions)
    # cls.rubrics         — [{"kind": "exclusion", "title": "À l'exclusion de :", "html": ...}]

result.modifiers         # [ClamlModifier(code, description)]
result.modifier_classes  # [ClamlModifierClass(modifier, sub_code, label, usage, rubrics)]
```

Rendering helpers can also be used standalone on ClaML elements:

```python
import xml.etree.ElementTree as ET
from cim10_claml import render_label_plain, render_rubric_blocks

el = ET.fromstring('<Label>Choléra</Label>')
render_label_plain(el)  # "Choléra"
```

## Fragment rendering

In ClaML, inclusion/exclusion rubrics encode the printed layout of the
CIM-10 book as `<Fragment>` sequences repeated across sibling rubrics. The
fragment type sequence always follows the grammar `item* list* item*`:
`type="list"` fragments sharing a prefix form indented lists, while
`type="item"` fragments at the edges form the brace groups (`{`) of the
printed book. `render_rubric_blocks` reconstitutes both.

**Shared list prefix → nested list.** The prefix "amnésie :" is repeated in
every rubric; the renderer factors it out as a parent node (F04, reduced):

```xml
<Class code="F04">
  <Rubric kind="exclusion"><Label>
    <Fragment type="list">amnésie :</Fragment>
    <Fragment type="list">SAI<Reference class="in brackets">R41.3</Reference></Fragment>
  </Label></Rubric>
  <Rubric kind="exclusion"><Label>
    <Fragment type="list">amnésie :</Fragment>
    <Fragment type="list">antérograde<Reference class="in brackets">R41.1</Reference></Fragment>
  </Label></Rubric>
  <Rubric kind="exclusion"><Label>
    <Fragment type="list">amnésie :</Fragment>
    <Fragment type="list">dissociative<Reference class="in brackets">F44.0</Reference></Fragment>
  </Label></Rubric>
</Class>
```

```python
render_rubric_blocks(ET.fromstring(xml))
```

returns one block (`kind="exclusion"`, `title="À l'exclusion de :"`) whose
`html` is (whitespace added for readability):

```html
<ul class="cim10-frag-list">
  <li>amnésie :
    <ul class="cim10-frag-list">
      <li>SAI (<a class="cim10-ref cim10-bracketed" data-link="class:R41.3">R41.3</a>)</li>
      <li>antérograde (<a class="cim10-ref cim10-bracketed" data-link="class:R41.1">R41.1</a>)</li>
      <li>dissociative (<a class="cim10-ref cim10-bracketed" data-link="class:F44.0">F44.0</a>)</li>
    </ul>
  </li>
</ul>
```

**Shared item suffix → brace group.** Here every rubric repeats the trailing
item "à Salmonella"; the renderer emits one column per fragment position,
with distinct values deduplicated — the on-screen equivalent of the printed
brace. Fragment-level `usage="dagger"` propagates to the `<li>` and appends
the `†` glyph; aster references get `*` (A02.2, reduced):

```xml
<Class code="A02.2">
  <Rubric kind="inclusion"><Label>
    <Fragment type="item" usage="dagger">Arthrite<Reference class="in brackets" usage="aster">M01.3</Reference></Fragment>
    <Fragment type="item">à Salmonella</Fragment>
  </Label></Rubric>
  <Rubric kind="inclusion"><Label>
    <Fragment type="item" usage="dagger">Méningite<Reference class="in brackets" usage="aster">G01</Reference></Fragment>
    <Fragment type="item">à Salmonella</Fragment>
  </Label></Rubric>
  <Rubric kind="inclusion"><Label>
    <Fragment type="item" usage="dagger">Ostéomyélite<Reference class="in brackets" usage="aster">M90.2</Reference></Fragment>
    <Fragment type="item">à Salmonella</Fragment>
  </Label></Rubric>
</Class>
```

returns one block (`kind="inclusion"`, `title="Comprend :"`):

```html
<div class="cim10-brace-group" data-cols="2" style="--cols:2">
  <ul class="cim10-brace-col">
    <li class="cim10-dagger">Arthrite† (<a class="cim10-ref cim10-bracketed cim10-aster" data-link="class:M01.3">M01.3*</a>)</li>
    <li class="cim10-dagger">Méningite† (<a class="cim10-ref cim10-bracketed cim10-aster" data-link="class:G01">G01*</a>)</li>
    <li class="cim10-dagger">Ostéomyélite† (<a class="cim10-ref cim10-bracketed cim10-aster" data-link="class:M90.2">M90.2*</a>)</li>
  </ul>
  <ul class="cim10-brace-col">
    <li>à Salmonella</li>
  </ul>
</div>
```

The rubric HTML is intentionally unstyled structure (`brace-group`,
`brace-col`, `frag-list`, `ref` CSS classes; `data-link="class:J45"` /
`data-link="modifier:S04E10_4"` attributes on references). Styling — CSS
grid columns from `--cols`, drawing the brace, list bullets — and link
resolution are up to the consumer.

Every emitted CSS class is namespaced to avoid clashing with the consumer's
stylesheet. The prefix defaults to `cim10-` (so `ref` → `cim10-ref`,
`brace-group` → `cim10-brace-group`, and classes carried through from the
source XML like `<Para class="headingIntro">` → `cim10-headingIntro`) and is
configurable via `css_prefix` on both `parse_cim10` and `render_rubric_blocks`:

```python
parse_cim10("cim10.xml", css_prefix="icd-")   # icd-ref, icd-brace-group, …
render_rubric_blocks(el, css_prefix="")        # bare: ref, brace-group, …
```

For the full format survey (rubric kinds, all 13 fragment patterns found in
the 2025 corpus, edge cases), see [docs/claml-format.md](docs/claml-format.md)
and [docs/fragment-cases.md](docs/fragment-cases.md).

## Data

This package contains **no CIM-10 data** — only a parser. The classification
itself is published by the ATIH (and derives from the WHO ICD-10, licensed
CC BY-NC-ND 3.0 IGO); obtain the ClaML XML from the ATIH and check its
license terms for your use case.

## License

MIT (the code only — see the data note above).
