permisapi-client 0.1.0__tar.gz → 0.2.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.
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/.gitignore +65 -65
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/PKG-INFO +1 -1
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/README.md +96 -96
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/permisapi_client/__init__.py +33 -33
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/permisapi_client/client.py +452 -384
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/permisapi_client/exceptions.py +106 -106
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/pyproject.toml +45 -45
- {permisapi_client-0.1.0 → permisapi_client-0.2.0}/tests/test_client.py +259 -195
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
# Python
|
|
2
|
-
__pycache__/
|
|
3
|
-
*.py[cod]
|
|
4
|
-
*$py.class
|
|
5
|
-
*.so
|
|
6
|
-
.Python
|
|
7
|
-
*.egg-info/
|
|
8
|
-
dist/
|
|
9
|
-
build/
|
|
10
|
-
.venv/
|
|
11
|
-
venv/
|
|
12
|
-
env/
|
|
13
|
-
|
|
14
|
-
# IDE
|
|
15
|
-
.vscode/
|
|
16
|
-
.idea/
|
|
17
|
-
*.swp
|
|
18
|
-
*.swo
|
|
19
|
-
|
|
20
|
-
# Environment
|
|
21
|
-
.env
|
|
22
|
-
.env.local
|
|
23
|
-
.env.*.backup
|
|
24
|
-
.env.backup-*
|
|
25
|
-
.env.*.local
|
|
26
|
-
|
|
27
|
-
# Secrets inventory (file listant les valeurs sensibles a copier dans
|
|
28
|
-
# 1Password/Bitwarden avant de changer de machine). Ne doit JAMAIS
|
|
29
|
-
# remonter sur git.
|
|
30
|
-
SECRETS_INVENTORY.md
|
|
31
|
-
|
|
32
|
-
# Claude Code workspace (launch.json personnel)
|
|
33
|
-
.claude/
|
|
34
|
-
|
|
35
|
-
# Data (too big, ignored)
|
|
36
|
-
data/raw/*.csv
|
|
37
|
-
data/raw/*.zip
|
|
38
|
-
data/processed/*.parquet
|
|
39
|
-
|
|
40
|
-
# Database
|
|
41
|
-
*.sqlite
|
|
42
|
-
*.db
|
|
43
|
-
|
|
44
|
-
# Logs
|
|
45
|
-
*.log
|
|
46
|
-
logs/
|
|
47
|
-
|
|
48
|
-
# Tests
|
|
49
|
-
.pytest_cache/
|
|
50
|
-
.coverage
|
|
51
|
-
htmlcov/
|
|
52
|
-
|
|
53
|
-
# OS
|
|
54
|
-
.DS_Store
|
|
55
|
-
Thumbs.db
|
|
56
|
-
desktop.ini
|
|
57
|
-
|
|
58
|
-
# Node (landing page)
|
|
59
|
-
landing/node_modules/
|
|
60
|
-
landing/.next/
|
|
61
|
-
landing/out/
|
|
62
|
-
|
|
63
|
-
# Post-piratage rotation (contient les nouvelles cles API en clair)
|
|
64
|
-
secrets_NEW_KEYS.json
|
|
65
|
-
scrape-downloads/
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# IDE
|
|
15
|
+
.vscode/
|
|
16
|
+
.idea/
|
|
17
|
+
*.swp
|
|
18
|
+
*.swo
|
|
19
|
+
|
|
20
|
+
# Environment
|
|
21
|
+
.env
|
|
22
|
+
.env.local
|
|
23
|
+
.env.*.backup
|
|
24
|
+
.env.backup-*
|
|
25
|
+
.env.*.local
|
|
26
|
+
|
|
27
|
+
# Secrets inventory (file listant les valeurs sensibles a copier dans
|
|
28
|
+
# 1Password/Bitwarden avant de changer de machine). Ne doit JAMAIS
|
|
29
|
+
# remonter sur git.
|
|
30
|
+
SECRETS_INVENTORY.md
|
|
31
|
+
|
|
32
|
+
# Claude Code workspace (launch.json personnel)
|
|
33
|
+
.claude/
|
|
34
|
+
|
|
35
|
+
# Data (too big, ignored)
|
|
36
|
+
data/raw/*.csv
|
|
37
|
+
data/raw/*.zip
|
|
38
|
+
data/processed/*.parquet
|
|
39
|
+
|
|
40
|
+
# Database
|
|
41
|
+
*.sqlite
|
|
42
|
+
*.db
|
|
43
|
+
|
|
44
|
+
# Logs
|
|
45
|
+
*.log
|
|
46
|
+
logs/
|
|
47
|
+
|
|
48
|
+
# Tests
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
.coverage
|
|
51
|
+
htmlcov/
|
|
52
|
+
|
|
53
|
+
# OS
|
|
54
|
+
.DS_Store
|
|
55
|
+
Thumbs.db
|
|
56
|
+
desktop.ini
|
|
57
|
+
|
|
58
|
+
# Node (landing page)
|
|
59
|
+
landing/node_modules/
|
|
60
|
+
landing/.next/
|
|
61
|
+
landing/out/
|
|
62
|
+
|
|
63
|
+
# Post-piratage rotation (contient les nouvelles cles API en clair)
|
|
64
|
+
secrets_NEW_KEYS.json
|
|
65
|
+
scrape-downloads/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: permisapi-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Client Python officiel pour PermisAPI : permis de construire France en API REST.
|
|
5
5
|
Project-URL: Homepage, https://permisapi.fr
|
|
6
6
|
Project-URL: Documentation, https://permisapi.fr/docs
|
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
# permisapi-client
|
|
2
|
-
|
|
3
|
-
Client Python officiel pour [PermisAPI](https://permisapi.fr) : accede aux 311k+ permis de construire France via une API REST moderne.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pip install permisapi-client
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Quickstart
|
|
12
|
-
|
|
13
|
-
```python
|
|
14
|
-
from permisapi_client import PermisAPI
|
|
15
|
-
|
|
16
|
-
client = PermisAPI(api_key="pk_live_...") # obtenue sur https://permisapi.fr/#pricing
|
|
17
|
-
|
|
18
|
-
# Liste
|
|
19
|
-
data = client.permits.list(dep_code="75", limit=10)
|
|
20
|
-
for permit in data["items"]:
|
|
21
|
-
print(permit["num_pa"], permit["adr_localite_ter"])
|
|
22
|
-
|
|
23
|
-
# Recherche geographique (PostGIS)
|
|
24
|
-
nearby = client.permits.near(lat=48.85, lng=2.35, radius_m=500)
|
|
25
|
-
|
|
26
|
-
# Detail
|
|
27
|
-
permit = client.permits.get("PC 075 115 24 B0001")
|
|
28
|
-
|
|
29
|
-
# Stats par commune
|
|
30
|
-
stats = client.stats.commune("75115")
|
|
31
|
-
print(stats["total"], stats["granted"])
|
|
32
|
-
|
|
33
|
-
# Itere sur toutes les pages
|
|
34
|
-
for p in client.permits.iter_all(dep_code="75"):
|
|
35
|
-
print(p["num_pa"])
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Async
|
|
39
|
-
|
|
40
|
-
```python
|
|
41
|
-
import asyncio
|
|
42
|
-
from permisapi_client import AsyncPermisAPI
|
|
43
|
-
|
|
44
|
-
async def main():
|
|
45
|
-
async with AsyncPermisAPI(api_key="pk_live_...") as client:
|
|
46
|
-
data = await client.permits.list(dep_code="75")
|
|
47
|
-
async for p in client.permits.iter_all(dep_code="75"):
|
|
48
|
-
print(p["num_pa"])
|
|
49
|
-
|
|
50
|
-
asyncio.run(main())
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Alertes webhook
|
|
54
|
-
|
|
55
|
-
```python
|
|
56
|
-
alert = client.alerts.create(
|
|
57
|
-
name="Paris 18e renovations",
|
|
58
|
-
center_lat=48.88,
|
|
59
|
-
center_lng=2.35,
|
|
60
|
-
radius_km=3,
|
|
61
|
-
permit_types=["PC_LOGEMENT"],
|
|
62
|
-
webhook_url="https://tonapp.fr/permisapi-hook",
|
|
63
|
-
)
|
|
64
|
-
# Le webhook_secret n'est renvoye QU'ICI. Stocke-le pour verifier les HMAC.
|
|
65
|
-
print(alert["webhook_secret"])
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Gestion des erreurs
|
|
69
|
-
|
|
70
|
-
```python
|
|
71
|
-
from permisapi_client import (
|
|
72
|
-
PermisAPI,
|
|
73
|
-
PermisAPIRateLimited,
|
|
74
|
-
PermisAPIQuotaExceeded,
|
|
75
|
-
PermisAPIUnauthorized,
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
client.permits.list()
|
|
80
|
-
except PermisAPIUnauthorized:
|
|
81
|
-
print("Cle API invalide")
|
|
82
|
-
except PermisAPIQuotaExceeded as e:
|
|
83
|
-
print(f"Quota mensuel atteint, retry dans {e.retry_after}s")
|
|
84
|
-
except PermisAPIRateLimited as e:
|
|
85
|
-
print(f"Burst limit, retry dans {e.retry_after}s")
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Licence
|
|
89
|
-
|
|
90
|
-
MIT. Source : SDES, base Sitadel, data.gouv.fr (Licence Ouverte Etalab).
|
|
91
|
-
|
|
92
|
-
## Liens
|
|
93
|
-
|
|
94
|
-
- Documentation : https://permisapi.fr/docs
|
|
95
|
-
- Reference API complete : https://api.permisapi.fr/docs
|
|
96
|
-
- Changelog : https://permisapi.fr/changelog
|
|
1
|
+
# permisapi-client
|
|
2
|
+
|
|
3
|
+
Client Python officiel pour [PermisAPI](https://permisapi.fr) : accede aux 311k+ permis de construire France via une API REST moderne.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install permisapi-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from permisapi_client import PermisAPI
|
|
15
|
+
|
|
16
|
+
client = PermisAPI(api_key="pk_live_...") # obtenue sur https://permisapi.fr/#pricing
|
|
17
|
+
|
|
18
|
+
# Liste
|
|
19
|
+
data = client.permits.list(dep_code="75", limit=10)
|
|
20
|
+
for permit in data["items"]:
|
|
21
|
+
print(permit["num_pa"], permit["adr_localite_ter"])
|
|
22
|
+
|
|
23
|
+
# Recherche geographique (PostGIS)
|
|
24
|
+
nearby = client.permits.near(lat=48.85, lng=2.35, radius_m=500)
|
|
25
|
+
|
|
26
|
+
# Detail
|
|
27
|
+
permit = client.permits.get("PC 075 115 24 B0001")
|
|
28
|
+
|
|
29
|
+
# Stats par commune
|
|
30
|
+
stats = client.stats.commune("75115")
|
|
31
|
+
print(stats["total"], stats["granted"])
|
|
32
|
+
|
|
33
|
+
# Itere sur toutes les pages
|
|
34
|
+
for p in client.permits.iter_all(dep_code="75"):
|
|
35
|
+
print(p["num_pa"])
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Async
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import asyncio
|
|
42
|
+
from permisapi_client import AsyncPermisAPI
|
|
43
|
+
|
|
44
|
+
async def main():
|
|
45
|
+
async with AsyncPermisAPI(api_key="pk_live_...") as client:
|
|
46
|
+
data = await client.permits.list(dep_code="75")
|
|
47
|
+
async for p in client.permits.iter_all(dep_code="75"):
|
|
48
|
+
print(p["num_pa"])
|
|
49
|
+
|
|
50
|
+
asyncio.run(main())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Alertes webhook
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
alert = client.alerts.create(
|
|
57
|
+
name="Paris 18e renovations",
|
|
58
|
+
center_lat=48.88,
|
|
59
|
+
center_lng=2.35,
|
|
60
|
+
radius_km=3,
|
|
61
|
+
permit_types=["PC_LOGEMENT"],
|
|
62
|
+
webhook_url="https://tonapp.fr/permisapi-hook",
|
|
63
|
+
)
|
|
64
|
+
# Le webhook_secret n'est renvoye QU'ICI. Stocke-le pour verifier les HMAC.
|
|
65
|
+
print(alert["webhook_secret"])
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Gestion des erreurs
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from permisapi_client import (
|
|
72
|
+
PermisAPI,
|
|
73
|
+
PermisAPIRateLimited,
|
|
74
|
+
PermisAPIQuotaExceeded,
|
|
75
|
+
PermisAPIUnauthorized,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
client.permits.list()
|
|
80
|
+
except PermisAPIUnauthorized:
|
|
81
|
+
print("Cle API invalide")
|
|
82
|
+
except PermisAPIQuotaExceeded as e:
|
|
83
|
+
print(f"Quota mensuel atteint, retry dans {e.retry_after}s")
|
|
84
|
+
except PermisAPIRateLimited as e:
|
|
85
|
+
print(f"Burst limit, retry dans {e.retry_after}s")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Licence
|
|
89
|
+
|
|
90
|
+
MIT. Source : SDES, base Sitadel, data.gouv.fr (Licence Ouverte Etalab).
|
|
91
|
+
|
|
92
|
+
## Liens
|
|
93
|
+
|
|
94
|
+
- Documentation : https://permisapi.fr/docs
|
|
95
|
+
- Reference API complete : https://api.permisapi.fr/docs
|
|
96
|
+
- Changelog : https://permisapi.fr/changelog
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
"""Client Python officiel pour PermisAPI.
|
|
2
|
-
|
|
3
|
-
Usage sync :
|
|
4
|
-
from permisapi_client import PermisAPI
|
|
5
|
-
client = PermisAPI(api_key="pk_live_...")
|
|
6
|
-
for permit in client.permits.list(dep_code="75", limit=10):
|
|
7
|
-
print(permit["num_pa"], permit["adr_localite_ter"])
|
|
8
|
-
|
|
9
|
-
Usage async :
|
|
10
|
-
from permisapi_client import AsyncPermisAPI
|
|
11
|
-
async with AsyncPermisAPI(api_key="pk_live_...") as client:
|
|
12
|
-
permits = await client.permits.near(lat=48.85, lng=2.35, radius_m=500)
|
|
13
|
-
"""
|
|
14
|
-
from permisapi_client.client import AsyncPermisAPI, PermisAPI
|
|
15
|
-
from permisapi_client.exceptions import (
|
|
16
|
-
PermisAPIError,
|
|
17
|
-
PermisAPIQuotaExceeded,
|
|
18
|
-
PermisAPIRateLimited,
|
|
19
|
-
PermisAPIUnauthorized,
|
|
20
|
-
PermisAPIValidationError,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
__all__ = [
|
|
24
|
-
"PermisAPI",
|
|
25
|
-
"AsyncPermisAPI",
|
|
26
|
-
"PermisAPIError",
|
|
27
|
-
"PermisAPIUnauthorized",
|
|
28
|
-
"PermisAPIRateLimited",
|
|
29
|
-
"PermisAPIQuotaExceeded",
|
|
30
|
-
"PermisAPIValidationError",
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
__version__ = "0.1.0"
|
|
1
|
+
"""Client Python officiel pour PermisAPI.
|
|
2
|
+
|
|
3
|
+
Usage sync :
|
|
4
|
+
from permisapi_client import PermisAPI
|
|
5
|
+
client = PermisAPI(api_key="pk_live_...")
|
|
6
|
+
for permit in client.permits.list(dep_code="75", limit=10):
|
|
7
|
+
print(permit["num_pa"], permit["adr_localite_ter"])
|
|
8
|
+
|
|
9
|
+
Usage async :
|
|
10
|
+
from permisapi_client import AsyncPermisAPI
|
|
11
|
+
async with AsyncPermisAPI(api_key="pk_live_...") as client:
|
|
12
|
+
permits = await client.permits.near(lat=48.85, lng=2.35, radius_m=500)
|
|
13
|
+
"""
|
|
14
|
+
from permisapi_client.client import AsyncPermisAPI, PermisAPI
|
|
15
|
+
from permisapi_client.exceptions import (
|
|
16
|
+
PermisAPIError,
|
|
17
|
+
PermisAPIQuotaExceeded,
|
|
18
|
+
PermisAPIRateLimited,
|
|
19
|
+
PermisAPIUnauthorized,
|
|
20
|
+
PermisAPIValidationError,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PermisAPI",
|
|
25
|
+
"AsyncPermisAPI",
|
|
26
|
+
"PermisAPIError",
|
|
27
|
+
"PermisAPIUnauthorized",
|
|
28
|
+
"PermisAPIRateLimited",
|
|
29
|
+
"PermisAPIQuotaExceeded",
|
|
30
|
+
"PermisAPIValidationError",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
__version__ = "0.1.0"
|