dg-kit 0.1.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.
Files changed (29) hide show
  1. dg_kit-0.1.0/PKG-INFO +99 -0
  2. dg_kit-0.1.0/README.md +85 -0
  3. dg_kit-0.1.0/pyproject.toml +31 -0
  4. dg_kit-0.1.0/src/dg_kit/__init__.py +0 -0
  5. dg_kit-0.1.0/src/dg_kit/base/__init__.py +0 -0
  6. dg_kit-0.1.0/src/dg_kit/base/business_information.py +60 -0
  7. dg_kit-0.1.0/src/dg_kit/base/convention.py +57 -0
  8. dg_kit-0.1.0/src/dg_kit/base/data_catalog.py +7 -0
  9. dg_kit-0.1.0/src/dg_kit/base/dataclasses/__init__.py +7 -0
  10. dg_kit-0.1.0/src/dg_kit/base/dataclasses/business_information.py +77 -0
  11. dg_kit-0.1.0/src/dg_kit/base/dataclasses/convention.py +30 -0
  12. dg_kit-0.1.0/src/dg_kit/base/dataclasses/data_catalog.py +64 -0
  13. dg_kit-0.1.0/src/dg_kit/base/dataclasses/logical_model.py +86 -0
  14. dg_kit-0.1.0/src/dg_kit/base/dataclasses/physical_model.py +38 -0
  15. dg_kit-0.1.0/src/dg_kit/base/enums.py +13 -0
  16. dg_kit-0.1.0/src/dg_kit/base/logical_model.py +66 -0
  17. dg_kit-0.1.0/src/dg_kit/base/physical_model.py +41 -0
  18. dg_kit-0.1.0/src/dg_kit/integrations/__init__.py +0 -0
  19. dg_kit-0.1.0/src/dg_kit/integrations/dbt/README.md +27 -0
  20. dg_kit-0.1.0/src/dg_kit/integrations/dbt/__init__.py +0 -0
  21. dg_kit-0.1.0/src/dg_kit/integrations/dbt/parser.py +202 -0
  22. dg_kit-0.1.0/src/dg_kit/integrations/notion/README.md +38 -0
  23. dg_kit-0.1.0/src/dg_kit/integrations/notion/__init__.py +0 -0
  24. dg_kit-0.1.0/src/dg_kit/integrations/notion/api.py +495 -0
  25. dg_kit-0.1.0/src/dg_kit/integrations/notion/formater.py +65 -0
  26. dg_kit-0.1.0/src/dg_kit/integrations/odm/README.md +46 -0
  27. dg_kit-0.1.0/src/dg_kit/integrations/odm/__init__.py +0 -0
  28. dg_kit-0.1.0/src/dg_kit/integrations/odm/attr_types.py +6 -0
  29. dg_kit-0.1.0/src/dg_kit/integrations/odm/parser.py +490 -0
