pydpm_xl 0.1.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.
Files changed (94) hide show
  1. py_dpm/AST/ASTConstructor.py +503 -0
  2. py_dpm/AST/ASTObjects.py +827 -0
  3. py_dpm/AST/ASTTemplate.py +101 -0
  4. py_dpm/AST/ASTVisitor.py +13 -0
  5. py_dpm/AST/MLGeneration.py +588 -0
  6. py_dpm/AST/ModuleAnalyzer.py +79 -0
  7. py_dpm/AST/ModuleDependencies.py +203 -0
  8. py_dpm/AST/WhereClauseChecker.py +12 -0
  9. py_dpm/AST/__init__.py +0 -0
  10. py_dpm/AST/check_operands.py +302 -0
  11. py_dpm/DataTypes/ScalarTypes.py +324 -0
  12. py_dpm/DataTypes/TimeClasses.py +370 -0
  13. py_dpm/DataTypes/TypePromotion.py +195 -0
  14. py_dpm/DataTypes/__init__.py +0 -0
  15. py_dpm/Exceptions/__init__.py +0 -0
  16. py_dpm/Exceptions/exceptions.py +84 -0
  17. py_dpm/Exceptions/messages.py +114 -0
  18. py_dpm/OperationScopes/OperationScopeService.py +247 -0
  19. py_dpm/OperationScopes/__init__.py +0 -0
  20. py_dpm/Operators/AggregateOperators.py +138 -0
  21. py_dpm/Operators/BooleanOperators.py +30 -0
  22. py_dpm/Operators/ClauseOperators.py +159 -0
  23. py_dpm/Operators/ComparisonOperators.py +69 -0
  24. py_dpm/Operators/ConditionalOperators.py +362 -0
  25. py_dpm/Operators/NumericOperators.py +101 -0
  26. py_dpm/Operators/Operator.py +388 -0
  27. py_dpm/Operators/StringOperators.py +27 -0
  28. py_dpm/Operators/TimeOperators.py +53 -0
  29. py_dpm/Operators/__init__.py +0 -0
  30. py_dpm/Utils/ValidationsGenerationUtils.py +429 -0
  31. py_dpm/Utils/__init__.py +0 -0
  32. py_dpm/Utils/operands_mapping.py +73 -0
  33. py_dpm/Utils/operator_mapping.py +89 -0
  34. py_dpm/Utils/tokens.py +172 -0
  35. py_dpm/Utils/utils.py +2 -0
  36. py_dpm/ValidationsGeneration/PropertiesConstraintsProcessor.py +190 -0
  37. py_dpm/ValidationsGeneration/Utils.py +364 -0
  38. py_dpm/ValidationsGeneration/VariantsProcessor.py +265 -0
  39. py_dpm/ValidationsGeneration/__init__.py +0 -0
  40. py_dpm/ValidationsGeneration/auxiliary_functions.py +98 -0
  41. py_dpm/__init__.py +61 -0
  42. py_dpm/api/__init__.py +140 -0
  43. py_dpm/api/ast_generator.py +438 -0
  44. py_dpm/api/complete_ast.py +241 -0
  45. py_dpm/api/data_dictionary_validation.py +577 -0
  46. py_dpm/api/migration.py +77 -0
  47. py_dpm/api/semantic.py +224 -0
  48. py_dpm/api/syntax.py +182 -0
  49. py_dpm/client.py +106 -0
  50. py_dpm/data_handlers.py +99 -0
  51. py_dpm/db_utils.py +117 -0
  52. py_dpm/grammar/__init__.py +0 -0
  53. py_dpm/grammar/dist/__init__.py +0 -0
  54. py_dpm/grammar/dist/dpm_xlLexer.interp +428 -0
  55. py_dpm/grammar/dist/dpm_xlLexer.py +804 -0
  56. py_dpm/grammar/dist/dpm_xlLexer.tokens +106 -0
  57. py_dpm/grammar/dist/dpm_xlParser.interp +249 -0
  58. py_dpm/grammar/dist/dpm_xlParser.py +5224 -0
  59. py_dpm/grammar/dist/dpm_xlParser.tokens +106 -0
  60. py_dpm/grammar/dist/dpm_xlParserListener.py +742 -0
  61. py_dpm/grammar/dist/dpm_xlParserVisitor.py +419 -0
  62. py_dpm/grammar/dist/listeners.py +10 -0
  63. py_dpm/grammar/dpm_xlLexer.g4 +435 -0
  64. py_dpm/grammar/dpm_xlParser.g4 +260 -0
  65. py_dpm/migration.py +282 -0
  66. py_dpm/models.py +2139 -0
  67. py_dpm/semantics/DAG/DAGAnalyzer.py +158 -0
  68. py_dpm/semantics/DAG/__init__.py +0 -0
  69. py_dpm/semantics/SemanticAnalyzer.py +320 -0
  70. py_dpm/semantics/Symbols.py +223 -0
  71. py_dpm/semantics/__init__.py +0 -0
  72. py_dpm/utils/__init__.py +0 -0
  73. py_dpm/utils/ast_serialization.py +481 -0
  74. py_dpm/views/data_types.sql +12 -0
  75. py_dpm/views/datapoints.sql +65 -0
  76. py_dpm/views/hierarchy_operand_reference.sql +11 -0
  77. py_dpm/views/hierarchy_preconditions.sql +13 -0
  78. py_dpm/views/hierarchy_variables.sql +26 -0
  79. py_dpm/views/hierarchy_variables_context.sql +14 -0
  80. py_dpm/views/key_components.sql +18 -0
  81. py_dpm/views/module_from_table.sql +11 -0
  82. py_dpm/views/open_keys.sql +13 -0
  83. py_dpm/views/operation_info.sql +27 -0
  84. py_dpm/views/operation_list.sql +18 -0
  85. py_dpm/views/operations_versions_from_module_version.sql +30 -0
  86. py_dpm/views/precondition_info.sql +17 -0
  87. py_dpm/views/report_type_operand_reference_info.sql +18 -0
  88. py_dpm/views/subcategory_info.sql +17 -0
  89. py_dpm/views/table_info.sql +19 -0
  90. pydpm_xl-0.1.10.dist-info/LICENSE +674 -0
  91. pydpm_xl-0.1.10.dist-info/METADATA +50 -0
  92. pydpm_xl-0.1.10.dist-info/RECORD +94 -0
  93. pydpm_xl-0.1.10.dist-info/WHEEL +4 -0
  94. pydpm_xl-0.1.10.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,364 @@
