pydpm_xl 0.1.39rc29__py3-none-any.whl → 0.1.39rc31__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.
- py_dpm/Utils/ast_serialization.py +16 -3
- py_dpm/__init__.py +1 -1
- py_dpm/api/complete_ast.py +37 -7
- py_dpm/client.py +225 -79
- {pydpm_xl-0.1.39rc29.dist-info → pydpm_xl-0.1.39rc31.dist-info}/METADATA +2 -2
- {pydpm_xl-0.1.39rc29.dist-info → pydpm_xl-0.1.39rc31.dist-info}/RECORD +10 -10
- {pydpm_xl-0.1.39rc29.dist-info → pydpm_xl-0.1.39rc31.dist-info}/WHEEL +0 -0
- {pydpm_xl-0.1.39rc29.dist-info → pydpm_xl-0.1.39rc31.dist-info}/entry_points.txt +0 -0
- {pydpm_xl-0.1.39rc29.dist-info → pydpm_xl-0.1.39rc31.dist-info}/licenses/LICENSE +0 -0
- {pydpm_xl-0.1.39rc29.dist-info → pydpm_xl-0.1.39rc31.dist-info}/top_level.txt +0 -0
|
@@ -360,13 +360,26 @@ class ASTToJSONVisitor(NodeVisitor):
|
|
|
360
360
|
'class_name': 'AggregationOp',
|
|
361
361
|
'op': node.op,
|
|
362
362
|
'operand': self.visit(node.operand),
|
|
363
|
-
'grouping_clause': None
|
|
364
363
|
}
|
|
365
|
-
# Add grouping_clause if present
|
|
364
|
+
# Add grouping_clause if present and has actual components
|
|
366
365
|
if hasattr(node, 'grouping_clause') and node.grouping_clause is not None:
|
|
367
|
-
|
|
366
|
+
gc = self.visit(node.grouping_clause)
|
|
367
|
+
# Only include grouping_clause if it has components
|
|
368
|
+
if gc and gc.get('components'):
|
|
369
|
+
result['grouping_clause'] = gc
|
|
368
370
|
return result
|
|
369
371
|
|
|
372
|
+
def visit_GroupingClause(self, node):
|
|
373
|
+
"""Visit GroupingClause nodes."""
|
|
374
|
+
components = getattr(node, 'components', None)
|
|
375
|
+
if components:
|
|
376
|
+
return {
|
|
377
|
+
'class_name': 'GroupingClause',
|
|
378
|
+
'components': components
|
|
379
|
+
}
|
|
380
|
+
# Return None for empty grouping clauses (will be filtered out)
|
|
381
|
+
return None
|
|
382
|
+
|
|
370
383
|
def visit_ComplexNumericOp(self, node):
|
|
371
384
|
"""Visit ComplexNumericOp nodes (max, min)."""
|
|
372
385
|
return {
|
py_dpm/__init__.py
CHANGED
|
@@ -41,7 +41,7 @@ Available packages:
|
|
|
41
41
|
- pydpm.api: Main APIs for migration, syntax, and semantic analysis
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
|
-
__version__ = "0.1.
|
|
44
|
+
__version__ = "0.1.39rc31"
|
|
45
45
|
__author__ = "MeaningfulData S.L."
|
|
46
46
|
__email__ = "info@meaningfuldata.eu"
|
|
47
47
|
__license__ = "GPL-3.0-or-later"
|
py_dpm/api/complete_ast.py
CHANGED
|
@@ -14,7 +14,12 @@ from typing import Dict, Any, Optional
|
|
|
14
14
|
from py_dpm.Utils.ast_serialization import ASTToJSONVisitor
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def generate_complete_ast(
|
|
17
|
+
def generate_complete_ast(
|
|
18
|
+
expression: str,
|
|
19
|
+
database_path: str = None,
|
|
20
|
+
connection_url: str = None,
|
|
21
|
+
release_id: Optional[int] = None
|
|
22
|
+
):
|
|
18
23
|
"""
|
|
19
24
|
Generate complete AST with all data fields, exactly like json_scripts examples.
|
|
20
25
|
|
|
@@ -25,6 +30,8 @@ def generate_complete_ast(expression: str, database_path: str = None, connection
|
|
|
25
30
|
expression: DPM-XL expression string
|
|
26
31
|
database_path: Path to SQLite database file (e.g., "./database.db")
|
|
27
32
|
connection_url: SQLAlchemy connection URL for PostgreSQL (optional)
|
|
33
|
+
release_id: Optional release ID to filter database lookups by specific release.
|
|
34
|
+
If None, uses all available data (release-agnostic).
|
|
28
35
|
|
|
29
36
|
Returns:
|
|
30
37
|
dict: {
|
|
@@ -87,7 +94,7 @@ def generate_complete_ast(expression: str, database_path: str = None, connection
|
|
|
87
94
|
session=session,
|
|
88
95
|
expression=expression,
|
|
89
96
|
ast=inner_ast,
|
|
90
|
-
release_id=
|
|
97
|
+
release_id=release_id
|
|
91
98
|
)
|
|
92
99
|
|
|
93
100
|
# Apply the data from operand checker to VarID nodes
|
|
@@ -246,7 +253,12 @@ def _check_data_fields_populated(ast_dict):
|
|
|
246
253
|
return False
|
|
247
254
|
|
|
248
255
|
|
|
249
|
-
def generate_complete_batch(
|
|
256
|
+
def generate_complete_batch(
|
|
257
|
+
expressions: list,
|
|
258
|
+
database_path: str = None,
|
|
259
|
+
connection_url: str = None,
|
|
260
|
+
release_id: Optional[int] = None
|
|
261
|
+
):
|
|
250
262
|
"""
|
|
251
263
|
Generate complete ASTs for multiple expressions.
|
|
252
264
|
|
|
@@ -254,20 +266,29 @@ def generate_complete_batch(expressions: list, database_path: str = None, connec
|
|
|
254
266
|
expressions: List of DPM-XL expression strings
|
|
255
267
|
database_path: Path to SQLite database file
|
|
256
268
|
connection_url: SQLAlchemy connection URL for PostgreSQL (optional)
|
|
269
|
+
release_id: Optional release ID to filter database lookups by specific release.
|
|
270
|
+
If None, uses all available data (release-agnostic).
|
|
257
271
|
|
|
258
272
|
Returns:
|
|
259
273
|
list: List of result dictionaries
|
|
260
274
|
"""
|
|
261
275
|
results = []
|
|
262
276
|
for i, expr in enumerate(expressions):
|
|
263
|
-
result = generate_complete_ast(
|
|
277
|
+
result = generate_complete_ast(
|
|
278
|
+
expr, database_path, connection_url, release_id=release_id
|
|
279
|
+
)
|
|
264
280
|
result['batch_index'] = i
|
|
265
281
|
results.append(result)
|
|
266
282
|
return results
|
|
267
283
|
|
|
268
284
|
|
|
269
285
|
# Convenience function with cleaner interface
|
|
270
|
-
def parse_with_data_fields(
|
|
286
|
+
def parse_with_data_fields(
|
|
287
|
+
expression: str,
|
|
288
|
+
database_path: str = None,
|
|
289
|
+
connection_url: str = None,
|
|
290
|
+
release_id: Optional[int] = None
|
|
291
|
+
):
|
|
271
292
|
"""
|
|
272
293
|
Simple function to parse expression and get AST with data fields.
|
|
273
294
|
|
|
@@ -275,11 +296,15 @@ def parse_with_data_fields(expression: str, database_path: str = None, connectio
|
|
|
275
296
|
expression: DPM-XL expression string
|
|
276
297
|
database_path: Path to SQLite database file
|
|
277
298
|
connection_url: SQLAlchemy connection URL for PostgreSQL (optional)
|
|
299
|
+
release_id: Optional release ID to filter database lookups by specific release.
|
|
300
|
+
If None, uses all available data (release-agnostic).
|
|
278
301
|
|
|
279
302
|
Returns:
|
|
280
303
|
dict: AST dictionary with data fields, or None if failed
|
|
281
304
|
"""
|
|
282
|
-
result = generate_complete_ast(
|
|
305
|
+
result = generate_complete_ast(
|
|
306
|
+
expression, database_path, connection_url, release_id=release_id
|
|
307
|
+
)
|
|
283
308
|
return result['ast'] if result['success'] else None
|
|
284
309
|
|
|
285
310
|
|
|
@@ -296,6 +321,7 @@ def generate_enriched_ast(
|
|
|
296
321
|
operation_code: Optional[str] = None,
|
|
297
322
|
table_context: Optional[Dict[str, Any]] = None,
|
|
298
323
|
precondition: Optional[str] = None,
|
|
324
|
+
release_id: Optional[int] = None,
|
|
299
325
|
) -> Dict[str, Any]:
|
|
300
326
|
"""
|
|
301
327
|
Generate enriched, engine-ready AST from DPM-XL expression.
|
|
@@ -311,6 +337,8 @@ def generate_enriched_ast(
|
|
|
311
337
|
operation_code: Optional operation code (defaults to "default_code")
|
|
312
338
|
table_context: Optional table context dict with keys: 'table', 'columns', 'rows', 'sheets', 'default', 'interval'
|
|
313
339
|
precondition: Optional precondition variable reference (e.g., {v_F_44_04})
|
|
340
|
+
release_id: Optional release ID to filter database lookups by specific release.
|
|
341
|
+
If None, uses all available data (release-agnostic).
|
|
314
342
|
|
|
315
343
|
Returns:
|
|
316
344
|
dict: {
|
|
@@ -321,7 +349,9 @@ def generate_enriched_ast(
|
|
|
321
349
|
"""
|
|
322
350
|
try:
|
|
323
351
|
# Generate complete AST first
|
|
324
|
-
complete_result = generate_complete_ast(
|
|
352
|
+
complete_result = generate_complete_ast(
|
|
353
|
+
expression, database_path, connection_url, release_id=release_id
|
|
354
|
+
)
|
|
325
355
|
|
|
326
356
|
if not complete_result['success']:
|
|
327
357
|
return {
|
py_dpm/client.py
CHANGED
|
@@ -6,26 +6,38 @@ import os
|
|
|
6
6
|
import sys
|
|
7
7
|
import pandas as pd
|
|
8
8
|
|
|
9
|
-
from py_dpm.api import API
|
|
9
|
+
from py_dpm.api import API, SemanticAPI
|
|
10
|
+
from py_dpm.api.semantic import SemanticValidationResult
|
|
10
11
|
from py_dpm.api.operation_scopes import OperationScopesAPI
|
|
11
12
|
from py_dpm.migration import run_migration
|
|
12
|
-
from py_dpm.Utils.tokens import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
from py_dpm.Utils.tokens import (
|
|
14
|
+
CODE,
|
|
15
|
+
ERROR,
|
|
16
|
+
ERROR_CODE,
|
|
17
|
+
EXPRESSION,
|
|
18
|
+
OP_VERSION_ID,
|
|
19
|
+
STATUS,
|
|
20
|
+
STATUS_CORRECT,
|
|
21
|
+
STATUS_UNKNOWN,
|
|
22
|
+
VALIDATIONS,
|
|
23
|
+
VALIDATION_TYPE,
|
|
24
|
+
VARIABLES,
|
|
25
|
+
)
|
|
16
26
|
from py_dpm.Exceptions.exceptions import SemanticError
|
|
17
27
|
|
|
18
28
|
|
|
19
29
|
console = Console()
|
|
20
30
|
|
|
31
|
+
|
|
21
32
|
@click.group()
|
|
22
33
|
@click.version_option()
|
|
23
34
|
def main():
|
|
24
35
|
"""pyDPM CLI - A command line interface for pyDPM"""
|
|
25
36
|
pass
|
|
26
37
|
|
|
38
|
+
|
|
27
39
|
@main.command()
|
|
28
|
-
@click.argument(
|
|
40
|
+
@click.argument("access_file", type=click.Path(exists=True))
|
|
29
41
|
def migrate_access(access_file: str):
|
|
30
42
|
"""
|
|
31
43
|
Migrates data from an Access database to a SQLite database.
|
|
@@ -44,45 +56,93 @@ def migrate_access(access_file: str):
|
|
|
44
56
|
|
|
45
57
|
|
|
46
58
|
@main.command()
|
|
47
|
-
@click.argument(
|
|
48
|
-
|
|
59
|
+
@click.argument("expression", type=str)
|
|
60
|
+
@click.option(
|
|
61
|
+
"--release-id", type=int, help="Release ID to use for validation", default=None
|
|
62
|
+
)
|
|
63
|
+
@click.option(
|
|
64
|
+
"--dpm-version",
|
|
65
|
+
type=str,
|
|
66
|
+
help="DPM Version (e.g. 4.2) to use for validation",
|
|
67
|
+
default=None,
|
|
68
|
+
)
|
|
69
|
+
def semantic(expression: str, release_id: int, dpm_version: str):
|
|
49
70
|
"""
|
|
50
71
|
Semantically analyses the input expression by applying the syntax validation, the operands checking, the data type
|
|
51
72
|
validation and the structure validation
|
|
52
73
|
:param expression: Expression to be analysed
|
|
53
74
|
:param release_id: ID of the release used. If None, gathers the live release
|
|
75
|
+
:param dpm_version: Version code of the release used.
|
|
54
76
|
Used only in DPM-ML generation
|
|
55
77
|
:return if Return_data is False, any Symbol, else data extracted from DB based on operands cell references
|
|
56
78
|
"""
|
|
57
79
|
|
|
80
|
+
if release_id is not None and dpm_version is not None:
|
|
81
|
+
raise click.UsageError("Cannot provide both --release-id and --dpm-version")
|
|
82
|
+
|
|
58
83
|
error_code = ""
|
|
59
84
|
validation_type = STATUS_UNKNOWN
|
|
60
85
|
|
|
61
|
-
|
|
86
|
+
semantic_api = SemanticAPI()
|
|
87
|
+
|
|
88
|
+
if dpm_version:
|
|
89
|
+
from py_dpm.models import Release
|
|
90
|
+
|
|
91
|
+
release_id = (
|
|
92
|
+
semantic_api.session.query(Release.releaseid)
|
|
93
|
+
.filter(Release.code == dpm_version)
|
|
94
|
+
.scalar()
|
|
95
|
+
)
|
|
96
|
+
if release_id is None:
|
|
97
|
+
console.print(
|
|
98
|
+
f"Error: DPM version '{dpm_version}' not found.", style="bold red"
|
|
99
|
+
)
|
|
100
|
+
sys.exit(1)
|
|
101
|
+
|
|
62
102
|
try:
|
|
63
103
|
validation_type = "OTHER"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
104
|
+
# Validate using the semantic API with release_id support
|
|
105
|
+
result: SemanticValidationResult = semantic_api.validate_expression(
|
|
106
|
+
expression, release_id=release_id
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if result.is_valid:
|
|
110
|
+
status = 200
|
|
111
|
+
message_error = ""
|
|
112
|
+
else:
|
|
113
|
+
status = 500
|
|
114
|
+
message_error = result.error_message
|
|
115
|
+
error_code = result.error_code or 1
|
|
116
|
+
|
|
67
117
|
except Exception as error:
|
|
68
118
|
status = 500
|
|
69
119
|
message_error = str(error)
|
|
70
120
|
error_code = 1
|
|
121
|
+
|
|
122
|
+
# Clean up resources
|
|
123
|
+
# semantic_api destructor handles this, but we can be explicit if needed
|
|
124
|
+
|
|
71
125
|
message_response = {
|
|
72
126
|
ERROR: message_error,
|
|
73
127
|
ERROR_CODE: error_code,
|
|
74
128
|
VALIDATION_TYPE: validation_type,
|
|
75
129
|
}
|
|
76
|
-
|
|
130
|
+
|
|
77
131
|
if error_code and status == 500:
|
|
78
|
-
console.print(
|
|
132
|
+
console.print(
|
|
133
|
+
f"Semantic validation failed for expression: {expression}.",
|
|
134
|
+
style="bold red",
|
|
135
|
+
)
|
|
136
|
+
if message_error:
|
|
137
|
+
console.print(f"Error: {message_error}", style="red")
|
|
79
138
|
else:
|
|
80
139
|
console.log(f"Semantic validation completed for expression: {expression}.")
|
|
81
140
|
console.print(f"Status: {status}", style="bold green")
|
|
82
141
|
return status
|
|
83
142
|
|
|
143
|
+
|
|
84
144
|
@main.command()
|
|
85
|
-
@click.argument(
|
|
145
|
+
@click.argument("expression", type=str)
|
|
86
146
|
def syntax(expression: str):
|
|
87
147
|
"""Perform syntactic analysis on a DPM expression."""
|
|
88
148
|
|
|
@@ -106,11 +166,21 @@ def syntax(expression: str):
|
|
|
106
166
|
|
|
107
167
|
|
|
108
168
|
@main.command()
|
|
109
|
-
@click.argument(
|
|
110
|
-
@click.option(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@click.option(
|
|
169
|
+
@click.argument("expression", type=str, required=False)
|
|
170
|
+
@click.option(
|
|
171
|
+
"--operation-vid", type=int, help="Operation version ID to associate scopes with"
|
|
172
|
+
)
|
|
173
|
+
@click.option(
|
|
174
|
+
"--tables", type=str, help="Comma-separated table VIDs (for low-level mode)"
|
|
175
|
+
)
|
|
176
|
+
@click.option(
|
|
177
|
+
"--preconditions", type=str, help="Comma-separated precondition item codes"
|
|
178
|
+
)
|
|
179
|
+
@click.option(
|
|
180
|
+
"--release-id",
|
|
181
|
+
type=int,
|
|
182
|
+
help="Release ID to filter modules (defaults to last release)",
|
|
183
|
+
)
|
|
114
184
|
def calculate_scopes(expression, operation_vid, tables, preconditions, release_id):
|
|
115
185
|
"""
|
|
116
186
|
Calculate operation scopes from a DPM-XL expression or table VIDs.
|
|
@@ -133,12 +203,14 @@ def calculate_scopes(expression, operation_vid, tables, preconditions, release_i
|
|
|
133
203
|
expression=expression,
|
|
134
204
|
operation_version_id=operation_vid,
|
|
135
205
|
release_id=release_id,
|
|
136
|
-
read_only=True
|
|
206
|
+
read_only=True,
|
|
137
207
|
)
|
|
138
208
|
elif tables:
|
|
139
209
|
# Low-level mode with table VIDs
|
|
140
|
-
table_vids = [int(t.strip()) for t in tables.split(
|
|
141
|
-
precondition_items =
|
|
210
|
+
table_vids = [int(t.strip()) for t in tables.split(",")]
|
|
211
|
+
precondition_items = (
|
|
212
|
+
[p.strip() for p in preconditions.split(",")] if preconditions else []
|
|
213
|
+
)
|
|
142
214
|
|
|
143
215
|
# Always use read_only=True for CLI to prevent accidental database modifications
|
|
144
216
|
result = api.calculate_scopes(
|
|
@@ -146,15 +218,20 @@ def calculate_scopes(expression, operation_vid, tables, preconditions, release_i
|
|
|
146
218
|
tables_vids=table_vids,
|
|
147
219
|
precondition_items=precondition_items,
|
|
148
220
|
release_id=release_id,
|
|
149
|
-
read_only=True
|
|
221
|
+
read_only=True,
|
|
150
222
|
)
|
|
151
223
|
else:
|
|
152
|
-
console.print(
|
|
224
|
+
console.print(
|
|
225
|
+
"Error: Either EXPRESSION or --tables must be provided",
|
|
226
|
+
style="bold red",
|
|
227
|
+
)
|
|
153
228
|
sys.exit(1)
|
|
154
229
|
|
|
155
230
|
# Check for errors
|
|
156
231
|
if result.has_error:
|
|
157
|
-
console.print(
|
|
232
|
+
console.print(
|
|
233
|
+
f"Error calculating scopes: {result.error_message}", style="bold red"
|
|
234
|
+
)
|
|
158
235
|
sys.exit(1)
|
|
159
236
|
|
|
160
237
|
# Display summary
|
|
@@ -168,9 +245,16 @@ def calculate_scopes(expression, operation_vid, tables, preconditions, release_i
|
|
|
168
245
|
summary_table.add_row("Total Scopes:", str(result.total_scopes))
|
|
169
246
|
summary_table.add_row("Existing Scopes:", str(len(result.existing_scopes)))
|
|
170
247
|
summary_table.add_row("New Scopes:", str(len(result.new_scopes)))
|
|
171
|
-
summary_table.add_row(
|
|
172
|
-
|
|
173
|
-
|
|
248
|
+
summary_table.add_row(
|
|
249
|
+
"Cross-Module:", "Yes" if result.is_cross_module else "No"
|
|
250
|
+
)
|
|
251
|
+
summary_table.add_row(
|
|
252
|
+
"Module Versions:", ", ".join(map(str, result.module_versions))
|
|
253
|
+
)
|
|
254
|
+
summary_table.add_row(
|
|
255
|
+
"Release ID:",
|
|
256
|
+
str(result.release_id) if result.release_id else "Default (Last)",
|
|
257
|
+
)
|
|
174
258
|
|
|
175
259
|
if result.expression:
|
|
176
260
|
summary_table.add_row("Expression:", result.expression)
|
|
@@ -189,29 +273,37 @@ def calculate_scopes(expression, operation_vid, tables, preconditions, release_i
|
|
|
189
273
|
scopes_table.add_column("From Date", justify="center")
|
|
190
274
|
|
|
191
275
|
for scope in result.existing_scopes:
|
|
192
|
-
module_vids = [
|
|
276
|
+
module_vids = [
|
|
277
|
+
str(comp.modulevid) for comp in scope.operation_scope_compositions
|
|
278
|
+
]
|
|
193
279
|
scope_type = "Cross-Module" if len(module_vids) > 1 else "Intra-Module"
|
|
194
|
-
from_date =
|
|
280
|
+
from_date = (
|
|
281
|
+
str(scope.fromsubmissiondate) if scope.fromsubmissiondate else "N/A"
|
|
282
|
+
)
|
|
195
283
|
|
|
196
284
|
scopes_table.add_row(
|
|
197
285
|
str(scope.operationscopeid),
|
|
198
286
|
"[yellow]Existing[/yellow]",
|
|
199
287
|
", ".join(module_vids),
|
|
200
288
|
scope_type,
|
|
201
|
-
from_date
|
|
289
|
+
from_date,
|
|
202
290
|
)
|
|
203
291
|
|
|
204
292
|
for scope in result.new_scopes:
|
|
205
|
-
module_vids = [
|
|
293
|
+
module_vids = [
|
|
294
|
+
str(comp.modulevid) for comp in scope.operation_scope_compositions
|
|
295
|
+
]
|
|
206
296
|
scope_type = "Cross-Module" if len(module_vids) > 1 else "Intra-Module"
|
|
207
|
-
from_date =
|
|
297
|
+
from_date = (
|
|
298
|
+
str(scope.fromsubmissiondate) if scope.fromsubmissiondate else "N/A"
|
|
299
|
+
)
|
|
208
300
|
|
|
209
301
|
scopes_table.add_row(
|
|
210
302
|
"[dim]New (not committed)[/dim]",
|
|
211
303
|
"[green]New[/green]",
|
|
212
304
|
", ".join(module_vids),
|
|
213
305
|
scope_type,
|
|
214
|
-
from_date
|
|
306
|
+
from_date,
|
|
215
307
|
)
|
|
216
308
|
|
|
217
309
|
console.print(scopes_table)
|
|
@@ -230,20 +322,28 @@ def calculate_scopes(expression, operation_vid, tables, preconditions, release_i
|
|
|
230
322
|
from py_dpm.models import ModuleVersion
|
|
231
323
|
|
|
232
324
|
for module_vid in result.module_versions:
|
|
233
|
-
module_df = ModuleVersion.get_module_version_by_vid(
|
|
325
|
+
module_df = ModuleVersion.get_module_version_by_vid(
|
|
326
|
+
api.session, module_vid
|
|
327
|
+
)
|
|
234
328
|
if not module_df.empty:
|
|
235
329
|
module = module_df.iloc[0]
|
|
236
330
|
modules_table.add_row(
|
|
237
|
-
str(module[
|
|
238
|
-
str(module[
|
|
239
|
-
str(module[
|
|
240
|
-
str(module[
|
|
241
|
-
|
|
331
|
+
str(module["ModuleVID"]),
|
|
332
|
+
str(module["Code"]),
|
|
333
|
+
str(module["Name"]),
|
|
334
|
+
str(module["FromReferenceDate"]),
|
|
335
|
+
(
|
|
336
|
+
str(module["ToReferenceDate"])
|
|
337
|
+
if module["ToReferenceDate"] is not pd.NaT
|
|
338
|
+
else "Open"
|
|
339
|
+
),
|
|
242
340
|
)
|
|
243
341
|
|
|
244
342
|
console.print(modules_table)
|
|
245
343
|
|
|
246
|
-
console.print(
|
|
344
|
+
console.print(
|
|
345
|
+
"\n[bold green]✓ Scope calculation completed successfully[/bold green]\n"
|
|
346
|
+
)
|
|
247
347
|
|
|
248
348
|
except SemanticError as e:
|
|
249
349
|
console.print(f"Semantic error: {str(e)}", style="bold red")
|
|
@@ -256,9 +356,13 @@ def calculate_scopes(expression, operation_vid, tables, preconditions, release_i
|
|
|
256
356
|
|
|
257
357
|
|
|
258
358
|
@main.command()
|
|
259
|
-
@click.argument(
|
|
260
|
-
@click.option(
|
|
261
|
-
@click.option(
|
|
359
|
+
@click.argument("expression", type=str, required=False)
|
|
360
|
+
@click.option("--operation-vid", type=int, help="Operation version ID")
|
|
361
|
+
@click.option(
|
|
362
|
+
"--release-id",
|
|
363
|
+
type=int,
|
|
364
|
+
help="Release ID to filter modules (defaults to last release)",
|
|
365
|
+
)
|
|
262
366
|
def get_scopes_metadata(expression, operation_vid, release_id):
|
|
263
367
|
"""
|
|
264
368
|
Get operation scopes with detailed module metadata.
|
|
@@ -276,15 +380,17 @@ def get_scopes_metadata(expression, operation_vid, release_id):
|
|
|
276
380
|
# Determine mode: expression-based or operation-vid-based
|
|
277
381
|
if expression:
|
|
278
382
|
scopes = api.get_scopes_with_metadata_from_expression(
|
|
279
|
-
expression=expression,
|
|
280
|
-
release_id=release_id
|
|
383
|
+
expression=expression, release_id=release_id
|
|
281
384
|
)
|
|
282
385
|
mode = "expression"
|
|
283
386
|
elif operation_vid:
|
|
284
387
|
scopes = api.get_scopes_with_metadata(operation_version_id=operation_vid)
|
|
285
388
|
mode = "operation_vid"
|
|
286
389
|
else:
|
|
287
|
-
console.print(
|
|
390
|
+
console.print(
|
|
391
|
+
"Error: Either EXPRESSION or --operation-vid must be provided",
|
|
392
|
+
style="bold red",
|
|
393
|
+
)
|
|
288
394
|
sys.exit(1)
|
|
289
395
|
|
|
290
396
|
# Display results
|
|
@@ -292,7 +398,9 @@ def get_scopes_metadata(expression, operation_vid, release_id):
|
|
|
292
398
|
console.print("\n[yellow]No scopes found[/yellow]\n")
|
|
293
399
|
return
|
|
294
400
|
|
|
295
|
-
console.print(
|
|
401
|
+
console.print(
|
|
402
|
+
f"\n[bold cyan]Operation Scopes with Metadata[/bold cyan] (Total: {len(scopes)})"
|
|
403
|
+
)
|
|
296
404
|
console.print("=" * 80)
|
|
297
405
|
|
|
298
406
|
for idx, scope in enumerate(scopes, 1):
|
|
@@ -314,7 +422,9 @@ def get_scopes_metadata(expression, operation_vid, release_id):
|
|
|
314
422
|
|
|
315
423
|
# Module versions
|
|
316
424
|
if scope.module_versions:
|
|
317
|
-
console.print(
|
|
425
|
+
console.print(
|
|
426
|
+
f"\n [bold]Modules ({len(scope.module_versions)}):[/bold]"
|
|
427
|
+
)
|
|
318
428
|
|
|
319
429
|
modules_table = Table(show_header=True, header_style="bold magenta")
|
|
320
430
|
modules_table.add_column("Module VID", justify="right")
|
|
@@ -328,15 +438,29 @@ def get_scopes_metadata(expression, operation_vid, release_id):
|
|
|
328
438
|
modules_table.add_row(
|
|
329
439
|
str(module.module_vid),
|
|
330
440
|
module.code,
|
|
331
|
-
|
|
441
|
+
(
|
|
442
|
+
module.name[:40] + "..."
|
|
443
|
+
if len(module.name) > 40
|
|
444
|
+
else module.name
|
|
445
|
+
),
|
|
332
446
|
module.version_number,
|
|
333
|
-
|
|
334
|
-
|
|
447
|
+
(
|
|
448
|
+
str(module.from_reference_date)
|
|
449
|
+
if module.from_reference_date
|
|
450
|
+
else "N/A"
|
|
451
|
+
),
|
|
452
|
+
(
|
|
453
|
+
str(module.to_reference_date)
|
|
454
|
+
if module.to_reference_date
|
|
455
|
+
else "Open"
|
|
456
|
+
),
|
|
335
457
|
)
|
|
336
458
|
|
|
337
459
|
console.print(modules_table)
|
|
338
460
|
|
|
339
|
-
console.print(
|
|
461
|
+
console.print(
|
|
462
|
+
"\n[bold green]✓ Scopes metadata retrieved successfully[/bold green]\n"
|
|
463
|
+
)
|
|
340
464
|
|
|
341
465
|
except Exception as e:
|
|
342
466
|
console.print(f"Error: {str(e)}", style="bold red")
|
|
@@ -346,9 +470,13 @@ def get_scopes_metadata(expression, operation_vid, release_id):
|
|
|
346
470
|
|
|
347
471
|
|
|
348
472
|
@main.command()
|
|
349
|
-
@click.argument(
|
|
350
|
-
@click.option(
|
|
351
|
-
@click.option(
|
|
473
|
+
@click.argument("expression", type=str, required=False)
|
|
474
|
+
@click.option("--operation-vid", type=int, help="Operation version ID")
|
|
475
|
+
@click.option(
|
|
476
|
+
"--release-id",
|
|
477
|
+
type=int,
|
|
478
|
+
help="Release ID to filter modules (defaults to last release)",
|
|
479
|
+
)
|
|
352
480
|
def get_tables_metadata(expression, operation_vid, release_id):
|
|
353
481
|
"""
|
|
354
482
|
Get tables from operation scopes with metadata.
|
|
@@ -366,13 +494,15 @@ def get_tables_metadata(expression, operation_vid, release_id):
|
|
|
366
494
|
# Determine mode: expression-based or operation-vid-based
|
|
367
495
|
if expression:
|
|
368
496
|
tables = api.get_tables_with_metadata_from_expression(
|
|
369
|
-
expression=expression,
|
|
370
|
-
release_id=release_id
|
|
497
|
+
expression=expression, release_id=release_id
|
|
371
498
|
)
|
|
372
499
|
elif operation_vid:
|
|
373
500
|
tables = api.get_tables_with_metadata(operation_version_id=operation_vid)
|
|
374
501
|
else:
|
|
375
|
-
console.print(
|
|
502
|
+
console.print(
|
|
503
|
+
"Error: Either EXPRESSION or --operation-vid must be provided",
|
|
504
|
+
style="bold red",
|
|
505
|
+
)
|
|
376
506
|
sys.exit(1)
|
|
377
507
|
|
|
378
508
|
# Display results
|
|
@@ -380,7 +510,9 @@ def get_tables_metadata(expression, operation_vid, release_id):
|
|
|
380
510
|
console.print("\n[yellow]No tables found[/yellow]\n")
|
|
381
511
|
return
|
|
382
512
|
|
|
383
|
-
console.print(
|
|
513
|
+
console.print(
|
|
514
|
+
f"\n[bold cyan]Tables with Metadata[/bold cyan] (Total: {len(tables)})"
|
|
515
|
+
)
|
|
384
516
|
console.print("=" * 80)
|
|
385
517
|
|
|
386
518
|
tables_table = Table(show_header=True, header_style="bold magenta")
|
|
@@ -391,17 +523,23 @@ def get_tables_metadata(expression, operation_vid, release_id):
|
|
|
391
523
|
|
|
392
524
|
for table in tables:
|
|
393
525
|
# Truncate long descriptions
|
|
394
|
-
description =
|
|
526
|
+
description = (
|
|
527
|
+
table.description[:60] + "..."
|
|
528
|
+
if len(table.description) > 60
|
|
529
|
+
else table.description
|
|
530
|
+
)
|
|
395
531
|
|
|
396
532
|
tables_table.add_row(
|
|
397
533
|
str(table.table_vid),
|
|
398
534
|
table.code,
|
|
399
535
|
table.name[:40] + "..." if len(table.name) > 40 else table.name,
|
|
400
|
-
description
|
|
536
|
+
description,
|
|
401
537
|
)
|
|
402
538
|
|
|
403
539
|
console.print(tables_table)
|
|
404
|
-
console.print(
|
|
540
|
+
console.print(
|
|
541
|
+
"\n[bold green]✓ Tables metadata retrieved successfully[/bold green]\n"
|
|
542
|
+
)
|
|
405
543
|
|
|
406
544
|
except Exception as e:
|
|
407
545
|
console.print(f"Error: {str(e)}", style="bold red")
|
|
@@ -411,10 +549,14 @@ def get_tables_metadata(expression, operation_vid, release_id):
|
|
|
411
549
|
|
|
412
550
|
|
|
413
551
|
@main.command()
|
|
414
|
-
@click.argument(
|
|
415
|
-
@click.option(
|
|
416
|
-
@click.option(
|
|
417
|
-
@click.option(
|
|
552
|
+
@click.argument("expression", type=str, required=False)
|
|
553
|
+
@click.option("--operation-vid", type=int, help="Operation version ID")
|
|
554
|
+
@click.option("--table-vid", type=int, help="Filter by specific table VID")
|
|
555
|
+
@click.option(
|
|
556
|
+
"--release-id",
|
|
557
|
+
type=int,
|
|
558
|
+
help="Release ID to filter modules (defaults to last release)",
|
|
559
|
+
)
|
|
418
560
|
def get_headers_metadata(expression, operation_vid, table_vid, release_id):
|
|
419
561
|
"""
|
|
420
562
|
Get headers from operation scopes with metadata.
|
|
@@ -433,17 +575,17 @@ def get_headers_metadata(expression, operation_vid, table_vid, release_id):
|
|
|
433
575
|
# Determine mode: expression-based or operation-vid-based
|
|
434
576
|
if expression:
|
|
435
577
|
headers = api.get_headers_with_metadata_from_expression(
|
|
436
|
-
expression=expression,
|
|
437
|
-
table_vid=table_vid,
|
|
438
|
-
release_id=release_id
|
|
578
|
+
expression=expression, table_vid=table_vid, release_id=release_id
|
|
439
579
|
)
|
|
440
580
|
elif operation_vid:
|
|
441
581
|
headers = api.get_headers_with_metadata(
|
|
442
|
-
operation_version_id=operation_vid,
|
|
443
|
-
table_vid=table_vid
|
|
582
|
+
operation_version_id=operation_vid, table_vid=table_vid
|
|
444
583
|
)
|
|
445
584
|
else:
|
|
446
|
-
console.print(
|
|
585
|
+
console.print(
|
|
586
|
+
"Error: Either EXPRESSION or --operation-vid must be provided",
|
|
587
|
+
style="bold red",
|
|
588
|
+
)
|
|
447
589
|
sys.exit(1)
|
|
448
590
|
|
|
449
591
|
# Display results
|
|
@@ -451,7 +593,9 @@ def get_headers_metadata(expression, operation_vid, table_vid, release_id):
|
|
|
451
593
|
console.print("\n[yellow]No headers found[/yellow]\n")
|
|
452
594
|
return
|
|
453
595
|
|
|
454
|
-
console.print(
|
|
596
|
+
console.print(
|
|
597
|
+
f"\n[bold cyan]Headers with Metadata[/bold cyan] (Total: {len(headers)})"
|
|
598
|
+
)
|
|
455
599
|
if table_vid:
|
|
456
600
|
console.print(f"[dim]Filtered by Table VID: {table_vid}[/dim]")
|
|
457
601
|
console.print("=" * 80)
|
|
@@ -469,11 +613,13 @@ def get_headers_metadata(expression, operation_vid, table_vid, release_id):
|
|
|
469
613
|
str(header.table_vid) if header.table_vid else "N/A",
|
|
470
614
|
header.code,
|
|
471
615
|
header.label[:50] + "..." if len(header.label) > 50 else header.label,
|
|
472
|
-
header.header_type
|
|
616
|
+
header.header_type,
|
|
473
617
|
)
|
|
474
618
|
|
|
475
619
|
console.print(headers_table)
|
|
476
|
-
console.print(
|
|
620
|
+
console.print(
|
|
621
|
+
"\n[bold green]✓ Headers metadata retrieved successfully[/bold green]\n"
|
|
622
|
+
)
|
|
477
623
|
|
|
478
624
|
except Exception as e:
|
|
479
625
|
console.print(f"Error: {str(e)}", style="bold red")
|
|
@@ -482,5 +628,5 @@ def get_headers_metadata(expression, operation_vid, table_vid, release_id):
|
|
|
482
628
|
api.session.close()
|
|
483
629
|
|
|
484
630
|
|
|
485
|
-
if __name__ ==
|
|
486
|
-
main()
|
|
631
|
+
if __name__ == "__main__":
|
|
632
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydpm_xl
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.39rc31
|
|
4
4
|
Summary: Python library for DPM-XL data processing and analysis
|
|
5
5
|
Author-email: "MeaningfulData S.L." <info@meaningfuldata.eu>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -25,7 +25,7 @@ Requires-Dist: antlr4-python3-runtime<4.9.3,>=4.9.2
|
|
|
25
25
|
Requires-Dist: pyodbc<5.2.0,>=5.1.0
|
|
26
26
|
Requires-Dist: click<8.2.0,>=8.1.6
|
|
27
27
|
Requires-Dist: python-dotenv<1.1.0,>=1.0.1
|
|
28
|
-
Requires-Dist: psycopg2<3.0.0,>=2.9.0
|
|
28
|
+
Requires-Dist: psycopg2-binary<3.0.0,>=2.9.0
|
|
29
29
|
Provides-Extra: test
|
|
30
30
|
Requires-Dist: pytest<8.0.0,>=7.4.0; extra == "test"
|
|
31
31
|
Requires-Dist: pytest-cov<5.0.0,>=4.1.0; extra == "test"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
py_dpm/__init__.py,sha256=
|
|
2
|
-
py_dpm/client.py,sha256=
|
|
1
|
+
py_dpm/__init__.py,sha256=Ka4t1a-16Iht1Iqn-XfNPTJuUt9FbyFX_3SCDdabdZQ,1863
|
|
2
|
+
py_dpm/client.py,sha256=f-D3-5-tNpBM2fr5H8ImdwZM5hyuqfCXJRJd66euHU8,22279
|
|
3
3
|
py_dpm/data_handlers.py,sha256=_aJqkyflJr1oNsOQ24nB9yzfrpxu28NPrpoil2aAm58,4395
|
|
4
4
|
py_dpm/db_utils.py,sha256=f0da6FT7HusGpLfGen4fceSvcojW4bzrp5sucgt6XVQ,7822
|
|
5
5
|
py_dpm/migration.py,sha256=ivO_ObvKzVomTns6qfo-o5FuciWxkXbMd_gJ4_tu7Xc,14110
|
|
@@ -35,7 +35,7 @@ py_dpm/Operators/TimeOperators.py,sha256=klcFIYFonGVRNhr66Ipu1lwOr9CxzDGIbA3QQLm
|
|
|
35
35
|
py_dpm/Operators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
36
|
py_dpm/Utils/ValidationsGenerationUtils.py,sha256=1yINJ_ANqr9BbIDdPm4WxRG2EQu0yaRICikYG1nIhzs,22652
|
|
37
37
|
py_dpm/Utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
py_dpm/Utils/ast_serialization.py,sha256=
|
|
38
|
+
py_dpm/Utils/ast_serialization.py,sha256=c23qwwGtjDJgOxoOo41g4p7CYdbkVPz3q_Q6KssMSIU,32462
|
|
39
39
|
py_dpm/Utils/operands_mapping.py,sha256=CDWvoKxh-nBcEKMxrUmMnaCveE8dExjeuFUa-lgK8Rg,2239
|
|
40
40
|
py_dpm/Utils/operator_mapping.py,sha256=SK8utWaUaQJybSmErkF_2blzUIgiVOUNGFozIk7dBg8,1926
|
|
41
41
|
py_dpm/Utils/tokens.py,sha256=VRIrPDi5ttwgH-on5Qt4-l4ho4bLA755-nfTalponcA,3496
|
|
@@ -47,7 +47,7 @@ py_dpm/ValidationsGeneration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
47
47
|
py_dpm/ValidationsGeneration/auxiliary_functions.py,sha256=4msDLINbwNb4gTc05x_351eb_VIra1H0lWE1hPijlVI,4187
|
|
48
48
|
py_dpm/api/__init__.py,sha256=41ILMv8oQ9mmw_zkIhRHVBnfYi1OQYEAFWqyqc3O8_U,6986
|
|
49
49
|
py_dpm/api/ast_generator.py,sha256=HjosdS1CkguPJDjp2JUqzO7WaRCS2GjXKuFgQXQkkPg,16143
|
|
50
|
-
py_dpm/api/complete_ast.py,sha256=
|
|
50
|
+
py_dpm/api/complete_ast.py,sha256=8hMPoVyVOHXIR5Qm8TGUuva8rONntN-TJoFQHU0ElW8,29892
|
|
51
51
|
py_dpm/api/data_dictionary.py,sha256=0k-tz0DNP1gZXlCA4EX9BoWHq2RB7nDnlsxRfbxNwow,33083
|
|
52
52
|
py_dpm/api/data_dictionary_validation.py,sha256=OtvErzLtDMryoxdO5ktACjvGWeDEqG6SXuvuJyzKdys,23495
|
|
53
53
|
py_dpm/api/migration.py,sha256=LW8V-sq9iBAuXUHWPGqWl-tvxz2vAGUvIYyZLtgfjKA,2512
|
|
@@ -88,9 +88,9 @@ py_dpm/views/precondition_info.sql,sha256=l24U8c6NtwFcUDFwb2FqH9h0xgpoi9WbvsKUgJ
|
|
|
88
88
|
py_dpm/views/report_type_operand_reference_info.sql,sha256=_lBK9HQILk9yLD7cW9Ieud-vfTc-KQiU3WGAnN6LuNY,977
|
|
89
89
|
py_dpm/views/subcategory_info.sql,sha256=V8UZiGjicmOWfBEHX2v6ktSMR4r_f1YiGD_LeN9Gn6o,812
|
|
90
90
|
py_dpm/views/table_info.sql,sha256=htRXIVJya44CZAqjO5sVaJJwiQBQ30oiLFFu5DiBpR4,793
|
|
91
|
-
pydpm_xl-0.1.
|
|
92
|
-
pydpm_xl-0.1.
|
|
93
|
-
pydpm_xl-0.1.
|
|
94
|
-
pydpm_xl-0.1.
|
|
95
|
-
pydpm_xl-0.1.
|
|
96
|
-
pydpm_xl-0.1.
|
|
91
|
+
pydpm_xl-0.1.39rc31.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
92
|
+
pydpm_xl-0.1.39rc31.dist-info/METADATA,sha256=TLfn-srr0mIT5UNqwsL_46ewXN9GsuO4M_RgaiueiF0,1599
|
|
93
|
+
pydpm_xl-0.1.39rc31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
94
|
+
pydpm_xl-0.1.39rc31.dist-info/entry_points.txt,sha256=W9j5xCzgvdfzOKSfEpkk2jSRhEcpmXZUVMwHrsEFAbY,45
|
|
95
|
+
pydpm_xl-0.1.39rc31.dist-info/top_level.txt,sha256=495PvWZRoKl2NvbQU25W7dqWIBHqY-mFMPt83uxPpcM,7
|
|
96
|
+
pydpm_xl-0.1.39rc31.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|