pydpm_xl 0.2.5rc1__py3-none-any.whl → 0.2.5rc2__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/dpm_xl/ast_generator.py +121 -14
- {pydpm_xl-0.2.5rc1.dist-info → pydpm_xl-0.2.5rc2.dist-info}/METADATA +1 -1
- {pydpm_xl-0.2.5rc1.dist-info → pydpm_xl-0.2.5rc2.dist-info}/RECORD +8 -8
- {pydpm_xl-0.2.5rc1.dist-info → pydpm_xl-0.2.5rc2.dist-info}/WHEEL +0 -0
- {pydpm_xl-0.2.5rc1.dist-info → pydpm_xl-0.2.5rc2.dist-info}/entry_points.txt +0 -0
- {pydpm_xl-0.2.5rc1.dist-info → pydpm_xl-0.2.5rc2.dist-info}/licenses/LICENSE +0 -0
- {pydpm_xl-0.2.5rc1.dist-info → pydpm_xl-0.2.5rc2.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.5rc2"
|
|
45
45
|
__author__ = "MeaningfulData S.L."
|
|
46
46
|
__email__ = "info@meaningfuldata.eu"
|
|
47
47
|
__license__ = "GPL-3.0-or-later"
|
|
@@ -1039,6 +1039,81 @@ class ASTGeneratorAPI:
|
|
|
1039
1039
|
extract_from_node(ast_dict)
|
|
1040
1040
|
return all_variables, variables_by_table
|
|
1041
1041
|
|
|
1042
|
+
def _extract_time_shifts_by_table(self, expression: str) -> Dict[str, str]:
|
|
1043
|
+
"""
|
|
1044
|
+
Extract time shift information for each table in the expression.
|
|
1045
|
+
|
|
1046
|
+
Uses the AST to properly parse the expression and find TimeShiftOp nodes
|
|
1047
|
+
to determine the ref_period for each table reference.
|
|
1048
|
+
|
|
1049
|
+
Args:
|
|
1050
|
+
expression: DPM-XL expression
|
|
1051
|
+
|
|
1052
|
+
Returns:
|
|
1053
|
+
Dict mapping table codes to ref_period values (e.g., {"C_01.00": "T-1Q"})
|
|
1054
|
+
Tables without time shifts default to "T".
|
|
1055
|
+
"""
|
|
1056
|
+
from py_dpm.dpm_xl.ast.template import ASTTemplate
|
|
1057
|
+
|
|
1058
|
+
time_shifts = {}
|
|
1059
|
+
current_period = ["t"] # Use list to allow mutation in nested function
|
|
1060
|
+
|
|
1061
|
+
class TimeShiftExtractor(ASTTemplate):
|
|
1062
|
+
"""Lightweight AST visitor that extracts time shifts for each table."""
|
|
1063
|
+
|
|
1064
|
+
def visit_TimeShiftOp(self, node):
|
|
1065
|
+
# Save current time period and compute new one
|
|
1066
|
+
previous_period = current_period[0]
|
|
1067
|
+
|
|
1068
|
+
period_indicator = node.period_indicator
|
|
1069
|
+
shift_number = node.shift_number
|
|
1070
|
+
|
|
1071
|
+
# Compute time period (same logic as ModuleDependencies)
|
|
1072
|
+
if "-" in str(shift_number):
|
|
1073
|
+
current_period[0] = f"t+{period_indicator}{shift_number}"
|
|
1074
|
+
else:
|
|
1075
|
+
current_period[0] = f"t-{period_indicator}{shift_number}"
|
|
1076
|
+
|
|
1077
|
+
# Visit operand (which contains the VarID)
|
|
1078
|
+
self.visit(node.operand)
|
|
1079
|
+
|
|
1080
|
+
# Restore previous time period
|
|
1081
|
+
current_period[0] = previous_period
|
|
1082
|
+
|
|
1083
|
+
def visit_VarID(self, node):
|
|
1084
|
+
if node.table and current_period[0] != "t":
|
|
1085
|
+
time_shifts[node.table] = current_period[0]
|
|
1086
|
+
|
|
1087
|
+
def convert_to_ref_period(internal_period: str) -> str:
|
|
1088
|
+
"""Convert internal time period format to ref_period format.
|
|
1089
|
+
|
|
1090
|
+
Internal format: "t+Q-1" or "t-Q1"
|
|
1091
|
+
Output format: "T-1Q" for one quarter back
|
|
1092
|
+
"""
|
|
1093
|
+
if internal_period.startswith("t+"):
|
|
1094
|
+
# e.g., "t+Q-1" -> "T-1Q"
|
|
1095
|
+
indicator = internal_period[2]
|
|
1096
|
+
number = internal_period[3:]
|
|
1097
|
+
if number.startswith("-"):
|
|
1098
|
+
return f"T{number}{indicator}"
|
|
1099
|
+
return f"T+{number}{indicator}"
|
|
1100
|
+
elif internal_period.startswith("t-"):
|
|
1101
|
+
# e.g., "t-Q1" -> "T-1Q"
|
|
1102
|
+
indicator = internal_period[2]
|
|
1103
|
+
number = internal_period[3:]
|
|
1104
|
+
return f"T-{number}{indicator}"
|
|
1105
|
+
return "T"
|
|
1106
|
+
|
|
1107
|
+
try:
|
|
1108
|
+
ast = self.syntax_api.parse_expression(expression)
|
|
1109
|
+
extractor = TimeShiftExtractor()
|
|
1110
|
+
extractor.visit(ast)
|
|
1111
|
+
|
|
1112
|
+
return {table: convert_to_ref_period(period) for table, period in time_shifts.items()}
|
|
1113
|
+
|
|
1114
|
+
except Exception:
|
|
1115
|
+
return {}
|
|
1116
|
+
|
|
1042
1117
|
def _detect_cross_module_dependencies(
|
|
1043
1118
|
self,
|
|
1044
1119
|
expression: str,
|
|
@@ -1075,7 +1150,7 @@ class ASTGeneratorAPI:
|
|
|
1075
1150
|
)
|
|
1076
1151
|
|
|
1077
1152
|
try:
|
|
1078
|
-
# Get tables with module info
|
|
1153
|
+
# Get tables with module info (includes module_version)
|
|
1079
1154
|
tables_with_modules = scopes_api.get_tables_with_metadata_from_expression(
|
|
1080
1155
|
expression=expression,
|
|
1081
1156
|
release_id=release_id
|
|
@@ -1091,10 +1166,35 @@ class ASTGeneratorAPI:
|
|
|
1091
1166
|
if scope_result.has_error or not scope_result.is_cross_module:
|
|
1092
1167
|
return {}, []
|
|
1093
1168
|
|
|
1169
|
+
# Extract time shifts for each table from expression
|
|
1170
|
+
time_shifts_by_table = self._extract_time_shifts_by_table(expression)
|
|
1171
|
+
|
|
1094
1172
|
# Determine primary module from first table if not provided
|
|
1095
1173
|
if primary_module_vid is None and tables_with_modules:
|
|
1096
1174
|
primary_module_vid = tables_with_modules[0].get("module_vid")
|
|
1097
1175
|
|
|
1176
|
+
# Helper to normalize table code (remove 't' prefix if present)
|
|
1177
|
+
def normalize_table_code(code: str) -> str:
|
|
1178
|
+
return code[1:] if code and code.startswith('t') else code
|
|
1179
|
+
|
|
1180
|
+
# Helper to lookup ref_period for a table
|
|
1181
|
+
def get_ref_period(table_code: str) -> str:
|
|
1182
|
+
if not table_code:
|
|
1183
|
+
return "T"
|
|
1184
|
+
ref = time_shifts_by_table.get(table_code)
|
|
1185
|
+
if not ref:
|
|
1186
|
+
ref = time_shifts_by_table.get(normalize_table_code(table_code))
|
|
1187
|
+
return ref or "T"
|
|
1188
|
+
|
|
1189
|
+
# Helper to lookup variables for a table
|
|
1190
|
+
def get_table_variables(table_code: str) -> dict:
|
|
1191
|
+
if not table_code:
|
|
1192
|
+
return {}
|
|
1193
|
+
variables = variables_by_table.get(table_code)
|
|
1194
|
+
if not variables:
|
|
1195
|
+
variables = variables_by_table.get(f"t{table_code}", {})
|
|
1196
|
+
return variables or {}
|
|
1197
|
+
|
|
1098
1198
|
# Group external tables by module
|
|
1099
1199
|
external_modules = {}
|
|
1100
1200
|
for table_info in tables_with_modules:
|
|
@@ -1106,36 +1206,38 @@ class ASTGeneratorAPI:
|
|
|
1106
1206
|
if not module_code:
|
|
1107
1207
|
continue
|
|
1108
1208
|
|
|
1109
|
-
# Get module URI
|
|
1209
|
+
# Get module URI
|
|
1110
1210
|
try:
|
|
1111
1211
|
module_uri = ExplorerQuery.get_module_url(
|
|
1112
1212
|
scopes_api.session,
|
|
1113
1213
|
module_code=module_code,
|
|
1114
1214
|
release_id=release_id,
|
|
1115
1215
|
)
|
|
1116
|
-
# Remove .json suffix if present (for consistency with expected format)
|
|
1117
1216
|
if module_uri.endswith(".json"):
|
|
1118
1217
|
module_uri = module_uri[:-5]
|
|
1119
1218
|
except Exception:
|
|
1120
1219
|
continue
|
|
1121
1220
|
|
|
1221
|
+
table_code = table_info.get("code")
|
|
1222
|
+
ref_period = get_ref_period(table_code)
|
|
1223
|
+
|
|
1122
1224
|
if module_uri not in external_modules:
|
|
1123
1225
|
external_modules[module_uri] = {
|
|
1124
1226
|
"module_vid": module_vid,
|
|
1227
|
+
"module_version": table_info.get("module_version"), # Already in table_info
|
|
1228
|
+
"ref_period": ref_period,
|
|
1125
1229
|
"tables": {},
|
|
1126
1230
|
"variables": {},
|
|
1127
1231
|
"from_date": None,
|
|
1128
1232
|
"to_date": None
|
|
1129
1233
|
}
|
|
1234
|
+
elif ref_period != "T":
|
|
1235
|
+
# Keep most specific ref_period (non-T takes precedence)
|
|
1236
|
+
external_modules[module_uri]["ref_period"] = ref_period
|
|
1130
1237
|
|
|
1131
|
-
# Add table
|
|
1132
|
-
table_code = table_info.get("code")
|
|
1238
|
+
# Add table and variables
|
|
1133
1239
|
if table_code:
|
|
1134
|
-
|
|
1135
|
-
table_variables = variables_by_table.get(table_code, {})
|
|
1136
|
-
if not table_variables:
|
|
1137
|
-
# Try with t prefix
|
|
1138
|
-
table_variables = variables_by_table.get(f"t{table_code}", {})
|
|
1240
|
+
table_variables = get_table_variables(table_code)
|
|
1139
1241
|
external_modules[module_uri]["tables"][table_code] = {
|
|
1140
1242
|
"variables": table_variables,
|
|
1141
1243
|
"open_keys": {}
|
|
@@ -1169,11 +1271,16 @@ class ASTGeneratorAPI:
|
|
|
1169
1271
|
# cross_instance_dependencies entry (one per external module)
|
|
1170
1272
|
from_date = data["from_date"]
|
|
1171
1273
|
to_date = data["to_date"]
|
|
1274
|
+
module_entry = {
|
|
1275
|
+
"URI": uri,
|
|
1276
|
+
"ref_period": data["ref_period"]
|
|
1277
|
+
}
|
|
1278
|
+
# Add module_version if available
|
|
1279
|
+
if data["module_version"]:
|
|
1280
|
+
module_entry["module_version"] = data["module_version"]
|
|
1281
|
+
|
|
1172
1282
|
cross_instance_dependencies.append({
|
|
1173
|
-
"modules": [
|
|
1174
|
-
"URI": uri,
|
|
1175
|
-
"ref_period": "T"
|
|
1176
|
-
}],
|
|
1283
|
+
"modules": [module_entry],
|
|
1177
1284
|
"affected_operations": [operation_code],
|
|
1178
1285
|
"from_reference_date": str(from_date) if from_date else "",
|
|
1179
1286
|
"to_reference_date": str(to_date) if to_date else ""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
py_dpm/__init__.py,sha256=
|
|
1
|
+
py_dpm/__init__.py,sha256=u6lHTsVyMlEyPdrczeNUNlN8D7Dge-Cr0MPlZs8iIqo,1861
|
|
2
2
|
py_dpm/api/__init__.py,sha256=n79vAD7qatlYaXaI2N5IAD9m_8Fgb00EOdapVXZYTpI,1081
|
|
3
3
|
py_dpm/api/dpm/__init__.py,sha256=HQflgiRbs1eDi3KTadNhxS1NoaG6PGQDVMvFnuIEfXo,506
|
|
4
4
|
py_dpm/api/dpm/data_dictionary.py,sha256=g0h6Yfschz7rboYly9LTbP-2SS5UxltU3AXu0v0tqrU,29457
|
|
@@ -7,7 +7,7 @@ py_dpm/api/dpm/hierarchical_queries.py,sha256=X4AbpsWy3iItOTVIdVbtaTmRgOHPf0Y64I
|
|
|
7
7
|
py_dpm/api/dpm/instance.py,sha256=v3DWzdaM5gPCecLjwjZ49FGfqZzUR3dPC0U8zGwdttk,3795
|
|
8
8
|
py_dpm/api/dpm/migration.py,sha256=9FT7zzz4QdUIRR6MD01gMODBtfq9HH_RF4hRgZqMcZc,2404
|
|
9
9
|
py_dpm/api/dpm_xl/__init__.py,sha256=aRjaMAf_i2a33UAGTg-TF1BfO6miOOrbCydTUqAVvRU,910
|
|
10
|
-
py_dpm/api/dpm_xl/ast_generator.py,sha256=
|
|
10
|
+
py_dpm/api/dpm_xl/ast_generator.py,sha256=w7aeRDGJ-ggJoRXnZ-Tn1rEeb1zoSF21co9pSV_KU6A,55327
|
|
11
11
|
py_dpm/api/dpm_xl/complete_ast.py,sha256=VkmcBatrydu97Inwp3pHjz93F38q2JRo-4Lohdu30RY,7684
|
|
12
12
|
py_dpm/api/dpm_xl/operation_scopes.py,sha256=7AyOFAn9h012JPF9H5EtZ3sPzv6DOxkoinpj5ArzVOc,48492
|
|
13
13
|
py_dpm/api/dpm_xl/semantic.py,sha256=Buo_t-sEv65r6RmYDy1xkCWGlU2pB2WQsDM-X-FX4cc,13629
|
|
@@ -76,9 +76,9 @@ py_dpm/exceptions/exceptions.py,sha256=6S3p-_i5O1oStvSMixt_JQG0xwTeSfBcdzrwL8yBy
|
|
|
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=OPSEPgSYAxhgqhKuxbMpMPTfBnaFNzURTrUUT4kvGKc,10820
|
|
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.5rc2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
80
|
+
pydpm_xl-0.2.5rc2.dist-info/METADATA,sha256=wtWYaml5W9vzWfDQTiUI3x_EByIduVBZV6bo6fLu31A,9305
|
|
81
|
+
pydpm_xl-0.2.5rc2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
82
|
+
pydpm_xl-0.2.5rc2.dist-info/entry_points.txt,sha256=6DDmBfw-AjtgvMHgq_I730i_LAAs_7-N3C95HD_bRr4,47
|
|
83
|
+
pydpm_xl-0.2.5rc2.dist-info/top_level.txt,sha256=495PvWZRoKl2NvbQU25W7dqWIBHqY-mFMPt83uxPpcM,7
|
|
84
|
+
pydpm_xl-0.2.5rc2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|