pydpm_xl 0.1.39rc31__py3-none-any.whl → 0.2.0__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 (123) hide show
  1. py_dpm/__init__.py +1 -1
  2. py_dpm/api/__init__.py +58 -189
  3. py_dpm/api/dpm/__init__.py +20 -0
  4. py_dpm/api/{data_dictionary.py → dpm/data_dictionary.py} +903 -984
  5. py_dpm/api/dpm/explorer.py +236 -0
  6. py_dpm/api/dpm/hierarchical_queries.py +142 -0
  7. py_dpm/api/{migration.py → dpm/migration.py} +16 -19
  8. py_dpm/api/{operation_scopes.py → dpm/operation_scopes.py} +319 -267
  9. py_dpm/api/dpm_xl/__init__.py +25 -0
  10. py_dpm/api/{ast_generator.py → dpm_xl/ast_generator.py} +3 -3
  11. py_dpm/api/{complete_ast.py → dpm_xl/complete_ast.py} +192 -168
  12. py_dpm/api/dpm_xl/semantic.py +354 -0
  13. py_dpm/api/{syntax.py → dpm_xl/syntax.py} +6 -5
  14. py_dpm/api/explorer.py +4 -0
  15. py_dpm/api/semantic.py +30 -306
  16. py_dpm/cli/__init__.py +9 -0
  17. py_dpm/{client.py → cli/main.py} +8 -8
  18. py_dpm/dpm/__init__.py +11 -0
  19. py_dpm/{models.py → dpm/models.py} +112 -88
  20. py_dpm/dpm/queries/base.py +100 -0
  21. py_dpm/dpm/queries/basic_objects.py +33 -0
  22. py_dpm/dpm/queries/explorer_queries.py +352 -0
  23. py_dpm/dpm/queries/filters.py +139 -0
  24. py_dpm/dpm/queries/glossary.py +45 -0
  25. py_dpm/dpm/queries/hierarchical_queries.py +838 -0
  26. py_dpm/dpm/queries/tables.py +133 -0
  27. py_dpm/dpm/utils.py +356 -0
  28. py_dpm/dpm_xl/__init__.py +8 -0
  29. py_dpm/dpm_xl/ast/__init__.py +14 -0
  30. py_dpm/{AST/ASTConstructor.py → dpm_xl/ast/constructor.py} +6 -6
  31. py_dpm/{AST/MLGeneration.py → dpm_xl/ast/ml_generation.py} +137 -87
  32. py_dpm/{AST/ModuleAnalyzer.py → dpm_xl/ast/module_analyzer.py} +7 -7
  33. py_dpm/{AST/ModuleDependencies.py → dpm_xl/ast/module_dependencies.py} +56 -41
  34. py_dpm/{AST/ASTObjects.py → dpm_xl/ast/nodes.py} +1 -1
  35. py_dpm/{AST/check_operands.py → dpm_xl/ast/operands.py} +16 -13
  36. py_dpm/{AST/ASTTemplate.py → dpm_xl/ast/template.py} +2 -2
  37. py_dpm/{AST/WhereClauseChecker.py → dpm_xl/ast/where_clause.py} +2 -2
  38. py_dpm/dpm_xl/grammar/__init__.py +18 -0
  39. py_dpm/dpm_xl/operators/__init__.py +19 -0
  40. py_dpm/{Operators/AggregateOperators.py → dpm_xl/operators/aggregate.py} +7 -7
  41. py_dpm/{Operators/NumericOperators.py → dpm_xl/operators/arithmetic.py} +6 -6
  42. py_dpm/{Operators/Operator.py → dpm_xl/operators/base.py} +5 -5
  43. py_dpm/{Operators/BooleanOperators.py → dpm_xl/operators/boolean.py} +5 -5
  44. py_dpm/{Operators/ClauseOperators.py → dpm_xl/operators/clause.py} +8 -8
  45. py_dpm/{Operators/ComparisonOperators.py → dpm_xl/operators/comparison.py} +5 -5
  46. py_dpm/{Operators/ConditionalOperators.py → dpm_xl/operators/conditional.py} +7 -7
  47. py_dpm/{Operators/StringOperators.py → dpm_xl/operators/string.py} +5 -5
  48. py_dpm/{Operators/TimeOperators.py → dpm_xl/operators/time.py} +6 -6
  49. py_dpm/{semantics/SemanticAnalyzer.py → dpm_xl/semantic_analyzer.py} +168 -68
  50. py_dpm/{semantics/Symbols.py → dpm_xl/symbols.py} +3 -3
  51. py_dpm/dpm_xl/types/__init__.py +13 -0
  52. py_dpm/{DataTypes/TypePromotion.py → dpm_xl/types/promotion.py} +2 -2
  53. py_dpm/{DataTypes/ScalarTypes.py → dpm_xl/types/scalar.py} +2 -2
  54. py_dpm/dpm_xl/utils/__init__.py +14 -0
  55. py_dpm/{data_handlers.py → dpm_xl/utils/data_handlers.py} +2 -2
  56. py_dpm/{Utils → dpm_xl/utils}/operands_mapping.py +1 -1
  57. py_dpm/{Utils → dpm_xl/utils}/operator_mapping.py +8 -8
  58. py_dpm/{OperationScopes/OperationScopeService.py → dpm_xl/utils/scopes_calculator.py} +148 -58
  59. py_dpm/{Utils/ast_serialization.py → dpm_xl/utils/serialization.py} +2 -2
  60. py_dpm/dpm_xl/validation/__init__.py +12 -0
  61. py_dpm/{Utils/ValidationsGenerationUtils.py → dpm_xl/validation/generation_utils.py} +2 -3
  62. py_dpm/{ValidationsGeneration/PropertiesConstraintsProcessor.py → dpm_xl/validation/property_constraints.py} +56 -21
  63. py_dpm/{ValidationsGeneration/auxiliary_functions.py → dpm_xl/validation/utils.py} +2 -2
  64. py_dpm/{ValidationsGeneration/VariantsProcessor.py → dpm_xl/validation/variants.py} +149 -55
  65. py_dpm/exceptions/__init__.py +23 -0
  66. py_dpm/{Exceptions → exceptions}/exceptions.py +7 -2
  67. pydpm_xl-0.2.0.dist-info/METADATA +278 -0
  68. pydpm_xl-0.2.0.dist-info/RECORD +88 -0
  69. pydpm_xl-0.2.0.dist-info/entry_points.txt +2 -0
  70. py_dpm/Exceptions/__init__.py +0 -0
  71. py_dpm/OperationScopes/__init__.py +0 -0
  72. py_dpm/Operators/__init__.py +0 -0
  73. py_dpm/Utils/__init__.py +0 -0
  74. py_dpm/Utils/utils.py +0 -2
  75. py_dpm/ValidationsGeneration/Utils.py +0 -364
  76. py_dpm/ValidationsGeneration/__init__.py +0 -0
  77. py_dpm/api/data_dictionary_validation.py +0 -614
  78. py_dpm/db_utils.py +0 -221
  79. py_dpm/grammar/__init__.py +0 -0
  80. py_dpm/grammar/dist/__init__.py +0 -0
  81. py_dpm/grammar/dpm_xlLexer.g4 +0 -437
  82. py_dpm/grammar/dpm_xlParser.g4 +0 -263
  83. py_dpm/semantics/DAG/DAGAnalyzer.py +0 -158
  84. py_dpm/semantics/DAG/__init__.py +0 -0
  85. py_dpm/semantics/__init__.py +0 -0
  86. py_dpm/views/data_types.sql +0 -12
  87. py_dpm/views/datapoints.sql +0 -65
  88. py_dpm/views/hierarchy_operand_reference.sql +0 -11
  89. py_dpm/views/hierarchy_preconditions.sql +0 -13
  90. py_dpm/views/hierarchy_variables.sql +0 -26
  91. py_dpm/views/hierarchy_variables_context.sql +0 -14
  92. py_dpm/views/key_components.sql +0 -18
  93. py_dpm/views/module_from_table.sql +0 -11
  94. py_dpm/views/open_keys.sql +0 -13
  95. py_dpm/views/operation_info.sql +0 -27
  96. py_dpm/views/operation_list.sql +0 -18
  97. py_dpm/views/operations_versions_from_module_version.sql +0 -30
  98. py_dpm/views/precondition_info.sql +0 -17
  99. py_dpm/views/report_type_operand_reference_info.sql +0 -18
  100. py_dpm/views/subcategory_info.sql +0 -17
  101. py_dpm/views/table_info.sql +0 -19
  102. pydpm_xl-0.1.39rc31.dist-info/METADATA +0 -53
  103. pydpm_xl-0.1.39rc31.dist-info/RECORD +0 -96
  104. pydpm_xl-0.1.39rc31.dist-info/entry_points.txt +0 -2
  105. /py_dpm/{AST → cli/commands}/__init__.py +0 -0
  106. /py_dpm/{migration.py → dpm/migration.py} +0 -0
  107. /py_dpm/{AST/ASTVisitor.py → dpm_xl/ast/visitor.py} +0 -0
  108. /py_dpm/{DataTypes → dpm_xl/grammar/generated}/__init__.py +0 -0
  109. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlLexer.interp +0 -0
  110. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlLexer.py +0 -0
  111. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlLexer.tokens +0 -0
  112. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParser.interp +0 -0
  113. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParser.py +0 -0
  114. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParser.tokens +0 -0
  115. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParserListener.py +0 -0
  116. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/dpm_xlParserVisitor.py +0 -0
  117. /py_dpm/{grammar/dist → dpm_xl/grammar/generated}/listeners.py +0 -0
  118. /py_dpm/{DataTypes/TimeClasses.py → dpm_xl/types/time.py} +0 -0
  119. /py_dpm/{Utils → dpm_xl/utils}/tokens.py +0 -0
  120. /py_dpm/{Exceptions → exceptions}/messages.py +0 -0
  121. {pydpm_xl-0.1.39rc31.dist-info → pydpm_xl-0.2.0.dist-info}/WHEEL +0 -0
  122. {pydpm_xl-0.1.39rc31.dist-info → pydpm_xl-0.2.0.dist-info}/licenses/LICENSE +0 -0
  123. {pydpm_xl-0.1.39rc31.dist-info → pydpm_xl-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,236 @@
1
+ from typing import List, Optional, Dict, Any
2
+
3
+ from py_dpm.api.dpm.data_dictionary import DataDictionaryAPI
4
+ from py_dpm.dpm.queries.explorer_queries import ExplorerQuery
5
+
6
+
7
+ class ExplorerQueryAPI:
8
+ """
9
+ Explorer API for introspection and "inverse" queries of the DPM structure.
10
+ Methods here answer "Where is X used?" or "What relates to Y?".
11
+
12
+ This class composes DataDictionaryAPI for basic queries but adds higher-order logic.
13
+ """
14
+
15
+ def __init__(self, data_dict_api: Optional[DataDictionaryAPI] = None):
16
+ self.api = data_dict_api or DataDictionaryAPI()
17
+
18
+ def close(self):
19
+ """
20
+ Explicitly close the underlying DataDictionaryAPI (and its session).
21
+ """
22
+ if hasattr(self, "api") and hasattr(self.api, "close"):
23
+ try:
24
+ self.api.close()
25
+ except Exception:
26
+ pass
27
+
28
+ def __enter__(self):
29
+ return self
30
+
31
+ def __exit__(self, exc_type, exc_val, exc_tb):
32
+ self.close()
33
+
34
+ # ==================== Existing Explorer Methods ====================
35
+
36
+ def get_properties_using_item(
37
+ self, item_code: str, release_id: Optional[int] = None
38
+ ) -> List[str]:
39
+ """
40
+ Find all property codes that use the given item code as a valid value.
41
+ (Inverse of getting valid items for a property).
42
+
43
+ Args:
44
+ item_code: The item code to search for (e.g. 'EUR')
45
+ release_id: Optional release ID
46
+
47
+ Returns:
48
+ List of property codes (e.g. ['sCRNCY', 'sTRNS_CRNCY'])
49
+ """
50
+ from py_dpm.dpm.models import ItemCategory, PropertyCategory, Category
51
+ from sqlalchemy.orm import aliased
52
+
53
+ session = self.api.session
54
+
55
+ # Aliases for clarity
56
+ ic_child = aliased(ItemCategory, name="ic_child") # The item (value)
57
+ ic_parent = aliased(ItemCategory, name="ic_parent") # The property
58
+
59
+ query = (
60
+ session.query(ic_parent.code)
61
+ .select_from(ic_child)
62
+ .join(Category, ic_child.categoryid == Category.categoryid)
63
+ .join(PropertyCategory, Category.categoryid == PropertyCategory.categoryid)
64
+ .join(ic_parent, PropertyCategory.propertyid == ic_parent.itemid)
65
+ .filter(ic_child.code == item_code)
66
+ .distinct()
67
+ )
68
+
69
+ if release_id is not None:
70
+ query = query.filter(
71
+ (ic_child.endreleaseid.is_(None))
72
+ | (ic_child.endreleaseid > release_id),
73
+ ic_child.startreleaseid <= release_id,
74
+ (ic_parent.endreleaseid.is_(None))
75
+ | (ic_parent.endreleaseid > release_id),
76
+ ic_parent.startreleaseid <= release_id,
77
+ )
78
+
79
+ results = query.all()
80
+ return [r.code for r in results]
81
+
82
+ def search_table(
83
+ self, query: str, release_id: Optional[int] = None
84
+ ) -> List[Dict[str, Any]]:
85
+ """
86
+ Search for tables by code or name substring.
87
+
88
+ Args:
89
+ query: Substring to search for
90
+ release_id: Optional release ID
91
+
92
+ Returns:
93
+ List of matching dictionaries with table info
94
+ """
95
+ from py_dpm.dpm.models import TableVersion
96
+ from sqlalchemy import or_
97
+
98
+ session = self.api.session
99
+ search_pattern = f"%{query}%"
100
+
101
+ db_query = session.query(
102
+ TableVersion.tablevid,
103
+ TableVersion.code,
104
+ TableVersion.name,
105
+ TableVersion.description,
106
+ ).filter(
107
+ or_(
108
+ TableVersion.code.like(search_pattern),
109
+ TableVersion.name.like(search_pattern),
110
+ )
111
+ )
112
+
113
+ if release_id is not None:
114
+ db_query = db_query.filter(
115
+ or_(
116
+ TableVersion.endreleaseid.is_(None),
117
+ TableVersion.endreleaseid > release_id,
118
+ ),
119
+ TableVersion.startreleaseid <= release_id,
120
+ )
121
+
122
+ results = db_query.all()
123
+ return [
124
+ {
125
+ "table_vid": r.tablevid,
126
+ "code": r.code,
127
+ "name": r.name,
128
+ "description": r.description,
129
+ }
130
+ for r in results
131
+ ]
132
+
133
+ def audit_table(self, table_code: str, release_id: Optional[int] = None) -> dict:
134
+ """
135
+ Provide a comprehensive audit of a table structure: dimensions, open keys, and basic stats.
136
+
137
+ Args:
138
+ table_code: Table code
139
+ release_id: Optional release ID
140
+
141
+ Returns:
142
+ Dict summarizing table metadata
143
+ """
144
+ table_info = self.api.get_table_version(table_code, release_id)
145
+ if not table_info:
146
+ return {"error": f"Table {table_code} not found"}
147
+
148
+ open_keys = self.api.get_open_keys_for_table(table_code, release_id)
149
+
150
+ # Dimensions (Header Rows/Cols) could be fetched if we had a method.
151
+ # For now we return what we can easily aggregate.
152
+
153
+ return {
154
+ "info": table_info,
155
+ "open_keys": open_keys,
156
+ "open_keys_count": len(open_keys),
157
+ }
158
+
159
+ # ==================== New Explorer Methods Backed by Query Layer ====================
160
+
161
+ def get_variable_usage(
162
+ self,
163
+ variable_id: Optional[int] = None,
164
+ variable_vid: Optional[int] = None,
165
+ release_id: Optional[int] = None,
166
+ date: Optional[str] = None,
167
+ release_code: Optional[str] = None,
168
+ ) -> List[Dict[str, Any]]:
169
+ """
170
+ Expose ExplorerQuery.get_variable_usage through the Explorer API.
171
+
172
+ Exactly one of variable_id or variable_vid must be provided.
173
+ Release arguments follow the same semantics as hierarchical queries:
174
+ at most one of release_id, date or release_code may be specified.
175
+ """
176
+ return ExplorerQuery.get_variable_usage(
177
+ self.api.session,
178
+ variable_id=variable_id,
179
+ variable_vid=variable_vid,
180
+ release_id=release_id,
181
+ date=date,
182
+ release_code=release_code,
183
+ )
184
+
185
+ def get_module_url(
186
+ self,
187
+ module_code: str,
188
+ date: Optional[str] = None,
189
+ release_id: Optional[int] = None,
190
+ release_code: Optional[str] = None,
191
+ ) -> str:
192
+ """
193
+ Get the EBA taxonomy URL for a module.
194
+
195
+ The URL has the form:
196
+ http://www.eba.europa.eu/eu/fr/xbrl/crr/fws/{framework_code}/{release_code}/mod/{module_code}.json
197
+
198
+ Exactly one of date, release_id or release_code may be specified.
199
+ If none are provided, the URL is built for the currently active
200
+ module version.
201
+ """
202
+ return ExplorerQuery.get_module_url(
203
+ self.api.session,
204
+ module_code=module_code,
205
+ date=date,
206
+ release_id=release_id,
207
+ release_code=release_code,
208
+ )
209
+
210
+ def get_variable_from_cell_address(
211
+ self,
212
+ table_code: str,
213
+ row_code: Optional[str] = None,
214
+ column_code: Optional[str] = None,
215
+ sheet_code: Optional[str] = None,
216
+ release_id: Optional[int] = None,
217
+ release_code: Optional[str] = None,
218
+ date: Optional[str] = None,
219
+ ) -> List[Dict[str, Any]]:
220
+ """
221
+ Resolve variable information from a cell address (table/row/column/sheet).
222
+
223
+ Row, column and sheet codes are optional and are only used when
224
+ provided. Release parameters follow the standard semantics; if none
225
+ are given, only active module versions are considered.
226
+ """
227
+ return ExplorerQuery.get_variable_from_cell_address(
228
+ self.api.session,
229
+ table_code=table_code,
230
+ row_code=row_code,
231
+ column_code=column_code,
232
+ sheet_code=sheet_code,
233
+ release_id=release_id,
234
+ release_code=release_code,
235
+ date=date,
236
+ )
@@ -0,0 +1,142 @@
1
+ """
2
+ Hierarchical Queries API
3
+
4
+ This module provides ORM-based query methods for accessing t he data dictionary.
5
+ All methods use SQLAlchemy ORM instead of raw SQL for PostgreSQL compatibility.
6
+ """
7
+
8
+ from typing import Optional, Dict, Any
9
+
10
+ from py_dpm.dpm.utils import get_session, get_engine
11
+ from py_dpm.dpm.queries.hierarchical_queries import HierarchicalQuery
12
+
13
+
14
+ class HierarchicalQueryAPI:
15
+ """
16
+ Main API for querying the data dictionary using ORM.
17
+
18
+ This class provides methods for:
19
+ - Table/row/column reference lookups
20
+ - Wildcard resolution
21
+ - Item and sheet validation
22
+ - Open key queries
23
+ - Metadata retrieval
24
+
25
+ All methods use SQLAlchemy ORM for database-agnostic queries.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ database_path: Optional[str] = None,
31
+ connection_url: Optional[str] = None,
32
+ ):
33
+ """
34
+ Initialize the Data Dictionary API.
35
+
36
+ Args:
37
+ database_path: Path to SQLite database (optional)
38
+ connection_url: SQLAlchemy connection URL for PostgreSQL (optional)
39
+ """
40
+ # engine is created but not stored since it's used globally/per-session
41
+ get_engine(database_path=database_path, connection_url=connection_url)
42
+ self.session = get_session()
43
+
44
+ def close(self):
45
+ """
46
+ Explicitly close the underlying SQLAlchemy session.
47
+ """
48
+ if hasattr(self, "session") and self.session:
49
+ try:
50
+ self.session.close()
51
+ except Exception:
52
+ pass
53
+
54
+ def __enter__(self):
55
+ return self
56
+
57
+ def __exit__(self, exc_type, exc_val, exc_tb):
58
+ self.close()
59
+
60
+ def get_module_version(
61
+ self,
62
+ module_code: str,
63
+ release_id: Optional[int] = None,
64
+ date: Optional[str] = None,
65
+ release_code: Optional[str] = None,
66
+ ) -> Dict[str, Any]:
67
+ """
68
+ Fetch list of available releases from database.
69
+
70
+ Returns:
71
+ List of dictionaries containing release info
72
+ """
73
+ # Use ReleaseQuery
74
+ return HierarchicalQuery.get_module_version(
75
+ self.session,
76
+ module_code=module_code,
77
+ release_id=release_id,
78
+ date=date,
79
+ release_code=release_code,
80
+ )
81
+
82
+ def get_all_frameworks(
83
+ self,
84
+ release_id: Optional[int] = None,
85
+ date: Optional[str] = None,
86
+ release_code: Optional[str] = None,
87
+ ) -> Dict[str, Any]:
88
+ """
89
+ Fetch list of available releases from database.
90
+
91
+ Returns:
92
+ List of dictionaries containing release info
93
+ """
94
+ # Use ReleaseQuery
95
+ return HierarchicalQuery.get_all_frameworks(
96
+ self.session,
97
+ release_id=release_id,
98
+ date=date,
99
+ release_code=release_code,
100
+ )
101
+
102
+ def get_table_details(
103
+ self,
104
+ table_code: str,
105
+ release_id: Optional[int] = None,
106
+ date: Optional[str] = None,
107
+ release_code: Optional[str] = None,
108
+ ) -> Dict[str, Any]:
109
+ """
110
+ Fetch table structure and cell data from database.
111
+
112
+ Returns:
113
+ Dictionary with DPM JSON structure
114
+ """
115
+ return HierarchicalQuery.get_table_details(
116
+ self.session,
117
+ table_code=table_code,
118
+ release_id=release_id,
119
+ date=date,
120
+ release_code=release_code,
121
+ )
122
+
123
+ def get_table_modelling(
124
+ self,
125
+ table_code: str,
126
+ release_id: Optional[int] = None,
127
+ date: Optional[str] = None,
128
+ release_code: Optional[str] = None,
129
+ ) -> Dict[str, Any]:
130
+ """
131
+ Placeholder API for table modelling metadata.
132
+
133
+ Mirrors the signature of get_table_details; the underlying query and
134
+ output format will be provided later.
135
+ """
136
+ return HierarchicalQuery.get_table_modelling(
137
+ self.session,
138
+ table_code=table_code,
139
+ release_id=release_id,
140
+ date=date,
141
+ release_code=release_code,
142
+ )
@@ -2,40 +2,38 @@ import os
2
2
  from typing import Optional
3
3
  from sqlalchemy.engine import Engine
4
4
 
5
- from py_dpm.migration import run_migration as _run_migration
5
+ from py_dpm.dpm.migration import run_migration as _run_migration
6
6
 
7
7
 
8
8
  class MigrationAPI:
9
9
  """
10
10
  API for database migration operations.
11
-
11
+
12
12
  This class provides methods to migrate data from Access databases to SQLite.
13
13
  """
14
-
14
+
15
15
  def __init__(self):
16
16
  """Initialize the Migration API."""
17
17
  pass
18
-
18
+
19
19
  def migrate_access_to_sqlite(
20
- self,
21
- access_file_path: str,
22
- sqlite_db_path: Optional[str] = None
20
+ self, access_file_path: str, sqlite_db_path: Optional[str] = None
23
21
  ) -> Engine:
24
22
  """
25
23
  Migrate data from an Access database to SQLite.
26
-
24
+
27
25
  Args:
28
26
  access_file_path (str): Path to the Access database file (.mdb or .accdb)
29
- sqlite_db_path (Optional[str]): Path for the SQLite database.
27
+ sqlite_db_path (Optional[str]): Path for the SQLite database.
30
28
  If None, defaults to "database.db"
31
-
29
+
32
30
  Returns:
33
31
  Engine: SQLAlchemy engine for the created SQLite database
34
-
32
+
35
33
  Raises:
36
34
  FileNotFoundError: If the Access file doesn't exist
37
35
  Exception: If migration fails
38
-
36
+
39
37
  Example:
40
38
  >>> from pydpm.api import MigrationAPI
41
39
  >>> migration = MigrationAPI()
@@ -43,10 +41,10 @@ class MigrationAPI:
43
41
  """
44
42
  if not os.path.exists(access_file_path):
45
43
  raise FileNotFoundError(f"Access file not found: {access_file_path}")
46
-
44
+
47
45
  if sqlite_db_path is None:
48
46
  sqlite_db_path = os.getenv("SQLITE_DB_PATH", "database.db")
49
-
47
+
50
48
  try:
51
49
  engine = _run_migration(access_file_path, sqlite_db_path)
52
50
  return engine
@@ -56,19 +54,18 @@ class MigrationAPI:
56
54
 
57
55
  # Convenience function for direct usage
58
56
  def migrate_access_to_sqlite(
59
- access_file_path: str,
60
- sqlite_db_path: Optional[str] = None
57
+ access_file_path: str, sqlite_db_path: Optional[str] = None
61
58
  ) -> Engine:
62
59
  """
63
60
  Convenience function to migrate Access database to SQLite.
64
-
61
+
65
62
  Args:
66
63
  access_file_path (str): Path to the Access database file
67
64
  sqlite_db_path (Optional[str]): Path for the SQLite database
68
-
65
+
69
66
  Returns:
70
67
  Engine: SQLAlchemy engine for the created SQLite database
71
-
68
+
72
69
  Example:
73
70
  >>> from pydpm.api.migration import migrate_access_to_sqlite
74
71
  >>> engine = migrate_access_to_sqlite("data.mdb", "output.db")