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.
- kvk_connect/__init__.py +11 -0
- kvk_connect/api/__init__.py +4 -0
- kvk_connect/api/client.py +183 -0
- kvk_connect/api/endpoints.py +24 -0
- kvk_connect/api/session.py +34 -0
- kvk_connect/cli/main.py +26 -0
- kvk_connect/db/__init__.py +0 -0
- kvk_connect/db/basisprofiel_reader.py +67 -0
- kvk_connect/db/basisprofiel_writer.py +73 -0
- kvk_connect/db/init.py +25 -0
- kvk_connect/db/kvkvestigingen_reader.py +41 -0
- kvk_connect/db/kvkvestigingen_writer.py +73 -0
- kvk_connect/db/signaal_reader.py +23 -0
- kvk_connect/db/signaal_writer.py +73 -0
- kvk_connect/db/vestigingenprofiel_reader.py +66 -0
- kvk_connect/db/vestigingsprofiel_writer.py +92 -0
- kvk_connect/logging_config.py +27 -0
- kvk_connect/mappers/__init__.py +1 -0
- kvk_connect/mappers/kvk_record_mapper.py +100 -0
- kvk_connect/mappers/map_mutatie_abonnement_api_to_mutatieabonnement.py +11 -0
- kvk_connect/mappers/map_vestigingen_api_to_vestigingsnummers.py +14 -0
- kvk_connect/mappers/map_vestigingsprofiel_api_to_vestigingsprofiel_domain.py +41 -0
- kvk_connect/models/__init__.py +0 -0
- kvk_connect/models/api/__init__.py +0 -0
- kvk_connect/models/api/abonnementen_api.py +42 -0
- kvk_connect/models/api/basisprofiel_api.py +233 -0
- kvk_connect/models/api/mutatie_abonnementen_api.py +40 -0
- kvk_connect/models/api/mutatiesignalen_api.py +44 -0
- kvk_connect/models/api/vestigingen_api.py +73 -0
- kvk_connect/models/api/vestigingsprofiel_api.py +71 -0
- kvk_connect/models/domain/__init__.py +6 -0
- kvk_connect/models/domain/basisprofiel.py +65 -0
- kvk_connect/models/domain/kvkvestigingsnummersdomain.py +28 -0
- kvk_connect/models/domain/mutatie_abonnement.py +20 -0
- kvk_connect/models/domain/vestigingsadresdomain.py +62 -0
- kvk_connect/models/domain/vestigingsadressendomain.py +48 -0
- kvk_connect/models/domain/vestigingsprofiel_domain.py +58 -0
- kvk_connect/models/orm/base.py +5 -0
- kvk_connect/models/orm/basisprofiel_orm.py +52 -0
- kvk_connect/models/orm/kvkvestigingen_orm.py +53 -0
- kvk_connect/models/orm/signaal_orm.py +40 -0
- kvk_connect/models/orm/vestigingsprofiel_orm.py +58 -0
- kvk_connect/services/__init__.py +4 -0
- kvk_connect/services/record_service.py +66 -0
- kvk_connect/utils/__init__.py +5 -0
- kvk_connect/utils/env.py +16 -0
- kvk_connect/utils/formatting.py +11 -0
- kvk_connect/utils/rate_limit.py +21 -0
- kvk_connect/utils/tools.py +131 -0
- kvk_connect-0.1.6.dist-info/METADATA +352 -0
- kvk_connect-0.1.6.dist-info/RECORD +52 -0
- 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,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
|
+
)
|