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
py_dpm/api/semantic.py ADDED
@@ -0,0 +1,224 @@
1
+ from typing import Dict, Any, Optional
2
+ from dataclasses import dataclass
3
+
4
+ # Import directly to avoid circular imports
5
+ from antlr4 import CommonTokenStream, InputStream
6
+ from py_dpm.grammar.dist.dpm_xlLexer import dpm_xlLexer
7
+ from py_dpm.grammar.dist.dpm_xlParser import dpm_xlParser
8
+ from py_dpm.grammar.dist.listeners import DPMErrorListener
9
+ from py_dpm.AST.ASTConstructor import ASTVisitor
10
+ from py_dpm.AST.check_operands import OperandsChecking
11
+ from py_dpm.semantics import SemanticAnalyzer
12
+ from py_dpm.db_utils import get_session, get_engine
13
+ from py_dpm.Exceptions.exceptions import SemanticError
14
+
15
+
16
+ @dataclass
17
+ class SemanticValidationResult:
18
+ """
19
+ Result of semantic validation.
20
+
21
+ Attributes:
22
+ is_valid (bool): Whether the semantic validation passed
23
+ error_message (Optional[str]): Error message if validation failed
24
+ error_code (Optional[str]): Error code if validation failed
25
+ expression (str): The original expression that was validated
26
+ validation_type (str): Type of validation performed
27
+ results (Optional[Any]): Additional results from semantic analysis
28
+ """
29
+ is_valid: bool
30
+ error_message: Optional[str]
31
+ error_code: Optional[str]
32
+ expression: str
33
+ validation_type: str
34
+ results: Optional[Any] = None
35
+
36
+
37
+ class SemanticAPI:
38
+ """
39
+ API for DPM-XL semantic validation and analysis.
40
+
41
+ This class provides methods to perform semantic analysis on DPM-XL expressions,
42
+ including operand checking, data type validation, and structure validation.
43
+ """
44
+
45
+ def __init__(self):
46
+ """Initialize the Semantic API."""
47
+ get_engine()
48
+ self.session = get_session()
49
+ self.error_listener = DPMErrorListener()
50
+ self.visitor = ASTVisitor()
51
+
52
+ def validate_expression(self, expression: str) -> SemanticValidationResult:
53
+ """
54
+ Perform semantic validation on a DPM-XL expression.
55
+
56
+ This includes syntax validation, operands checking, data type validation,
57
+ and structure validation.
58
+
59
+ Args:
60
+ expression (str): The DPM-XL expression to validate
61
+
62
+ Returns:
63
+ SemanticValidationResult: Result containing validation status and details
64
+
65
+ Example:
66
+ >>> from pydpm.api import SemanticAPI
67
+ >>> semantic = SemanticAPI()
68
+ >>> result = semantic.validate_expression("{tC_01.00, r0100, c0010} + {tC_01.00, r0200, c0010}")
69
+ >>> print(result.is_valid)
70
+ True
71
+ """
72
+ try:
73
+ # Parse expression to AST
74
+ input_stream = InputStream(expression)
75
+ lexer = dpm_xlLexer(input_stream)
76
+ lexer._listeners = [self.error_listener]
77
+ token_stream = CommonTokenStream(lexer)
78
+
79
+ parser = dpm_xlParser(token_stream)
80
+ parser._listeners = [self.error_listener]
81
+ parse_tree = parser.start()
82
+
83
+ if parser._syntaxErrors > 0:
84
+ return SemanticValidationResult(
85
+ is_valid=False,
86
+ error_message="Syntax errors detected",
87
+ error_code="SYNTAX_ERROR",
88
+ expression=expression,
89
+ validation_type="SEMANTIC"
90
+ )
91
+
92
+ # Generate AST
93
+ ast = self.visitor.visit(parse_tree)
94
+
95
+ # Perform semantic analysis
96
+ oc = OperandsChecking(session=self.session, expression=expression, ast=ast, release_id=None)
97
+ semanticAnalysis = SemanticAnalyzer.InputAnalyzer(expression)
98
+
99
+ semanticAnalysis.data = oc.data
100
+ semanticAnalysis.key_components = oc.key_components
101
+ semanticAnalysis.open_keys = oc.open_keys
102
+ semanticAnalysis.preconditions = oc.preconditions
103
+
104
+ results = semanticAnalysis.visit(ast)
105
+
106
+ return SemanticValidationResult(
107
+ is_valid=True,
108
+ error_message=None,
109
+ error_code=None,
110
+ expression=expression,
111
+ validation_type="SEMANTIC",
112
+ results=results
113
+ )
114
+
115
+ except SemanticError as e:
116
+ return SemanticValidationResult(
117
+ is_valid=False,
118
+ error_message=str(e),
119
+ error_code=getattr(e, 'code', None),
120
+ expression=expression,
121
+ validation_type="SEMANTIC"
122
+ )
123
+ except Exception as e:
124
+ return SemanticValidationResult(
125
+ is_valid=False,
126
+ error_message=str(e),
127
+ error_code="UNKNOWN",
128
+ expression=expression,
129
+ validation_type="SEMANTIC"
130
+ )
131
+
132
+ def analyze_expression(self, expression: str) -> Dict[str, Any]:
133
+ """
134
+ Perform detailed semantic analysis on a DPM-XL expression.
135
+
136
+ Args:
137
+ expression (str): The DPM-XL expression to analyze
138
+
139
+ Returns:
140
+ Dict[str, Any]: Detailed analysis results
141
+
142
+ Raises:
143
+ Exception: If analysis fails
144
+
145
+ Example:
146
+ >>> from pydpm.api import SemanticAPI
147
+ >>> semantic = SemanticAPI()
148
+ >>> analysis = semantic.analyze_expression("{tC_01.00, r0100, c0010}")
149
+ """
150
+ result = self.validate_expression(expression)
151
+
152
+ if not result.is_valid:
153
+ raise Exception(f"Semantic analysis failed: {result.error_message}")
154
+
155
+ # Extract additional analysis information
156
+ analysis = {
157
+ 'expression': expression,
158
+ 'is_valid': True,
159
+ 'results': result.results,
160
+ 'data_types': getattr(result.results, 'type', None) if result.results else None,
161
+ 'components': getattr(result.results, 'components', None) if result.results else None
162
+ }
163
+
164
+ return analysis
165
+
166
+ def is_valid_semantics(self, expression: str) -> bool:
167
+ """
168
+ Quick check if expression has valid semantics.
169
+
170
+ Args:
171
+ expression (str): The DPM-XL expression to check
172
+
173
+ Returns:
174
+ bool: True if semantics are valid, False otherwise
175
+
176
+ Example:
177
+ >>> from pydpm.api import SemanticAPI
178
+ >>> semantic = SemanticAPI()
179
+ >>> is_valid = semantic.is_valid_semantics("{tC_01.00, r0100, c0010}")
180
+ """
181
+ result = self.validate_expression(expression)
182
+ return result.is_valid
183
+
184
+ def __del__(self):
185
+ """Clean up resources."""
186
+ if hasattr(self, 'session'):
187
+ self.session.close()
188
+
189
+
190
+ # Convenience functions for direct usage
191
+ def validate_expression(expression: str) -> SemanticValidationResult:
192
+ """
193
+ Convenience function to validate DPM-XL expression semantics.
194
+
195
+ Args:
196
+ expression (str): The DPM-XL expression to validate
197
+
198
+ Returns:
199
+ SemanticValidationResult: Result containing validation status and details
200
+
201
+ Example:
202
+ >>> from pydpm.api.semantic import validate_expression
203
+ >>> result = validate_expression("{tC_01.00, r0100, c0010}")
204
+ """
205
+ api = SemanticAPI()
206
+ return api.validate_expression(expression)
207
+
208
+
209
+ def is_valid_semantics(expression: str) -> bool:
210
+ """
211
+ Convenience function to check if expression has valid semantics.
212
+
213
+ Args:
214
+ expression (str): The DPM-XL expression to check
215
+
216
+ Returns:
217
+ bool: True if semantics are valid, False otherwise
218
+
219
+ Example:
220
+ >>> from pydpm.api.semantic import is_valid_semantics
221
+ >>> is_valid = is_valid_semantics("{tC_01.00, r0100, c0010}")
222
+ """
223
+ api = SemanticAPI()
224
+ return api.is_valid_semantics(expression)
py_dpm/api/syntax.py ADDED
@@ -0,0 +1,182 @@
1
+ from typing import Dict, Any, Optional
2
+ from dataclasses import dataclass
3
+
4
+ # Import directly to avoid circular imports
5
+ from antlr4 import CommonTokenStream, InputStream
6
+ from py_dpm.grammar.dist.dpm_xlLexer import dpm_xlLexer
7
+ from py_dpm.grammar.dist.dpm_xlParser import dpm_xlParser
8
+ from py_dpm.grammar.dist.listeners import DPMErrorListener
9
+ from py_dpm.AST.ASTConstructor import ASTVisitor
10
+
11
+
12
+ @dataclass
13
+ class SyntaxValidationResult:
14
+ """
15
+ Result of syntax validation.
16
+
17
+ Attributes:
18
+ is_valid (bool): Whether the syntax is valid
19
+ error_message (Optional[str]): Error message if validation failed
20
+ expression (str): The original expression that was validated
21
+ """
22
+ is_valid: bool
23
+ error_message: Optional[str]
24
+ expression: str
25
+
26
+
27
+ class SyntaxAPI:
28
+ """
29
+ API for DPM-XL syntax validation and analysis.
30
+
31
+ This class provides methods to validate DPM-XL expression syntax.
32
+ """
33
+
34
+ def __init__(self):
35
+ """Initialize the Syntax API."""
36
+ self.error_listener = DPMErrorListener()
37
+ self.visitor = ASTVisitor()
38
+
39
+ def validate_expression(self, expression: str) -> SyntaxValidationResult:
40
+ """
41
+ Validate the syntax of a DPM-XL expression.
42
+
43
+ Args:
44
+ expression (str): The DPM-XL expression to validate
45
+
46
+ Returns:
47
+ SyntaxValidationResult: Result containing validation status and details
48
+
49
+ Example:
50
+ >>> from pydpm.api import SyntaxAPI
51
+ >>> syntax = SyntaxAPI()
52
+ >>> result = syntax.validate_expression("{tC_01.00, r0100, c0010}")
53
+ >>> print(result.is_valid)
54
+ True
55
+ """
56
+ try:
57
+ # Use direct ANTLR validation
58
+ input_stream = InputStream(expression)
59
+ lexer = dpm_xlLexer(input_stream)
60
+ lexer._listeners = [self.error_listener]
61
+ token_stream = CommonTokenStream(lexer)
62
+
63
+ parser = dpm_xlParser(token_stream)
64
+ parser._listeners = [self.error_listener]
65
+ parse_tree = parser.start()
66
+
67
+ if parser._syntaxErrors == 0:
68
+ return SyntaxValidationResult(
69
+ is_valid=True,
70
+ error_message=None,
71
+ expression=expression
72
+ )
73
+ else:
74
+ return SyntaxValidationResult(
75
+ is_valid=False,
76
+ error_message="Syntax errors detected",
77
+ expression=expression
78
+ )
79
+ except SyntaxError as e:
80
+ return SyntaxValidationResult(
81
+ is_valid=False,
82
+ error_message=str(e),
83
+ expression=expression
84
+ )
85
+ except Exception as e:
86
+ return SyntaxValidationResult(
87
+ is_valid=False,
88
+ error_message=f"Unexpected error: {str(e)}",
89
+ expression=expression
90
+ )
91
+
92
+ def parse_expression(self, expression: str):
93
+ """
94
+ Parse a DPM-XL expression and return the AST.
95
+
96
+ Args:
97
+ expression (str): The DPM-XL expression to parse
98
+
99
+ Returns:
100
+ AST: The Abstract Syntax Tree for the expression
101
+
102
+ Raises:
103
+ Exception: If parsing fails
104
+
105
+ Example:
106
+ >>> from pydpm.api import SyntaxAPI
107
+ >>> syntax = SyntaxAPI()
108
+ >>> ast = syntax.parse_expression("{tC_01.00, r0100, c0010}")
109
+ """
110
+ # Parse directly using ANTLR and AST visitor
111
+ try:
112
+ input_stream = InputStream(expression)
113
+ lexer = dpm_xlLexer(input_stream)
114
+ token_stream = CommonTokenStream(lexer)
115
+ parser = dpm_xlParser(token_stream)
116
+ parse_tree = parser.start()
117
+
118
+ # Create AST visitor and visit the parse tree
119
+ ast = self.visitor.visit(parse_tree)
120
+ return ast
121
+ except Exception as e:
122
+ raise Exception(f"Failed to parse DPM-XL expression '{expression}': {str(e)}")
123
+
124
+ def is_valid_syntax(self, expression: str) -> bool:
125
+ """
126
+ Quick check if expression has valid syntax.
127
+
128
+ Args:
129
+ expression (str): The DPM-XL expression to check
130
+
131
+ Returns:
132
+ bool: True if syntax is valid, False otherwise
133
+
134
+ Example:
135
+ >>> from pydpm.api import SyntaxAPI
136
+ >>> syntax = SyntaxAPI()
137
+ >>> is_valid = syntax.is_valid_syntax("{tC_01.00, r0100, c0010}")
138
+ """
139
+ # Use existing validate_expression method
140
+ result = self.validate_expression(expression)
141
+ return result.is_valid
142
+
143
+ def __del__(self):
144
+ """Clean up resources."""
145
+ pass
146
+
147
+
148
+ # Convenience functions for direct usage
149
+ def validate_expression(expression: str) -> SyntaxValidationResult:
150
+ """
151
+ Convenience function to validate DPM-XL expression syntax.
152
+
153
+ Args:
154
+ expression (str): The DPM-XL expression to validate
155
+
156
+ Returns:
157
+ SyntaxValidationResult: Result containing validation status and details
158
+
159
+ Example:
160
+ >>> from pydpm.api.syntax import validate_expression
161
+ >>> result = validate_expression("{tC_01.00, r0100, c0010}")
162
+ """
163
+ api = SyntaxAPI()
164
+ return api.validate_expression(expression)
165
+
166
+
167
+ def is_valid_syntax(expression: str) -> bool:
168
+ """
169
+ Convenience function to check if expression has valid syntax.
170
+
171
+ Args:
172
+ expression (str): The DPM-XL expression to check
173
+
174
+ Returns:
175
+ bool: True if syntax is valid, False otherwise
176
+
177
+ Example:
178
+ >>> from pydpm.api.syntax import is_valid_syntax
179
+ >>> is_valid = is_valid_syntax("{tC_01.00, r0100, c0010}")
180
+ """
181
+ api = SyntaxAPI()
182
+ return api.is_valid_syntax(expression)
py_dpm/client.py ADDED
@@ -0,0 +1,106 @@
1
+ import click
2
+ from rich.console import Console
3
+ from rich.table import Table
4
+ from rich.text import Text
5
+ import os
6
+ import sys
7
+
8
+ from py_dpm.api import API
9
+ from py_dpm.migration import run_migration
10
+ from py_dpm.Utils.tokens import CODE, ERROR, ERROR_CODE, EXPRESSION, OP_VERSION_ID, STATUS, \
11
+ STATUS_CORRECT, STATUS_UNKNOWN, VALIDATIONS, \
12
+ VALIDATION_TYPE, \
13
+ VARIABLES
14
+ from py_dpm.Exceptions.exceptions import SemanticError
15
+
16
+
17
+ console = Console()
18
+
19
+ @click.group()
20
+ @click.version_option()
21
+ def main():
22
+ """pyDPM CLI - A command line interface for pyDPM"""
23
+ pass
24
+
25
+ @main.command()
26
+ @click.argument('access_file', type=click.Path(exists=True))
27
+ def migrate_access(access_file: str):
28
+ """
29
+ Migrates data from an Access database to a SQLite database.
30
+
31
+ ACCESS_FILE: Path to the Access database file (.mdb or .accdb).
32
+ """
33
+
34
+ sqlite_db = os.getenv("SQLITE_DB_PATH", "database.db")
35
+ console.print(f"Starting migration from '{access_file}' to '{sqlite_db}'...")
36
+ try:
37
+ run_migration(access_file, sqlite_db)
38
+ console.print("Migration completed successfully.", style="bold green")
39
+ except Exception as e:
40
+ console.print(f"An error occurred during migration: {e}", style="bold red")
41
+ sys.exit(1)
42
+
43
+
44
+ @main.command()
45
+ @click.argument('expression', type=str)
46
+ def semantic(expression: str):
47
+ """
48
+ Semantically analyses the input expression by applying the syntax validation, the operands checking, the data type
49
+ validation and the structure validation
50
+ :param expression: Expression to be analysed
51
+ :param release_id: ID of the release used. If None, gathers the live release
52
+ Used only in DPM-ML generation
53
+ :return if Return_data is False, any Symbol, else data extracted from DB based on operands cell references
54
+ """
55
+
56
+ error_code = ""
57
+ validation_type = STATUS_UNKNOWN
58
+
59
+ api = API()
60
+ try:
61
+ validation_type = "OTHER"
62
+ api.semantic_validation(expression)
63
+ status = 200
64
+ message_error = ''
65
+ except Exception as error:
66
+ status = 500
67
+ message_error = str(error)
68
+ error_code = 1
69
+ message_response = {
70
+ ERROR: message_error,
71
+ ERROR_CODE: error_code,
72
+ VALIDATION_TYPE: validation_type,
73
+ }
74
+ api.session.close()
75
+ if error_code and status == 500:
76
+ console.print(f"Semantic validation failed for expression: {expression}.", style="bold red")
77
+ else:
78
+ console.log(f"Semantic validation completed for expression: {expression}.")
79
+ console.print(f"Status: {status}", style="bold green")
80
+ return status
81
+
82
+ @main.command()
83
+ @click.argument('expression', type=str)
84
+ def syntax(expression: str):
85
+ """Perform syntactic analysis on a DPM expression."""
86
+
87
+ status = 0
88
+ api = API()
89
+ try:
90
+ api.syntax_validation(expression)
91
+ message_formatted = Text("Syntax OK", style="bold green")
92
+ except SyntaxError as e:
93
+ message = str(e)
94
+ message_formatted = Text(f"Syntax Error: {message}", style="bold red")
95
+ status = 0
96
+ except Exception as e:
97
+ message = str(e)
98
+ message_formatted = Text(f"Unexpected Error: {message}", style="bold red")
99
+ status = 1
100
+
101
+ console.print(message_formatted)
102
+
103
+ return status
104
+
105
+ if __name__ == '__main__':
106
+ main()
@@ -0,0 +1,99 @@
1
+ import pandas as pd
2
+
3
+ from py_dpm.Exceptions.exceptions import SemanticError
4
+ from py_dpm.Utils.tokens import *
5
+
6
+
7
+ def filter_data_by_cell_element(series, cell_elements, element_name, table_code):
8
+ """
9
+ Filter data by cell elements
10
+ :param series: data to be filtered
11
+ :param cell_elements: rows, columns or sheets using to filter data
12
+ :param element_name: name of cell elements using to filter data
13
+ :return: filtered data
14
+ """
15
+ if len(cell_elements) == 1 and '-' not in cell_elements[0]:
16
+ series = series[series[element_name] == cell_elements[0]]
17
+ elif len(cell_elements) == 1 and '-' in cell_elements[0]:
18
+ limits = cell_elements[0].split('-')
19
+ series = series[series[element_name].between(limits[0], limits[1])]
20
+ else:
21
+ range_control = any(['-' in x for x in cell_elements])
22
+ if range_control: # Range in cell elements, we must separate them
23
+ data_range = []
24
+ data_single = []
25
+ for x in cell_elements:
26
+ if '-' in x:
27
+ limits = x.split('-')
28
+ data_range += list(series[series[element_name].between(limits[0], limits[1])][element_name].unique())
29
+ else:
30
+ data_single.append(x)
31
+ cell_elements = sorted(list(set(data_range + data_single)))
32
+ series = series[series[element_name].isin(cell_elements)]
33
+ cells_not_found = [x for x in cell_elements if x not in list(series[element_name].unique())]
34
+
35
+ if cells_not_found:
36
+ header = "rows" if element_name == ROW_CODE else "columns" if element_name == COLUMN_CODE else "sheets"
37
+ cell_elements = ", ".join([f"{header[0]}{x}" for x in cells_not_found]) if cells_not_found else None
38
+ op_pos = [table_code, cell_elements]
39
+ cell_exp = ", ".join(x for x in op_pos if x is not None)
40
+ raise SemanticError("1-2", cell_expression=cell_exp)
41
+ return series
42
+
43
+
44
+ def filter_all_data(data, table_code, rows, cols, sheets):
45
+ df = data[data["table_code"] == table_code].reset_index(drop=True)
46
+ if rows and rows[0] != '*':
47
+ df = filter_data_by_cell_element(df, rows, ROW_CODE, table_code)
48
+ if cols and cols[0] != '*':
49
+ df = filter_data_by_cell_element(df, cols, COLUMN_CODE, table_code)
50
+ if sheets and sheets[0] != '*':
51
+ df = filter_data_by_cell_element(df, sheets, SHEET_CODE, table_code)
52
+ df = df.reset_index(drop=True)
53
+ return df
54
+
55
+
56
+ def generate_xyz(data: pd.DataFrame):
57
+
58
+ for letter in [INDEX_X, INDEX_Y, INDEX_Z]:
59
+ data[letter] = None
60
+
61
+ number_of_rows = len(list(data[ROW_CODE].unique()))
62
+ number_of_columns = len(list(data[COLUMN_CODE].unique()))
63
+ number_of_sheets = len(list(data[SHEET_CODE].unique()))
64
+ group = []
65
+
66
+ if number_of_rows > 1:
67
+ data.sort_values(by=[ROW_CODE], inplace=True)
68
+ data[INDEX_X] = data[ROW_CODE].rank(method='dense').astype(int)
69
+ group.append(ROW_CODE)
70
+
71
+ if number_of_columns > 1:
72
+ data.sort_values(by=[COLUMN_CODE], inplace=True)
73
+ if data[INDEX_X].isnull().all():
74
+ data[INDEX_Y] = data[COLUMN_CODE].rank(method='dense').astype(int)
75
+ else:
76
+ data_groups = list(data.groupby(ROW_CODE))
77
+ for _, group_data in data_groups:
78
+ group_data.sort_values(by=[COLUMN_CODE], inplace=True)
79
+ group_data[INDEX_Y] = group_data[COLUMN_CODE].rank(method='dense').astype(int)
80
+ # Add to data[INDEX_Y] the values of group_data[INDEX_Y]
81
+ data.loc[group_data.index, INDEX_Y] = group_data[INDEX_Y]
82
+ group.append(COLUMN_CODE)
83
+
84
+ if number_of_sheets > 1:
85
+ data.sort_values(by=[SHEET_CODE], inplace=True)
86
+ if len(group) == 0:
87
+ data[INDEX_Z] = data[SHEET_CODE].rank(method='dense').astype(int)
88
+ else:
89
+ data_groups = list(data.groupby(group))
90
+ for _, group_data in data_groups:
91
+ group_data.sort_values(by=[SHEET_CODE], inplace=True)
92
+ group_data[INDEX_Z] = group_data[SHEET_CODE].rank(method='dense').astype(int)
93
+ data.loc[group_data.index, INDEX_Z] = group_data[INDEX_Z]
94
+ group.append(SHEET_CODE)
95
+ if len(group) > 0:
96
+ data.sort_values(by=group, inplace=True)
97
+ data.drop_duplicates(keep="first", inplace=True)
98
+ list_xyz = data.to_dict(orient='records')
99
+ return list_xyz