permisapi-client 0.7.2__tar.gz → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: permisapi-client
3
- Version: 0.7.2
3
+ Version: 0.8.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
@@ -30,4 +30,4 @@ __all__ = [
30
30
  "PermisAPIValidationError",
31
31
  ]
32
32
 
33
- __version__ = "0.7.2"
33
+ __version__ = "0.8.0"
@@ -13,7 +13,7 @@ from permisapi_client.exceptions import error_from_response
13
13
 
14
14
  DEFAULT_BASE_URL = "https://api.permisapi.fr"
15
15
  DEFAULT_TIMEOUT = 15.0
16
- USER_AGENT = "permisapi-client-python/0.7.2"
16
+ USER_AGENT = "permisapi-client-python/0.8.0"
17
17
 
18
18
 
19
19
  def _raise_for_status(resp: httpx.Response) -> None:
@@ -65,11 +65,30 @@ class _PermitsResource:
65
65
  Tout autre champ retourne 422 (et non ignore silencieusement).
66
66
 
67
67
  Filtre `min_score` (Pro+ uniquement) : retourne uniquement les
68
- permits avec un Score Opportunite MDB >= N (0-100). Use case
68
+ permis avec un Score Opportunite MDB >= N (0-100). Use case
69
69
  marchand de biens : shortliste les top opportunites en 1 call
70
70
  au lieu de fetch + filter cote client. Cout quota special :
71
71
  `limit` unites au lieu de 1 (anti-abus, equivalent a N calls
72
72
  /score). Header X-RateLimit-Cost confirme.
73
+
74
+ Filtre `max_risk` (Pro+ uniquement) : exclut les permis dont
75
+ la commune est classee au-dessus du tier specifie (low /
76
+ moderate / high / critical). Exemple `max_risk="moderate"`
77
+ retourne uniquement les permis low + moderate. Cout quota =
78
+ limit (anti-abus, equivalent a N calls /risks). Lookup via
79
+ colonne materialisee `permits.risk_tier` (refresh par
80
+ scripts/materialize_risks.py mensuel).
81
+
82
+ Filtre `plu_zone_type` (Pro+ uniquement) : prefix de zonage
83
+ urbanisme parmi U / AU / A / N / OTHER. Exemple
84
+ `plu_zone_type="U"` retourne uniquement les permis en zone
85
+ urbaine constructible. Cout quota = limit.
86
+
87
+ Filtre `plu_constructible` (Pro+ uniquement) : booleen.
88
+ `plu_constructible=True` retourne uniquement les permis ou le
89
+ verdict heuristique est constructible (zone U ou AU). Pratique
90
+ combine avec `min_score` pour shortlister les vraies
91
+ opportunites MDB. Cout quota = limit.
73
92
  """
74
93
  return self._c._get("/v1/permits", params=params)
75
94
 
@@ -108,6 +127,24 @@ class _PermitsResource:
108
127
  params={"section": section, "numero": numero},
109
128
  )
110
129
 
130
+ def parcelle(self, num_pa: str) -> dict[str, Any]:
131
+ """GET /v1/permits/{num_pa}/parcelle : geometrie cadastre DGFiP.
132
+
133
+ Plans Pro et superieurs uniquement (402 si Free / Explorer).
134
+ Retourne {num_pa, comm_code, section, numero, cadastre_id,
135
+ surface_m2 (contenance officielle DGFiP), geometry (GeoJSON
136
+ Polygon WGS84), cached_at, source}.
137
+
138
+ 404 si la parcelle n'est pas encore en cache (commune hors
139
+ couverture Etalab ou pas encore materialise par
140
+ scripts/ingest_cadastre.py).
141
+
142
+ Use case : afficher le polygon precis sur une carte (vs juste
143
+ un point lat/lng), comparer surface DGFiP vs Sitadel, cross-ref
144
+ avec DVF en parcelle precise.
145
+ """
146
+ return self._c._get(f"/v1/permits/{num_pa}/parcelle")
147
+
111
148
  def dvf(
112
149
  self,
113
150
  num_pa: str,
@@ -437,6 +474,37 @@ class PermisAPI:
437
474
  self.alerts = _AlertsResource(self)
438
475
  self.me = _MeResource(self)
439
476
 
477
+ def search(
478
+ self,
479
+ q: str,
480
+ *,
481
+ dep_code: str | None = None,
482
+ limit: int = 20,
483
+ ) -> list[dict[str, Any]]:
484
+ """GET /v1/search : recherche fuzzy par texte sur les adresses.
485
+
486
+ Utilise pg_trgm cote DB. Insensible aux accents et a la casse,
487
+ tolerant aux typos courants. Latence ~50-200ms.
488
+
489
+ Tous plans (Free, Explorer, Pro, Business, Enterprise) avec
490
+ respect du scope geo. Cout quota = 1 unite (endpoint
491
+ d'acquisition, pas premium).
492
+
493
+ Args:
494
+ q: texte libre, min 2 chars (ex "republique bordeaux",
495
+ "rue victor hugo paris", "cours de l'ile")
496
+ dep_code: optionnel, restreindre a un departement
497
+ limit: 1-50, default 20
498
+
499
+ Returns:
500
+ Liste de PermitSummary tries par pertinence (similarity)
501
+ puis date desc.
502
+ """
503
+ params: dict[str, Any] = {"q": q, "limit": limit}
504
+ if dep_code is not None:
505
+ params["dep_code"] = dep_code
506
+ return self._get("/v1/search", params=params)
507
+
440
508
  def close(self) -> None:
441
509
  self._http.close()
442
510
 
@@ -553,6 +621,11 @@ class _AsyncPermitsResource:
553
621
  Voir _PermitsResource.risks pour la doc complete."""
554
622
  return await self._c._get(f"/v1/permits/{num_pa}/risks")
555
623
 
624
+ async def parcelle(self, num_pa: str) -> dict[str, Any]:
625
+ """GET /v1/permits/{num_pa}/parcelle : version async. Plans Pro+ only.
626
+ Voir _PermitsResource.parcelle pour la doc complete."""
627
+ return await self._c._get(f"/v1/permits/{num_pa}/parcelle")
628
+
556
629
  async def full_view(self, num_pa: str) -> dict[str, Any]:
557
630
  """GET /v1/permits/{num_pa}/360 : version async. Vue agregee 6-en-1.
558
631
 
@@ -689,6 +762,20 @@ class AsyncPermisAPI:
689
762
  self.alerts = _AsyncAlertsResource(self)
690
763
  self.me = _AsyncMeResource(self)
691
764
 
765
+ async def search(
766
+ self,
767
+ q: str,
768
+ *,
769
+ dep_code: str | None = None,
770
+ limit: int = 20,
771
+ ) -> list[dict[str, Any]]:
772
+ """GET /v1/search : version async. Voir PermisAPI.search pour
773
+ la doc complete (fuzzy pg_trgm sur full_address)."""
774
+ params: dict[str, Any] = {"q": q, "limit": limit}
775
+ if dep_code is not None:
776
+ params["dep_code"] = dep_code
777
+ return await self._get("/v1/search", params=params)
778
+
692
779
  async def close(self) -> None:
693
780
  await self._http.aclose()
694
781
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "permisapi-client"
7
- version = "0.7.2"
7
+ version = "0.8.0"
8
8
  description = "Client Python officiel pour PermisAPI : permis de construire France en API REST."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"