Metadata-Version: 2.4
Name: metrix-oked-sdk
Version: 0.2.0
Summary: Offline typed reference for the Kazakhstan OKED classifier (NK RK 03-2019)
Project-URL: Repository, https://github.com/astaldo42/metrix-oked-sdk
Project-URL: Issue Tracker, https://github.com/astaldo42/metrix-oked-sdk/issues
Author-email: Metrix <dev@metrix.com.ai>
License: MIT
License-File: LICENSE
Keywords: classifier,kazakhstan,metrix,oked,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.12
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# metrix-oked

Typed offline reference for the Kazakhstan OKED classifier — НК РК 03-2019 (amendments № 1–3).

The full classifier (2097 entries) is bundled as package data: no external APIs, no network
calls at runtime. Data is loaded lazily once per process, loading is thread-safe, all objects
are immutable.

## Installation

```bash
pip install metrix-oked-sdk
```

## Quick start

```python
from metrix_oked import Oked

oked = Oked()

entry = oked.get("01.19.9")  # dots are normalized: same as oked.get("01199")
print(entry.name)   # Выращивание прочих одно- или двухлетних культур, ...
print(entry.label)  # 01199 - Выращивание прочих одно- или двухлетних культур, ...

# Autocomplete
for entry in oked.search("разработка", limit=5):
    print(entry.label)

# Code prefix search
oked.search("01")  # subclasses 01111, 01112, ... in code order
```

## Code levels

| Level | `Level` member | Code format | Example | Count |
|---|---|---|---|---|
| Секция | `Level.SECTION` | letter | `A` | 21 |
| Раздел | `Level.DIVISION` | 2 digits | `01` | 88 |
| Группа | `Level.GROUP` | 3 digits | `011` | 272 |
| Класс | `Level.CLASS` | 4 digits | `0111` | 614 |
| Подкласс | `Level.SUBCLASS` | 5 digits | `01111` | 1102 |

## API

### `oked.get(code) -> OkedEntry`

Exact lookup by a code of any level. Dots and case are normalized
(`"01.19.9"` → `"01199"`, `"a"` → `"A"`). Raises `OkedNotFoundError` for unknown codes.

```python
oked.get("A")        # OkedEntry(code="A", level=Level.SECTION, ...)
oked.get("01.19.9")  # OkedEntry(code="01199", level=Level.SUBCLASS, ...)
```

### `oked.get_or_none(code) -> OkedEntry | None`

Same as `get`, but returns `None` instead of raising.

### `oked.get_name(code) -> str` / `oked.get_label(code) -> str`

Shortcuts for the entry name / the `"code - name"` string.

```python
oked.get_name("01111")   # "Выращивание зерновых и зернобобовых культур, ..."
oked.get_label("01111")  # "01111 - Выращивание зерновых и зернобобовых культур, ..."
```

### `oked.search(query, *, limit=20, levels=(Level.SUBCLASS,)) -> list[OkedEntry]`

Autocomplete search:

- a query of digits/dots is a **code prefix** search (`"01"`, `"01.19"`), results in code order;
- anything else is a **case-insensitive substring** search over names (cyrillic-aware);
  matches at a word boundary rank first, ties are ordered by code.

```python
oked.search("разработка")                          # 08121, 41100, ...
oked.search("01", limit=5)                         # first five 01xxx subclasses
oked.search("сельское", levels=(Level.SECTION,))   # search sections only
```

### `oked.parents(code) -> list[OkedEntry]`

Ancestors chain, from the immediate parent up to the section.

```python
[e.code for e in oked.parents("01111")]  # ["0111", "011", "01", "A"]
```

### `oked.children(code) -> list[OkedEntry]`

Direct descendants, in code order.

```python
[e.code for e in oked.children("011")]  # ["0111", "0112", ...]
```

### `oked.is_valid(code) -> bool`

```python
oked.is_valid("01.19.9")  # True
oked.is_valid("99999")    # False
```

### `oked.validate(code, *, level=Level.SUBCLASS) -> str | None`

Canonical code if `code` exists **and** is at `level`, else `None`. For field validation where a
code must resolve to a specific granularity, not just any known code.

```python
oked.validate("01.19.9")               # "01199"
oked.validate("A")                     # None — a section, not a subclass
oked.validate("A", level=Level.SECTION)  # "A"
```

## Error handling

```python
from metrix_oked import OkedError, OkedNotFoundError

try:
    oked.get("99999")
except OkedNotFoundError as e:
    print(e.code)  # "99999"
```

## Requirements

- Python 3.12+
- No runtime dependencies