dg_kit-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.3
2
+ Name: dg_kit
3
+ Version: 0.1.0
4
+ Summary: Data Governance Kit provides programmatic access to data governance metadata with integrations for different Logical and Physical Modeling tools.
5
+ Author: Chelidze Georgii
6
+ Author-email: Chelidze Georgii <chelidze.georgii.d@gmail.com>
7
+ License: MIT
8
+ Requires-Dist: pyyaml>=6.0.3 ; extra == 'dbt'
9
+ Requires-Dist: notion-client>=2.7.0 ; extra == 'notion'
10
+ Requires-Python: >=3.10
11
+ Provides-Extra: dbt
12
+ Provides-Extra: notion
13
+ Description-Content-Type: text/markdown
14
+
15
+ ## Data Governance Kit (dg_kit)
16
+
17
+ Data Governance Kit helps you access Data Governance information programmatically.
18
+ It provides core objects that model Physical Model, Logical Model, Business Information,
19
+ and related governance metadata. Integrations let you pull this data from tools like
20
+ dbt, Oracle Data Modeler, and Notion, with more connectors planned in upcoming releases.
21
+
22
+ This toolkit is handy for building Data Governance CI gates, strengthening Data Ops
23
+ practices, and keeping governance checks close to your delivery workflows.
24
+
25
+ ## Requirements
26
+ - Python >= 3.10
27
+
28
+ ## Install
29
+ ```bash
30
+ pip install -e .
31
+ ```
32
+
33
+ Optional extras:
34
+ ```bash
35
+ pip install -e ".[dbt]"
36
+ pip install -e ".[notion]"
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### Parse an Oracle Data Modeler project
42
+ ```python
43
+ from dg_kit.integrations.odm.parser import ODMParser
44
+
45
+ parser = ODMParser("path/to/model.dmd")
46
+ bi = parser.parse_bi()
47
+ lm = parser.parse_lm()
48
+
49
+ print(lm.version, len(lm.entities))
50
+ ```
51
+
52
+ ### Parse a dbt project into a physical model
53
+ ```python
54
+ from dg_kit.integrations.dbt.parser import DBTParser
55
+
56
+ pm = DBTParser("path/to/dbt_project").parse_pm()
57
+ print(pm.version, len(pm.tables))
58
+ ```
59
+
60
+ ### Validate with conventions
61
+ ```python
62
+ from dg_kit.base.convention import Convention, ConventionValidator
63
+ from dg_kit.base.enums import ConventionRuleSeverity
64
+
65
+ convention = Convention("example")
66
+
67
+ @convention.rule(
68
+ name="has-entities",
69
+ severity=ConventionRuleSeverity.ERROR,
70
+ description="Logical model must contain at least one entity",
71
+ )
72
+ def has_entities(lm, pm):
73
+ return set() if lm.entities else {("no entities")}
74
+
75
+ issues = ConventionValidator(lm, pm, convention).validate()
76
+ ```
77
+
78
+ ### Sync to Notion data catalog
79
+ ```python
80
+ from dg_kit.integrations.notion.api import NotionDataCatalog
81
+
82
+ catalog = NotionDataCatalog(
83
+ notion_token="secret",
84
+ dc_table_id="data_source_id",
85
+ )
86
+ rows = catalog.pull()
87
+ print(len(rows))
88
+ ```
89
+
90
+ ## Development
91
+ Run tests:
92
+ ```bash
93
+ pytest
94
+ ```
95
+
96
+ Export requirements with uv:
97
+ ```bash
98
+ uv export --extra dbt --extra notion --group test -o requirements.txt
99
+ ```
dg_kit-0.1.0/README.md ADDED
@@ -0,0 +1,85 @@
1
+ ## Data Governance Kit (dg_kit)
2
+
3
+ Data Governance Kit helps you access Data Governance information programmatically.
4
+ It provides core objects that model Physical Model, Logical Model, Business Information,
5
+ and related governance metadata. Integrations let you pull this data from tools like
6
+ dbt, Oracle Data Modeler, and Notion, with more connectors planned in upcoming releases.
7
+
8
+ This toolkit is handy for building Data Governance CI gates, strengthening Data Ops
9
+ practices, and keeping governance checks close to your delivery workflows.
10
+
11
+ ## Requirements
12
+ - Python >= 3.10
13
+
14
+ ## Install
15
+ ```bash
16
+ pip install -e .
17
+ ```
18
+
19
+ Optional extras:
20
+ ```bash
21
+ pip install -e ".[dbt]"
22
+ pip install -e ".[notion]"
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ### Parse an Oracle Data Modeler project
28
+ ```python
29
+ from dg_kit.integrations.odm.parser import ODMParser
30
+
31
+ parser = ODMParser("path/to/model.dmd")
32
+ bi = parser.parse_bi()
33
+ lm = parser.parse_lm()
34
+
35
+ print(lm.version, len(lm.entities))
36
+ ```
37
+
38
+ ### Parse a dbt project into a physical model
39
+ ```python
40
+ from dg_kit.integrations.dbt.parser import DBTParser
41
+
42
+ pm = DBTParser("path/to/dbt_project").parse_pm()
43
+ print(pm.version, len(pm.tables))
44
+ ```
45
+
46
+ ### Validate with conventions
47
+ ```python
48
+ from dg_kit.base.convention import Convention, ConventionValidator
49
+ from dg_kit.base.enums import ConventionRuleSeverity
50
+
51
+ convention = Convention("example")
52
+
53
+ @convention.rule(
54
+ name="has-entities",
55
+ severity=ConventionRuleSeverity.ERROR,
56
+ description="Logical model must contain at least one entity",
57
+ )
58
+ def has_entities(lm, pm):
59
+ return set() if lm.entities else {("no entities")}
60
+
61
+ issues = ConventionValidator(lm, pm, convention).validate()
62
+ ```
63
+
64
+ ### Sync to Notion data catalog
65
+ ```python
66
+ from dg_kit.integrations.notion.api import NotionDataCatalog
67
+
68
+ catalog = NotionDataCatalog(
69
+ notion_token="secret",
70
+ dc_table_id="data_source_id",
71
+ )
72
+ rows = catalog.pull()
73
+ print(len(rows))
74
+ ```
75
+
76
+ ## Development
77
+ Run tests:
78
+ ```bash
79
+ pytest
80
+ ```
81
+
82
+ Export requirements with uv:
83
+ ```bash
84
+ uv export --extra dbt --extra notion --group test -o requirements.txt
85
+ ```
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.9.26,<0.10.0"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "dg_kit"
7
+ version = "0.1.0"
8
+ description = "Data Governance Kit provides programmatic access to data governance metadata with integrations for different Logical and Physical Modeling tools."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Chelidze Georgii", email = "chelidze.georgii.d@gmail.com" }]
13
+
14
+ [project.optional-dependencies]
15
+ notion = [
16
+ "notion-client>=2.7.0",
17
+ ]
18
+ dbt = [
19
+ "pyyaml>=6.0.3",
20
+ ]
21
+
22
+ [dependency-groups]
23
+ dev = [
24
+ "pre-commit>=4.5.1",
25
+ "pyright>=1.1.408",
26
+ "pytest>=9.0.2",
27
+ "ruff>=0.15.0",
28
+ ]
29
+ test = [
30
+ "pytest>=9.0.2",
31
+ ]
File without changes
File without changes
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+ from typing import Dict
3
+
4
+
5
+ from dg_kit.base.dataclasses.business_information import (
6
+ Team,
7
+ Contact,
8
+ Document,
9
+ Email,
10
+ Url,
11
+ )
12
+
13
+
14
+ class BusinessInformation:
15
+ def __init__(self, version: str):
16
+ self.version = version
17
+ self.teams: Dict[str, Team] = {}
18
+ self.contacts: Dict[str, Contact] = {}
19
+ self.documents: Dict[str, Document] = {}
20
+ self.emails: Dict[str, Email] = {}
21
+ self.urls: Dict[str, Url] = {}
22
+ self.all_units_by_id: Dict[str, Team | Contact | Document | Email | Url] = {}
23
+ self.all_units_by_natural_key: Dict[
24
+ str, Team | Contact | Document | Email | Url
25
+ ] = {}
26
+
27
+ def register_team(self, team: Team) -> None:
28
+ self.teams[team.id] = team
29
+ self.all_units_by_id[team.id] = team
30
+ self.all_units_by_natural_key[team.natural_key] = team
31
+
32
+ def register_contact(self, contact: Contact) -> None:
33
+ self.contacts[contact.id] = contact
34
+ self.all_units_by_id[contact.id] = contact
35
+ self.all_units_by_natural_key[contact.natural_key] = contact
36
+
37
+ def register_document(self, document: Document) -> None:
38
+ self.documents[document.id] = document
39
+ self.all_units_by_id[document.id] = document
40
+ self.all_units_by_natural_key[document.natural_key] = document
41
+
42
+ def register_email(self, email: Email) -> None:
43
+ self.emails[email.id] = email
44
+ self.all_units_by_id[email.id] = email
45
+ self.all_units_by_natural_key[email.natural_key] = email
46
+
47
+ def register_url(self, url: Url) -> None:
48
+ self.urls[url.id] = url
49
+ self.all_units_by_id[url.id] = url
50
+ self.all_units_by_natural_key[url.natural_key] = url
51
+
52
+
53
+ class BusinessInformationDatabase:
54
+ def __init__(self):
55
+ self.business_information: Dict[str, BusinessInformation] = {}
56
+
57
+ def register_business_information(
58
+ self, business_information: BusinessInformation
59
+ ) -> None:
60
+ self.business_information[business_information.version] = business_information
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List
4
+
5
+ from dg_kit.base.physical_model import PhysicalModel
6
+ from dg_kit.base.logical_model import LogicalModel
7
+ from dg_kit.base.enums import ConventionRuleSeverity
8
+ from dg_kit.base.dataclasses.convention import (
9
+ ConventionRule,
10
+ ConventionRuleFn,
11
+ ConventionBreach,
12
+ )
13
+
14
+
15
+ class Convention:
16
+ def __init__(self, name: str):
17
+ self.name = name
18
+ self._rules: List[ConventionRule] = []
19
+
20
+ def rule(
21
+ self,
22
+ name: str,
23
+ severity: ConventionRuleSeverity,
24
+ description: str,
25
+ ):
26
+ def decorator(fn: ConventionRuleFn) -> ConventionRuleFn:
27
+ self._rules.append(ConventionRule(name, severity, description, fn))
28
+ return fn
29
+
30
+ return decorator
31
+
32
+ @property
33
+ def rules(self) -> List[ConventionRule]:
34
+ return list(self._rules)
35
+
36
+
37
+ class ConventionValidator:
38
+ def __init__(self, lm: LogicalModel, pm: PhysicalModel, convention: Convention):
39
+ self.lm = lm
40
+ self.pm = pm
41
+ self.convention = convention
42
+
43
+ def validate(self) -> List[ConventionBreach]:
44
+ issues: List[ConventionBreach] = []
45
+
46
+ for rule in self.convention.rules:
47
+ res = rule.fn(self.lm, self.pm)
48
+ for issue in res:
49
+ issue = ConventionBreach(
50
+ severity=rule.severity,
51
+ message=issue.message,
52
+ unit_id=issue.unit_id,
53
+ unit_natural_key=issue.unit_natural_key,
54
+ )
55
+ issues.append(issue)
56
+
57
+ return issues
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ from abc import ABC
5
+
6
+
7
+ class DataCatalog(ABC): ...
@@ -0,0 +1,7 @@
1
+ import hashlib
2
+
3
+
4
+ def id_generator(*parts: str, size: int = 16) -> str:
5
+ h = hashlib.blake2b(digest_size=size)
6
+ h.update("\x1f".join(parts).encode("utf-8"))
7
+ return h.hexdigest()
@@ -0,0 +1,77 @@
1
+ """
2
+ Docstring for dg_kit.base.dataclasses.business_information
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional, Tuple
9
+
10
+ from dg_kit.base.dataclasses import id_generator
11
+
12
+
13
+ @dataclass(frozen=True, slots=True)
14
+ class SlackChannelUrl:
15
+ id: str = field(init=False)
16
+ natural_key: str
17
+ name: str
18
+ url: Optional[str]
19
+
20
+ def __post_init__(self) -> None:
21
+ object.__setattr__(self, "id", id_generator(self.natural_key))
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class Email:
26
+ id: str = field(init=False)
27
+ natural_key: str
28
+ name: str
29
+ email_address: Optional[str]
30
+
31
+ def __post_init__(self) -> None:
32
+ object.__setattr__(self, "id", id_generator(self.natural_key))
33
+
34
+
35
+ @dataclass(frozen=True, slots=True)
36
+ class Url:
37
+ id: str = field(init=False)
38
+ natural_key: str
39
+ name: str
40
+ url: Optional[str]
41
+
42
+ def __post_init__(self) -> None:
43
+ object.__setattr__(self, "id", id_generator(self.natural_key))
44
+
45
+
46
+ @dataclass(frozen=True, slots=True)
47
+ class Contact:
48
+ id: str = field(init=False)
49
+ natural_key: str
50
+ name: str
51
+ emails: Tuple[Email, ...]
52
+ urls: Tuple[Url, ...]
53
+
54
+ def __post_init__(self) -> None:
55
+ object.__setattr__(self, "id", id_generator(self.natural_key))
56
+
57
+
58
+ @dataclass(frozen=True, slots=True)
59
+ class Team:
60
+ id: str = field(init=False)
61
+ natural_key: str
62
+ name: str
63
+ contacts: Tuple[Contact, ...]
64
+
65
+ def __post_init__(self) -> None:
66
+ object.__setattr__(self, "id", id_generator(self.natural_key))
67
+
68
+
69
+ @dataclass(frozen=True, slots=True)
70
+ class Document:
71
+ id: str = field(init=False)
72
+ natural_key: str
73
+ name: str
74
+ reference: Optional[str]
75
+
76
+ def __post_init__(self) -> None:
77
+ object.__setattr__(self, "id", id_generator(self.natural_key))
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional, Protocol, Set
5
+
6
+ from dg_kit.base.enums import ConventionRuleSeverity
7
+ from dg_kit.base.logical_model import LogicalModel
8
+ from dg_kit.base.physical_model import PhysicalModel
9
+
10
+
11
+ @dataclass(frozen=True, slots=True)
12
+ class ConventionBreach:
13
+ severity: ConventionRuleSeverity
14
+ message: str
15
+ unit_id: Optional[str] = None
16
+ unit_natural_key: Optional[str] = None
17
+
18
+
19
+ class ConventionRuleFn(Protocol):
20
+ def __call__(
21
+ self, lm: LogicalModel, pm: PhysicalModel
22
+ ) -> Set[ConventionBreach]: ...
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class ConventionRule:
27
+ name: str
28
+ severity: ConventionRuleSeverity
29
+ description: str
30
+ fn: ConventionRuleFn
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, Tuple
4
+
5
+ from datetime import datetime
6
+
7
+ from dataclasses import dataclass
8
+
9
+ from dg_kit.base.enums import DataUnitType
10
+ from dg_kit.base.dataclasses.business_information import Document, Team
11
+
12
+
13
+ @dataclass(frozen=True, slots=True)
14
+ class DataCatalogRow:
15
+ data_unit_name: str
16
+ data_unit_type: DataUnitType
17
+ domain: str
18
+ data_unit_uuid: str
19
+ last_edited_time: Optional[datetime] = None
20
+ created_time: Optional[datetime] = None
21
+
22
+ def __post_init__(self) -> None: ...
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class EntityTypeDataUnitPageInfo:
27
+ data_unit_type: DataUnitType
28
+ description: str
29
+ linked_documents: Tuple[Document, ...]
30
+ responsible_parties: Tuple[Team, ...]
31
+ master_source_systems: Tuple[str, ...]
32
+ core_layer_mapping: Tuple[str, ...]
33
+ pk_attributes_page_ids: Tuple[str, ...]
34
+ attributes_page_ids: Tuple[str, ...]
35
+ relationes_page_ids: Tuple[str, ...]
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class AttributeTypeDataUnitPageInfo:
40
+ parent_entity_page_id: str
41
+ data_unit_type: DataUnitType
42
+ description: str
43
+ data_type: str
44
+ sensitivity_type: str
45
+ linked_documents: Tuple[Document, ...]
46
+ responsible_parties: Tuple[Team, ...]
47
+ core_layer_mapping: Tuple[str, ...]
48
+ master_source_systems: Tuple[str, ...]
49
+
50
+
51
+ @dataclass(frozen=True, slots=True)
52
+ class RelationTypeDataUnitPageInfo:
53
+ data_unit_type: DataUnitType
54
+ description: str
55
+ linked_documents: Tuple[Document, ...]
56
+ responsible_parties: Tuple[Team, ...]
57
+ core_layer_mapping: Tuple[str, ...]
58
+ master_source_systems: Tuple[str, ...]
59
+ source_entity_page_id: str
60
+ target_entity_page_id: str
61
+ optional_source: bool
62
+ optional_target: bool
63
+ source_cardinality: str
64
+ target_cardinality: str
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime
5
+ from typing import Optional, Tuple
6
+
7
+ from dg_kit.base.dataclasses import id_generator
8
+ from dg_kit.base.dataclasses.business_information import Team, Document
9
+
10
+
11
+ @dataclass(frozen=True, slots=True)
12
+ class EntityIdentifier:
13
+ id: str = field(init=False)
14
+ natural_key: str
15
+ name: Optional[str]
16
+ is_pk: bool
17
+ entity_id: str
18
+ used_attributes_ids: Tuple[str, ...]
19
+
20
+ def __post_init__(self) -> None:
21
+ object.__setattr__(self, "id", id_generator(self.natural_key))
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class Relation:
26
+ id: str = field(init=False)
27
+ natural_key: str
28
+ name: str
29
+ domain: Optional[str]
30
+ description: str
31
+ pm_map: Tuple[str, ...]
32
+ master_source_systems: Tuple[str, ...]
33
+ responsible_parties: Tuple[Team, ...]
34
+ documents: Tuple[Document, ...]
35
+ source_entity_id: str
36
+ target_entity_id: str
37
+ optional_source: Optional[bool]
38
+ optional_target: Optional[bool]
39
+ source_cardinality: Optional[str]
40
+ target_cardinality: Optional[str]
41
+ created_by: Optional[str] = None
42
+ created_time: Optional[datetime] = None
43
+
44
+ def __post_init__(self) -> None:
45
+ object.__setattr__(self, "id", id_generator(self.natural_key))
46
+
47
+
48
+ @dataclass(frozen=True, slots=True)
49
+ class Attribute:
50
+ id: str = field(init=False)
51
+ natural_key: str
52
+ name: str
53
+ domain: Optional[str]
54
+ description: str
55
+ sensitivity_type: str
56
+ data_type: str
57
+ pm_map: Tuple[str, ...]
58
+ master_source_systems: Tuple[str, ...]
59
+ responsible_parties: Tuple[Team, ...]
60
+ documents: Tuple[Document, ...]
61
+ entity_id: str
62
+ created_by: Optional[str] = None
63
+ created_time: Optional[datetime] = None
64
+
65
+ def __post_init__(self) -> None:
66
+ object.__setattr__(self, "id", id_generator(self.natural_key))
67
+
68
+
69
+ @dataclass(frozen=True, slots=True)
70
+ class Entity:
71
+ id: str = field(init=False)
72
+ natural_key: str
73
+ name: str
74
+ domain: Optional[str]
75
+ description: str
76
+ identifiers: Tuple[EntityIdentifier, ...]
77
+ attributes: Tuple[str, ...]
78
+ pm_map: Tuple[str, ...]
79
+ master_source_systems: Tuple[str, ...]
80
+ responsible_parties: Tuple[Team, ...]
81
+ documents: Tuple[Document, ...]
82
+ created_by: Optional[str] = None
83
+ created_time: Optional[datetime] = None
84
+
85
+ def __post_init__(self) -> None:
86
+ object.__setattr__(self, "id", id_generator(self.natural_key))
@@ -0,0 +1,38 @@
1
+ from dataclasses import dataclass, field
2
+ from dg_kit.base.dataclasses import id_generator
3
+
4
+
5
+ @dataclass(frozen=True, slots=True)
6
+ class Layer:
7
+ id: str = field(init=False)
8
+ natural_key: str
9
+ name: str
10
+ is_landing: bool
11
+
12
+ def __post_init__(self) -> None:
13
+ object.__setattr__(self, "id", id_generator(self.natural_key))
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class Table:
18
+ id: str = field(init=False)
19
+ natural_key: str
20
+ layer_id: str
21
+ name: str
22
+
23
+ def __post_init__(self) -> None:
24
+ object.__setattr__(self, "id", id_generator(self.natural_key))
25
+
26
+
27
+ @dataclass(frozen=True, slots=True)
28
+ class Column:
29
+ id: str = field(init=False)
30
+ natural_key: str
31
+ layer_id: str
32
+ table_id: str
33
+ name: str
34
+ data_type: str
35
+ description: str = ""
36
+
37
+ def __post_init__(self) -> None:
38
+ object.__setattr__(self, "id", id_generator(self.natural_key))
@@ -0,0 +1,13 @@
1
+ from enum import StrEnum
2
+
3
+
4
+ class ConventionRuleSeverity(StrEnum):
5
+ INFO = "info"
6
+ WARN = "warning"
7
+ ERROR = "error"
8
+
9
+
10
+ class DataUnitType(StrEnum):
11
+ ENTITY = "entity"
12
+ ATTRIBUTE = "attribute"
13
+ RELATION = "relation"