pycharter 0.0.20__py3-none-any.whl → 0.0.22__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 (222) hide show
  1. api/dependencies/__init__.py +2 -1
  2. api/dependencies/database.py +71 -5
  3. api/main.py +47 -8
  4. api/models/contracts.py +6 -4
  5. api/models/metadata.py +11 -7
  6. api/models/schemas.py +16 -10
  7. api/routes/v1/contracts.py +498 -226
  8. api/routes/v1/metadata.py +52 -211
  9. api/routes/v1/schemas.py +1 -1
  10. api/routes/v1/settings.py +88 -1
  11. api/utils.py +224 -0
  12. pycharter/__init__.py +149 -93
  13. pycharter/data/templates/template_transform_advanced.yaml +50 -0
  14. pycharter/data/templates/template_transform_simple.yaml +59 -0
  15. pycharter/db/models/base.py +1 -2
  16. pycharter/etl_generator/orchestrator.py +463 -487
  17. pycharter/metadata_store/postgres.py +16 -191
  18. pycharter/metadata_store/sqlite.py +12 -41
  19. {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/METADATA +284 -62
  20. pycharter-0.0.22.dist-info/RECORD +358 -0
  21. ui/static/404/index.html +1 -1
  22. ui/static/404.html +1 -1
  23. ui/static/__next.__PAGE__.txt +1 -1
  24. ui/static/__next._full.txt +2 -2
  25. ui/static/__next._head.txt +1 -1
  26. ui/static/__next._index.txt +2 -2
  27. ui/static/__next._tree.txt +2 -2
  28. ui/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
  29. ui/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
  30. ui/static/_next/static/chunks/c4fa4f4114b7c352.js +1 -0
  31. ui/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
  32. ui/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
  33. ui/static/_not-found/__next._full.txt +2 -2
  34. ui/static/_not-found/__next._head.txt +1 -1
  35. ui/static/_not-found/__next._index.txt +2 -2
  36. ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  37. ui/static/_not-found/__next._not-found.txt +1 -1
  38. ui/static/_not-found/__next._tree.txt +2 -2
  39. ui/static/_not-found/index.html +1 -1
  40. ui/static/_not-found/index.txt +2 -2
  41. ui/static/contracts/__next._full.txt +3 -3
  42. ui/static/contracts/__next._head.txt +1 -1
  43. ui/static/contracts/__next._index.txt +2 -2
  44. ui/static/contracts/__next._tree.txt +2 -2
  45. ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  46. ui/static/contracts/__next.contracts.txt +1 -1
  47. ui/static/contracts/index.html +1 -1
  48. ui/static/contracts/index.txt +3 -3
  49. ui/static/documentation/__next._full.txt +3 -3
  50. ui/static/documentation/__next._head.txt +1 -1
  51. ui/static/documentation/__next._index.txt +2 -2
  52. ui/static/documentation/__next._tree.txt +2 -2
  53. ui/static/documentation/__next.documentation.__PAGE__.txt +2 -2
  54. ui/static/documentation/__next.documentation.txt +1 -1
  55. ui/static/documentation/index.html +2 -2
  56. ui/static/documentation/index.txt +3 -3
  57. ui/static/index.html +1 -1
  58. ui/static/index.txt +2 -2
  59. ui/static/metadata/__next._full.txt +2 -2
  60. ui/static/metadata/__next._head.txt +1 -1
  61. ui/static/metadata/__next._index.txt +2 -2
  62. ui/static/metadata/__next._tree.txt +2 -2
  63. ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  64. ui/static/metadata/__next.metadata.txt +1 -1
  65. ui/static/metadata/index.html +1 -1
  66. ui/static/metadata/index.txt +2 -2
  67. ui/static/quality/__next._full.txt +2 -2
  68. ui/static/quality/__next._head.txt +1 -1
  69. ui/static/quality/__next._index.txt +2 -2
  70. ui/static/quality/__next._tree.txt +2 -2
  71. ui/static/quality/__next.quality.__PAGE__.txt +1 -1
  72. ui/static/quality/__next.quality.txt +1 -1
  73. ui/static/quality/index.html +2 -2
  74. ui/static/quality/index.txt +2 -2
  75. ui/static/rules/__next._full.txt +2 -2
  76. ui/static/rules/__next._head.txt +1 -1
  77. ui/static/rules/__next._index.txt +2 -2
  78. ui/static/rules/__next._tree.txt +2 -2
  79. ui/static/rules/__next.rules.__PAGE__.txt +1 -1
  80. ui/static/rules/__next.rules.txt +1 -1
  81. ui/static/rules/index.html +1 -1
  82. ui/static/rules/index.txt +2 -2
  83. ui/static/schemas/__next._full.txt +2 -2
  84. ui/static/schemas/__next._head.txt +1 -1
  85. ui/static/schemas/__next._index.txt +2 -2
  86. ui/static/schemas/__next._tree.txt +2 -2
  87. ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  88. ui/static/schemas/__next.schemas.txt +1 -1
  89. ui/static/schemas/index.html +1 -1
  90. ui/static/schemas/index.txt +2 -2
  91. ui/static/settings/__next._full.txt +2 -2
  92. ui/static/settings/__next._head.txt +1 -1
  93. ui/static/settings/__next._index.txt +2 -2
  94. ui/static/settings/__next._tree.txt +2 -2
  95. ui/static/settings/__next.settings.__PAGE__.txt +1 -1
  96. ui/static/settings/__next.settings.txt +1 -1
  97. ui/static/settings/index.html +1 -1
  98. ui/static/settings/index.txt +2 -2
  99. ui/static/static/.gitkeep +0 -0
  100. ui/static/static/404/index.html +1 -0
  101. ui/static/static/404.html +1 -0
  102. ui/static/static/__next.__PAGE__.txt +10 -0
  103. ui/static/static/__next._full.txt +30 -0
  104. ui/static/static/__next._head.txt +7 -0
  105. ui/static/static/__next._index.txt +9 -0
  106. ui/static/static/__next._tree.txt +2 -0
  107. ui/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
  108. ui/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
  109. ui/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
  110. ui/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
  111. ui/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
  112. ui/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
  113. ui/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +1 -0
  114. ui/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
  115. ui/static/static/_next/static/chunks/75d88a058d8ffaa6.js +1 -0
  116. ui/static/static/_next/static/chunks/8c89634cf6bad76f.js +1 -0
  117. ui/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
  118. ui/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
  119. ui/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  120. ui/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
  121. ui/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
  122. ui/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
  123. ui/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
  124. ui/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
  125. ui/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
  126. ui/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
  127. ui/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
  128. ui/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
  129. ui/static/static/_not-found/__next._full.txt +17 -0
  130. ui/static/static/_not-found/__next._head.txt +7 -0
  131. ui/static/static/_not-found/__next._index.txt +9 -0
  132. ui/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
  133. ui/static/static/_not-found/__next._not-found.txt +4 -0
  134. ui/static/static/_not-found/__next._tree.txt +2 -0
  135. ui/static/static/_not-found/index.html +1 -0
  136. ui/static/static/_not-found/index.txt +17 -0
  137. ui/static/static/contracts/__next._full.txt +21 -0
  138. ui/static/static/contracts/__next._head.txt +7 -0
  139. ui/static/static/contracts/__next._index.txt +9 -0
  140. ui/static/static/contracts/__next._tree.txt +2 -0
  141. ui/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
  142. ui/static/static/contracts/__next.contracts.txt +4 -0
  143. ui/static/static/contracts/index.html +1 -0
  144. ui/static/static/contracts/index.txt +21 -0
  145. ui/static/static/documentation/__next._full.txt +21 -0
  146. ui/static/static/documentation/__next._head.txt +7 -0
  147. ui/static/static/documentation/__next._index.txt +9 -0
  148. ui/static/static/documentation/__next._tree.txt +2 -0
  149. ui/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
  150. ui/static/static/documentation/__next.documentation.txt +4 -0
  151. ui/static/static/documentation/index.html +93 -0
  152. ui/static/static/documentation/index.txt +21 -0
  153. ui/static/static/index.html +1 -0
  154. ui/static/static/index.txt +30 -0
  155. ui/static/static/metadata/__next._full.txt +21 -0
  156. ui/static/static/metadata/__next._head.txt +7 -0
  157. ui/static/static/metadata/__next._index.txt +9 -0
  158. ui/static/static/metadata/__next._tree.txt +2 -0
  159. ui/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
  160. ui/static/static/metadata/__next.metadata.txt +4 -0
  161. ui/static/static/metadata/index.html +1 -0
  162. ui/static/static/metadata/index.txt +21 -0
  163. ui/static/static/quality/__next._full.txt +21 -0
  164. ui/static/static/quality/__next._head.txt +7 -0
  165. ui/static/static/quality/__next._index.txt +9 -0
  166. ui/static/static/quality/__next._tree.txt +2 -0
  167. ui/static/static/quality/__next.quality.__PAGE__.txt +9 -0
  168. ui/static/static/quality/__next.quality.txt +4 -0
  169. ui/static/static/quality/index.html +2 -0
  170. ui/static/static/quality/index.txt +21 -0
  171. ui/static/static/rules/__next._full.txt +21 -0
  172. ui/static/static/rules/__next._head.txt +7 -0
  173. ui/static/static/rules/__next._index.txt +9 -0
  174. ui/static/static/rules/__next._tree.txt +2 -0
  175. ui/static/static/rules/__next.rules.__PAGE__.txt +9 -0
  176. ui/static/static/rules/__next.rules.txt +4 -0
  177. ui/static/static/rules/index.html +1 -0
  178. ui/static/static/rules/index.txt +21 -0
  179. ui/static/static/schemas/__next._full.txt +21 -0
  180. ui/static/static/schemas/__next._head.txt +7 -0
  181. ui/static/static/schemas/__next._index.txt +9 -0
  182. ui/static/static/schemas/__next._tree.txt +2 -0
  183. ui/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
  184. ui/static/static/schemas/__next.schemas.txt +4 -0
  185. ui/static/static/schemas/index.html +1 -0
  186. ui/static/static/schemas/index.txt +21 -0
  187. ui/static/static/settings/__next._full.txt +21 -0
  188. ui/static/static/settings/__next._head.txt +7 -0
  189. ui/static/static/settings/__next._index.txt +9 -0
  190. ui/static/static/settings/__next._tree.txt +2 -0
  191. ui/static/static/settings/__next.settings.__PAGE__.txt +9 -0
  192. ui/static/static/settings/__next.settings.txt +4 -0
  193. ui/static/static/settings/index.html +1 -0
  194. ui/static/static/settings/index.txt +21 -0
  195. ui/static/static/validation/__next._full.txt +21 -0
  196. ui/static/static/validation/__next._head.txt +7 -0
  197. ui/static/static/validation/__next._index.txt +9 -0
  198. ui/static/static/validation/__next._tree.txt +2 -0
  199. ui/static/static/validation/__next.validation.__PAGE__.txt +9 -0
  200. ui/static/static/validation/__next.validation.txt +4 -0
  201. ui/static/static/validation/index.html +1 -0
  202. ui/static/static/validation/index.txt +21 -0
  203. ui/static/validation/__next._full.txt +2 -2
  204. ui/static/validation/__next._head.txt +1 -1
  205. ui/static/validation/__next._index.txt +2 -2
  206. ui/static/validation/__next._tree.txt +2 -2
  207. ui/static/validation/__next.validation.__PAGE__.txt +1 -1
  208. ui/static/validation/__next.validation.txt +1 -1
  209. ui/static/validation/index.html +1 -1
  210. ui/static/validation/index.txt +2 -2
  211. pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +0 -160
  212. pycharter-0.0.20.dist-info/RECORD +0 -247
  213. {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/WHEEL +0 -0
  214. {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/entry_points.txt +0 -0
  215. {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/licenses/LICENSE +0 -0
  216. {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/top_level.txt +0 -0
  217. /ui/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
  218. /ui/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
  219. /ui/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
  220. /ui/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
  221. /ui/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
  222. /ui/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
api/utils.py ADDED
@@ -0,0 +1,224 @@
1
+ """
2
+ Shared utilities for API routes.
3
+
4
+ This module provides common helper functions used across API endpoints,
5
+ including UUID handling, database queries, and error handling.
6
+ """
7
+
8
+ import logging
9
+ import uuid
10
+ from typing import Optional, TypeVar, Union
11
+
12
+ from fastapi import HTTPException, status
13
+ from sqlalchemy import cast, String
14
+ from sqlalchemy.orm import Session
15
+ from sqlalchemy.orm.decl_api import DeclarativeMeta
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ T = TypeVar("T")
20
+
21
+
22
+ def safe_uuid_to_str(value: Optional[Union[uuid.UUID, str]]) -> Optional[str]:
23
+ """
24
+ Safely convert a UUID or string to a string representation.
25
+
26
+ Handles both UUID objects and string representations, including
27
+ cases where SQLite might store UUIDs as strings.
28
+
29
+ Args:
30
+ value: UUID object, string, or None
31
+
32
+ Returns:
33
+ String representation of UUID, or None if value is None
34
+ """
35
+ if value is None:
36
+ return None
37
+ if isinstance(value, uuid.UUID):
38
+ return str(value)
39
+ if isinstance(value, str):
40
+ # If it's already a string, validate it's a valid UUID format
41
+ try:
42
+ # Validate and normalize the UUID string
43
+ uuid_obj = uuid.UUID(value)
44
+ return str(uuid_obj)
45
+ except (ValueError, AttributeError):
46
+ # If it's not a valid UUID, return as-is (might be a schema name)
47
+ return value
48
+ # Fallback: convert to string
49
+ return str(value)
50
+
51
+
52
+ def ensure_uuid(value: Optional[Union[uuid.UUID, str, bytes]]) -> Optional[uuid.UUID]:
53
+ """
54
+ Ensure a value is a UUID object, converting from string or bytes if needed.
55
+
56
+ Args:
57
+ value: UUID object, string, bytes, or None
58
+
59
+ Returns:
60
+ UUID object, or None if value is None
61
+
62
+ Raises:
63
+ ValueError: If value cannot be converted to UUID
64
+ """
65
+ if value is None:
66
+ return None
67
+ if isinstance(value, uuid.UUID):
68
+ return value
69
+ if isinstance(value, str):
70
+ # Handle empty strings
71
+ if not value.strip():
72
+ return None
73
+ return uuid.UUID(value)
74
+ if isinstance(value, bytes):
75
+ # Handle bytes (sometimes database drivers return UUIDs as bytes)
76
+ return uuid.UUID(value.decode('utf-8'))
77
+ # Try to convert to string first, then to UUID
78
+ try:
79
+ return uuid.UUID(str(value))
80
+ except (ValueError, AttributeError) as e:
81
+ raise ValueError(f"Cannot convert {type(value)} ({value}) to UUID: {e}")
82
+
83
+
84
+ def get_by_id_with_fallback(
85
+ db: Session,
86
+ model: DeclarativeMeta,
87
+ id_value: Union[uuid.UUID, str],
88
+ error_message: Optional[str] = None,
89
+ ) -> Optional[T]:
90
+ """
91
+ Get a database record by ID with fallback strategies for UUID compatibility.
92
+
93
+ This function tries multiple strategies to find a record:
94
+ 1. Direct UUID comparison
95
+ 2. String comparison (for SQLite compatibility)
96
+ 3. Query all and compare by string (final fallback)
97
+
98
+ Args:
99
+ db: Database session
100
+ model: SQLAlchemy model class
101
+ id_value: UUID or string ID to search for
102
+ error_message: Optional custom error message if not found
103
+
104
+ Returns:
105
+ Model instance if found, None otherwise
106
+ """
107
+ # Try UUID comparison first
108
+ try:
109
+ if isinstance(id_value, str):
110
+ id_uuid = uuid.UUID(id_value)
111
+ else:
112
+ id_uuid = id_value
113
+
114
+ record = db.query(model).filter(model.id == id_uuid).first()
115
+ if record:
116
+ return record
117
+ except (ValueError, TypeError):
118
+ pass
119
+
120
+ # Try string comparison (for SQLite compatibility)
121
+ id_str = safe_uuid_to_str(id_value)
122
+ if id_str:
123
+ try:
124
+ record = db.query(model).filter(
125
+ cast(model.id, String) == id_str
126
+ ).first()
127
+ if record:
128
+ return record
129
+ except Exception:
130
+ pass
131
+
132
+ # Final fallback: query all and compare by string
133
+ try:
134
+ all_records = db.query(model).all()
135
+ for record in all_records:
136
+ record_id_str = safe_uuid_to_str(record.id)
137
+ if record_id_str and id_str:
138
+ if (record_id_str == id_str or
139
+ record_id_str.lower() == id_str.lower()):
140
+ return record
141
+ except Exception as e:
142
+ logger.debug(f"Fallback query failed: {e}")
143
+
144
+ return None
145
+
146
+
147
+ def get_by_id_or_404(
148
+ db: Session,
149
+ model: DeclarativeMeta,
150
+ id_value: Union[uuid.UUID, str],
151
+ error_message: Optional[str] = None,
152
+ model_name: Optional[str] = None,
153
+ ) -> T:
154
+ """
155
+ Get a database record by ID or raise 404 if not found.
156
+
157
+ Args:
158
+ db: Database session
159
+ model: SQLAlchemy model class
160
+ id_value: UUID or string ID to search for
161
+ error_message: Optional custom error message
162
+ model_name: Optional model name for error message
163
+
164
+ Returns:
165
+ Model instance
166
+
167
+ Raises:
168
+ HTTPException: 404 if record not found
169
+ """
170
+ record = get_by_id_with_fallback(db, model, id_value, error_message)
171
+
172
+ if not record:
173
+ id_str = safe_uuid_to_str(id_value)
174
+ model_display = model_name or model.__name__
175
+ default_message = f"{model_display} with ID {id_str} not found"
176
+ message = error_message or default_message
177
+
178
+ # Log diagnostic info
179
+ total = db.query(model).count()
180
+ logger.error(
181
+ f"{model_display} not found: {id_str}. "
182
+ f"Total {model_display.lower()}s: {total}"
183
+ )
184
+
185
+ raise HTTPException(
186
+ status_code=status.HTTP_404_NOT_FOUND,
187
+ detail=message,
188
+ )
189
+
190
+ return record
191
+
192
+
193
+ def model_to_dict(model_instance) -> dict:
194
+ """
195
+ Convert SQLAlchemy model instance to dictionary.
196
+
197
+ Handles UUIDs, datetimes, and JSON fields appropriately.
198
+
199
+ Args:
200
+ model_instance: SQLAlchemy model instance
201
+
202
+ Returns:
203
+ Dictionary representation of the model
204
+ """
205
+ if model_instance is None:
206
+ return {}
207
+
208
+ result = {}
209
+ for column in model_instance.__table__.columns:
210
+ value = getattr(model_instance, column.name, None)
211
+ if value is None:
212
+ result[column.name] = None
213
+ # Convert UUID to string
214
+ elif hasattr(value, 'hex'):
215
+ result[column.name] = str(value)
216
+ # Convert datetime to ISO format string
217
+ elif hasattr(value, 'isoformat'):
218
+ result[column.name] = value.isoformat()
219
+ # Keep JSON fields as-is
220
+ elif isinstance(value, (dict, list)):
221
+ result[column.name] = value
222
+ else:
223
+ result[column.name] = value
224
+ return result
pycharter/__init__.py CHANGED
@@ -1,35 +1,109 @@
1
1
  """
2
2
  PyCharter - Data Contract Management and Validation
3
3
 
4
- Six core services:
4
+ Core Services:
5
5
  1. Contract Parser - Reads and decomposes data contract files
6
- 1b. Contract Builder - Constructs consolidated contracts from separate artifacts
7
- 2. Metadata Store Client - Database operations for metadata storage
8
- 3. Pydantic Generator - Generates Pydantic models from JSON Schema
9
- 4. JSON Schema Converter - Converts Pydantic models to JSON Schema
10
- 5. Runtime Validator - Lightweight validation utility
6
+ 2. Contract Builder - Constructs consolidated contracts from separate artifacts
7
+ 3. Metadata Store - Database operations for metadata storage
8
+ 4. Pydantic Generator - Generates Pydantic models from JSON Schema
9
+ 5. JSON Schema Converter - Converts Pydantic models to JSON Schema
10
+ 6. Runtime Validator - Validation utilities
11
+ 7. Quality Assurance - Data quality checking and monitoring
12
+
13
+ API Organization:
14
+ - Tier 1: Primary Interfaces (Classes - Use these for best performance)
15
+ - Tier 2: Convenience Functions (Quick start - one-off use cases)
16
+ - Tier 3: Low-Level Utilities (When you already have models/schemas)
11
17
  """
12
18
 
13
- __version__ = "0.0.2"
19
+ __version__ = "0.0.22"
14
20
 
15
- # Service 1b: Contract Builder
16
- from pycharter.contract_builder import (
17
- ContractArtifacts,
18
- build_contract,
19
- build_contract_from_store,
21
+ # ============================================================================
22
+ # TIER 1: PRIMARY INTERFACES (Classes - Use these for best performance)
23
+ # ============================================================================
24
+
25
+ # Runtime Validator (PRIMARY INTERFACE)
26
+ from pycharter.runtime_validator import (
27
+ Validator, # ⭐ PRIMARY: Use this for validation
28
+ create_validator,
29
+ )
30
+
31
+ # Quality Assurance (PRIMARY INTERFACE)
32
+ from pycharter.quality import (
33
+ QualityCheck, # ⭐ PRIMARY: Use this for quality checks
34
+ QualityCheckOptions,
35
+ QualityReport,
36
+ QualityThresholds,
20
37
  )
21
38
 
22
- # Service 1: Contract Parser
39
+ # Metadata Store (PRIMARY INTERFACE)
40
+ from pycharter.metadata_store import MetadataStoreClient
41
+
42
+ # ============================================================================
43
+ # TIER 2: CONVENIENCE FUNCTIONS (Quick start - one-off use cases)
44
+ # ============================================================================
45
+
46
+ # Contract Parser
23
47
  from pycharter.contract_parser import (
24
48
  ContractMetadata,
25
49
  parse_contract,
26
50
  parse_contract_file,
27
51
  )
28
52
 
29
- # Service 2: Metadata Store Client
30
- from pycharter.metadata_store import MetadataStoreClient
53
+ # Contract Builder
54
+ from pycharter.contract_builder import (
55
+ ContractArtifacts,
56
+ build_contract,
57
+ build_contract_from_store,
58
+ )
59
+
60
+ # Pydantic Generator - Input type helpers (convenience)
61
+ from pycharter.pydantic_generator import (
62
+ from_dict, # Quick: schema from dict
63
+ from_file, # Quick: schema from file
64
+ from_json, # Quick: schema from JSON string
65
+ from_url, # Quick: schema from URL
66
+ generate_model, # Advanced: when you need more control
67
+ generate_model_file,
68
+ )
69
+
70
+ # JSON Schema Converter - Output type helpers (convenience)
71
+ from pycharter.json_schema_converter import (
72
+ to_dict, # Quick: schema to dict
73
+ to_file, # Quick: schema to file
74
+ to_json, # Quick: schema to JSON string
75
+ model_to_schema, # Advanced: core conversion
76
+ )
77
+
78
+ # Runtime Validator - Data source helpers (convenience)
79
+ from pycharter.runtime_validator import (
80
+ ValidationResult,
81
+ # Quick validation functions (use Validator class for multiple validations)
82
+ validate_with_store, # Quick: validate with metadata store
83
+ validate_batch_with_store, # Quick: batch validate with metadata store
84
+ validate_with_contract, # Quick: validate with contract file/dict
85
+ validate_batch_with_contract, # Quick: batch validate with contract file/dict
86
+ get_model_from_store, # Quick: get model from store
87
+ get_model_from_contract, # Quick: get model from contract
88
+ # Decorators
89
+ validate_input,
90
+ validate_output,
91
+ validate_with_contract_decorator,
92
+ )
93
+
94
+ # ============================================================================
95
+ # TIER 3: LOW-LEVEL UTILITIES (When you already have models/schemas)
96
+ # ============================================================================
97
+
98
+ from pycharter.runtime_validator import (
99
+ validate, # Low-level: validate with existing model
100
+ validate_batch, # Low-level: batch validate with existing model
101
+ )
102
+
103
+ # ============================================================================
104
+ # METADATA STORE IMPLEMENTATIONS
105
+ # ============================================================================
31
106
 
32
- # Optional metadata store implementations
33
107
  try:
34
108
  from pycharter.metadata_store import InMemoryMetadataStore
35
109
  except ImportError:
@@ -50,104 +124,86 @@ try:
50
124
  except ImportError:
51
125
  RedisMetadataStore = None # type: ignore[assignment,misc]
52
126
 
53
- # Service 4: JSON Schema Converter
54
- from pycharter.json_schema_converter import (
55
- model_to_schema,
56
- to_dict,
57
- to_file,
58
- to_json,
59
- )
60
-
61
- # Service 3: Pydantic Generator
62
- from pycharter.pydantic_generator import (
63
- from_dict,
64
- from_file,
65
- from_json,
66
- from_url,
67
- generate_model,
68
- generate_model_file,
69
- )
127
+ try:
128
+ from pycharter.metadata_store import SQLiteMetadataStore
129
+ except ImportError:
130
+ SQLiteMetadataStore = None # type: ignore[assignment,misc]
70
131
 
71
- # Service 5: Runtime Validator
72
- from pycharter.runtime_validator import (
73
- ValidationResult,
74
- get_model_from_contract,
75
- get_model_from_store,
76
- validate,
77
- validate_batch,
78
- validate_batch_with_contract,
79
- validate_batch_with_store,
80
- validate_input,
81
- validate_output,
82
- validate_with_contract,
83
- validate_with_contract_decorator,
84
- validate_with_store,
85
- )
132
+ # ============================================================================
133
+ # QUALITY ASSURANCE - Additional utilities (if needed for advanced use)
134
+ # ============================================================================
86
135
 
87
- # Service 6: Quality Assurance
88
136
  from pycharter.quality import (
89
- DataProfiler,
90
- FieldQualityMetrics,
91
- QualityCheck,
92
- QualityCheckOptions,
93
137
  QualityMetrics,
94
- QualityReport,
95
138
  QualityScore,
96
- QualityThresholds,
97
- ViolationRecord,
139
+ FieldQualityMetrics,
98
140
  ViolationTracker,
141
+ ViolationRecord,
142
+ DataProfiler,
99
143
  )
100
144
 
101
145
  __all__ = [
102
- # Contract Parser
146
+ # ========================================================================
147
+ # TIER 1: PRIMARY INTERFACES
148
+ # ========================================================================
149
+ "Validator", # ⭐ PRIMARY: Use for validation
150
+ "create_validator",
151
+ "QualityCheck", # ⭐ PRIMARY: Use for quality checks
152
+ "QualityCheckOptions",
153
+ "QualityReport",
154
+ "QualityThresholds",
155
+ "MetadataStoreClient",
156
+ # ========================================================================
157
+ # TIER 2: CONVENIENCE FUNCTIONS
158
+ # ========================================================================
159
+ # Contract Management
103
160
  "parse_contract",
104
161
  "parse_contract_file",
105
162
  "ContractMetadata",
106
- # Contract Builder
107
163
  "build_contract",
108
164
  "build_contract_from_store",
109
165
  "ContractArtifacts",
110
- # Metadata Store Client
111
- "MetadataStoreClient",
166
+ # Pydantic Generator (input type helpers)
167
+ "from_dict", # Quick: dict → model
168
+ "from_file", # Quick: file → model
169
+ "from_json", # Quick: JSON string → model
170
+ "from_url", # Quick: URL → model
171
+ "generate_model", # Advanced: more control
172
+ "generate_model_file",
173
+ # JSON Schema Converter (output type helpers)
174
+ "to_dict", # Quick: model → dict
175
+ "to_file", # Quick: model → file
176
+ "to_json", # Quick: model → JSON string
177
+ "model_to_schema", # Advanced: core conversion
178
+ # Runtime Validator (data source helpers)
179
+ "ValidationResult",
180
+ "validate_with_store", # Quick: store → validate
181
+ "validate_batch_with_store", # Quick: batch validate with store
182
+ "validate_with_contract", # Quick: contract → validate
183
+ "validate_batch_with_contract", # Quick: batch validate with contract
184
+ "get_model_from_store", # Quick: store → model
185
+ "get_model_from_contract", # Quick: contract → model
186
+ "validate_input", # Decorator
187
+ "validate_output", # Decorator
188
+ "validate_with_contract_decorator",
189
+ # ========================================================================
190
+ # TIER 3: LOW-LEVEL UTILITIES
191
+ # ========================================================================
192
+ "validate", # Low-level: model → validate
193
+ "validate_batch", # Low-level: model → batch validate
194
+ # ========================================================================
195
+ # METADATA STORE IMPLEMENTATIONS
196
+ # ========================================================================
112
197
  "InMemoryMetadataStore",
113
198
  "MongoDBMetadataStore",
114
199
  "PostgresMetadataStore",
115
200
  "RedisMetadataStore",
116
- # Pydantic Generator
117
- "generate_model",
118
- "generate_model_file",
119
- "from_dict",
120
- "from_file",
121
- "from_json",
122
- "from_url",
123
- # JSON Schema Converter
124
- "to_dict",
125
- "to_file",
126
- "to_json",
127
- "model_to_schema",
128
- # Runtime Validator
129
- "validate",
130
- "validate_batch",
131
- "ValidationResult",
132
- # Database-backed validation
133
- "validate_with_store",
134
- "validate_batch_with_store",
135
- "get_model_from_store",
136
- # Contract-based validation (no database)
137
- "validate_with_contract",
138
- "validate_batch_with_contract",
139
- "get_model_from_contract",
140
- # Decorators
141
- "validate_input",
142
- "validate_output",
143
- "validate_with_contract_decorator",
144
- # Quality Assurance
145
- "QualityCheck",
201
+ "SQLiteMetadataStore",
202
+ # ========================================================================
203
+ # QUALITY ASSURANCE - Additional utilities
204
+ # ========================================================================
146
205
  "QualityMetrics",
147
206
  "QualityScore",
148
- "QualityReport",
149
- "QualityThresholds",
150
- "QualityCheckOptions",
151
207
  "FieldQualityMetrics",
152
208
  "ViolationTracker",
153
209
  "ViolationRecord",
@@ -0,0 +1,50 @@
1
+ # Advanced Transformation Template
2
+ # Use this template for complex transformations with JSONata and custom functions
3
+
4
+ title: advanced_transformation
5
+ description: Advanced transformation with JSONata and custom functions
6
+ version: 1.0.0
7
+
8
+ # Step 1: Simple operations (optional but recommended for basic cleanup)
9
+ transform:
10
+ rename:
11
+ oldName: new_name
12
+ convert:
13
+ price: float
14
+ timestamp: datetime
15
+ defaults:
16
+ source: "api"
17
+
18
+ # Step 2: JSONata for complex transformations
19
+ jsonata:
20
+ expression: |
21
+ $.{
22
+ "ticker": symbol,
23
+ "price_change": price - previousClose,
24
+ "price_change_pct": ((price - previousClose) / previousClose) * 100,
25
+ "is_positive": price > previousClose,
26
+ "formatted_date": $fromMillis(timestamp),
27
+ "metadata": {
28
+ "source": source,
29
+ "processed_at": $now()
30
+ }
31
+ }
32
+ mode: "record" # Apply to each record individually
33
+ # mode: "batch" # Apply to entire dataset
34
+
35
+ # Step 3: Custom Python function for business logic
36
+ custom_function:
37
+ # Option 1: Using module and function
38
+ module: "myproject.transforms"
39
+ function: "optimize_portfolio"
40
+ mode: "batch"
41
+ kwargs:
42
+ method: "min_volatility"
43
+ solver: "ipopt"
44
+ constraints:
45
+ max_weight: 0.1
46
+ min_weight: 0.01
47
+
48
+ # Option 2: Using callable path (alternative)
49
+ # callable: "myproject.transforms.optimize_portfolio"
50
+ # mode: "batch"
@@ -0,0 +1,59 @@
1
+ # Simple Transformation Template
2
+ # Use this template for basic field transformations
3
+
4
+ title: simple_transformation
5
+ description: Simple transformation configuration
6
+ version: 1.0.0
7
+
8
+ # Simple operations (applied in order: rename → convert → defaults → add → select → drop)
9
+ transform:
10
+ # Rename fields (old_name: new_name)
11
+ rename:
12
+ oldName: new_name
13
+ camelCase: snake_case
14
+ userId: user_id
15
+
16
+ # Convert field types
17
+ convert:
18
+ price: float
19
+ quantity: integer
20
+ active: boolean
21
+ created_at: datetime
22
+
23
+ # Set default values for missing fields
24
+ defaults:
25
+ status: "pending"
26
+ priority: 0
27
+
28
+ # Add computed fields
29
+ add:
30
+ full_name: "${first_name} ${last_name}"
31
+ created_at: "now()"
32
+ record_id: "uuid()"
33
+
34
+ # Keep only these fields (removes all others)
35
+ # select:
36
+ # - field1
37
+ # - field2
38
+
39
+ # Remove these fields (keeps all others)
40
+ # drop:
41
+ # - internal_id
42
+ # - debug_info
43
+
44
+ # Advanced: JSONata for complex transformations (optional)
45
+ # jsonata:
46
+ # expression: |
47
+ # $.{
48
+ # "ticker": symbol,
49
+ # "avg_price": $average(prices)
50
+ # }
51
+ # mode: "record" # or "batch"
52
+
53
+ # Advanced: Custom Python function (optional)
54
+ # custom_function:
55
+ # module: "myproject.transforms"
56
+ # function: "optimize_data"
57
+ # mode: "batch"
58
+ # kwargs:
59
+ # method: "min_volatility"
@@ -3,8 +3,7 @@ Base SQLAlchemy configuration
3
3
  """
4
4
 
5
5
  from sqlalchemy import create_engine, event
6
- from sqlalchemy.ext.declarative import declarative_base
7
- from sqlalchemy.orm import sessionmaker
6
+ from sqlalchemy.orm import declarative_base, sessionmaker
8
7
  from sqlalchemy.schema import CreateTable
9
8
 
10
9
  Base = declarative_base()