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.
- py_dpm/AST/ASTConstructor.py +503 -0
- py_dpm/AST/ASTObjects.py +827 -0
- py_dpm/AST/ASTTemplate.py +101 -0
- py_dpm/AST/ASTVisitor.py +13 -0
- py_dpm/AST/MLGeneration.py +588 -0
- py_dpm/AST/ModuleAnalyzer.py +79 -0
- py_dpm/AST/ModuleDependencies.py +203 -0
- py_dpm/AST/WhereClauseChecker.py +12 -0
- py_dpm/AST/__init__.py +0 -0
- py_dpm/AST/check_operands.py +302 -0
- py_dpm/DataTypes/ScalarTypes.py +324 -0
- py_dpm/DataTypes/TimeClasses.py +370 -0
- py_dpm/DataTypes/TypePromotion.py +195 -0
- py_dpm/DataTypes/__init__.py +0 -0
- py_dpm/Exceptions/__init__.py +0 -0
- py_dpm/Exceptions/exceptions.py +84 -0
- py_dpm/Exceptions/messages.py +114 -0
- py_dpm/OperationScopes/OperationScopeService.py +247 -0
- py_dpm/OperationScopes/__init__.py +0 -0
- py_dpm/Operators/AggregateOperators.py +138 -0
- py_dpm/Operators/BooleanOperators.py +30 -0
- py_dpm/Operators/ClauseOperators.py +159 -0
- py_dpm/Operators/ComparisonOperators.py +69 -0
- py_dpm/Operators/ConditionalOperators.py +362 -0
- py_dpm/Operators/NumericOperators.py +101 -0
- py_dpm/Operators/Operator.py +388 -0
- py_dpm/Operators/StringOperators.py +27 -0
- py_dpm/Operators/TimeOperators.py +53 -0
- py_dpm/Operators/__init__.py +0 -0
- py_dpm/Utils/ValidationsGenerationUtils.py +429 -0
- py_dpm/Utils/__init__.py +0 -0
- py_dpm/Utils/operands_mapping.py +73 -0
- py_dpm/Utils/operator_mapping.py +89 -0
- py_dpm/Utils/tokens.py +172 -0
- py_dpm/Utils/utils.py +2 -0
- py_dpm/ValidationsGeneration/PropertiesConstraintsProcessor.py +190 -0
- py_dpm/ValidationsGeneration/Utils.py +364 -0
- py_dpm/ValidationsGeneration/VariantsProcessor.py +265 -0
- py_dpm/ValidationsGeneration/__init__.py +0 -0
- py_dpm/ValidationsGeneration/auxiliary_functions.py +98 -0
- py_dpm/__init__.py +61 -0
- py_dpm/api/__init__.py +140 -0
- py_dpm/api/ast_generator.py +438 -0
- py_dpm/api/complete_ast.py +241 -0
- py_dpm/api/data_dictionary_validation.py +577 -0
- py_dpm/api/migration.py +77 -0
- py_dpm/api/semantic.py +224 -0
- py_dpm/api/syntax.py +182 -0
- py_dpm/client.py +106 -0
- py_dpm/data_handlers.py +99 -0
- py_dpm/db_utils.py +117 -0
- py_dpm/grammar/__init__.py +0 -0
- py_dpm/grammar/dist/__init__.py +0 -0
- py_dpm/grammar/dist/dpm_xlLexer.interp +428 -0
- py_dpm/grammar/dist/dpm_xlLexer.py +804 -0
- py_dpm/grammar/dist/dpm_xlLexer.tokens +106 -0
- py_dpm/grammar/dist/dpm_xlParser.interp +249 -0
- py_dpm/grammar/dist/dpm_xlParser.py +5224 -0
- py_dpm/grammar/dist/dpm_xlParser.tokens +106 -0
- py_dpm/grammar/dist/dpm_xlParserListener.py +742 -0
- py_dpm/grammar/dist/dpm_xlParserVisitor.py +419 -0
- py_dpm/grammar/dist/listeners.py +10 -0
- py_dpm/grammar/dpm_xlLexer.g4 +435 -0
- py_dpm/grammar/dpm_xlParser.g4 +260 -0
- py_dpm/migration.py +282 -0
- py_dpm/models.py +2139 -0
- py_dpm/semantics/DAG/DAGAnalyzer.py +158 -0
- py_dpm/semantics/DAG/__init__.py +0 -0
- py_dpm/semantics/SemanticAnalyzer.py +320 -0
- py_dpm/semantics/Symbols.py +223 -0
- py_dpm/semantics/__init__.py +0 -0
- py_dpm/utils/__init__.py +0 -0
- py_dpm/utils/ast_serialization.py +481 -0
- py_dpm/views/data_types.sql +12 -0
- py_dpm/views/datapoints.sql +65 -0
- py_dpm/views/hierarchy_operand_reference.sql +11 -0
- py_dpm/views/hierarchy_preconditions.sql +13 -0
- py_dpm/views/hierarchy_variables.sql +26 -0
- py_dpm/views/hierarchy_variables_context.sql +14 -0
- py_dpm/views/key_components.sql +18 -0
- py_dpm/views/module_from_table.sql +11 -0
- py_dpm/views/open_keys.sql +13 -0
- py_dpm/views/operation_info.sql +27 -0
- py_dpm/views/operation_list.sql +18 -0
- py_dpm/views/operations_versions_from_module_version.sql +30 -0
- py_dpm/views/precondition_info.sql +17 -0
- py_dpm/views/report_type_operand_reference_info.sql +18 -0
- py_dpm/views/subcategory_info.sql +17 -0
- py_dpm/views/table_info.sql +19 -0
- pydpm_xl-0.1.10.dist-info/LICENSE +674 -0
- pydpm_xl-0.1.10.dist-info/METADATA +50 -0
- pydpm_xl-0.1.10.dist-info/RECORD +94 -0
- pydpm_xl-0.1.10.dist-info/WHEEL +4 -0
- 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
|