pydpm_xl 0.2.8__py3-none-any.whl → 0.2.10__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/__init__.py +1 -1
- py_dpm/api/__init__.py +3 -9
- py_dpm/api/dpm_xl/__init__.py +3 -12
- py_dpm/api/dpm_xl/ast_generator.py +92 -47
- py_dpm/api/dpm_xl/complete_ast.py +14 -127
- py_dpm/dpm/migration.py +40 -45
- py_dpm/dpm/models.py +22 -2
- py_dpm/dpm_xl/ast/operands.py +41 -32
- py_dpm/dpm_xl/utils/serialization.py +15 -0
- {pydpm_xl-0.2.8.dist-info → pydpm_xl-0.2.10.dist-info}/METADATA +12 -13
- {pydpm_xl-0.2.8.dist-info → pydpm_xl-0.2.10.dist-info}/RECORD +15 -15
- {pydpm_xl-0.2.8.dist-info → pydpm_xl-0.2.10.dist-info}/WHEEL +0 -0
- {pydpm_xl-0.2.8.dist-info → pydpm_xl-0.2.10.dist-info}/entry_points.txt +0 -0
- {pydpm_xl-0.2.8.dist-info → pydpm_xl-0.2.10.dist-info}/licenses/LICENSE +0 -0
- {pydpm_xl-0.2.8.dist-info → pydpm_xl-0.2.10.dist-info}/top_level.txt +0 -0
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.2.
|
|
44
|
+
__version__ = "0.2.10"
|
|
45
45
|
__author__ = "MeaningfulData S.L."
|
|
46
46
|
__email__ = "info@meaningfuldata.eu"
|
|
47
47
|
__license__ = "GPL-3.0-or-later"
|
py_dpm/api/__init__.py
CHANGED
|
@@ -11,10 +11,7 @@ from py_dpm.api.dpm_xl import (
|
|
|
11
11
|
SemanticAPI,
|
|
12
12
|
ASTGeneratorAPI,
|
|
13
13
|
OperationScopesAPI,
|
|
14
|
-
|
|
15
|
-
generate_enriched_ast,
|
|
16
|
-
enrich_ast_with_metadata,
|
|
17
|
-
parse_with_data_fields,
|
|
14
|
+
generate_validations_script,
|
|
18
15
|
)
|
|
19
16
|
|
|
20
17
|
# Import from general DPM API
|
|
@@ -40,9 +37,6 @@ __all__ = [
|
|
|
40
37
|
"SemanticAPI",
|
|
41
38
|
"ASTGeneratorAPI",
|
|
42
39
|
"OperationScopesAPI",
|
|
43
|
-
#
|
|
44
|
-
"
|
|
45
|
-
"generate_enriched_ast",
|
|
46
|
-
"enrich_ast_with_metadata",
|
|
47
|
-
"parse_with_data_fields",
|
|
40
|
+
# Standalone function
|
|
41
|
+
"generate_validations_script",
|
|
48
42
|
]
|
py_dpm/api/dpm_xl/__init__.py
CHANGED
|
@@ -9,13 +9,7 @@ from py_dpm.api.dpm_xl.semantic import SemanticAPI
|
|
|
9
9
|
from py_dpm.api.dpm_xl.ast_generator import ASTGeneratorAPI
|
|
10
10
|
from py_dpm.api.dpm_xl.operation_scopes import OperationScopesAPI
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
from py_dpm.api.dpm_xl.complete_ast import (
|
|
14
|
-
generate_complete_ast,
|
|
15
|
-
generate_enriched_ast,
|
|
16
|
-
enrich_ast_with_metadata,
|
|
17
|
-
parse_with_data_fields,
|
|
18
|
-
)
|
|
12
|
+
from py_dpm.api.dpm_xl.complete_ast import generate_validations_script
|
|
19
13
|
|
|
20
14
|
__all__ = [
|
|
21
15
|
# Class-based APIs
|
|
@@ -23,9 +17,6 @@ __all__ = [
|
|
|
23
17
|
"SemanticAPI",
|
|
24
18
|
"ASTGeneratorAPI",
|
|
25
19
|
"OperationScopesAPI",
|
|
26
|
-
# Standalone
|
|
27
|
-
"
|
|
28
|
-
"generate_enriched_ast",
|
|
29
|
-
"enrich_ast_with_metadata",
|
|
30
|
-
"parse_with_data_fields",
|
|
20
|
+
# Standalone function
|
|
21
|
+
"generate_validations_script",
|
|
31
22
|
]
|
|
@@ -32,7 +32,7 @@ class ASTGeneratorAPI:
|
|
|
32
32
|
- Returns: AST with data fields populated (datapoint IDs, operand references)
|
|
33
33
|
- Use for: AST analysis with complete metadata, matching json_scripts/*.json format
|
|
34
34
|
|
|
35
|
-
3. **
|
|
35
|
+
3. **Validations Script** (generate_validations_script):
|
|
36
36
|
- Requires database connection
|
|
37
37
|
- Extends complete AST with framework structure for execution engines
|
|
38
38
|
- Returns: Engine-ready AST with operations, variables, tables, preconditions sections
|
|
@@ -90,7 +90,7 @@ class ASTGeneratorAPI:
|
|
|
90
90
|
|
|
91
91
|
**What you DON'T get:**
|
|
92
92
|
- Data fields (datapoint IDs, operand references) - use generate_complete_ast()
|
|
93
|
-
- Framework structure - use
|
|
93
|
+
- Framework structure - use generate_validations_script()
|
|
94
94
|
|
|
95
95
|
Args:
|
|
96
96
|
expression: DPM-XL expression string
|
|
@@ -347,7 +347,7 @@ class ASTGeneratorAPI:
|
|
|
347
347
|
return [(expressions, "default_code", None)]
|
|
348
348
|
return expressions
|
|
349
349
|
|
|
350
|
-
def
|
|
350
|
+
def generate_validations_script(
|
|
351
351
|
self,
|
|
352
352
|
expressions: Union[str, List[Tuple[str, str, Optional[str]]]],
|
|
353
353
|
release_code: Optional[str] = None,
|
|
@@ -360,9 +360,9 @@ class ASTGeneratorAPI:
|
|
|
360
360
|
module_version_number: Optional[str] = None,
|
|
361
361
|
) -> Dict[str, Any]:
|
|
362
362
|
"""
|
|
363
|
-
Generate
|
|
363
|
+
Generate validations script with engine-ready AST and framework structure.
|
|
364
364
|
|
|
365
|
-
This
|
|
365
|
+
This method generates the complete validations script by wrapping ASTs in an engine-ready
|
|
366
366
|
framework structure with operations, variables, tables, and preconditions sections.
|
|
367
367
|
This is the format required by business rule execution engines.
|
|
368
368
|
|
|
@@ -370,7 +370,7 @@ class ASTGeneratorAPI:
|
|
|
370
370
|
expression/operation/precondition tuples for generating scripts with multiple operations.
|
|
371
371
|
|
|
372
372
|
**What you get:**
|
|
373
|
-
-
|
|
373
|
+
- Complete AST with data fields PLUS:
|
|
374
374
|
- Framework structure: operations, variables, tables, preconditions
|
|
375
375
|
- Module metadata: version, release info, dates
|
|
376
376
|
- Dependency information (including cross-module dependencies)
|
|
@@ -428,14 +428,14 @@ class ASTGeneratorAPI:
|
|
|
428
428
|
|
|
429
429
|
Example:
|
|
430
430
|
>>> generator = ASTGeneratorAPI(database_path="data.db")
|
|
431
|
-
>>> # Single expression
|
|
432
|
-
>>> result = generator.
|
|
431
|
+
>>> # Single expression
|
|
432
|
+
>>> result = generator.generate_validations_script(
|
|
433
433
|
... "{tF_01.00, r0010, c0010}",
|
|
434
434
|
... release_code="4.2",
|
|
435
435
|
... )
|
|
436
436
|
>>>
|
|
437
437
|
>>> # Multiple expressions with operations and preconditions
|
|
438
|
-
>>> result = generator.
|
|
438
|
+
>>> result = generator.generate_validations_script(
|
|
439
439
|
... [
|
|
440
440
|
... ("{tF_01.00, r0010, c0010} = 0", "v1234_m", None),
|
|
441
441
|
... ("{tF_01.00, r0020, c0010} > 0", "v1235_m", "{v_F_44_04}"),
|
|
@@ -984,6 +984,7 @@ class ASTGeneratorAPI:
|
|
|
984
984
|
"""
|
|
985
985
|
from py_dpm.dpm.utils import get_engine
|
|
986
986
|
from py_dpm.api.dpm import DataDictionaryAPI
|
|
987
|
+
from py_dpm.api.dpm_xl.operation_scopes import OperationScopesAPI
|
|
987
988
|
|
|
988
989
|
# Initialize database connection
|
|
989
990
|
engine = get_engine(database_path=self.database_path, connection_url=self.connection_url)
|
|
@@ -1017,6 +1018,12 @@ class ASTGeneratorAPI:
|
|
|
1017
1018
|
connection_url=self.connection_url
|
|
1018
1019
|
)
|
|
1019
1020
|
|
|
1021
|
+
# Initialize OperationScopesAPI once for all expressions (performance optimization)
|
|
1022
|
+
scopes_api = OperationScopesAPI(
|
|
1023
|
+
database_path=self.database_path,
|
|
1024
|
+
connection_url=self.connection_url
|
|
1025
|
+
)
|
|
1026
|
+
|
|
1020
1027
|
# Primary module info will be determined from the first expression or module_code
|
|
1021
1028
|
primary_module_info = None
|
|
1022
1029
|
namespace = None
|
|
@@ -1035,6 +1042,21 @@ class ASTGeneratorAPI:
|
|
|
1035
1042
|
complete_ast = complete_result["ast"]
|
|
1036
1043
|
context = complete_result.get("context") or table_context
|
|
1037
1044
|
|
|
1045
|
+
# Get tables with modules for this expression FIRST (reuse scopes_api from outer scope)
|
|
1046
|
+
# This is done before _get_primary_module_info to pass precomputed values
|
|
1047
|
+
tables_with_modules = scopes_api.get_tables_with_metadata_from_expression(
|
|
1048
|
+
expression=expression,
|
|
1049
|
+
release_id=release_id
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
# Calculate scope_result once (avoid duplicate calls in other methods)
|
|
1053
|
+
scope_result = scopes_api.calculate_scopes_from_expression(
|
|
1054
|
+
expression=expression,
|
|
1055
|
+
release_id=release_id,
|
|
1056
|
+
read_only=True
|
|
1057
|
+
)
|
|
1058
|
+
all_tables_with_modules.extend(tables_with_modules)
|
|
1059
|
+
|
|
1038
1060
|
# Get primary module info from first expression (or use module_code)
|
|
1039
1061
|
if primary_module_info is None:
|
|
1040
1062
|
primary_module_info = self._get_primary_module_info(
|
|
@@ -1042,6 +1064,9 @@ class ASTGeneratorAPI:
|
|
|
1042
1064
|
primary_module_vid=primary_module_vid,
|
|
1043
1065
|
release_id=release_id,
|
|
1044
1066
|
module_code=module_code,
|
|
1067
|
+
# Performance optimization: pass precomputed values
|
|
1068
|
+
tables_with_modules=tables_with_modules,
|
|
1069
|
+
scopes_api=scopes_api,
|
|
1045
1070
|
)
|
|
1046
1071
|
namespace = primary_module_info.get("module_uri", "default_module")
|
|
1047
1072
|
|
|
@@ -1066,18 +1091,6 @@ class ASTGeneratorAPI:
|
|
|
1066
1091
|
# Clean extra fields from data entries
|
|
1067
1092
|
self._clean_ast_data_entries(ast_with_coords)
|
|
1068
1093
|
|
|
1069
|
-
# Get tables with modules for this expression
|
|
1070
|
-
from py_dpm.api.dpm_xl.operation_scopes import OperationScopesAPI
|
|
1071
|
-
scopes_api = OperationScopesAPI(
|
|
1072
|
-
database_path=self.database_path,
|
|
1073
|
-
connection_url=self.connection_url
|
|
1074
|
-
)
|
|
1075
|
-
tables_with_modules = scopes_api.get_tables_with_metadata_from_expression(
|
|
1076
|
-
expression=expression,
|
|
1077
|
-
release_id=release_id
|
|
1078
|
-
)
|
|
1079
|
-
all_tables_with_modules.extend(tables_with_modules)
|
|
1080
|
-
|
|
1081
1094
|
# Build mapping of table_code -> module_vid
|
|
1082
1095
|
# Prefer the module VID that matches the detected primary module
|
|
1083
1096
|
table_to_module = {}
|
|
@@ -1179,6 +1192,10 @@ class ASTGeneratorAPI:
|
|
|
1179
1192
|
operation_code=operation_code,
|
|
1180
1193
|
release_id=release_id,
|
|
1181
1194
|
preferred_module_dependencies=preferred_module_dependencies,
|
|
1195
|
+
# Performance optimization: pass precomputed values to avoid redundant work
|
|
1196
|
+
tables_with_modules=tables_with_modules,
|
|
1197
|
+
scopes_api=scopes_api,
|
|
1198
|
+
scope_result=scope_result,
|
|
1182
1199
|
)
|
|
1183
1200
|
|
|
1184
1201
|
# Merge dependency modules (avoid table duplicates)
|
|
@@ -1313,6 +1330,8 @@ class ASTGeneratorAPI:
|
|
|
1313
1330
|
primary_module_vid: Optional[int],
|
|
1314
1331
|
release_id: Optional[int],
|
|
1315
1332
|
module_code: Optional[str] = None,
|
|
1333
|
+
tables_with_modules: Optional[List[Dict[str, Any]]] = None,
|
|
1334
|
+
scopes_api: Optional[Any] = None,
|
|
1316
1335
|
) -> Dict[str, Any]:
|
|
1317
1336
|
"""
|
|
1318
1337
|
Detect and return metadata for the primary module from the expression.
|
|
@@ -1323,6 +1342,10 @@ class ASTGeneratorAPI:
|
|
|
1323
1342
|
release_id: Optional release ID for filtering
|
|
1324
1343
|
module_code: Optional module code (e.g., "FINREP9") - takes precedence over
|
|
1325
1344
|
primary_module_vid if provided
|
|
1345
|
+
tables_with_modules: Optional precomputed tables with module metadata
|
|
1346
|
+
(performance optimization to avoid redundant database queries)
|
|
1347
|
+
scopes_api: Optional precomputed OperationScopesAPI instance
|
|
1348
|
+
(performance optimization to reuse database connections)
|
|
1326
1349
|
|
|
1327
1350
|
Returns:
|
|
1328
1351
|
Dict with module_uri, module_code, module_version, framework_code,
|
|
@@ -1341,20 +1364,28 @@ class ASTGeneratorAPI:
|
|
|
1341
1364
|
"module_vid": None,
|
|
1342
1365
|
}
|
|
1343
1366
|
|
|
1367
|
+
# Track if we created the scopes_api locally (need to close it)
|
|
1368
|
+
local_scopes_api = False
|
|
1369
|
+
|
|
1344
1370
|
try:
|
|
1345
|
-
scopes_api
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1371
|
+
# Reuse provided scopes_api or create a new one
|
|
1372
|
+
if scopes_api is None:
|
|
1373
|
+
scopes_api = OperationScopesAPI(
|
|
1374
|
+
database_path=self.database_path,
|
|
1375
|
+
connection_url=self.connection_url
|
|
1376
|
+
)
|
|
1377
|
+
local_scopes_api = True
|
|
1349
1378
|
|
|
1350
|
-
#
|
|
1351
|
-
tables_with_modules
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1379
|
+
# Reuse provided tables_with_modules or fetch if not available
|
|
1380
|
+
if tables_with_modules is None:
|
|
1381
|
+
tables_with_modules = scopes_api.get_tables_with_metadata_from_expression(
|
|
1382
|
+
expression=expression,
|
|
1383
|
+
release_id=release_id
|
|
1384
|
+
)
|
|
1355
1385
|
|
|
1356
1386
|
if not tables_with_modules:
|
|
1357
|
-
|
|
1387
|
+
if local_scopes_api:
|
|
1388
|
+
scopes_api.close()
|
|
1358
1389
|
return default_info
|
|
1359
1390
|
|
|
1360
1391
|
# Determine primary module
|
|
@@ -1408,7 +1439,8 @@ class ASTGeneratorAPI:
|
|
|
1408
1439
|
to_date = module.get("to_reference_date", to_date)
|
|
1409
1440
|
break
|
|
1410
1441
|
|
|
1411
|
-
|
|
1442
|
+
if local_scopes_api:
|
|
1443
|
+
scopes_api.close()
|
|
1412
1444
|
|
|
1413
1445
|
return {
|
|
1414
1446
|
"module_uri": module_uri or "default_module",
|
|
@@ -1864,6 +1896,9 @@ class ASTGeneratorAPI:
|
|
|
1864
1896
|
operation_code: str,
|
|
1865
1897
|
release_id: Optional[int] = None,
|
|
1866
1898
|
preferred_module_dependencies: Optional[List[str]] = None,
|
|
1899
|
+
tables_with_modules: Optional[List[Dict[str, Any]]] = None,
|
|
1900
|
+
scopes_api: Optional[Any] = None,
|
|
1901
|
+
scope_result: Optional[Any] = None,
|
|
1867
1902
|
) -> tuple:
|
|
1868
1903
|
"""
|
|
1869
1904
|
Detect cross-module dependencies for a single expression.
|
|
@@ -1879,6 +1914,12 @@ class ASTGeneratorAPI:
|
|
|
1879
1914
|
release_id: Optional release ID for filtering
|
|
1880
1915
|
preferred_module_dependencies: Optional list of module codes to prefer when
|
|
1881
1916
|
a table belongs to multiple modules
|
|
1917
|
+
tables_with_modules: Optional precomputed tables with module metadata
|
|
1918
|
+
(performance optimization to avoid redundant database queries)
|
|
1919
|
+
scopes_api: Optional precomputed OperationScopesAPI instance
|
|
1920
|
+
(performance optimization to reuse database connections)
|
|
1921
|
+
scope_result: Optional precomputed scope result from calculate_scopes_from_expression
|
|
1922
|
+
(performance optimization to avoid redundant computation)
|
|
1882
1923
|
|
|
1883
1924
|
Returns:
|
|
1884
1925
|
Tuple of (dependency_modules, cross_instance_dependencies)
|
|
@@ -1889,24 +1930,28 @@ class ASTGeneratorAPI:
|
|
|
1889
1930
|
from py_dpm.dpm.queries.explorer_queries import ExplorerQuery
|
|
1890
1931
|
import logging
|
|
1891
1932
|
|
|
1892
|
-
scopes_api
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1933
|
+
# Reuse provided scopes_api or create a new one
|
|
1934
|
+
if scopes_api is None:
|
|
1935
|
+
scopes_api = OperationScopesAPI(
|
|
1936
|
+
database_path=self.database_path,
|
|
1937
|
+
connection_url=self.connection_url
|
|
1938
|
+
)
|
|
1896
1939
|
|
|
1897
1940
|
try:
|
|
1898
|
-
#
|
|
1899
|
-
tables_with_modules
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1941
|
+
# Reuse provided tables_with_modules or fetch if not available
|
|
1942
|
+
if tables_with_modules is None:
|
|
1943
|
+
tables_with_modules = scopes_api.get_tables_with_metadata_from_expression(
|
|
1944
|
+
expression=expression,
|
|
1945
|
+
release_id=release_id
|
|
1946
|
+
)
|
|
1903
1947
|
|
|
1904
|
-
#
|
|
1905
|
-
scope_result
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1948
|
+
# Reuse provided scope_result or compute if not available
|
|
1949
|
+
if scope_result is None:
|
|
1950
|
+
scope_result = scopes_api.calculate_scopes_from_expression(
|
|
1951
|
+
expression=expression,
|
|
1952
|
+
release_id=release_id,
|
|
1953
|
+
read_only=True
|
|
1954
|
+
)
|
|
1910
1955
|
|
|
1911
1956
|
if scope_result.has_error or not scope_result.is_cross_module:
|
|
1912
1957
|
return {}, []
|
|
@@ -1,85 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
|
|
3
|
+
Validations Script API - Generate engine-ready ASTs for validation frameworks.
|
|
4
4
|
|
|
5
|
-
This module provides
|
|
6
|
-
All AST-related functionality is now consolidated in the ASTGeneratorAPI class.
|
|
5
|
+
This module provides a standalone function that delegates to ASTGeneratorAPI.
|
|
7
6
|
|
|
8
|
-
For
|
|
7
|
+
For direct class usage:
|
|
9
8
|
from py_dpm.api.dpm_xl import ASTGeneratorAPI
|
|
10
9
|
|
|
11
10
|
generator = ASTGeneratorAPI(database_path="data.db")
|
|
12
|
-
result = generator.
|
|
11
|
+
result = generator.generate_validations_script(expressions)
|
|
13
12
|
"""
|
|
14
13
|
|
|
15
14
|
from typing import Dict, Any, Optional, List, Union, Tuple
|
|
16
15
|
from py_dpm.api.dpm_xl.ast_generator import ASTGeneratorAPI
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
def
|
|
20
|
-
expression: str,
|
|
21
|
-
database_path: str = None,
|
|
22
|
-
connection_url: str = None,
|
|
23
|
-
release_id: Optional[int] = None,
|
|
24
|
-
):
|
|
25
|
-
"""
|
|
26
|
-
Generate complete AST with all data fields, exactly like json_scripts examples.
|
|
27
|
-
|
|
28
|
-
This function delegates to ASTGeneratorAPI for backwards compatibility.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
expression: DPM-XL expression string
|
|
32
|
-
database_path: Path to SQLite database file (e.g., "./database.db")
|
|
33
|
-
connection_url: SQLAlchemy connection URL for PostgreSQL (optional)
|
|
34
|
-
release_id: Optional release ID to filter database lookups by specific release.
|
|
35
|
-
If None, uses all available data (release-agnostic).
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
dict with keys:
|
|
39
|
-
success, ast, context, error, data_populated, semantic_result
|
|
40
|
-
"""
|
|
41
|
-
generator = ASTGeneratorAPI(
|
|
42
|
-
database_path=database_path,
|
|
43
|
-
connection_url=connection_url,
|
|
44
|
-
enable_semantic_validation=True
|
|
45
|
-
)
|
|
46
|
-
return generator.generate_complete_ast(expression, release_id=release_id)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# Convenience function with cleaner interface
|
|
50
|
-
def parse_with_data_fields(
|
|
51
|
-
expression: str,
|
|
52
|
-
database_path: str = None,
|
|
53
|
-
connection_url: str = None,
|
|
54
|
-
release_id: Optional[int] = None,
|
|
55
|
-
):
|
|
56
|
-
"""
|
|
57
|
-
Simple function to parse expression and get AST with data fields.
|
|
58
|
-
|
|
59
|
-
This function delegates to ASTGeneratorAPI for backwards compatibility.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
expression: DPM-XL expression string
|
|
63
|
-
database_path: Path to SQLite database file
|
|
64
|
-
connection_url: SQLAlchemy connection URL for PostgreSQL (optional)
|
|
65
|
-
release_id: Optional release ID to filter database lookups by specific release.
|
|
66
|
-
If None, uses all available data (release-agnostic).
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
dict: AST dictionary with data fields, or None if failed
|
|
70
|
-
"""
|
|
71
|
-
result = generate_complete_ast(
|
|
72
|
-
expression, database_path, connection_url, release_id=release_id
|
|
73
|
-
)
|
|
74
|
-
return result["ast"] if result["success"] else None
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
# ============================================================================
|
|
78
|
-
# AST Enrichment Functions - Create engine-ready ASTs
|
|
79
|
-
# ============================================================================
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def generate_enriched_ast(
|
|
18
|
+
def generate_validations_script(
|
|
83
19
|
expressions: Union[str, List[Tuple[str, str, Optional[str]]]],
|
|
84
20
|
database_path: Optional[str] = None,
|
|
85
21
|
connection_url: Optional[str] = None,
|
|
@@ -91,15 +27,15 @@ def generate_enriched_ast(
|
|
|
91
27
|
preferred_module_dependencies: Optional[List[str]] = None,
|
|
92
28
|
) -> Dict[str, Any]:
|
|
93
29
|
"""
|
|
94
|
-
Generate
|
|
30
|
+
Generate validations script with engine-ready AST from DPM-XL expression(s).
|
|
95
31
|
|
|
96
|
-
This function delegates to ASTGeneratorAPI
|
|
32
|
+
This function delegates to ASTGeneratorAPI.generate_validations_script().
|
|
97
33
|
|
|
98
|
-
Supports both single expressions
|
|
99
|
-
|
|
34
|
+
Supports both single expressions and multiple expression/operation/precondition
|
|
35
|
+
tuples for generating scripts with multiple operations.
|
|
100
36
|
|
|
101
37
|
Args:
|
|
102
|
-
expressions: Either a single DPM-XL expression string
|
|
38
|
+
expressions: Either a single DPM-XL expression string,
|
|
103
39
|
or a list of tuples: [(expression, operation_code, precondition), ...].
|
|
104
40
|
Each tuple contains:
|
|
105
41
|
- expression (str): The DPM-XL expression (required)
|
|
@@ -125,15 +61,15 @@ def generate_enriched_ast(
|
|
|
125
61
|
}
|
|
126
62
|
|
|
127
63
|
Example:
|
|
128
|
-
>>> # Single expression
|
|
129
|
-
>>> result =
|
|
64
|
+
>>> # Single expression
|
|
65
|
+
>>> result = generate_validations_script(
|
|
130
66
|
... "{tF_01.00, r0010, c0010}",
|
|
131
67
|
... database_path="data.db",
|
|
132
68
|
... release_code="4.2",
|
|
133
69
|
... )
|
|
134
70
|
>>>
|
|
135
71
|
>>> # Multiple expressions
|
|
136
|
-
>>> result =
|
|
72
|
+
>>> result = generate_validations_script(
|
|
137
73
|
... [
|
|
138
74
|
... ("{tF_01.00, r0010, c0010} = 0", "v1234_m", None),
|
|
139
75
|
... ("{tF_01.00, r0020, c0010} > 0", "v1235_m", "{v_F_44_04}"),
|
|
@@ -148,7 +84,7 @@ def generate_enriched_ast(
|
|
|
148
84
|
connection_url=connection_url,
|
|
149
85
|
enable_semantic_validation=True
|
|
150
86
|
)
|
|
151
|
-
return generator.
|
|
87
|
+
return generator.generate_validations_script(
|
|
152
88
|
expressions=expressions,
|
|
153
89
|
release_code=release_code,
|
|
154
90
|
table_context=table_context,
|
|
@@ -157,52 +93,3 @@ def generate_enriched_ast(
|
|
|
157
93
|
module_code=module_code,
|
|
158
94
|
preferred_module_dependencies=preferred_module_dependencies,
|
|
159
95
|
)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def enrich_ast_with_metadata(
|
|
163
|
-
ast_dict: Dict[str, Any],
|
|
164
|
-
expression: str,
|
|
165
|
-
context: Optional[Dict[str, Any]],
|
|
166
|
-
database_path: Optional[str] = None,
|
|
167
|
-
connection_url: Optional[str] = None,
|
|
168
|
-
release_code: Optional[str] = None,
|
|
169
|
-
operation_code: Optional[str] = None,
|
|
170
|
-
precondition: Optional[str] = None,
|
|
171
|
-
release_id: Optional[int] = None,
|
|
172
|
-
primary_module_vid: Optional[int] = None,
|
|
173
|
-
) -> Dict[str, Any]:
|
|
174
|
-
"""
|
|
175
|
-
Add framework structure (operations, variables, tables, preconditions) to complete AST.
|
|
176
|
-
|
|
177
|
-
This function delegates to ASTGeneratorAPI for backwards compatibility.
|
|
178
|
-
|
|
179
|
-
Args:
|
|
180
|
-
ast_dict: Complete AST dictionary (from generate_complete_ast)
|
|
181
|
-
expression: Original DPM-XL expression
|
|
182
|
-
context: Context dict with table, rows, columns, sheets, default, interval
|
|
183
|
-
database_path: Path to SQLite database
|
|
184
|
-
connection_url: PostgreSQL connection URL (takes precedence)
|
|
185
|
-
release_code: DPM release code (e.g., "4.2")
|
|
186
|
-
operation_code: Operation code (defaults to "default_code")
|
|
187
|
-
precondition: Precondition variable reference (e.g., {v_F_44_04})
|
|
188
|
-
release_id: Optional release ID to filter database lookups
|
|
189
|
-
primary_module_vid: Optional module VID of the module being exported
|
|
190
|
-
|
|
191
|
-
Returns:
|
|
192
|
-
dict: Engine-ready AST with framework structure
|
|
193
|
-
"""
|
|
194
|
-
generator = ASTGeneratorAPI(
|
|
195
|
-
database_path=database_path,
|
|
196
|
-
connection_url=connection_url,
|
|
197
|
-
enable_semantic_validation=True
|
|
198
|
-
)
|
|
199
|
-
return generator._enrich_ast_with_metadata(
|
|
200
|
-
ast_dict=ast_dict,
|
|
201
|
-
expression=expression,
|
|
202
|
-
context=context,
|
|
203
|
-
release_code=release_code,
|
|
204
|
-
operation_code=operation_code,
|
|
205
|
-
precondition=precondition,
|
|
206
|
-
release_id=release_id,
|
|
207
|
-
primary_module_vid=primary_module_vid,
|
|
208
|
-
)
|
py_dpm/dpm/migration.py
CHANGED
|
@@ -103,14 +103,16 @@ def _extract_with_pyodbc(access_file):
|
|
|
103
103
|
import pyodbc
|
|
104
104
|
except ImportError:
|
|
105
105
|
raise Exception("pyodbc not available")
|
|
106
|
-
|
|
106
|
+
|
|
107
|
+
import decimal
|
|
108
|
+
|
|
107
109
|
# Try different Access drivers
|
|
108
110
|
drivers_to_try = [
|
|
109
111
|
r'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};',
|
|
110
112
|
r'DRIVER={Microsoft Access Driver (*.mdb)};',
|
|
111
113
|
r'DRIVER={MDBTools};'
|
|
112
114
|
]
|
|
113
|
-
|
|
115
|
+
|
|
114
116
|
conn = None
|
|
115
117
|
for driver in drivers_to_try:
|
|
116
118
|
try:
|
|
@@ -120,10 +122,10 @@ def _extract_with_pyodbc(access_file):
|
|
|
120
122
|
break
|
|
121
123
|
except pyodbc.Error:
|
|
122
124
|
continue
|
|
123
|
-
|
|
125
|
+
|
|
124
126
|
if not conn:
|
|
125
127
|
raise Exception("No suitable ODBC driver found for Access database")
|
|
126
|
-
|
|
128
|
+
|
|
127
129
|
try:
|
|
128
130
|
# Get all table names
|
|
129
131
|
cursor = conn.cursor()
|
|
@@ -132,63 +134,56 @@ def _extract_with_pyodbc(access_file):
|
|
|
132
134
|
table_name = table_info.table_name
|
|
133
135
|
if not table_name.startswith('MSys'): # Skip system tables
|
|
134
136
|
tables.append(table_name)
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
data = {}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
|
|
139
140
|
# Extract each table
|
|
140
141
|
for table_name in tables:
|
|
141
142
|
print(table_name)
|
|
142
143
|
try:
|
|
143
144
|
cursor.execute(f"SELECT * FROM [{table_name}]")
|
|
144
|
-
|
|
145
|
+
|
|
146
|
+
# Get column metadata from cursor.description
|
|
147
|
+
# Each entry is: (name, type_code, display_size, internal_size, precision, scale, null_ok)
|
|
148
|
+
# type_code is a Python type (str, int, float, decimal.Decimal, etc.)
|
|
149
|
+
column_info = []
|
|
150
|
+
for col_desc in cursor.description:
|
|
151
|
+
col_name = col_desc[0]
|
|
152
|
+
col_type = col_desc[1] # Python type from ODBC metadata
|
|
153
|
+
column_info.append((col_name, col_type))
|
|
154
|
+
|
|
155
|
+
columns = [info[0] for info in column_info]
|
|
145
156
|
rows = cursor.fetchall()
|
|
146
|
-
|
|
157
|
+
|
|
147
158
|
if rows:
|
|
148
159
|
# Convert to DataFrame
|
|
149
160
|
df = pd.DataFrame([list(row) for row in rows], columns=columns)
|
|
150
161
|
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# Test numeric conversion
|
|
169
|
-
numeric_series = pd.to_numeric(df[column], errors='coerce')
|
|
170
|
-
|
|
171
|
-
if not has_leading_zeros and not numeric_series.isna().all():
|
|
172
|
-
numeric_columns.append(column)
|
|
173
|
-
except Exception:
|
|
174
|
-
continue
|
|
175
|
-
|
|
176
|
-
# Convert only the identified numeric columns
|
|
177
|
-
for col in numeric_columns:
|
|
178
|
-
try:
|
|
179
|
-
df[col] = pd.to_numeric(df[col], errors='coerce')
|
|
180
|
-
except (ValueError, TypeError):
|
|
181
|
-
# Keep as string if conversion fails
|
|
182
|
-
pass
|
|
183
|
-
|
|
162
|
+
# Use the actual column types from Access schema metadata
|
|
163
|
+
# instead of inferring from data values (fixes Windows vs Linux inconsistency)
|
|
164
|
+
numeric_types = (int, float, decimal.Decimal)
|
|
165
|
+
|
|
166
|
+
for col_name, col_type in column_info:
|
|
167
|
+
if col_type in numeric_types:
|
|
168
|
+
# Column is defined as numeric in Access schema - convert to numeric
|
|
169
|
+
try:
|
|
170
|
+
df[col_name] = pd.to_numeric(df[col_name], errors='coerce')
|
|
171
|
+
except (ValueError, TypeError):
|
|
172
|
+
pass
|
|
173
|
+
else:
|
|
174
|
+
# Column is defined as text/other in Access schema - keep as string
|
|
175
|
+
df[col_name] = df[col_name].astype(object)
|
|
176
|
+
mask = df[col_name].notna()
|
|
177
|
+
df.loc[mask, col_name] = df.loc[mask, col_name].astype(str)
|
|
178
|
+
|
|
184
179
|
data[table_name] = df
|
|
185
|
-
|
|
180
|
+
|
|
186
181
|
except Exception as e:
|
|
187
182
|
print(f"Error processing table {table_name}: {e}", file=sys.stderr)
|
|
188
183
|
continue
|
|
189
|
-
|
|
184
|
+
|
|
190
185
|
return data
|
|
191
|
-
|
|
186
|
+
|
|
192
187
|
finally:
|
|
193
188
|
conn.close()
|
|
194
189
|
|
py_dpm/dpm/models.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import List
|
|
2
|
+
from typing import Dict, Hashable, List, Tuple
|
|
3
3
|
from sqlalchemy import (
|
|
4
4
|
Boolean,
|
|
5
5
|
Column,
|
|
@@ -36,6 +36,11 @@ class SerializationMixin:
|
|
|
36
36
|
Base = declarative_base(cls=SerializationMixin)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def _get_engine_cache_key(session) -> Hashable:
|
|
40
|
+
bind = session.get_bind()
|
|
41
|
+
return getattr(bind, "url", repr(bind))
|
|
42
|
+
|
|
43
|
+
|
|
39
44
|
def _read_sql_with_connection(sql, session):
|
|
40
45
|
"""
|
|
41
46
|
Execute pd.read_sql with proper connection handling to avoid pandas warnings.
|
|
@@ -2319,6 +2324,11 @@ class ViewDatapoints(Base):
|
|
|
2319
2324
|
context_id = Column(Integer)
|
|
2320
2325
|
variable_vid = Column(String)
|
|
2321
2326
|
|
|
2327
|
+
_TABLE_DATA_CACHE: Dict[
|
|
2328
|
+
Tuple[Hashable, str, Tuple[str, ...] | None, Tuple[str, ...] | None, Tuple[str, ...] | None, int | None],
|
|
2329
|
+
pd.DataFrame,
|
|
2330
|
+
] = {}
|
|
2331
|
+
|
|
2322
2332
|
@classmethod
|
|
2323
2333
|
def _create_base_query_with_aliases(cls, session):
|
|
2324
2334
|
"""
|
|
@@ -2552,7 +2562,16 @@ class ViewDatapoints(Base):
|
|
|
2552
2562
|
def get_table_data(
|
|
2553
2563
|
cls, session, table, rows=None, columns=None, sheets=None, release_id=None
|
|
2554
2564
|
):
|
|
2555
|
-
|
|
2565
|
+
engine_key = _get_engine_cache_key(session)
|
|
2566
|
+
rows_key = tuple(rows) if rows is not None else None
|
|
2567
|
+
columns_key = tuple(columns) if columns is not None else None
|
|
2568
|
+
sheets_key = tuple(sheets) if sheets is not None else None
|
|
2569
|
+
cache_key = (engine_key, table, rows_key, columns_key, sheets_key, release_id)
|
|
2570
|
+
|
|
2571
|
+
cached = cls._TABLE_DATA_CACHE.get(cache_key)
|
|
2572
|
+
if cached is not None:
|
|
2573
|
+
return cached
|
|
2574
|
+
|
|
2556
2575
|
query, aliases = cls._create_base_query_with_aliases(session)
|
|
2557
2576
|
|
|
2558
2577
|
# Add column selections
|
|
@@ -2669,6 +2688,7 @@ class ViewDatapoints(Base):
|
|
|
2669
2688
|
data = _check_ranges_values_are_present(data, "column_code", columns)
|
|
2670
2689
|
data = _check_ranges_values_are_present(data, "sheet_code", sheets)
|
|
2671
2690
|
|
|
2691
|
+
cls._TABLE_DATA_CACHE[cache_key] = data
|
|
2672
2692
|
return data
|
|
2673
2693
|
|
|
2674
2694
|
@classmethod
|
py_dpm/dpm_xl/ast/operands.py
CHANGED
|
@@ -2,6 +2,7 @@ from abc import ABC
|
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import warnings
|
|
5
|
+
from typing import Dict, Hashable, Tuple
|
|
5
6
|
|
|
6
7
|
# Suppress pandas UserWarning about SQLAlchemy connection types
|
|
7
8
|
warnings.filterwarnings("ignore", message=".*pandas only supports SQLAlchemy.*")
|
|
@@ -43,6 +44,9 @@ from py_dpm.dpm_xl.utils.data_handlers import filter_all_data
|
|
|
43
44
|
operand_elements = ["table", "rows", "cols", "sheets", "default", "interval"]
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
_HEADERS_CACHE: Dict[Tuple[Hashable, int, Tuple[str, ...]], pd.DataFrame] = {}
|
|
48
|
+
|
|
49
|
+
|
|
46
50
|
def _create_operand_label(node):
|
|
47
51
|
label = generate_new_label()
|
|
48
52
|
node.label = label
|
|
@@ -185,42 +189,47 @@ class OperandsChecking(ASTTemplate, ABC):
|
|
|
185
189
|
if len(table_codes) == 0:
|
|
186
190
|
return
|
|
187
191
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
192
|
+
engine = self.session.get_bind()
|
|
193
|
+
engine_key: Hashable = getattr(engine, "url", repr(engine))
|
|
194
|
+
cache_key = (engine_key, self.release_id, tuple(sorted(table_codes)))
|
|
195
|
+
|
|
196
|
+
df_headers = _HEADERS_CACHE.get(cache_key)
|
|
197
|
+
if df_headers is None:
|
|
198
|
+
query = (
|
|
199
|
+
self.session.query(
|
|
200
|
+
TableVersion.code.label("Code"),
|
|
201
|
+
TableVersion.startreleaseid.label("StartReleaseID"),
|
|
202
|
+
TableVersion.endreleaseid.label("EndReleaseID"),
|
|
203
|
+
Header.direction.label("Direction"),
|
|
204
|
+
Table.hasopenrows.label("HasOpenRows"),
|
|
205
|
+
Table.hasopencolumns.label("HasOpenColumns"),
|
|
206
|
+
Table.hasopensheets.label("HasOpenSheets"),
|
|
207
|
+
)
|
|
208
|
+
.join(Table, Table.tableid == TableVersion.tableid)
|
|
209
|
+
.join(
|
|
210
|
+
TableVersionHeader,
|
|
211
|
+
TableVersion.tablevid == TableVersionHeader.tablevid,
|
|
212
|
+
)
|
|
213
|
+
.join(Header, Header.headerid == TableVersionHeader.headerid)
|
|
214
|
+
.filter(TableVersion.code.in_(table_codes))
|
|
215
|
+
.distinct()
|
|
202
216
|
)
|
|
203
|
-
.join(Header, Header.headerid == TableVersionHeader.headerid)
|
|
204
|
-
.filter(TableVersion.code.in_(table_codes))
|
|
205
|
-
.distinct()
|
|
206
|
-
)
|
|
207
217
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
218
|
+
query = filter_by_release(
|
|
219
|
+
query,
|
|
220
|
+
start_col=TableVersion.startreleaseid,
|
|
221
|
+
end_col=TableVersion.endreleaseid,
|
|
222
|
+
release_id=self.release_id,
|
|
223
|
+
)
|
|
215
224
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
225
|
+
from py_dpm.dpm.models import (
|
|
226
|
+
_compile_query_for_pandas,
|
|
227
|
+
_read_sql_with_connection,
|
|
228
|
+
)
|
|
221
229
|
|
|
222
|
-
|
|
223
|
-
|
|
230
|
+
compiled_query = _compile_query_for_pandas(query.statement, self.session)
|
|
231
|
+
df_headers = _read_sql_with_connection(compiled_query, self.session)
|
|
232
|
+
_HEADERS_CACHE[cache_key] = df_headers
|
|
224
233
|
|
|
225
234
|
for table in table_codes:
|
|
226
235
|
table_headers = df_headers[df_headers["Code"] == table]
|
|
@@ -452,6 +452,21 @@ class ASTToJSONVisitor(NodeVisitor):
|
|
|
452
452
|
'shift_number': node.shift_number
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
def visit_RenameOp(self, node):
|
|
456
|
+
"""Visit RenameOp nodes and serialize as RenameClauseOp."""
|
|
457
|
+
return {
|
|
458
|
+
'class_name': 'RenameClauseOp',
|
|
459
|
+
'operand': self.visit(node.operand),
|
|
460
|
+
'clauses': [self._serialize_rename_node(rn) for rn in node.rename_nodes]
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
def _serialize_rename_node(self, node):
|
|
464
|
+
"""Serialize a RenameNode as a clause dictionary."""
|
|
465
|
+
return {
|
|
466
|
+
'from_component': node.old_name,
|
|
467
|
+
'to_component': node.new_name
|
|
468
|
+
}
|
|
469
|
+
|
|
455
470
|
def visit_PreconditionItem(self, node):
|
|
456
471
|
"""Visit PreconditionItem nodes."""
|
|
457
472
|
result = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydpm_xl
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10
|
|
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
|
|
@@ -210,22 +210,21 @@ else:
|
|
|
210
210
|
print(f"Error: {result.error}")
|
|
211
211
|
```
|
|
212
212
|
|
|
213
|
-
####
|
|
213
|
+
#### Validations Script Generation
|
|
214
214
|
|
|
215
215
|
```python
|
|
216
|
-
from py_dpm.api import
|
|
216
|
+
from py_dpm.api import generate_validations_script
|
|
217
217
|
|
|
218
|
-
# Generate
|
|
219
|
-
result =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
218
|
+
# Generate engine-ready validations script
|
|
219
|
+
result = generate_validations_script(
|
|
220
|
+
"{tT_01.00, r0010, c0010}",
|
|
221
|
+
database_path="data.db",
|
|
222
|
+
release_code="4.2"
|
|
223
|
+
)
|
|
224
|
+
if result["success"]:
|
|
225
|
+
print(f"Enriched AST: {result['enriched_ast']}")
|
|
223
226
|
else:
|
|
224
|
-
print(f"
|
|
225
|
-
|
|
226
|
-
# Generate enriched AST (ready for execution engine)
|
|
227
|
-
enriched = generate_enriched_ast("{tT_01.00, r0010, c0010}", release_id=123)
|
|
228
|
-
print(f"Enriched AST: {enriched}")
|
|
227
|
+
print(f"Error: {result['error']}")
|
|
229
228
|
```
|
|
230
229
|
|
|
231
230
|
#### Migration
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
py_dpm/__init__.py,sha256=
|
|
2
|
-
py_dpm/api/__init__.py,sha256=
|
|
1
|
+
py_dpm/__init__.py,sha256=8RZiE-F0XPTFPGEyFhEp9cS-0ahXQN5PMGSlLRrarL8,1859
|
|
2
|
+
py_dpm/api/__init__.py,sha256=bPWo4KWW99GmGkQbq0yQ3_tbTxvidEVVrWmcDXWz-xA,828
|
|
3
3
|
py_dpm/api/dpm/__init__.py,sha256=HQflgiRbs1eDi3KTadNhxS1NoaG6PGQDVMvFnuIEfXo,506
|
|
4
4
|
py_dpm/api/dpm/data_dictionary.py,sha256=q6w_5bFdc6WPd5Z601PpDaCcnIw39CnI4wdby3GJmFU,29893
|
|
5
5
|
py_dpm/api/dpm/explorer.py,sha256=xgrSdh2D83RivypF26WWo20rbQifYBEH7PXvINoi07Y,10861
|
|
6
6
|
py_dpm/api/dpm/hierarchical_queries.py,sha256=X4AbpsWy3iItOTVIdVbtaTmRgOHPf0Y64Ig-_377uns,4054
|
|
7
7
|
py_dpm/api/dpm/instance.py,sha256=v3DWzdaM5gPCecLjwjZ49FGfqZzUR3dPC0U8zGwdttk,3795
|
|
8
8
|
py_dpm/api/dpm/migration.py,sha256=9FT7zzz4QdUIRR6MD01gMODBtfq9HH_RF4hRgZqMcZc,2404
|
|
9
|
-
py_dpm/api/dpm_xl/__init__.py,sha256=
|
|
10
|
-
py_dpm/api/dpm_xl/ast_generator.py,sha256=
|
|
11
|
-
py_dpm/api/dpm_xl/complete_ast.py,sha256=
|
|
9
|
+
py_dpm/api/dpm_xl/__init__.py,sha256=SOsCtwxuCkFQIdYoTcU6tVJdK1j96dPF4ZQpr8okLPc,576
|
|
10
|
+
py_dpm/api/dpm_xl/ast_generator.py,sha256=hmfy2qV5t_RFGgmid7YuCXbrkjknv1KuQaJGxbXuHLs,97583
|
|
11
|
+
py_dpm/api/dpm_xl/complete_ast.py,sha256=jjn9tsns4X7EN4RV5mFh5DRxxZfBOuX2b1wic4oB_ls,3967
|
|
12
12
|
py_dpm/api/dpm_xl/operation_scopes.py,sha256=v2t3f2-AiF39hspNtpf_PA94T69JqbymFK9a5hpckRs,48831
|
|
13
13
|
py_dpm/api/dpm_xl/semantic.py,sha256=Ddmh2Wj_iXIpQZ4jCjqOI-6ddFCquaO9RTu6W9i1Rts,13968
|
|
14
14
|
py_dpm/api/dpm_xl/syntax.py,sha256=Ke_kKd9ModoJ6siL3GPT9j9QClmopryCRcdDAT3M5-E,5954
|
|
@@ -16,8 +16,8 @@ py_dpm/cli/__init__.py,sha256=UrfGHoQ0sZLjWfA0hoOoI4iTrn-bjr2f9Q8wDWd5nMo,133
|
|
|
16
16
|
py_dpm/cli/main.py,sha256=LJ7JBk7lyWXe7ZYxnbxmohM1Dbha4sIdQzSTYKd9ZNo,22457
|
|
17
17
|
py_dpm/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
py_dpm/dpm/__init__.py,sha256=moagUo5Gxf24-Tl9FL_3n2wmVoD_oXtpC-YIGktH_rc,212
|
|
19
|
-
py_dpm/dpm/migration.py,sha256=
|
|
20
|
-
py_dpm/dpm/models.py,sha256=
|
|
19
|
+
py_dpm/dpm/migration.py,sha256=I_OX1xS2eE3L2V1RAPyHOqFn3Z54NWE0ozLAJdQrLcc,13810
|
|
20
|
+
py_dpm/dpm/models.py,sha256=YyOYXzN4tIT2rO6-PP-53C6f7skatgnz7VY_5SnOsLQ,136414
|
|
21
21
|
py_dpm/dpm/utils.py,sha256=3w06_kKiFRPqHvRUfN_0nNQIpppfidFybdDge97qzI0,14918
|
|
22
22
|
py_dpm/dpm/queries/base.py,sha256=EddMeJMwtp63DyyIFO7_XxGvdlCtJQWWpeOVImlKp4I,3648
|
|
23
23
|
py_dpm/dpm/queries/basic_objects.py,sha256=JOXC235lMDfVENrFAhZAl7_nqePJ4RrwJhFF0WDyk0M,955
|
|
@@ -35,7 +35,7 @@ py_dpm/dpm_xl/ast/ml_generation.py,sha256=Lw_1Btln2x1ewD9xH-2Ea4NJJP3PIqFoivWASX
|
|
|
35
35
|
py_dpm/dpm_xl/ast/module_analyzer.py,sha256=ZnldoYn-s41UMiJpcAV6hjIwH6fssZeOpc564epngg8,2872
|
|
36
36
|
py_dpm/dpm_xl/ast/module_dependencies.py,sha256=tbCqoDcE1n1lJOjtbpD3rNPkXrLk-k2rM5zyVwmsNpc,8355
|
|
37
37
|
py_dpm/dpm_xl/ast/nodes.py,sha256=5ob8MsCW0fPZgz9yP_6IgVTH2SGeoTk5VncJuQ2SgrE,25035
|
|
38
|
-
py_dpm/dpm_xl/ast/operands.py,sha256=
|
|
38
|
+
py_dpm/dpm_xl/ast/operands.py,sha256=0Yye5z6l0BBh9nrMlkVVNy3fYgbeUzZYLmP5Jvzr95M,21606
|
|
39
39
|
py_dpm/dpm_xl/ast/template.py,sha256=QhYm7Jh_a-ws3kSmf0hqXFLzB_quO9GgKcmcFe22_fg,3045
|
|
40
40
|
py_dpm/dpm_xl/ast/visitor.py,sha256=yL9UpPMQlq8ToHR8COyFYpuSChnDRjnkQHbCyYX0tsY,509
|
|
41
41
|
py_dpm/dpm_xl/ast/where_clause.py,sha256=g3cslQ8vmlm0doqQ_ghjXzhzItc_xlC_bQ9odn87FGk,328
|
|
@@ -69,16 +69,16 @@ py_dpm/dpm_xl/utils/data_handlers.py,sha256=a0E-IaP_-CDKLcj-Gt2ggAziKIOUiwnT2D9I
|
|
|
69
69
|
py_dpm/dpm_xl/utils/operands_mapping.py,sha256=LG0hPlUuTM2X2uWOtiD6HkmNeDEJkWJ8gV-Fxej_8QM,2241
|
|
70
70
|
py_dpm/dpm_xl/utils/operator_mapping.py,sha256=BFgbVbSCSuutFNHJ4gtgm5VuG38pcl8Kmfi-sefg6JU,1913
|
|
71
71
|
py_dpm/dpm_xl/utils/scopes_calculator.py,sha256=do_emsUqD1TbrjguKlOOqFleaVhxzqm-NnlgdrdIb6I,20906
|
|
72
|
-
py_dpm/dpm_xl/utils/serialization.py,sha256=
|
|
72
|
+
py_dpm/dpm_xl/utils/serialization.py,sha256=4ZBHIP-67_19QSxqzkv8GPh3RNJ861if26GKbV9Bxj8,33088
|
|
73
73
|
py_dpm/dpm_xl/utils/tokens.py,sha256=VRIrPDi5ttwgH-on5Qt4-l4ho4bLA755-nfTalponcA,3496
|
|
74
74
|
py_dpm/exceptions/__init__.py,sha256=yDERfUxYW7NUUEiTQChGpuJx6abr7IDe2XUpwVFPtvM,416
|
|
75
75
|
py_dpm/exceptions/exceptions.py,sha256=6S3p-_i5O1oStvSMixt_JQG0xwTeSfBcdzrwL8yBy6Q,2413
|
|
76
76
|
py_dpm/exceptions/messages.py,sha256=UwY6QIK8c-POcDCc9HYbZFGArCIYAanUGNh2LNKPx3U,7534
|
|
77
77
|
py_dpm/instance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
78
|
py_dpm/instance/instance.py,sha256=gRSg2dh1nEa0Srx9yKcN3bxiYidvZyRU_jsTNaKkP5I,10882
|
|
79
|
-
pydpm_xl-0.2.
|
|
80
|
-
pydpm_xl-0.2.
|
|
81
|
-
pydpm_xl-0.2.
|
|
82
|
-
pydpm_xl-0.2.
|
|
83
|
-
pydpm_xl-0.2.
|
|
84
|
-
pydpm_xl-0.2.
|
|
79
|
+
pydpm_xl-0.2.10.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
80
|
+
pydpm_xl-0.2.10.dist-info/METADATA,sha256=SPIgOJT90xDaqE4CYrE2VIsWXOAzGmXbLlI8Yl7yv9I,9154
|
|
81
|
+
pydpm_xl-0.2.10.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
82
|
+
pydpm_xl-0.2.10.dist-info/entry_points.txt,sha256=6DDmBfw-AjtgvMHgq_I730i_LAAs_7-N3C95HD_bRr4,47
|
|
83
|
+
pydpm_xl-0.2.10.dist-info/top_level.txt,sha256=495PvWZRoKl2NvbQU25W7dqWIBHqY-mFMPt83uxPpcM,7
|
|
84
|
+
pydpm_xl-0.2.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|