kvk-connect 0.1.6__py3-none-any.whl

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.
Files changed (52) hide show
  1. kvk_connect/__init__.py +11 -0
  2. kvk_connect/api/__init__.py +4 -0
  3. kvk_connect/api/client.py +183 -0
  4. kvk_connect/api/endpoints.py +24 -0
  5. kvk_connect/api/session.py +34 -0
  6. kvk_connect/cli/main.py +26 -0
  7. kvk_connect/db/__init__.py +0 -0
  8. kvk_connect/db/basisprofiel_reader.py +67 -0
  9. kvk_connect/db/basisprofiel_writer.py +73 -0
  10. kvk_connect/db/init.py +25 -0
  11. kvk_connect/db/kvkvestigingen_reader.py +41 -0
  12. kvk_connect/db/kvkvestigingen_writer.py +73 -0
  13. kvk_connect/db/signaal_reader.py +23 -0
  14. kvk_connect/db/signaal_writer.py +73 -0
  15. kvk_connect/db/vestigingenprofiel_reader.py +66 -0
  16. kvk_connect/db/vestigingsprofiel_writer.py +92 -0
  17. kvk_connect/logging_config.py +27 -0
  18. kvk_connect/mappers/__init__.py +1 -0
  19. kvk_connect/mappers/kvk_record_mapper.py +100 -0
  20. kvk_connect/mappers/map_mutatie_abonnement_api_to_mutatieabonnement.py +11 -0
  21. kvk_connect/mappers/map_vestigingen_api_to_vestigingsnummers.py +14 -0
  22. kvk_connect/mappers/map_vestigingsprofiel_api_to_vestigingsprofiel_domain.py +41 -0
  23. kvk_connect/models/__init__.py +0 -0
  24. kvk_connect/models/api/__init__.py +0 -0
  25. kvk_connect/models/api/abonnementen_api.py +42 -0
  26. kvk_connect/models/api/basisprofiel_api.py +233 -0
  27. kvk_connect/models/api/mutatie_abonnementen_api.py +40 -0
  28. kvk_connect/models/api/mutatiesignalen_api.py +44 -0
  29. kvk_connect/models/api/vestigingen_api.py +73 -0
  30. kvk_connect/models/api/vestigingsprofiel_api.py +71 -0
  31. kvk_connect/models/domain/__init__.py +6 -0
  32. kvk_connect/models/domain/basisprofiel.py +65 -0
  33. kvk_connect/models/domain/kvkvestigingsnummersdomain.py +28 -0
  34. kvk_connect/models/domain/mutatie_abonnement.py +20 -0
  35. kvk_connect/models/domain/vestigingsadresdomain.py +62 -0
  36. kvk_connect/models/domain/vestigingsadressendomain.py +48 -0
  37. kvk_connect/models/domain/vestigingsprofiel_domain.py +58 -0
  38. kvk_connect/models/orm/base.py +5 -0
  39. kvk_connect/models/orm/basisprofiel_orm.py +52 -0
  40. kvk_connect/models/orm/kvkvestigingen_orm.py +53 -0
  41. kvk_connect/models/orm/signaal_orm.py +40 -0
  42. kvk_connect/models/orm/vestigingsprofiel_orm.py +58 -0
  43. kvk_connect/services/__init__.py +4 -0
  44. kvk_connect/services/record_service.py +66 -0
  45. kvk_connect/utils/__init__.py +5 -0
  46. kvk_connect/utils/env.py +16 -0
  47. kvk_connect/utils/formatting.py +11 -0
  48. kvk_connect/utils/rate_limit.py +21 -0
  49. kvk_connect/utils/tools.py +131 -0
  50. kvk_connect-0.1.6.dist-info/METADATA +352 -0
  51. kvk_connect-0.1.6.dist-info/RECORD +52 -0
  52. kvk_connect-0.1.6.dist-info/WHEEL +4 -0
