Metadata-Version: 2.4
Name: psgc-core
Version: 0.0.1
Summary: Framework-agnostic PSGC code normalization and hierarchy rules.
Author-email: Mars Veloso <marsveloso@gmail.com>
Requires-Python: >=3.14
Description-Content-Type: text/markdown

# psgc-core

`psgc-core` is the small rules engine that sits between raw PSGC data and the
applications that need to use it correctly.

Its job is not to download PSGC data, parse Excel workbooks, store records, or
build UI. Its job is to answer the recurring domain questions every downstream
consumer asks once it already has a PSGC code:

- is this a region, province, city, municipality, sub-municipality, or barangay?
- what is this record's parent?
- what are its ancestors?
- does NCR, HUC placement, or Manila sub-municipality logic change the answer?

Without a shared package, those rules get reimplemented across apps and drift
over time. `psgc-core` exists so the tricky parts of PSGC behavior live in one
tested place.

## Why this package exists

Most systems that consume PSGC data do not just display a 10-digit code. They
need to derive hierarchy.

Typical downstream work looks like this:

- grouping schools, offices, or datasets by region or province/HUC
- building breadcrumbs and drilldown navigation
- validating incoming PSGC identifiers
- computing parent filters from a locality or barangay row
- handling PSGC edge cases consistently across reports and services

Those tasks sound simple until the exceptions matter:

- NCR localities do not sit under a province layer
- highly urbanized city placements can parent directly to the region
- Manila `SubMun` rows are children of Manila city
- some barangay parent derivation needs the municipality-collapse fallback

`psgc-core` isolates those rules as pure functions so every host application can
reuse the same behavior.

## Where it fits

This repository is intentionally narrow.

- [`../deped-psgc`](../deped-psgc/README.md) creates cleaned PSGC tabular data
  from the PSA workbook
- `psgc-core` interprets PSGC codes and geo values from that data
- downstream apps use both: the exported dataset plus the shared rule helpers

In practical terms, the flow is:

1. `deped-psgc` reads the official PSGC Excel workbook and writes a cleaned CSV.
2. Each CSV row carries fields such as `id`, `name`, and `geo`.
3. A downstream app reads those rows and calls `psgc-core` to derive hierarchy,
   labels, parent codes, and ancestor chains.

That separation matters because the workbook-export logic and the hierarchy rules
change for different reasons. Parsing the source file is one concern; applying
PSGC structure rules correctly everywhere is another.

## Example Use Case

Assume `../deped-psgc` has already exported a row like this:

```text
id=1380601001
name=Barangay 649
geo=Bgy
```

An application usually needs more than the raw row:

```python
from psgc_core import ancestor_codes, candidate_parent_code, kind_label

row = {
    "id": "1380601001",
    "geo": "Bgy",
}

parent = candidate_parent_code(row["id"], row["geo"])
ancestors = ancestor_codes(row["id"], row["geo"])
label = kind_label(row["geo"])

print(label)
print(parent)
print(ancestors)
```

Output:

```python
"Barangay"
"1380601000"
["1300000000", "1380600000", "1380601000"]
```

That is the value of the package:

- the app does not need to embed Manila-specific PSGC logic itself
- the same parent/ancestor behavior is reused everywhere
- tests live beside the rules instead of being copied into each consumer

## Package Scope

`psgc-core` owns only pure PSGC rules:

- code normalization and validation
- `geo` alias normalization into canonical types
- stable kind labels and hierarchy ranks
- suffix-based geographic classification
- parent-code and ancestor derivation
- explicit handling for NCR, HUC placement, Manila sub-municipalities, and the
  barangay municipality-collapse edge case

It does not own:

- workbook parsing
- CSV export
- persistence
- framework integration
- query building
- UI rendering

## Install

```bash
uv pip install -e .
```

## Public API

```python
from psgc_core import (
    GeoType,
    NormalizedKind,
    ancestor_codes,
    candidate_parent_code,
    infer_geo,
    is_huc_placement,
    is_manila_submun,
    is_ncr,
    kind_label,
    kind_rank,
    muni_code,
    normalize_geo,
    normalize_code,
    normalized_kind,
    provhuc_code,
    region_code,
    validate_code,
)
```

## Notes

- `infer_geo()` is suffix-based. It can distinguish `Reg`, `Prov`, locality-shape
  codes (`Mun`), and `Bgy`.
- `candidate_parent_code()` and `ancestor_codes()` use the provided geo type to
  preserve `City` and `SubMun` behavior where suffix-only inference is
  insufficient.
- `normalize_geo()` accepts the aliases used by current host apps:
  `reg`, `region`, `prov`, `province`, `city`, `mun`, `municipality`,
  `submun`, `submunicipal`, `submunicipality`, `bgy`, `brgy`, and `barangay`.
- `normalized_kind()`, `kind_label()`, and `kind_rank()` are the canonical
  package-level helpers for PSGC tree UIs and summaries.
