belgica 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- belgica-0.5.0/.gitignore +22 -0
- belgica-0.5.0/LICENSE +24 -0
- belgica-0.5.0/LICENSE-DATA +28 -0
- belgica-0.5.0/PKG-INFO +87 -0
- belgica-0.5.0/README.md +63 -0
- belgica-0.5.0/hatch_build.py +30 -0
- belgica-0.5.0/pyproject.toml +84 -0
- belgica-0.5.0/src/belgica/__init__.py +145 -0
- belgica-0.5.0/src/belgica/_arrondissement_boundaries.py +69 -0
- belgica-0.5.0/src/belgica/_canton_boundaries.py +66 -0
- belgica-0.5.0/src/belgica/_competence.py +80 -0
- belgica-0.5.0/src/belgica/_contacts.py +45 -0
- belgica-0.5.0/src/belgica/_court_locations.py +33 -0
- belgica-0.5.0/src/belgica/_courts.py +166 -0
- belgica-0.5.0/src/belgica/_data.py +13 -0
- belgica-0.5.0/src/belgica/_geometry.py +73 -0
- belgica-0.5.0/src/belgica/_interest_rates.py +51 -0
- belgica-0.5.0/src/belgica/_places.py +51 -0
- belgica-0.5.0/src/belgica/_postal_codes.py +72 -0
- belgica-0.5.0/src/belgica/_territory.py +132 -0
- belgica-0.5.0/src/belgica/_time.py +100 -0
- belgica-0.5.0/src/belgica/data/arrondissement-boundaries.json +316 -0
- belgica-0.5.0/src/belgica/data/arrondissements.json +110 -0
- belgica-0.5.0/src/belgica/data/canton-boundaries.json +3859 -0
- belgica-0.5.0/src/belgica/data/cantons.json +5511 -0
- belgica-0.5.0/src/belgica/data/court-locations.json +2643 -0
- belgica-0.5.0/src/belgica/data/courts.json +21134 -0
- belgica-0.5.0/src/belgica/data/houses-of-justice.json +1043 -0
- belgica-0.5.0/src/belgica/data/interest-rates.json +449 -0
- belgica-0.5.0/src/belgica/data/judicial-areas.json +42 -0
- belgica-0.5.0/src/belgica/data/municipalities.json +5087 -0
- belgica-0.5.0/src/belgica/data/postal-codes.json +5762 -0
- belgica-0.5.0/src/belgica/data/prisons.json +1822 -0
- belgica-0.5.0/src/belgica/data/provinces.json +152 -0
- belgica-0.5.0/src/belgica/data/regions.json +44 -0
- belgica-0.5.0/src/belgica/py.typed +0 -0
- belgica-0.5.0/src/belgica/types.py +306 -0
- belgica-0.5.0/tests/conftest.py +10 -0
- belgica-0.5.0/tests/test_api.py +104 -0
- belgica-0.5.0/tests/test_parity.py +149 -0
- belgica-0.5.0/tests/test_types.py +82 -0
- belgica-0.5.0/uv.lock +417 -0
belgica-0.5.0/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
.DS_Store
|
|
3
|
+
docs/.vitepress/dist/
|
|
4
|
+
docs/.vitepress/cache/
|
|
5
|
+
dist/
|
|
6
|
+
coverage/
|
|
7
|
+
.sources/
|
|
8
|
+
csv/
|
|
9
|
+
# Generated from data/courts.json and data/cantons.json by pnpm build:data-files; published, not committed
|
|
10
|
+
data/courts/
|
|
11
|
+
data/cantons/
|
|
12
|
+
# Python package: the data and licences are copied in at build time
|
|
13
|
+
python/src/belgica/data/
|
|
14
|
+
python/LICENSE
|
|
15
|
+
python/LICENSE-DATA
|
|
16
|
+
python/tests/.cases/
|
|
17
|
+
python/.venv/
|
|
18
|
+
python/dist/
|
|
19
|
+
__pycache__/
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.ruff_cache/
|
belgica-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pieter-Jan Scheir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
This licence covers the code (src/, scripts/, test/ and the built files in
|
|
24
|
+
dist/). The data in data/ is licensed under CC BY 4.0; see LICENSE-DATA.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
The data in data/ is licensed under the Creative Commons Attribution 4.0
|
|
2
|
+
International licence (CC BY 4.0):
|
|
3
|
+
https://creativecommons.org/licenses/by/4.0/legalcode
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026 Pieter-Jan Scheir
|
|
6
|
+
|
|
7
|
+
You may share and adapt the data for any purpose, including commercially, as
|
|
8
|
+
long as you give appropriate credit, link to the licence, and indicate if
|
|
9
|
+
changes were made. Credit "belgica (https://github.com/pieterjanscheir/belgica)"
|
|
10
|
+
and pass on the source attributions below.
|
|
11
|
+
|
|
12
|
+
Sources:
|
|
13
|
+
|
|
14
|
+
- Municipalities (data/municipalities.json) and the REFNIS codes in
|
|
15
|
+
data/cantons.json: © Statbel (Directorate-general Statistics - Statistics
|
|
16
|
+
Belgium), CC BY 4.0, https://statbel.fgov.be/
|
|
17
|
+
- Postal codes (data/postal-codes.json) and the municipality of each address
|
|
18
|
+
(address.municipality in data/courts.json, data/prisons.json and
|
|
19
|
+
data/houses-of-justice.json): Statbel's conversion table from postal codes
|
|
20
|
+
to REFNIS codes, © Statbel, CC BY 4.0, https://statbel.fgov.be/en/cc-40
|
|
21
|
+
- Cantons, arrondissements and judicial areas: the annex to the Belgian
|
|
22
|
+
Judicial Code, as consolidated on Justel (https://www.ejustice.just.fgov.be/).
|
|
23
|
+
Official acts are not protected by copyright in Belgium (art. XI.172 §2 WER).
|
|
24
|
+
- Interest rates: FOD Financiën and the Belgisch Staatsblad.
|
|
25
|
+
- Court addresses and contacts: the courts' own websites and just.fgov.be.
|
|
26
|
+
|
|
27
|
+
The data is provided as is, without warranty. It is reference data, not legal
|
|
28
|
+
advice: check the official sources before relying on it.
|
belgica-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: belgica
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Belgian legal reference data: courts, judicial cantons and arrondissements, municipalities and postal codes, and legal and commercial interest rates.
|
|
5
|
+
Project-URL: Homepage, https://pieterjanscheir.github.io/belgica/
|
|
6
|
+
Project-URL: Documentation, https://pieterjanscheir.github.io/belgica/guide/python
|
|
7
|
+
Project-URL: Source, https://github.com/pieterjanscheir/belgica
|
|
8
|
+
Project-URL: Issues, https://github.com/pieterjanscheir/belgica/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/pieterjanscheir/belgica/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Pieter-Jan Scheir
|
|
11
|
+
License-Expression: MIT AND CC-BY-4.0
|
|
12
|
+
Keywords: arrondissements,belgian,belgium,courts,geojson,interest-rates,judicial-cantons,justice-of-the-peace,legal,municipalities,postal-codes,refnis,vredegerecht
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Legal Industry
|
|
16
|
+
Classifier: Natural Language :: Dutch
|
|
17
|
+
Classifier: Natural Language :: French
|
|
18
|
+
Classifier: Natural Language :: German
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# belgica
|
|
26
|
+
|
|
27
|
+
Belgian legal reference data for Python: every court with its registries' contacts, prisons,
|
|
28
|
+
houses of justice, judicial cantons and arrondissements, municipalities and their postal codes,
|
|
29
|
+
and the legal and commercial interest rates. Every record says where it comes from, and the
|
|
30
|
+
scraped data is rebuilt from the official sources every week.
|
|
31
|
+
|
|
32
|
+
The same data and helpers as the [`belgica` npm package](https://www.npmjs.com/package/belgica),
|
|
33
|
+
with the same version number. The tests check that both give the same answers.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install belgica
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Python 3.11 or later, no dependencies. Documentation:
|
|
40
|
+
**https://pieterjanscheir.github.io/belgica/guide/python**
|
|
41
|
+
|
|
42
|
+
It's reference data, not legal advice: check the official sources before relying on it.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from belgica import (
|
|
46
|
+
find_arrondissements_at,
|
|
47
|
+
find_cantons_at,
|
|
48
|
+
find_competent_courts,
|
|
49
|
+
find_courts,
|
|
50
|
+
find_courts_for_postal_code,
|
|
51
|
+
get_cantons_for_municipality,
|
|
52
|
+
get_contact,
|
|
53
|
+
get_court,
|
|
54
|
+
get_court_location,
|
|
55
|
+
get_interest_rate,
|
|
56
|
+
get_justice_of_the_peace,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
get_court('court-of-appeal-brussel')['name'] # {'nl': 'Hof van Beroep Brussel', 'fr': "Cour d'appel de Bruxelles"}
|
|
60
|
+
get_contact(get_court('court-of-appeal-liege'), 'email', 'Greffe pénal') # 'liege.ca.greffepenal@just.fgov.be'
|
|
61
|
+
find_courts(type='enterprise-court', arrondissement='west-vlaanderen')
|
|
62
|
+
|
|
63
|
+
[canton] = get_cantons_for_municipality('44088') # merelbeke-melle
|
|
64
|
+
get_justice_of_the_peace(canton['id'])['address'] # where to file
|
|
65
|
+
get_cantons_for_municipality('44021', on='2025-09-15') # Gent 1–5 on that day: a split city
|
|
66
|
+
find_cantons_at(3.7268, 51.053) # [gent-1]: the canton of a point
|
|
67
|
+
find_arrondissements_at(3.7268, 51.053) # [oost-vlaanderen]: and its arrondissement
|
|
68
|
+
get_court_location('justice-of-the-peace-gent-1')['geometry'] # the court on the map, a GeoJSON Point
|
|
69
|
+
find_competent_courts('23088', 'police-court') # [police-court-vilvoorde]: which court, by REFNIS code
|
|
70
|
+
find_courts_for_postal_code('1040', 'police-court') # per municipality: Brussel and Etterbeek share the code
|
|
71
|
+
|
|
72
|
+
get_interest_rate('2024-06-15', 'legal') # InterestRateResult(rate=5.75, period_from='2024-01-01', period_to='2024-12-31')
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The records are the dicts from the JSON files, with their keys as in the JSON (`validFrom`,
|
|
76
|
+
`postalCode`), typed with the `TypedDict`s in `belgica.types`. Each data file is read on first
|
|
77
|
+
use; the canton boundaries (22 MB) only by `find_cantons_at` and `get_canton_boundary`, the
|
|
78
|
+
arrondissement boundaries only by `find_arrondissements_at` and `get_arrondissement_boundary`.
|
|
79
|
+
|
|
80
|
+
Lookups take `on`, a day as a `datetime.date` or `'YYYY-MM-DD'`, and answer for today without it.
|
|
81
|
+
A day before a dataset's coverage raises a `ValueError` rather than answer with today's data.
|
|
82
|
+
|
|
83
|
+
## Licence
|
|
84
|
+
|
|
85
|
+
Code MIT. Data CC BY 4.0: municipality, province, region and postal-code data © Statbel, canton and
|
|
86
|
+
arrondissement boundaries and court locations © National Geographic Institute (NGI). See the
|
|
87
|
+
[repository](https://github.com/pieterjanscheir/belgica) for the sources of every dataset.
|
belgica-0.5.0/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# belgica
|
|
2
|
+
|
|
3
|
+
Belgian legal reference data for Python: every court with its registries' contacts, prisons,
|
|
4
|
+
houses of justice, judicial cantons and arrondissements, municipalities and their postal codes,
|
|
5
|
+
and the legal and commercial interest rates. Every record says where it comes from, and the
|
|
6
|
+
scraped data is rebuilt from the official sources every week.
|
|
7
|
+
|
|
8
|
+
The same data and helpers as the [`belgica` npm package](https://www.npmjs.com/package/belgica),
|
|
9
|
+
with the same version number. The tests check that both give the same answers.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install belgica
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Python 3.11 or later, no dependencies. Documentation:
|
|
16
|
+
**https://pieterjanscheir.github.io/belgica/guide/python**
|
|
17
|
+
|
|
18
|
+
It's reference data, not legal advice: check the official sources before relying on it.
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from belgica import (
|
|
22
|
+
find_arrondissements_at,
|
|
23
|
+
find_cantons_at,
|
|
24
|
+
find_competent_courts,
|
|
25
|
+
find_courts,
|
|
26
|
+
find_courts_for_postal_code,
|
|
27
|
+
get_cantons_for_municipality,
|
|
28
|
+
get_contact,
|
|
29
|
+
get_court,
|
|
30
|
+
get_court_location,
|
|
31
|
+
get_interest_rate,
|
|
32
|
+
get_justice_of_the_peace,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
get_court('court-of-appeal-brussel')['name'] # {'nl': 'Hof van Beroep Brussel', 'fr': "Cour d'appel de Bruxelles"}
|
|
36
|
+
get_contact(get_court('court-of-appeal-liege'), 'email', 'Greffe pénal') # 'liege.ca.greffepenal@just.fgov.be'
|
|
37
|
+
find_courts(type='enterprise-court', arrondissement='west-vlaanderen')
|
|
38
|
+
|
|
39
|
+
[canton] = get_cantons_for_municipality('44088') # merelbeke-melle
|
|
40
|
+
get_justice_of_the_peace(canton['id'])['address'] # where to file
|
|
41
|
+
get_cantons_for_municipality('44021', on='2025-09-15') # Gent 1–5 on that day: a split city
|
|
42
|
+
find_cantons_at(3.7268, 51.053) # [gent-1]: the canton of a point
|
|
43
|
+
find_arrondissements_at(3.7268, 51.053) # [oost-vlaanderen]: and its arrondissement
|
|
44
|
+
get_court_location('justice-of-the-peace-gent-1')['geometry'] # the court on the map, a GeoJSON Point
|
|
45
|
+
find_competent_courts('23088', 'police-court') # [police-court-vilvoorde]: which court, by REFNIS code
|
|
46
|
+
find_courts_for_postal_code('1040', 'police-court') # per municipality: Brussel and Etterbeek share the code
|
|
47
|
+
|
|
48
|
+
get_interest_rate('2024-06-15', 'legal') # InterestRateResult(rate=5.75, period_from='2024-01-01', period_to='2024-12-31')
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The records are the dicts from the JSON files, with their keys as in the JSON (`validFrom`,
|
|
52
|
+
`postalCode`), typed with the `TypedDict`s in `belgica.types`. Each data file is read on first
|
|
53
|
+
use; the canton boundaries (22 MB) only by `find_cantons_at` and `get_canton_boundary`, the
|
|
54
|
+
arrondissement boundaries only by `find_arrondissements_at` and `get_arrondissement_boundary`.
|
|
55
|
+
|
|
56
|
+
Lookups take `on`, a day as a `datetime.date` or `'YYYY-MM-DD'`, and answer for today without it.
|
|
57
|
+
A day before a dataset's coverage raises a `ValueError` rather than answer with today's data.
|
|
58
|
+
|
|
59
|
+
## Licence
|
|
60
|
+
|
|
61
|
+
Code MIT. Data CC BY 4.0: municipality, province, region and postal-code data © Statbel, canton and
|
|
62
|
+
arrondissement boundaries and court locations © National Geographic Institute (NGI). See the
|
|
63
|
+
[repository](https://github.com/pieterjanscheir/belgica) for the sources of every dataset.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Copies the data and the licences from the repository into the package before a build.
|
|
2
|
+
|
|
3
|
+
Building from the repository (python/ in a checkout) takes the files from ../; building a wheel
|
|
4
|
+
from the sdist finds them already in it.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import shutil
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def copy_from_repository(root: Path) -> None:
|
|
15
|
+
repository = root.parent
|
|
16
|
+
if not (repository / 'package.json').exists():
|
|
17
|
+
return
|
|
18
|
+
target = root / 'src' / 'belgica' / 'data'
|
|
19
|
+
target.mkdir(exist_ok=True)
|
|
20
|
+
for stale in target.glob('*.json'):
|
|
21
|
+
stale.unlink()
|
|
22
|
+
for file in (repository / 'data').glob('*.json'):
|
|
23
|
+
shutil.copyfile(file, target / file.name)
|
|
24
|
+
for name in ('LICENSE', 'LICENSE-DATA'):
|
|
25
|
+
shutil.copyfile(repository / name, root / name)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CustomBuildHook(BuildHookInterface): # type: ignore[type-arg]
|
|
29
|
+
def initialize(self, version: str, build_data: dict[str, Any]) -> None:
|
|
30
|
+
copy_from_repository(Path(self.root))
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "belgica"
|
|
3
|
+
# The same version as the npm package: test_package.py checks it against ../package.json
|
|
4
|
+
version = "0.5.0"
|
|
5
|
+
description = "Belgian legal reference data: courts, judicial cantons and arrondissements, municipalities and postal codes, and legal and commercial interest rates."
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT AND CC-BY-4.0"
|
|
8
|
+
license-files = ["LICENSE", "LICENSE-DATA"]
|
|
9
|
+
authors = [{ name = "Pieter-Jan Scheir" }]
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = []
|
|
12
|
+
keywords = [
|
|
13
|
+
"belgium",
|
|
14
|
+
"belgian",
|
|
15
|
+
"legal",
|
|
16
|
+
"courts",
|
|
17
|
+
"judicial-cantons",
|
|
18
|
+
"arrondissements",
|
|
19
|
+
"municipalities",
|
|
20
|
+
"refnis",
|
|
21
|
+
"postal-codes",
|
|
22
|
+
"geojson",
|
|
23
|
+
"interest-rates",
|
|
24
|
+
"vredegerecht",
|
|
25
|
+
"justice-of-the-peace",
|
|
26
|
+
]
|
|
27
|
+
classifiers = [
|
|
28
|
+
"Development Status :: 4 - Beta",
|
|
29
|
+
"Intended Audience :: Developers",
|
|
30
|
+
"Intended Audience :: Legal Industry",
|
|
31
|
+
"Natural Language :: Dutch",
|
|
32
|
+
"Natural Language :: French",
|
|
33
|
+
"Natural Language :: German",
|
|
34
|
+
"Programming Language :: Python :: 3",
|
|
35
|
+
"Topic :: Scientific/Engineering :: GIS",
|
|
36
|
+
"Typing :: Typed",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://pieterjanscheir.github.io/belgica/"
|
|
41
|
+
Documentation = "https://pieterjanscheir.github.io/belgica/guide/python"
|
|
42
|
+
Source = "https://github.com/pieterjanscheir/belgica"
|
|
43
|
+
Issues = "https://github.com/pieterjanscheir/belgica/issues"
|
|
44
|
+
Changelog = "https://github.com/pieterjanscheir/belgica/blob/main/CHANGELOG.md"
|
|
45
|
+
|
|
46
|
+
[dependency-groups]
|
|
47
|
+
# hatchling: the tests copy the data in with hatch_build.py
|
|
48
|
+
dev = ["hatchling>=1.27", "pytest>=8", "ruff>=0.14", "mypy>=1.18"]
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = ["hatchling>=1.27"]
|
|
52
|
+
build-backend = "hatchling.build"
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.hooks.custom]
|
|
55
|
+
# hatch_build.py copies ../data, ../LICENSE and ../LICENSE-DATA in
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.sdist]
|
|
58
|
+
# Copied in by the build hook, so not in git
|
|
59
|
+
artifacts = ["src/belgica/data/*.json", "LICENSE", "LICENSE-DATA"]
|
|
60
|
+
# Made by pnpm build:cases for the day they were made
|
|
61
|
+
exclude = ["tests/.cases"]
|
|
62
|
+
|
|
63
|
+
[tool.hatch.build.targets.wheel]
|
|
64
|
+
packages = ["src/belgica"]
|
|
65
|
+
artifacts = ["src/belgica/data/*.json"]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
69
|
+
|
|
70
|
+
[tool.ruff]
|
|
71
|
+
line-length = 100
|
|
72
|
+
target-version = "py311"
|
|
73
|
+
# The examples there are laid out for reading, one call a line
|
|
74
|
+
extend-exclude = ["README.md"]
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint]
|
|
77
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.format]
|
|
80
|
+
quote-style = "single"
|
|
81
|
+
|
|
82
|
+
[tool.mypy]
|
|
83
|
+
strict = true
|
|
84
|
+
files = ["src", "tests"]
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Belgian legal reference data: courts, judicial cantons and arrondissements, municipalities and
|
|
2
|
+
postal codes, prisons, houses of justice, and legal and commercial interest rates.
|
|
3
|
+
|
|
4
|
+
The records are the dicts from the JSON files, typed in `belgica.types`. Each data file is read
|
|
5
|
+
on first use. Lookups take `on`, a day as a `date` or 'YYYY-MM-DD', and give today's answer
|
|
6
|
+
without it. Documentation: https://pieterjanscheir.github.io/belgica/guide/python
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ._arrondissement_boundaries import (
|
|
10
|
+
ARRONDISSEMENT_BOUNDARIES_COVERAGE_FROM,
|
|
11
|
+
arrondissement_boundaries,
|
|
12
|
+
find_arrondissements_at,
|
|
13
|
+
get_arrondissement_boundary,
|
|
14
|
+
)
|
|
15
|
+
from ._canton_boundaries import (
|
|
16
|
+
CANTON_BOUNDARIES_COVERAGE_FROM,
|
|
17
|
+
canton_boundaries,
|
|
18
|
+
find_cantons_at,
|
|
19
|
+
get_canton_boundary,
|
|
20
|
+
)
|
|
21
|
+
from ._competence import (
|
|
22
|
+
CantonCourt,
|
|
23
|
+
PostalCodeCourts,
|
|
24
|
+
find_competent_courts,
|
|
25
|
+
find_courts_for_postal_code,
|
|
26
|
+
find_justices_of_the_peace,
|
|
27
|
+
)
|
|
28
|
+
from ._contacts import format_iban, format_phone_number, get_contact
|
|
29
|
+
from ._court_locations import COURT_LOCATIONS_COVERAGE_FROM, court_locations, get_court_location
|
|
30
|
+
from ._courts import (
|
|
31
|
+
COURTS_COVERAGE_FROM,
|
|
32
|
+
courts,
|
|
33
|
+
find_courts,
|
|
34
|
+
format_court_name,
|
|
35
|
+
get_court,
|
|
36
|
+
get_courts_for_canton,
|
|
37
|
+
get_divisions,
|
|
38
|
+
get_justice_of_the_peace,
|
|
39
|
+
)
|
|
40
|
+
from ._interest_rates import (
|
|
41
|
+
InterestRateResult,
|
|
42
|
+
get_interest_rate,
|
|
43
|
+
get_latest_known_rate_date,
|
|
44
|
+
interest_rates,
|
|
45
|
+
)
|
|
46
|
+
from ._places import (
|
|
47
|
+
HOUSES_OF_JUSTICE_COVERAGE_FROM,
|
|
48
|
+
PRISONS_COVERAGE_FROM,
|
|
49
|
+
get_house_of_justice,
|
|
50
|
+
get_prison,
|
|
51
|
+
houses_of_justice,
|
|
52
|
+
prisons,
|
|
53
|
+
)
|
|
54
|
+
from ._postal_codes import (
|
|
55
|
+
POSTAL_CODES_COVERAGE_FROM,
|
|
56
|
+
get_municipalities_for_postal_code,
|
|
57
|
+
get_postal_code,
|
|
58
|
+
get_postal_codes,
|
|
59
|
+
postal_codes,
|
|
60
|
+
)
|
|
61
|
+
from ._territory import (
|
|
62
|
+
TERRITORY_COVERAGE_FROM,
|
|
63
|
+
arrondissements,
|
|
64
|
+
cantons,
|
|
65
|
+
get_arrondissement,
|
|
66
|
+
get_arrondissement_for_municipality,
|
|
67
|
+
get_canton,
|
|
68
|
+
get_cantons_for_municipality,
|
|
69
|
+
get_judicial_area,
|
|
70
|
+
get_municipality,
|
|
71
|
+
get_province,
|
|
72
|
+
get_province_for_municipality,
|
|
73
|
+
get_region,
|
|
74
|
+
get_region_for_municipality,
|
|
75
|
+
judicial_areas,
|
|
76
|
+
municipalities,
|
|
77
|
+
provinces,
|
|
78
|
+
regions,
|
|
79
|
+
)
|
|
80
|
+
from ._time import Day, applies_on, to_day
|
|
81
|
+
|
|
82
|
+
__all__ = [
|
|
83
|
+
'ARRONDISSEMENT_BOUNDARIES_COVERAGE_FROM',
|
|
84
|
+
'CANTON_BOUNDARIES_COVERAGE_FROM',
|
|
85
|
+
'COURTS_COVERAGE_FROM',
|
|
86
|
+
'COURT_LOCATIONS_COVERAGE_FROM',
|
|
87
|
+
'HOUSES_OF_JUSTICE_COVERAGE_FROM',
|
|
88
|
+
'POSTAL_CODES_COVERAGE_FROM',
|
|
89
|
+
'PRISONS_COVERAGE_FROM',
|
|
90
|
+
'TERRITORY_COVERAGE_FROM',
|
|
91
|
+
'CantonCourt',
|
|
92
|
+
'Day',
|
|
93
|
+
'InterestRateResult',
|
|
94
|
+
'PostalCodeCourts',
|
|
95
|
+
'applies_on',
|
|
96
|
+
'arrondissement_boundaries',
|
|
97
|
+
'arrondissements',
|
|
98
|
+
'canton_boundaries',
|
|
99
|
+
'cantons',
|
|
100
|
+
'court_locations',
|
|
101
|
+
'courts',
|
|
102
|
+
'find_arrondissements_at',
|
|
103
|
+
'find_cantons_at',
|
|
104
|
+
'find_competent_courts',
|
|
105
|
+
'find_courts',
|
|
106
|
+
'find_courts_for_postal_code',
|
|
107
|
+
'find_justices_of_the_peace',
|
|
108
|
+
'format_court_name',
|
|
109
|
+
'format_iban',
|
|
110
|
+
'format_phone_number',
|
|
111
|
+
'get_arrondissement',
|
|
112
|
+
'get_arrondissement_boundary',
|
|
113
|
+
'get_arrondissement_for_municipality',
|
|
114
|
+
'get_canton',
|
|
115
|
+
'get_canton_boundary',
|
|
116
|
+
'get_cantons_for_municipality',
|
|
117
|
+
'get_contact',
|
|
118
|
+
'get_court',
|
|
119
|
+
'get_court_location',
|
|
120
|
+
'get_courts_for_canton',
|
|
121
|
+
'get_divisions',
|
|
122
|
+
'get_house_of_justice',
|
|
123
|
+
'get_interest_rate',
|
|
124
|
+
'get_judicial_area',
|
|
125
|
+
'get_justice_of_the_peace',
|
|
126
|
+
'get_latest_known_rate_date',
|
|
127
|
+
'get_municipalities_for_postal_code',
|
|
128
|
+
'get_municipality',
|
|
129
|
+
'get_postal_code',
|
|
130
|
+
'get_postal_codes',
|
|
131
|
+
'get_prison',
|
|
132
|
+
'get_province',
|
|
133
|
+
'get_province_for_municipality',
|
|
134
|
+
'get_region',
|
|
135
|
+
'get_region_for_municipality',
|
|
136
|
+
'houses_of_justice',
|
|
137
|
+
'interest_rates',
|
|
138
|
+
'judicial_areas',
|
|
139
|
+
'municipalities',
|
|
140
|
+
'postal_codes',
|
|
141
|
+
'prisons',
|
|
142
|
+
'provinces',
|
|
143
|
+
'regions',
|
|
144
|
+
'to_day',
|
|
145
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""The boundaries of the judicial arrondissements, and the arrondissement of a point"""
|
|
2
|
+
|
|
3
|
+
from functools import cache
|
|
4
|
+
|
|
5
|
+
from ._data import load
|
|
6
|
+
from ._geometry import BoundingBox, bounding_box, contains, in_box, require_coordinates
|
|
7
|
+
from ._territory import get_arrondissement
|
|
8
|
+
from ._time import Day, applying_on, pick_version, versions_by_id
|
|
9
|
+
from .types import Arrondissement, ArrondissementBoundary
|
|
10
|
+
|
|
11
|
+
ARRONDISSEMENT_BOUNDARIES_COVERAGE_FROM = '2026-10-21'
|
|
12
|
+
"""The boundaries describe the situation from the day the NGI's file was first read for them. A
|
|
13
|
+
boundary the NGI changes replaces the previous one; an arrondissement the NGI drops keeps its
|
|
14
|
+
boundary with a `validTo`. Lookups for an earlier day raise a `ValueError`."""
|
|
15
|
+
|
|
16
|
+
_DATASET = 'arrondissement boundaries'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@cache
|
|
20
|
+
def arrondissement_boundaries() -> tuple[ArrondissementBoundary, ...]:
|
|
21
|
+
"""Every boundary, every version, read on first use"""
|
|
22
|
+
return tuple(load('arrondissement-boundaries'))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@cache
|
|
26
|
+
def _by_id() -> dict[str, list[ArrondissementBoundary]]:
|
|
27
|
+
return versions_by_id(arrondissement_boundaries())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_arrondissement_boundary(id: str, on: Day | None = None) -> ArrondissementBoundary | None:
|
|
31
|
+
"""The boundary of an arrondissement, by arrondissement id: today's version, or the one of a
|
|
32
|
+
given day"""
|
|
33
|
+
return pick_version(_by_id().get(id), on, ARRONDISSEMENT_BOUNDARIES_COVERAGE_FROM, _DATASET)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@cache
|
|
37
|
+
def _index() -> list[tuple[ArrondissementBoundary, BoundingBox]]:
|
|
38
|
+
return [
|
|
39
|
+
(boundary, bounding_box(boundary['geometry'])) for boundary in arrondissement_boundaries()
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def find_arrondissements_at(
|
|
44
|
+
longitude: float, latitude: float, on: Day | None = None
|
|
45
|
+
) -> list[Arrondissement]:
|
|
46
|
+
"""The arrondissements whose boundary holds a point, on a day (today by default). Longitude
|
|
47
|
+
first, then latitude, in decimal degrees (WGS 84), as GeoJSON orders them. One arrondissement
|
|
48
|
+
for a point in Belgium; none outside it; two for a point on the line between two. Raises
|
|
49
|
+
`ValueError` for coordinates that aren't finite numbers, or a day before the coverage."""
|
|
50
|
+
require_coordinates(longitude, latitude)
|
|
51
|
+
applying = {
|
|
52
|
+
id(boundary)
|
|
53
|
+
for boundary in applying_on(
|
|
54
|
+
arrondissement_boundaries(), on, ARRONDISSEMENT_BOUNDARIES_COVERAGE_FROM, _DATASET
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
found = []
|
|
58
|
+
for boundary, box in _index():
|
|
59
|
+
if (
|
|
60
|
+
id(boundary) in applying
|
|
61
|
+
and in_box(box, longitude, latitude)
|
|
62
|
+
and contains(boundary['geometry'], longitude, latitude)
|
|
63
|
+
):
|
|
64
|
+
# Every boundary belongs to an arrondissement that applies on the same days
|
|
65
|
+
# (test/arrondissement-boundaries.test.ts)
|
|
66
|
+
arrondissement = get_arrondissement(boundary['arrondissement'], on)
|
|
67
|
+
if arrondissement:
|
|
68
|
+
found.append(arrondissement)
|
|
69
|
+
return found
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""The boundaries of the judicial cantons, and the canton of a point"""
|
|
2
|
+
|
|
3
|
+
from functools import cache
|
|
4
|
+
|
|
5
|
+
from ._data import load
|
|
6
|
+
from ._geometry import BoundingBox, bounding_box, contains, in_box, require_coordinates
|
|
7
|
+
from ._territory import get_canton
|
|
8
|
+
from ._time import Day, applying_on, pick_version, versions_by_id
|
|
9
|
+
from .types import Canton, CantonBoundary
|
|
10
|
+
|
|
11
|
+
CANTON_BOUNDARIES_COVERAGE_FROM = '2026-10-21'
|
|
12
|
+
"""The boundaries describe the situation from the day the NGI's file was first read. A boundary
|
|
13
|
+
the NGI changes replaces the previous one; a canton the NGI drops keeps its boundary with a
|
|
14
|
+
`validTo`. Lookups for an earlier day raise a `ValueError`."""
|
|
15
|
+
|
|
16
|
+
_DATASET = 'canton boundaries'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@cache
|
|
20
|
+
def canton_boundaries() -> tuple[CantonBoundary, ...]:
|
|
21
|
+
"""Every boundary, every version. About 22 MB of JSON, read on first use."""
|
|
22
|
+
return tuple(load('canton-boundaries'))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@cache
|
|
26
|
+
def _by_id() -> dict[str, list[CantonBoundary]]:
|
|
27
|
+
return versions_by_id(canton_boundaries())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_canton_boundary(id: str, on: Day | None = None) -> CantonBoundary | None:
|
|
31
|
+
"""The boundary of a canton, by canton id: today's version, or the one of a given day"""
|
|
32
|
+
return pick_version(_by_id().get(id), on, CANTON_BOUNDARIES_COVERAGE_FROM, _DATASET)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@cache
|
|
36
|
+
def _index() -> list[tuple[CantonBoundary, BoundingBox]]:
|
|
37
|
+
return [(boundary, bounding_box(boundary['geometry'])) for boundary in canton_boundaries()]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def find_cantons_at(longitude: float, latitude: float, on: Day | None = None) -> list[Canton]:
|
|
41
|
+
"""The cantons whose boundary holds a point, on a day (today by default). Longitude first,
|
|
42
|
+
then latitude, in decimal degrees (WGS 84), as GeoJSON orders them. One canton for a point in
|
|
43
|
+
Belgium; none outside it; two for a point on the line between two cantons. The boundary is the
|
|
44
|
+
NGI's digitisation of the annex to the Judicial Code: where a point lies close to a boundary,
|
|
45
|
+
the canton's `territory` text decides. Raises `ValueError` for coordinates that aren't finite
|
|
46
|
+
numbers, or a day before the coverage."""
|
|
47
|
+
require_coordinates(longitude, latitude)
|
|
48
|
+
applying = {
|
|
49
|
+
id(boundary)
|
|
50
|
+
for boundary in applying_on(
|
|
51
|
+
canton_boundaries(), on, CANTON_BOUNDARIES_COVERAGE_FROM, _DATASET
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
found = []
|
|
55
|
+
for boundary, box in _index():
|
|
56
|
+
if (
|
|
57
|
+
id(boundary) in applying
|
|
58
|
+
and in_box(box, longitude, latitude)
|
|
59
|
+
and contains(boundary['geometry'], longitude, latitude)
|
|
60
|
+
):
|
|
61
|
+
# Every boundary belongs to a canton that applies on the same days
|
|
62
|
+
# (tests/test_data.py)
|
|
63
|
+
canton = get_canton(boundary['canton'], on)
|
|
64
|
+
if canton:
|
|
65
|
+
found.append(canton)
|
|
66
|
+
return found
|