@@ -0,0 +1,44 @@
1
+ # ruff: noqa: D102
2
+ from dataclasses import dataclass, field
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+
7
+ @dataclass
8
+ class MutatieSignaal:
9
+ id: str
10
+ kvknummer: str
11
+ signaal_type: str
12
+ timestamp: datetime | None = None
13
+ vestigingsnummer: str | None = None
14
+
15
+ @classmethod
16
+ def from_dict(cls, data: dict[str, Any]) -> "MutatieSignaal": # noqa: D102
17
+ ts = data.get("timestamp", "")
18
+ dt = datetime.fromisoformat(ts.replace("Z", "+00:00")) if ts else None
19
+ return cls(
20
+ id=data.get("id", ""),
21
+ timestamp=dt,
22
+ kvknummer=data.get("kvknummer", ""),
23
+ signaal_type=data.get("signaalType", ""),
24
+ vestigingsnummer=data.get("vestigingsnummer"),
25
+ )
26
+
27
+
28
+ @dataclass
29
+ class MutatiesAPI:
30
+ pagina: int
31
+ aantal: int
32
+ totaal: int
33
+ totaal_paginas: int
34
+ signalen: list[MutatieSignaal] = field(default_factory=list)
35
+
36
+ @classmethod
37
+ def from_dict(cls, data: dict[str, Any]) -> "MutatiesAPI": # noqa: D102
38
+ return cls(
39
+ pagina=data.get("pagina", 0),
40
+ aantal=data.get("aantal", 0),
41
+ totaal=data.get("totaal", 0),
42
+ totaal_paginas=data.get("totaalPaginas", 0),
43
+ signalen=[MutatieSignaal.from_dict(s) for s in data.get("signalen", [])],
44
+ )
@@ -0,0 +1,73 @@
1
+ # ruff: noqa: D102
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import asdict, dataclass, field
6
+ from typing import Any
7
+
8
+ # Reuse Link from basisprofiel models
9
+ from .basisprofiel_api import Link
10
+
11
+
12
+ @dataclass
13
+ class Vestiging:
14
+ vestigingsnummer: str = ""
15
+ eerste_handelsnaam: str = ""
16
+ ind_hoofdvestiging: str = ""
17
+ ind_adres_afgeschermd: str = ""
18
+ ind_commerciele_vestiging: str = ""
19
+ volledig_adres: str = ""
20
+ links: list[Link] = field(default_factory=list)
21
+
22
+ @staticmethod
23
+ def from_dict(d: dict[str, Any] | None) -> Vestiging | None: # noqa: D102
24
+ if not d:
25
+ return None
26
+ return Vestiging(
27
+ vestigingsnummer=d.get("vestigingsnummer", "") or "",
28
+ eerste_handelsnaam=d.get("eersteHandelsnaam", "") or "",
29
+ ind_hoofdvestiging=d.get("indHoofdvestiging", "") or "",
30
+ ind_adres_afgeschermd=d.get("indAdresAfgeschermd", "") or "",
31
+ ind_commerciele_vestiging=d.get("indCommercieleVestiging", "") or "",
32
+ volledig_adres=d.get("volledigAdres", "") or "",
33
+ links=[link for link in (Link.from_dict(x) for x in d.get("links", [])) if link],
34
+ )
35
+
36
+
37
+ @dataclass
38
+ class VestigingenAPI:
39
+ kvk_nummer: str = ""
40
+ aantal_commerciele_vestigingen: int | None = None
41
+ aantal_niet_commerciele_vestigingen: int | None = None
42
+ totaal_aantal_vestigingen: int | None = None
43
+ vestigingen: list[Vestiging] = field(default_factory=list)
44
+ links: list[Link] = field(default_factory=list)
45
+
46
+ @staticmethod
47
+ def from_dict(d: dict[str, Any]) -> VestigingenAPI: # noqa: D102
48
+ return VestigingenAPI(
49
+ kvk_nummer=d.get("kvkNummer", "") or "",
50
+ aantal_commerciele_vestigingen=d.get("aantalCommercieleVestigingen"),
51
+ aantal_niet_commerciele_vestigingen=d.get("aantalNietCommercieleVestigingen"),
52
+ totaal_aantal_vestigingen=d.get("totaalAantalVestigingen"),
53
+ vestigingen=[v for v in (Vestiging.from_dict(x) for x in d.get("vestigingen", [])) if v],
54
+ links=[link for link in (Link.from_dict(x) for x in d.get("links", [])) if link],
55
+ )
56
+
57
+ @staticmethod
58
+ def load_from_file(path: str, encoding: str = "utf-8") -> VestigingenAPI: # noqa: D102
59
+ with open(path, encoding=encoding) as f:
60
+ data = json.load(f)
61
+ return VestigingenAPI.from_dict(data)
62
+
63
+ @staticmethod
64
+ def load_from_json(json_str: str) -> VestigingenAPI: # noqa: D102
65
+ data = json.loads(json_str)
66
+ return VestigingenAPI.from_dict(data)
67
+
68
+ @staticmethod
69
+ def load_from_dict(data: dict[str, Any]) -> VestigingenAPI: # noqa: D102
70
+ return VestigingenAPI.from_dict(data)
71
+
72
+ def to_dict(self) -> dict[str, Any]: # noqa: D102
73
+ return asdict(self)
@@ -0,0 +1,71 @@
1
+ # ruff: noqa: D102
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import asdict, dataclass, field
6
+ from typing import Any
7
+
8
+ from .basisprofiel_api import Adres, HandelNaam, Link, MaterieleRegistratie, SBIActiviteit
9
+
10
+
11
+ @dataclass
12
+ class VestigingsProfielAPI:
13
+ vestigingsnummer: str = ""
14
+ kvk_nummer: str = ""
15
+ rsin: str = ""
16
+ ind_non_mailing: str = ""
17
+ formele_registratiedatum: str = ""
18
+ materiele_registratie: MaterieleRegistratie | None = None
19
+ statutaire_naam: str = ""
20
+ eerste_handelsnaam: str = ""
21
+ ind_hoofdvestiging: str = ""
22
+ ind_commerciele_vestiging: str = ""
23
+ voltijd_werkzame_personen: int | None = None
24
+ totaal_werkzame_personen: int | None = None
25
+ deeltijd_werkzame_personen: int | None = None
26
+ handelsnamen: list[HandelNaam] = field(default_factory=list)
27
+ adressen: list[Adres] = field(default_factory=list)
28
+ websites: list[str] = field(default_factory=list)
29
+ sbi_activiteiten: list[SBIActiviteit] = field(default_factory=list)
30
+ links: list[Link] = field(default_factory=list)
31
+
32
+ @staticmethod
33
+ def from_dict(d: dict[str, Any]) -> VestigingsProfielAPI: # noqa: D102
34
+ return VestigingsProfielAPI(
35
+ vestigingsnummer=d.get("vestigingsnummer", "") or "",
36
+ kvk_nummer=d.get("kvkNummer", "") or "",
37
+ rsin=d.get("rsin", "") or "",
38
+ ind_non_mailing=d.get("indNonMailing", "") or "",
39
+ formele_registratiedatum=d.get("formeleRegistratiedatum", "") or "",
40
+ materiele_registratie=MaterieleRegistratie.from_dict(d.get("materieleRegistratie")),
41
+ statutaire_naam=d.get("statutaireNaam", "") or "",
42
+ eerste_handelsnaam=d.get("eersteHandelsnaam", "") or "",
43
+ ind_hoofdvestiging=d.get("indHoofdvestiging", "") or "",
44
+ ind_commerciele_vestiging=d.get("indCommercieleVestiging", "") or "",
45
+ voltijd_werkzame_personen=d.get("voltijdWerkzamePersonen"),
46
+ totaal_werkzame_personen=d.get("totaalWerkzamePersonen"),
47
+ deeltijd_werkzame_personen=d.get("deeltijdWerkzamePersonen"),
48
+ handelsnamen=[h for h in (HandelNaam.from_dict(x) for x in d.get("handelsnamen", [])) if h],
49
+ adressen=[a for a in (Adres.from_dict(x) for x in d.get("adressen", [])) if a],
50
+ websites=list(d.get("websites", []) or []),
51
+ sbi_activiteiten=[s for s in (SBIActiviteit.from_dict(x) for x in d.get("sbiActiviteiten", [])) if s],
52
+ links=[link for link in (Link.from_dict(x) for x in d.get("links", [])) if link],
53
+ )
54
+
55
+ @staticmethod
56
+ def load_from_file(path: str, encoding: str = "utf-8") -> VestigingsProfielAPI: # noqa: D102
57
+ with open(path, encoding=encoding) as f:
58
+ data = json.load(f)
59
+ return VestigingsProfielAPI.from_dict(data)
60
+
61
+ @staticmethod
62
+ def load_from_json(json_str: str) -> VestigingsProfielAPI: # noqa: D102
63
+ data = json.loads(json_str)
64
+ return VestigingsProfielAPI.from_dict(data)
65
+
66
+ @staticmethod
67
+ def load_from_dict(data: dict[str, Any]) -> VestigingsProfielAPI: # noqa: D102
68
+ return VestigingsProfielAPI.from_dict(data)
69
+
70
+ def to_dict(self) -> dict[str, Any]: # noqa: D102
71
+ return asdict(self)
@@ -0,0 +1,6 @@
1
+ from .basisprofiel import BasisProfielDomain
2
+ from .kvkvestigingsnummersdomain import KvKVestigingsNummersDomain
3
+ from .vestigingsadresdomain import VestigingsAdresDomain
4
+ from .vestigingsadressendomain import VestigingsAdressenDomain
5
+
6
+ __all__ = ["BasisProfielDomain", "KvKVestigingsNummersDomain", "VestigingsAdressenDomain", "VestigingsAdresDomain"]
@@ -0,0 +1,65 @@
1
+ # ruff: noqa: D102
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import asdict, dataclass
5
+ from typing import Any
6
+
7
+
8
+ @dataclass
9
+ class BasisProfielDomain:
10
+ """Dit is ons domeinmodel van een KVK record.
11
+
12
+ Alleen de velden die hier staan schrijven we weg naar CSV/SQL
13
+ """
14
+
15
+ kvk_nummer: str | None = None
16
+ naam: str | None = None
17
+ hoofdactiviteit: str | None = None
18
+ hoofdactiviteit_omschrijving: str | None = None
19
+ activiteit_overig: str | None = None
20
+ rechtsvorm: str | None = None
21
+ rechtsvorm_uitgebreid: str | None = None
22
+ eerste_handelsnaam: str | None = None
23
+ vestigingsnummer: str | None = None
24
+ totaal_werkzame_personen: int | None = None
25
+ websites: str | None = None
26
+ registratie_datum_aanvang: str | None = None
27
+ registratie_datum_einde: str | None = None
28
+ adres_type: str | None = None
29
+ postbusnummer: str | None = None
30
+ adres_straatnaam: str | None = None
31
+ adres_toevoeging: str | None = None
32
+ adres_postcode: str | None = None
33
+ adres_plaats: str | None = None
34
+ gps_latitude: str | None = None
35
+ gps_longitude: str | None = None
36
+
37
+ @staticmethod
38
+ def from_dict(d: dict[str, Any]) -> BasisProfielDomain: # noqa: D102
39
+ return BasisProfielDomain(
40
+ kvk_nummer=d.get("kvkNummer"),
41
+ naam=d.get("naam"),
42
+ hoofdactiviteit=d.get("hoofdactiviteit"),
43
+ hoofdactiviteit_omschrijving=d.get("hoofdactiviteitOmschrijving"),
44
+ activiteit_overig=d.get("activiteitOverig"),
45
+ rechtsvorm=d.get("rechtsvorm"),
46
+ rechtsvorm_uitgebreid=d.get("rechtsvormUitgebreid"),
47
+ eerste_handelsnaam=d.get("eersteHandelsnaam"),
48
+ vestigingsnummer=d.get("vestigingsnummer"),
49
+ totaal_werkzame_personen=d.get("totaalWerkzamePersonen"),
50
+ websites=d.get("websites"),
51
+ registratie_datum_aanvang=d.get("RegistratieDatumAanvang"),
52
+ registratie_datum_einde=d.get("RegistratieDatumEinde"),
53
+ adres_type=d.get("AdresType"),
54
+ postbusnummer=d.get("Postbusnummer"),
55
+ adres_straatnaam=d.get("AdresStraatnaam"),
56
+ adres_toevoeging=d.get("AdresToevoeging"),
57
+ adres_postcode=d.get("AdresPostcode"),
58
+ adres_plaats=d.get("AdresPlaats"),
59
+ gps_latitude=d.get("gpsLatitude"),
60
+ gps_longitude=d.get("gpsLongitude"),
61
+ )
62
+
63
+ def to_dict(self) -> dict[str, Any]:
64
+ """Converteer domeinmodel naar dictionary."""
65
+ return asdict(self)
@@ -0,0 +1,28 @@
1
+ # ruff: noqa: D102
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import dataclass, field
6
+ from typing import Any
7
+
8
+
9
+ @dataclass
10
+ class KvKVestigingsNummersDomain:
11
+ """Domein model voor een lijst van alle vestigingsnummers (strings)."""
12
+
13
+ kvk_nummer: str = ""
14
+ vestigingsnummers: list[str] = field(default_factory=list)
15
+
16
+ @staticmethod
17
+ def from_dict(d: dict[str, Any]) -> KvKVestigingsNummersDomain: # noqa: D102
18
+ return KvKVestigingsNummersDomain(
19
+ kvk_nummer=d.get("kvkNummer", "") or "", vestigingsnummers=d.get("vestigingen") or []
20
+ )
21
+
22
+ @staticmethod
23
+ def load_from_json(json_str: str) -> KvKVestigingsNummersDomain: # noqa: D102
24
+ data = json.loads(json_str)
25
+ return KvKVestigingsNummersDomain.from_dict(data)
26
+
27
+ def to_dict(self) -> dict: # noqa: D102
28
+ return {"kvkNummer": self.kvk_nummer, "vestigingen": list(self.vestigingsnummers)}
@@ -0,0 +1,20 @@
1
+ # ruff: noqa: D102
2
+ import json
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class MutatieAbonnementDomain:
8
+ abonnement_ids: list[str]
9
+
10
+ @staticmethod
11
+ def from_dict(data: list[str]) -> "MutatieAbonnementDomain": # noqa: D102
12
+ return MutatieAbonnementDomain(abonnement_ids=data)
13
+
14
+ @staticmethod
15
+ def from_json(json_str: str) -> "MutatieAbonnementDomain": # noqa: D102
16
+ data = json.loads(json_str)
17
+ return MutatieAbonnementDomain.from_dict(data)
18
+
19
+ def to_list(self) -> list[str]: # noqa: D102
20
+ return self.abonnement_ids
@@ -0,0 +1,62 @@
1
+ # ruff: noqa: D102
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import asdict, dataclass
6
+ from typing import Any
7
+
8
+
9
+ @dataclass
10
+ class VestigingsAdresDomain:
11
+ """Domein model voor een vestigingsadres van een KVK vestiging, behorende bij een KVK record."""
12
+
13
+ kvk_nummer: str | None = None
14
+ vestigingsnummer: str | None = None
15
+ adres_type: str | None = None
16
+ postbusnummer: str | None = None
17
+ adres_straatnaam: str | None = None
18
+ adres_toevoeging: str | None = None
19
+ adres_postcode: str | None = None
20
+ adres_plaats: str | None = None
21
+ gps_latitude: str | None = None
22
+ gps_longitude: str | None = None
23
+
24
+ @staticmethod
25
+ def from_dict(d: dict[str, Any]) -> VestigingsAdresDomain: # noqa: D102
26
+ def clean(val):
27
+ return val # if val not in ("", None) else None
28
+
29
+ return VestigingsAdresDomain(
30
+ kvk_nummer=clean(d.get("kvkNummer")),
31
+ vestigingsnummer=clean(d.get("vestigingsnummer")),
32
+ adres_type=clean(d.get("AdresType")),
33
+ postbusnummer=clean(d.get("Postbusnummer")),
34
+ adres_straatnaam=clean(d.get("AdresStraatnaam")),
35
+ adres_toevoeging=clean(d.get("AdresToevoeging")),
36
+ adres_postcode=clean(d.get("AdresPostcode")),
37
+ adres_plaats=clean(d.get("AdresPlaats")),
38
+ gps_latitude=clean(d.get("gpsLatitude")),
39
+ gps_longitude=clean(d.get("gpsLongitude")),
40
+ )
41
+
42
+ @staticmethod
43
+ def from_list(data: list[dict[str, Any]]) -> list[VestigingsAdresDomain]: # noqa: D102
44
+ return [VestigingsAdresDomain.from_dict(item) for item in data or []]
45
+
46
+ @staticmethod
47
+ def load_from_file(path: str, encoding: str = "utf-8") -> list[VestigingsAdresDomain]: # noqa: D102
48
+ with open(path, encoding=encoding) as f:
49
+ data = json.load(f)
50
+ if not isinstance(data, list):
51
+ raise ValueError("JSON must be a list of vestigingsprofielen.")
52
+ return VestigingsAdresDomain.from_list(data)
53
+
54
+ @staticmethod
55
+ def load_from_json(json_str: str) -> list[VestigingsAdresDomain]: # noqa: D102
56
+ data = json.loads(json_str)
57
+ if not isinstance(data, list):
58
+ raise ValueError("JSON must be a list of vestigingsprofielen.")
59
+ return VestigingsAdresDomain.from_list(data)
60
+
61
+ def to_dict(self) -> dict[str, Any]: # noqa: D102
62
+ return asdict(self)
@@ -0,0 +1,48 @@
1
+ # ruff: noqa: D102
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import asdict, dataclass, field
6
+ from typing import Any
7
+
8
+ from kvk_connect.models.domain.vestigingsadresdomain import VestigingsAdresDomain
9
+
10
+
11
+ @dataclass
12
+ class VestigingsAdressenDomain:
13
+ """Domein model voor alle adressen behorende bij een KVVKRecord."""
14
+
15
+ adressen: list[VestigingsAdresDomain] = field(default_factory=list)
16
+
17
+ @staticmethod
18
+ def from_list(data: list[Any]) -> VestigingsAdressenDomain:
19
+ """Maak een VestigingsAdressen object van een lijst dicts of Vestigingsprofiel instanties."""
20
+ profielen = [
21
+ VestigingsAdresDomain.from_dict(item) if not isinstance(item, VestigingsAdresDomain) else item
22
+ for item in (data or [])
23
+ ]
24
+ return VestigingsAdressenDomain(adressen=profielen)
25
+
26
+ @staticmethod
27
+ def load_from_file(path: str, encoding: str = "utf-8") -> VestigingsAdressenDomain: # noqa: D102
28
+ with open(path, encoding=encoding) as f:
29
+ data = json.load(f)
30
+ if not isinstance(data, list):
31
+ raise ValueError("JSON must be a list of vestigingsprofielen.")
32
+ return VestigingsAdressenDomain.from_list(data)
33
+
34
+ @staticmethod
35
+ def load_from_json(json_str: str) -> VestigingsAdressenDomain: # noqa: D102
36
+ data = json.loads(json_str)
37
+ if not isinstance(data, list):
38
+ raise ValueError("JSON must be a list of vestigingsprofielen.")
39
+ return VestigingsAdressenDomain.from_list(data)
40
+
41
+ def add_adres(self, adres: VestigingsAdresDomain) -> None: # noqa: D102
42
+ self.adressen.append(adres)
43
+
44
+ def add_adressen(self, adres: VestigingsAdressenDomain) -> None: # noqa: D102
45
+ self.adressen.extend(adres.adressen)
46
+
47
+ def to_dict(self) -> dict: # noqa: D102
48
+ return asdict(self)
@@ -0,0 +1,58 @@
1
+ # ruff: noqa: D102
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import asdict, dataclass
5
+ from typing import Any
6
+
7
+
8
+ @dataclass
9
+ class VestigingsProfielDomain:
10
+ """Dataclass voor vestigingsprofiel domeinmodel (gefilterde velden)."""
11
+
12
+ vestigingsnummer: str | None = None
13
+
14
+ cor_adres_volledig: str | None = None
15
+ cor_adres_postcode: str | None = None
16
+ cor_adres_postbusnummer: int | None = None
17
+ cor_adres_plaats: str | None = None
18
+ cor_adres_land: str | None = None
19
+
20
+ bzk_adres_volledig: str | None = None
21
+ bzk_adres_straatnaam: str | None = None
22
+ bzk_adres_huisnummer: int | None = None
23
+ bzk_adres_postcode: str | None = None
24
+ bzk_adres_plaats: str | None = None
25
+ bzk_adres_land: str | None = None
26
+ bzk_adres_gps_latitude: str | None = None
27
+ bzk_adres_gps_longitude: str | None = None
28
+
29
+ registratie_datum_aanvang_vestiging: str | None = None
30
+ registratie_datum_einde_vestiging: str | None = None
31
+
32
+ @staticmethod
33
+ def from_dict(d: dict[str, Any]) -> VestigingsProfielDomain:
34
+ """Maak een VestigingsProfielDomain uit een dictionary."""
35
+ if not d:
36
+ return VestigingsProfielDomain()
37
+ return VestigingsProfielDomain(
38
+ vestigingsnummer=d.get("vestigingsnummer"),
39
+ cor_adres_volledig=d.get("corAdresVolledig"),
40
+ cor_adres_postcode=d.get("corAdresPostcode"),
41
+ cor_adres_postbusnummer=d.get("corAdresPostbusnummer"),
42
+ cor_adres_plaats=d.get("corAdresPlaats"),
43
+ cor_adres_land=d.get("corAdresLand"),
44
+ bzk_adres_volledig=d.get("bzkAdresVolledig"),
45
+ bzk_adres_straatnaam=d.get("bzkAdresStraatnaam"),
46
+ bzk_adres_huisnummer=d.get("bzkAdresHuisnummer"),
47
+ bzk_adres_postcode=d.get("bzkAdresPostcode"),
48
+ bzk_adres_plaats=d.get("bzkAdresPlaats"),
49
+ bzk_adres_land=d.get("bzkAdresLand"),
50
+ bzk_adres_gps_latitude=d.get("bzkAdresGpsLatitude"),
51
+ bzk_adres_gps_longitude=d.get("bzkAdresGpsLongitude"),
52
+ registratie_datum_aanvang_vestiging=d.get("RegistratieDatumAanvangVestiging"),
53
+ registratie_datum_einde_vestiging=d.get("RegistratieDatumEindeVestiging"),
54
+ )
55
+
56
+ def to_dict(self) -> dict[str, Any]:
57
+ """Converteer domeinmodel naar dictionary."""
58
+ return asdict(self)
@@ -0,0 +1,5 @@
1
+ from sqlalchemy.orm import DeclarativeBase
2
+
3
+
4
+ class Base(DeclarativeBase):
5
+ pass
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import UTC, datetime
4
+
5
+ from sqlalchemy import Date, DateTime, Index, Integer, String, Text
6
+ from sqlalchemy.orm import Mapped, mapped_column
7
+
8
+ from kvk_connect.models.orm.base import Base
9
+
10
+
11
+ class BasisProfielORM(Base):
12
+ __tablename__ = "basisprofielen"
13
+
14
+ # Primary key
15
+ kvk_nummer: Mapped[str] = mapped_column("kvkNummer", String(8), primary_key=True)
16
+
17
+ # Text fields
18
+ naam: Mapped[str | None] = mapped_column("naam", Text)
19
+ eerste_handelsnaam: Mapped[str | None] = mapped_column("eersteHandelsnaam", Text)
20
+ websites: Mapped[str | None] = mapped_column("websites", Text)
21
+
22
+ # String fields
23
+ hoofdactiviteit: Mapped[str | None] = mapped_column("hoofdactiviteit", String(255))
24
+ hoofdactiviteit_omschrijving: Mapped[str | None] = mapped_column("hoofdactiviteitOmschrijving", String(255))
25
+ activiteit_overig: Mapped[str | None] = mapped_column("activiteitOverig", String(255))
26
+ rechtsvorm: Mapped[str | None] = mapped_column("rechtsvorm", String(128))
27
+ rechtsvorm_uitgebreid: Mapped[str | None] = mapped_column("rechtsvormUitgebreid", String(255))
28
+
29
+ # Integer field
30
+ totaal_werkzame_personen: Mapped[int | None] = mapped_column("totaalWerkzamePersonen", Integer)
31
+
32
+ # Date fields
33
+ registratie_datum_aanvang: Mapped[datetime | None] = mapped_column("RegistratieDatumAanvang", Date)
34
+ registratie_datum_einde: Mapped[datetime | None] = mapped_column("RegistratieDatumEinde", Date)
35
+
36
+ # Timestamp fields with defaults
37
+ created_at: Mapped[datetime] = mapped_column(
38
+ "created_at", DateTime(timezone=True), default=lambda: datetime.now(UTC)
39
+ )
40
+
41
+ last_updated: Mapped[datetime] = mapped_column(
42
+ "last_updated",
43
+ DateTime(timezone=True),
44
+ default=lambda: datetime.now(UTC),
45
+ onupdate=lambda: datetime.now(UTC),
46
+ index=True, # Index voor last_updated filtering
47
+ )
48
+
49
+ __table_args__ = (
50
+ # Composite index voor outdated checks
51
+ Index("ix_basisprofiel_kvk_updated", kvk_nummer, last_updated),
52
+ )
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import UTC, datetime
4
+
5
+ from sqlalchemy import DateTime, Index, String
6
+ from sqlalchemy.orm import Mapped, mapped_column
7
+
8
+ from kvk_connect.models.orm.base import Base
9
+
10
+
11
+ class VestigingenORM(Base):
12
+ """Relatie tussen KvK-nummers en hun vestigingen.
13
+
14
+ Relatie:
15
+ - 1 record met vestigingsnummer='000000000000' = KvK zonder vestigingen (1:0)
16
+ - N records met vestigingsnummer ingevuld = KvK met vestigingen (1:N)
17
+
18
+ Gebruikt een sentinel waarde ('000000000000') i.p.v. NULL voor betere
19
+ upsert performance met merge().
20
+ """
21
+
22
+ __tablename__ = "vestigingen"
23
+
24
+ # Sentinel waarde voor ontbrekende vestigingsnummers
25
+ SENTINEL_VESTIGINGSNUMMER = "000000000000"
26
+
27
+ # Composite primary key
28
+ kvk_nummer: Mapped[str] = mapped_column("kvkNummer", String(8), primary_key=True)
29
+ vestigingsnummer: Mapped[str] = mapped_column("vestigingsnummer", String(12), primary_key=True)
30
+
31
+ # Timestamp fields with defaults
32
+ created_at: Mapped[datetime] = mapped_column(
33
+ "created_at", DateTime(timezone=True), default=lambda: datetime.now(UTC)
34
+ )
35
+
36
+ last_updated: Mapped[datetime] = mapped_column(
37
+ "last_updated",
38
+ DateTime(timezone=True),
39
+ default=lambda: datetime.now(UTC),
40
+ onupdate=lambda: datetime.now(UTC),
41
+ index=True,
42
+ )
43
+
44
+ __table_args__ = (
45
+ # Index voor filteren op sentinel waarden
46
+ Index(
47
+ "ix_kvkvestigingen_vestigingsnummer_filtered",
48
+ vestigingsnummer,
49
+ postgresql_where=vestigingsnummer != SENTINEL_VESTIGINGSNUMMER,
50
+ ),
51
+ # Composite index voor joins met vestigingsprofiel
52
+ Index("ix_kvkvestigingen_vest_updated", vestigingsnummer, last_updated),
53
+ )
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ from sqlalchemy import DateTime, Index, String
6
+ from sqlalchemy.orm import Mapped, mapped_column
7
+
8
+ from kvk_connect.models.orm.base import Base
9
+
10
+ """
11
+ ORM model for the 'signalen' table.
12
+ """
13
+
14
+
15
+ class SignaalORM(Base):
16
+ __tablename__ = "signalen"
17
+
18
+ # Primary key (UUID)
19
+ id: Mapped[str] = mapped_column("id", String(36), primary_key=True)
20
+
21
+ # Required fields with indexes
22
+ timestamp: Mapped[datetime] = mapped_column(
23
+ "timestamp",
24
+ DateTime,
25
+ index=True, # Index voor timestamp filtering
26
+ )
27
+ kvknummer: Mapped[str] = mapped_column(
28
+ "kvknummer",
29
+ String(8),
30
+ index=True, # Index voor joins met basisprofiel
31
+ )
32
+ signaal_type: Mapped[str] = mapped_column("signaalType", String(100))
33
+
34
+ # Optional field
35
+ vestigingsnummer: Mapped[str | None] = mapped_column("vestigingsnummer", String(12))
36
+
37
+ __table_args__ = (
38
+ # Composite index voor outdated checks
39
+ Index("ix_signaal_kvk_timestamp", kvknummer, timestamp),
40
+ )