1
+ import json
2
+ import warnings
3
+ from datetime import datetime
4
+ from http.client import BAD_REQUEST, INTERNAL_SERVER_ERROR
5
+ from pathlib import Path
6
+
7
+ import pandas as pd
8
+
9
+ from py_dpm.Exceptions.exceptions import ScriptingError, SemanticError
10
+ from py_dpm.models import OperationVersion, SubCategory, TableVersionCell, ViewReportTypeOperandReferenceInfo
11
+ from py_dpm.db_utils import get_session
12
+
13
+
14
+ def format_table_code(code: str):
15
+ if not code or pd.isna(code):
16
+ return None
17
+ code = code.replace(' ', '_')
18
+ return code
19
+
20
+ def assing_sign(formula):
21
+ if "<" in formula:
22
+ return "negative" # negative
23
+ elif ">" in formula:
24
+ return "positive" # positive
25
+ else:
26
+ return None
27
+
28
+
29
+ def read_external_sign_data(**kwargs):
30
+ path = Path(__file__).parent / 'utils_report' / 'external_data'
31
+
32
+ eba_rules = path / 'EBA_Validation_Rules_2022-12-12.xlsx'
33
+ sht = '3.2(3.2.1, 3.2.2)' # wb['v3.1(3.1.1)'] # the last one at 21/06/2022
34
+
35
+ eba_df = pd.read_excel(eba_rules, sheet_name=sht)
36
+ sign_validations = eba_df[eba_df['Type'] == 'Sign']
37
+ sign_validations['Table Code'] = sign_validations['T1'].map(lambda x: x.replace(' ', '_'))#map(format_table_code)
38
+ sign_validations["Sign"] = sign_validations["Formula"].map(assing_sign)
39
+ sign_validations = sign_validations[["Table Code", "Sign", "ID"]]
40
+ update_sign_db(sign_validations)
41
+ return sign_validations
42
+
43
+
44
+ def update_sign_db(sign_validations): # TODO: this does not work because DDL restrictions
45
+ session = get_session()
46
+ # sign_validations = read_external_sign_data()
47
+ neg_tables = sign_validations[sign_validations["Sign"]=='negative']["Table Code"].unique().tolist()
48
+ pos_tables = sign_validations[sign_validations["Sign"]=='positive']["Table Code"].unique().tolist()
49
+ all_neg_table_version_ids = TableVersionCell.get_sign_query(session=session, sign=neg_tables)
50
+ all_pos_table_version_ids = TableVersionCell.get_sign_query(session=session, sign=pos_tables)
51
+ for table_version in all_neg_table_version_ids:
52
+ table_version.Sign = "negative"
53
+ session.commit()
54
+ for table_version in all_pos_table_version_ids:
55
+ table_version.Sign = "positive"
56
+ session.commit()
57
+
58
+ # session.commit()
59
+ session.close()
60
+
61
+ def _error_to_dict(error):
62
+ if isinstance(error, SemanticError):
63
+ status = BAD_REQUEST
64
+ message_error = error.args[0]
65
+ error_code = error.args[1]
66
+ elif isinstance(error, SyntaxError):
67
+ status = BAD_REQUEST
68
+ message_error = error.args[0]
69
+ error_code = "Syntax"
70
+ elif isinstance(error, ScriptingError):
71
+ status = BAD_REQUEST
72
+ message_error = error.args[0]
73
+ error_code = error.args[1]
74
+ else:
75
+ status = INTERNAL_SERVER_ERROR
76
+ message_error = str(error)
77
+ now = datetime.now()
78
+ warnings.warn(f"[{now}] Unhandled exception:\n{error}", RuntimeWarning)
79
+ error_code = "Other"
80
+
81
+ return status, message_error, error_code
82
+
83
+
84
+ class ExternalData:
85
+
86
+ def __init__(self):
87
+ self.proposed_rules, self.rejected_rules = self.read_external_data()
88
+
89
+ def read_external_data(self, **kwargs):
90
+ path = Path(__file__).parent / 'utils_report' / 'external_data'
91
+
92
+ proposed_rules_path = path / 'out_put_proposed_rules.xlsx'
93
+ rejected_rules_path = path / 'RejectedRuleProposals.csv'
94
+ proposed_rules = pd.read_excel(proposed_rules_path)
95
+ rejected_rules = pd.read_csv(rejected_rules_path, encoding='latin-1')
96
+ return proposed_rules, rejected_rules
97
+
98
+ @staticmethod
99
+ def get_expression_from_code(code: str):
100
+ if not code or pd.isna(code):
101
+ return None
102
+ sql_session = get_session()
103
+ expression = OperationVersion.get_operations_from_code(sql_session, code)
104
+ expression = expression["Expression"].iloc[0] # TODO check if there is more than one expression take the last one maybe
105
+ sql_session.close()
106
+ return expression
107
+
108
+ @classmethod
109
+ def create_report(cls, report_df:pd.DataFrame, info_dict:dict, path:Path, name_report:str):
110
+ path_info = path / f"{name_report}_info.json"
111
+ path_validations = path / f"{name_report}_validations.csv"
112
+ report_df.to_csv(path_validations, index=False)
113
+ with open(path_info, 'w') as f:
114
+ f.write(json.dumps(info_dict))
115
+
116
+
117
+ class ExternalDataHierarchies(ExternalData):
118
+
119
+ def __init__(self):
120
+ super().__init__()
121
+ # self.proposed_rules = self.proposed_rules.rename(columns={'HierarchyCode':'Hierarchy Code'})
122
+ self.proposed_rules = self.proposed_rules.dropna(subset=['Hierarchy Code']).reset_index(drop=True)
123
+ self.rejected_rules = self.rejected_rules.dropna(subset=['HierarchyCode']).reset_index(drop=True)
124
+
125
+ def get_hierarchy_subcategory(self, subcategory_code):
126
+ proposed_rules, rejected_rules = self.proposed_rules, self.rejected_rules
127
+
128
+ proposed_rules = proposed_rules[proposed_rules['Hierarchy Code'] == subcategory_code]
129
+ rejected_rules = rejected_rules[rejected_rules['HierarchyCode'] == subcategory_code]
130
+
131
+ return proposed_rules, rejected_rules
132
+
133
+ def compare_all_hierarchies_report(self, validations: list):
134
+ total_number_of_validations_created = 0
135
+ total_number_of_validations_proposed = 0
136
+ total_number_of_validations_rejected = 0
137
+ total_matches_codes = 0
138
+ total_duplicated_codes = 0
139
+ total_codes_dont_match_with_proposed = 0
140
+ total_missing_codes = 0
141
+ total_expressions_dont_match_with_proposed = 0
142
+ sql_session = get_session()
143
+ subcategory_codes = SubCategory.get_codes(session=sql_session)
144
+ sql_session.close()
145
+ subcategory_codes = [subcategory_code[0] for subcategory_code in subcategory_codes]
146
+ total_info = {}
147
+ total_merged = pd.DataFrame()
148
+ for subcategory_code in subcategory_codes:
149
+ validations_subcategory = [elto for elto in validations if elto['subcategory_code'] == subcategory_code]
150
+ merged, info = self.compare_hierarchies_report(validations_subcategory, subcategory_code)
151
+ total_merged = pd.concat([total_merged, merged])
152
+ for key, value in info.items():
153
+ total_info[key] = value
154
+ total_number_of_validations_created += value["number_of_validations_created"]
155
+ total_number_of_validations_proposed += value["number_of_validations_proposed"]
156
+ total_number_of_validations_rejected += value["number_of_validations_rejected"]
157
+ total_matches_codes += len(value["matches_codes(generated_expressions)"])
158
+ if "errors" in value.keys():
159
+ total_duplicated_codes += len(value["errors"]["duplicated_codes"])
160
+ total_codes_dont_match_with_proposed += len(value["errors"]["codes_dont_match_with_proposed"])
161
+ total_missing_codes += len(value["errors"]["missing_codes"])
162
+ total_expressions_dont_match_with_proposed += len(value["errors"]["expressions_dont_match_with_proposed"])
163
+
164
+ # aditional actions on total info
165
+ description = {}
166
+ description["total_number_of_validations_created"] = total_number_of_validations_created
167
+ description["total_number_of_validations_proposed"] = total_number_of_validations_proposed
168
+ description["total_number_of_validations_rejected"] = total_number_of_validations_rejected
169
+ description["total_matches_codes(generated_expressions)"] = total_matches_codes
170
+ description["total_duplicated_codes"] = total_duplicated_codes
171
+ description["total_codes_dont_match_with_proposed"] = total_codes_dont_match_with_proposed
172
+ description["total_missing_codes"] = total_missing_codes
173
+ description["total_expressions_dont_match_with_proposed"] = total_expressions_dont_match_with_proposed
174
+ final_info = {}
175
+ final_info["resume"] = description
176
+ final_info["subcategories"] = total_info
177
+
178
+ return total_merged, final_info
179
+
180
+ def compare_hierarchies_report(self, validations: list, subcategory_code: str):
181
+ proposed, rejected = self.get_hierarchy_subcategory(subcategory_code=subcategory_code)
182
+ proposed_specific = pd.DataFrame(columns=['ID', 'Formula', 'ProposedAction', 'Review Action', 'Hierarchy Code']) if proposed.empty else proposed[['ID', 'Formula', 'ProposedAction', 'Review Action', 'Hierarchy Code']]
183
+ rejected_specific = pd.DataFrame(columns=['ID', 'Formula', 'ProposedAction', 'Review Action', 'Hierarchy Code']) if rejected.empty else rejected[['ID', 'Formula', 'ProposedAction', 'ReviewAction', 'HierarchyCode']].rename(columns={'HierarchyCode':'Hierarchy Code', 'ReviewAction':'Review Action'})
184
+ info = {}
185
+ errors = {}
186
+ errors[subcategory_code] = {}
187
+ errors[subcategory_code]['duplicated_codes'] = []
188
+
189
+ proposed_codes = proposed_specific[~proposed_specific['ID'].isna()]["ID"].unique().tolist()
190
+ rejected_codes = rejected_specific['ID'].unique().tolist()
191
+ total_external = pd.concat([proposed_specific, rejected_specific])
192
+ total_external_codes = total_external['ID'].unique().tolist()
193
+ # validations_codes
194
+ validations_codes = []
195
+
196
+ [validations_codes.extend(elto['operation_code']) for elto in validations]
197
+
198
+ if len(validations_codes) != len(set(validations_codes)):
199
+ errors[subcategory_code]['duplicated_codes'] = list(set([code for code in validations_codes if validations_codes.count(code) > 1]))
200
+ validations_codes = list(set(validations_codes))
201
+
202
+ errors[subcategory_code]['codes_dont_match_with_proposed'] = [code for code in validations_codes if code not in total_external_codes] # TODO
203
+ errors[subcategory_code]['missing_codes'] = [code for code in proposed_codes if code not in validations_codes]
204
+
205
+ #create comparative report
206
+ #first adapt validations
207
+ validations_to_df = []
208
+ errors[subcategory_code]['expressions_dont_match_with_proposed'] = []
209
+
210
+ for elto in validations:
211
+ is_duplicated = False
212
+ if len(elto['operation_code']) == 0:
213
+ if elto['expression'] not in errors[subcategory_code]['expressions_dont_match_with_proposed']:
214
+ errors[subcategory_code]['expressions_dont_match_with_proposed'].append(elto['expression'])
215
+ validations_to_df.append(
216
+ {'code':None, 'expression':elto['expression'], 'status':elto['status'],
217
+ 'subcategory_code':subcategory_code, 'is_duplicated':is_duplicated
218
+ }
219
+ )
220
+ if len(set(elto['operation_code'])) > 1:
221
+ is_duplicated = True
222
+ errors[subcategory_code]['duplicated_codes'].extend(elto['operation_code'])
223
+
224
+ for op_code in elto['operation_code']:
225
+ validations_to_df.append(
226
+ {'code':op_code, 'expression':elto['expression'], 'status':elto['status'],
227
+ 'subcategory_code':subcategory_code, 'is_duplicated':is_duplicated
228
+ }
229
+ )
230
+
231
+ info[subcategory_code] = {'number_of_validations_created':len(validations)}
232
+ info[subcategory_code]['number_of_validations_proposed'] = len(proposed_specific)
233
+ info[subcategory_code]['number_of_validations_rejected'] = len(rejected_specific)
234
+ comparative_report = pd.DataFrame(validations_to_df)
235
+ if comparative_report.empty:
236
+ comparative_report = pd.DataFrame(columns=['code', 'expression', 'status', 'subcategory_code', 'is_duplicated'])
237
+
238
+ merged = comparative_report.merge(total_external, left_on='code', right_on='ID', how='outer', indicator=True)
239
+ info[subcategory_code]['matches_codes(generated_expressions)'] = merged[~merged['code'].isna()]["code"].unique().tolist()
240
+ # external_code, external_expression, proposed action, code, expression, ...
241
+
242
+ # if not merged.empty:
243
+ merged["expression_from_db"] = merged["code"].map(ExternalData.get_expression_from_code)
244
+ del merged['_merge']
245
+ errors[subcategory_code]['duplicated_codes'] = list(set(errors[subcategory_code]['duplicated_codes']))
246
+ info[subcategory_code]["errors"] = errors[subcategory_code] # TODO
247
+ proposed_expressions_without_code = proposed_specific[proposed_specific['ID'].isna()]["Formula"].unique().tolist()
248
+ if proposed_expressions_without_code:
249
+ info[subcategory_code]['proposed_expressions_without_code'] = proposed_expressions_without_code
250
+ info[subcategory_code]['aditional info']=[{elto["expression"]:elto["aproximated_operations"]} for elto in validations if not elto["operation_code"]]
251
+ return merged, info
252
+
253
+ # @staticmethod
254
+ # def create_report(report_df:pd.DataFrame, info_dict:dict, subcategory_code:str=None):
255
+ # name_report = subcategory_code if subcategory_code else 'all_subcategories'
256
+ # path = Path(__file__).parent / 'utils_report' / 'hierarchies_report'
257
+ # path_errors = path / f"{name_report}_info.json"
258
+ # path_validations = path / f"{name_report}_validations.csv"
259
+ # report_df.to_csv(path_validations, index=False)
260
+ # with open(path_errors, 'w') as f:
261
+ # f.write(json.dumps(info_dict))
262
+
263
+ @classmethod
264
+ def create_report(cls, report_df:pd.DataFrame, info_dict:dict, subcategory_code:str=None):
265
+ name_report = subcategory_code if subcategory_code else 'all_subcategories'
266
+ path = Path(__file__).parent / 'utils_report' / 'hierarchies_report'
267
+ super().create_report(report_df, info_dict, path, name_report)
268
+
269
+
270
+ class ExternalDataSign(ExternalData):
271
+
272
+ def __init__(self):
273
+ super().__init__()
274
+ self.proposed_rules = self.proposed_rules[self.proposed_rules['Type']=="Sign"]
275
+ self.rejected_rules = self.rejected_rules[self.rejected_rules['Type']=="Sign"]
276
+
277
+
278
+ def match_code(self, validations: list, report_type: str):
279
+ """
280
+ """
281
+ for elto in validations:
282
+ total_cells_id = elto["cell_data"]
283
+ sql_session = get_session()
284
+ matched_codes = ViewReportTypeOperandReferenceInfo.get_cell_report_operations(sql_session, total_cells_id, report_type)
285
+ elto["operation_code"] = matched_codes
286
+ sql_session.close()
287
+ # new version end
288
+
289
+ return validations
290
+
291
+ def compare_all_sign_report(self, validations: list):
292
+ proposed, rejected = self.proposed_rules, self.rejected_rules
293
+ proposed_specific = pd.DataFrame(columns=['ID', 'Formula', 'ProposedAction', 'Review Action', 'Framework']) if proposed.empty else proposed[['ID', 'Formula', 'ProposedAction', 'Review Action', 'Framework']]
294
+ rejected_specific = pd.DataFrame(columns=['ID', 'Formula', 'ProposedAction', 'Review Action']) if rejected.empty else rejected[['ID', 'Formula', 'ProposedAction', 'ReviewAction']].rename(columns={'ReviewAction':'Review Action'})
295
+ info = {}
296
+ errors = {}
297
+
298
+ proposed_codes = proposed_specific[~proposed_specific['ID'].isna()]["ID"].unique().tolist()
299
+ rejected_codes = rejected_specific['ID'].unique().tolist()
300
+ total_external = pd.concat([proposed_specific, rejected_specific])
301
+ total_external_codes = total_external['ID'].unique().tolist()
302
+ # validations_codes
303
+ validations_codes = []
304
+
305
+ [validations_codes.extend(elto['operation_code']) for elto in validations]
306
+
307
+ if len(validations_codes) != len(set(validations_codes)):
308
+ errors['duplicated_codes'] = list(set([code for code in validations_codes if validations_codes.count(code) > 1]))
309
+ validations_codes = list(set(validations_codes))
310
+
311
+ # no match expressions
312
+ errors['expressions_dont_match_with_proposed'] = [elto['expression'] for elto in validations if len(elto['operation_code']) == 0]
313
+ # missing codes
314
+ errors['missing_codes'] = [code for code in total_external_codes if code not in validations_codes]
315
+ # exceded codes
316
+ errors['exceded_codes'] = [code for code in validations_codes if code not in total_external_codes]
317
+
318
+ validations_to_df = []
319
+ for elto in validations:
320
+ if len(elto['operation_code']) == 0:
321
+ validations_to_df.append(
322
+ {'code':None, 'expression':elto['expression'], 'status':elto['status'],
323
+ 'table':None
324
+ }
325
+ )
326
+ elif len(elto['operation_code']) == 1:
327
+ validations_to_df.append(
328
+ {'code':elto['operation_code'][0], 'expression':elto['expression'], 'status':elto['status'],
329
+ 'table':None
330
+ }
331
+ )
332
+ else:
333
+ for code in elto['operation_code']:
334
+ validations_to_df.append(
335
+ {'code':code, 'expression':elto['expression'], 'status':elto['status'],
336
+ 'table':None
337
+ }
338
+ )
339
+ comparative_report = pd.DataFrame(validations_to_df)
340
+ if comparative_report.empty:
341
+ comparative_report = pd.DataFrame(columns=['code', 'expression', 'status', 'subcategory_code', 'is_duplicated'])
342
+
343
+ merged = comparative_report.merge(total_external, left_on='code', right_on='ID', how='outer', indicator=True)
344
+ merged["expression_from_db"] = merged["code"].map(ExternalData.get_expression_from_code)
345
+ del merged['_merge']
346
+ # total_merged, total_info = proposed_specific, rejected_specific
347
+
348
+ return merged, errors
349
+
350
+ @classmethod
351
+ def create_report(cls, report_df:pd.DataFrame, info_dict:dict):
352
+ name_report = 'signes'
353
+ path = Path(__file__).parent / 'utils_report' / 'signes_report'
354
+ super().create_report(report_df, info_dict, path, name_report)
355
+
356
+ class ExternalDataExistence(ExternalData):
357
+
358
+ def __init__(self):
359
+ super().__init__()
360
+ self.proposed_rules = self.proposed_rules[self.proposed_rules['Type']=="Existence"]
361
+ self.rejected_rules = self.rejected_rules[self.rejected_rules['Type']=="Existence"]
362
+
363
+ def compare_all_existence_report(self, validations: list):
364
+ pass
@@ -0,0 +1,265 @@
1
+ import copy
2
+ import re
3
+
4
+ import pandas as pd
5
+
6
+ from py_dpm.AST.ASTObjects import VarID, WithExpression
7
+ from py_dpm.AST.ASTTemplate import ASTTemplate
8
+ from py_dpm.Exceptions import exceptions
9
+ from py_dpm.models import ModuleVersionComposition, TableGroup, TableGroupComposition, TableVersion, ViewDatapoints
10
+ from py_dpm.Utils.tokens import EXPRESSION, STATUS, STATUS_CORRECT, STATUS_INCOMPLETE, STATUS_INCORRECT, VALIDATION_CODE
11
+ from py_dpm.data_handlers import filter_all_data
12
+
13
+ cell_components = ['table', 'rows', 'cols', 'sheets']
14
+ TABLE_ID = 'TableID'
15
+ MODULE_VID = 'ModuleVID'
16
+ TABLE_CODE = 'Code'
17
+ GROUP = 'group'
18
+ MODULE_VERSION_ID = 'module_version_id'
19
+ MODULE_CODE = 'module_code'
20
+
21
+
22
+ class VariantsProcessorChecker(ASTTemplate):
23
+ def __init__(self, ast):
24
+ super().__init__()
25
+ self._is_variant = False
26
+ self.visit(ast)
27
+
28
+ @property
29
+ def is_variant(self):
30
+ return self._is_variant
31
+
32
+ def visit_VarID(self, node: VarID):
33
+ if node.is_table_group:
34
+ self._is_variant = True
35
+
36
+
37
+ class VariantsProcessor(ASTTemplate):
38
+ """
39
+ Class to generate individual validations from a validation defined on groups.
40
+
41
+ :parameter expression: DPM-XL expression.
42
+ :parameter ast: Abstract Syntax Tree of expression.
43
+ :parameter session: SQLAlchemy Session to be used to connect to the DB.
44
+ :parameter validation_code: Code of parent validation.
45
+ """
46
+
47
+ def __init__(self, expression, ast, session, validation_code, release_id):
48
+ super().__init__()
49
+ self.expression = expression
50
+ self.AST = ast
51
+ self.session = session
52
+ self.validation_code = validation_code
53
+ self.current_validation = 1
54
+ self.partial_selection = None
55
+ self.release_id = release_id
56
+
57
+ self.table_data = {}
58
+ self.group_tables = []
59
+ self.table_groups_compositions = {}
60
+ self.tables_without_cells = []
61
+ self.children_suffix = None
62
+ self.equal_children_suffix = True
63
+
64
+ self.visit(self.AST)
65
+
66
+ def check_if_table_has_cells(self, table_code, rows, cols, sheets):
67
+ """
68
+ Method to check if an individual table has cells for rows, columns and sheets of validation.
69
+ :param table_code: code of table.
70
+ :param rows: rows.
71
+ :param cols: columns.
72
+ :param sheets: sheets.
73
+ :return: True if table has cells, False otherwise.
74
+ """
75
+ if table_code not in self.table_data:
76
+ datapoints = ViewDatapoints.get_table_data(session=self.session, table=table_code, release_id=self.release_id)
77
+ self.table_data[table_code] = datapoints
78
+ else:
79
+ datapoints = self.table_data[table_code]
80
+ data = filter_all_data(data=datapoints, table_code=table_code, rows=rows, cols=cols, sheets=sheets)
81
+ return not data.empty
82
+
83
+ def check_table_from_module(self, node, table_module_id, is_abstract):
84
+ """
85
+ Method to get table_versions of a table group when there are cells for those tables.
86
+ :param node: var_id node
87
+ :param table_module_id: module id
88
+ :param is_abstract: flag that represents if table is abstract
89
+ :return: table version if table version has cells, None otherwise.
90
+ """
91
+
92
+ table_versions = TableVersion.get_tables_versions_of_table_group_compositions(session=self.session,
93
+ table_id=table_module_id,
94
+ is_abstract=is_abstract,
95
+ release_id=self.release_id)
96
+
97
+ for table_version in table_versions:
98
+ table_with_cells = self.check_if_table_has_cells(table_version.Code, node.rows, node.cols, node.sheets)
99
+ if table_with_cells:
100
+ return table_version
101
+ else:
102
+ if table_version.TableID not in self.tables_without_cells:
103
+ self.tables_without_cells.append(table_version.TableID)
104
+ return None
105
+
106
+ def generate_child_expression(self, group_code, table_code):
107
+ """
108
+ Method to replace group_code by table_code in order to generate new validations.
109
+ :param group_code: Group code.
110
+ :param table_code: Table code.
111
+ """
112
+ groups = re.search("(" + re.escape(group_code) + '[^.$]' + ")", self.expression)
113
+ if f"g{group_code}" in self.expression and groups:
114
+ group = groups.group(0)
115
+ suffix = table_code[-(len(table_code)-len(group_code)):]
116
+ if group:
117
+ if not self.children_suffix:
118
+ self.children_suffix = suffix
119
+ else:
120
+ if self.children_suffix != suffix:
121
+ self.equal_children_suffix = False
122
+ return
123
+ self.expression = re.sub(re.escape(f"g{group_code}") + '[^.$]', f"t{table_code}" + group[-1], self.expression)
124
+
125
+ def generate_child_expressions(self):
126
+ """
127
+ Method to generate all the individual validations.
128
+ :return: Expressions and expressions with errors.
129
+ """
130
+ if self.group_tables:
131
+ final_expressions = []
132
+ final_expressions_with_errors = []
133
+ df_tables = pd.DataFrame.from_records(self.group_tables)
134
+ df_tables = df_tables[~df_tables[TABLE_ID].isin(self.tables_without_cells)]
135
+ df_tables.drop_duplicates(inplace=True)
136
+
137
+ table_ids = df_tables[TABLE_ID].tolist()
138
+ modules_df = ModuleVersionComposition.get_modules_from_table_ids(session=self.session, table_ids=table_ids,
139
+ release_id=self.release_id)
140
+
141
+ data = pd.merge(df_tables, modules_df, on=[TABLE_ID])
142
+
143
+ expression = copy.deepcopy(self.expression)
144
+ for module, modules_tables in data.groupby(MODULE_VID):
145
+ modules_tables.apply(lambda x: self.generate_child_expression(x[GROUP], x[TABLE_CODE]), axis=1)
146
+ module_code = modules_tables[MODULE_CODE].unique().tolist()[0]
147
+ if len(modules_tables) < len(self.table_groups_compositions):
148
+ final_expressions_with_errors.append(
149
+ {EXPRESSION: self.expression, MODULE_VERSION_ID: module, MODULE_CODE: module_code})
150
+ else:
151
+ if self.equal_children_suffix:
152
+ final_expressions.append(
153
+ {EXPRESSION: self.expression, MODULE_VERSION_ID: module, MODULE_CODE: module_code})
154
+ else:
155
+ final_expressions_with_errors.append(
156
+ {EXPRESSION: self.expression, MODULE_VERSION_ID: module, MODULE_CODE: module_code})
157
+ self.equal_children_suffix = True
158
+ self.expression = copy.deepcopy(expression)
159
+ self.children_suffix = None
160
+
161
+ return final_expressions, final_expressions_with_errors
162
+ else:
163
+ raise exceptions.SemanticError("5-2-1")
164
+
165
+ def create_validation(self, expression_info, status):
166
+ """
167
+ Method to centralize creation of validations given expression and status of new validation
168
+ :param expression_info: Dictionary with information about expression, module_vid and module_code of validation
169
+ :param status: Status of validation
170
+ :return: Validation with code, expression and status
171
+ """
172
+
173
+ validation_code = self.generate_child_validation_code() if status == STATUS_CORRECT else None
174
+
175
+ validation = {VALIDATION_CODE: validation_code,
176
+ EXPRESSION: expression_info[EXPRESSION],
177
+ STATUS: status,
178
+ MODULE_VERSION_ID: expression_info[MODULE_VERSION_ID],
179
+ MODULE_CODE: expression_info[MODULE_CODE]}
180
+ return validation
181
+
182
+ def create_validation_new_format(self, expressions_dict):
183
+ """
184
+ Method to centralize creation of validations given expression and status of new validation
185
+ :param expression_info: Dictionary with information about expression, module_vid and module_code of validation
186
+ :param status: Status of validation
187
+ :return: Validation with code, expression and status
188
+ """
189
+
190
+ correct_expressions = expressions_dict[STATUS_CORRECT]
191
+ incomplete_expressions = expressions_dict[STATUS_INCOMPLETE]
192
+ incorrect_expressions = expressions_dict[STATUS_INCORRECT]
193
+ unique_correct_expressions = pd.DataFrame.from_records(correct_expressions)['expression'].unique().tolist() if correct_expressions else []
194
+ unique_incomplete_expressions = pd.DataFrame.from_records(incomplete_expressions)['expression'].unique().tolist() if incomplete_expressions else []
195
+ unique_incorrect_expressions = pd.DataFrame.from_records(incorrect_expressions)['expression'].unique().tolist() if incorrect_expressions else []
196
+ validations = []
197
+ if correct_expressions:
198
+ v = self._aux_create_validation_new_format(correct_expressions, unique_correct_expressions, STATUS_CORRECT)
199
+ validations.extend(v)
200
+ if incomplete_expressions:
201
+ v = self._aux_create_validation_new_format(incomplete_expressions, unique_incomplete_expressions, STATUS_INCOMPLETE)
202
+ validations.extend(v)
203
+ if incorrect_expressions:
204
+ v = self._aux_create_validation_new_format(incorrect_expressions, unique_incorrect_expressions, STATUS_INCORRECT)
205
+ validations.extend(v)
206
+
207
+ return validations
208
+
209
+ def _aux_create_validation_new_format(self, expressions, unique_expressions, status):
210
+ validations = []
211
+ for expr in unique_expressions:
212
+ aux = {}
213
+ aux[EXPRESSION] = expr
214
+ aux[STATUS] = status
215
+ if status == STATUS_CORRECT:
216
+ aux[VALIDATION_CODE] = self.generate_child_validation_code()
217
+ else:
218
+ aux[VALIDATION_CODE] = None
219
+ aux['scopes'] = []
220
+ for elto in expressions:
221
+ if elto[EXPRESSION] == aux[EXPRESSION]:
222
+ aux['scopes'].append(
223
+ {"module_versions_ids": [elto[MODULE_VERSION_ID]],
224
+ "module_code": elto[MODULE_CODE]
225
+ }
226
+ )
227
+ validations.append(aux)
228
+ return validations
229
+
230
+ def generate_child_validation_code(self):
231
+ """
232
+ Method to calculate validation codes for new validations.
233
+ :return: New code for the individual validation.
234
+ """
235
+ child_code = f"{self.validation_code}-{self.current_validation}"
236
+ self.current_validation += 1
237
+ return child_code
238
+
239
+ def visit_WithExpression(self, node: WithExpression):
240
+ self.partial_selection = node.partial_selection
241
+ self.visit(node.partial_selection)
242
+ self.visit(node.expression)
243
+
244
+ def visit_VarID(self, node: VarID):
245
+ if self.partial_selection:
246
+ for attribute in cell_components:
247
+ if not getattr(node, attribute, False) and hasattr(self.partial_selection, attribute):
248
+ setattr(node, attribute, getattr(self.partial_selection, attribute))
249
+
250
+ if node.is_table_group or self.partial_selection.is_table_group:
251
+ if node.table:
252
+ if node.table not in self.table_groups_compositions:
253
+ group: TableGroup = TableGroup.get_group_from_code(session=self.session, group_code=node.table)
254
+ if not group:
255
+ raise exceptions.SemanticError("1-6", table_group=node.table)
256
+ table_groups_compositions = TableGroupComposition.get_from_parent_table_code(code=node.table, session=self.session)
257
+ self.table_groups_compositions[node.table] = table_groups_compositions
258
+ else:
259
+ table_groups_compositions = self.table_groups_compositions[node.table]
260
+
261
+ for table_id, is_abstract in table_groups_compositions:
262
+ table_version = self.check_table_from_module(node, table_id, is_abstract)
263
+ if table_version:
264
+ self.group_tables.append(
265
+ {GROUP: node.table, TABLE_CODE: table_version.Code, TABLE_ID: table_version.TableID})
File without changes