pydpm_xl 0.1.10__py3-none-any.whl → 0.1.39rc24__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 (44) hide show
  1. py_dpm/AST/ASTConstructor.py +11 -0
  2. py_dpm/AST/ASTObjects.py +67 -3
  3. py_dpm/AST/ASTTemplate.py +5 -1
  4. py_dpm/AST/MLGeneration.py +12 -0
  5. py_dpm/AST/check_operands.py +303 -78
  6. py_dpm/OperationScopes/OperationScopeService.py +94 -30
  7. py_dpm/Operators/ClauseOperators.py +41 -0
  8. py_dpm/Operators/ConditionalOperators.py +21 -4
  9. py_dpm/{utils → Utils}/ast_serialization.py +289 -37
  10. py_dpm/Utils/operator_mapping.py +3 -2
  11. py_dpm/Utils/tokens.py +1 -0
  12. py_dpm/__init__.py +2 -8
  13. py_dpm/api/__init__.py +73 -4
  14. py_dpm/api/ast_generator.py +6 -3
  15. py_dpm/api/complete_ast.py +558 -21
  16. py_dpm/api/data_dictionary.py +984 -0
  17. py_dpm/api/data_dictionary_validation.py +44 -7
  18. py_dpm/api/migration.py +1 -1
  19. py_dpm/api/operation_scopes.py +1230 -0
  20. py_dpm/api/semantic.py +173 -65
  21. py_dpm/client.py +380 -0
  22. py_dpm/db_utils.py +110 -6
  23. py_dpm/grammar/dist/dpm_xlLexer.interp +5 -1
  24. py_dpm/grammar/dist/dpm_xlLexer.py +882 -708
  25. py_dpm/grammar/dist/dpm_xlLexer.tokens +46 -45
  26. py_dpm/grammar/dist/dpm_xlParser.interp +3 -1
  27. py_dpm/grammar/dist/dpm_xlParser.py +762 -614
  28. py_dpm/grammar/dist/dpm_xlParser.tokens +46 -45
  29. py_dpm/grammar/dist/dpm_xlParserListener.py +11 -3
  30. py_dpm/grammar/dist/dpm_xlParserVisitor.py +7 -3
  31. py_dpm/grammar/dist/listeners.py +1 -1
  32. py_dpm/grammar/dpm_xlLexer.g4 +3 -1
  33. py_dpm/grammar/dpm_xlParser.g4 +5 -2
  34. py_dpm/migration.py +122 -15
  35. py_dpm/models.py +1718 -502
  36. py_dpm/semantics/SemanticAnalyzer.py +26 -9
  37. {pydpm_xl-0.1.10.dist-info → pydpm_xl-0.1.39rc24.dist-info}/METADATA +17 -14
  38. {pydpm_xl-0.1.10.dist-info → pydpm_xl-0.1.39rc24.dist-info}/RECORD +45 -43
  39. {pydpm_xl-0.1.10.dist-info → pydpm_xl-0.1.39rc24.dist-info}/WHEEL +2 -1
  40. pydpm_xl-0.1.39rc24.dist-info/entry_points.txt +2 -0
  41. pydpm_xl-0.1.39rc24.dist-info/top_level.txt +1 -0
  42. py_dpm/utils/__init__.py +0 -0
  43. pydpm_xl-0.1.10.dist-info/entry_points.txt +0 -3
  44. {pydpm_xl-0.1.10.dist-info → pydpm_xl-0.1.39rc24.dist-info/licenses}/LICENSE +0 -0
@@ -220,6 +220,9 @@ class ASTVisitor(dpm_xlParserVisitor):
220
220
  elif isinstance(ctx_list[2], dpm_xlParser.RenameExprContext):
221
221
  rename_nodes = self.visitRenameExpr(ctx_list[2])
222
222
  return RenameOp(operand=operand, rename_nodes=rename_nodes)
223
+ elif isinstance(ctx_list[2], dpm_xlParser.SubExprContext):
224
+ property_code, value = self.visitSubExpr(ctx_list[2])
225
+ return SubOp(operand=operand, property_code=property_code, value=value)
223
226
 
224
227
  def visitWhereExpr(self, ctx: dpm_xlParser.WhereExprContext):
225
228
  return self.visit(ctx.getChild(1))
@@ -241,6 +244,14 @@ class ASTVisitor(dpm_xlParserVisitor):
241
244
  new_name = self.visit(ctx_list[2])
242
245
  return RenameNode(old_name=old_name, new_name=new_name)
243
246
 
247
+ def visitSubExpr(self, ctx: dpm_xlParser.SubExprContext):
248
+ # SUB propertyCode EQ (literal | select | itemReference)
249
+ ctx_list = list(ctx.getChildren())
250
+ property_code = self.visit(ctx_list[1]) # propertyCode
251
+ # ctx_list[2] is EQ
252
+ value = self.visit(ctx_list[3]) # literal, select, or itemReference
253
+ return property_code, value
254
+
244
255
  def create_bin_op(self, ctx: dpm_xlParser.ExpressionContext):
245
256
  ctx_list = list(ctx.getChildren())
246
257
 
py_dpm/AST/ASTObjects.py CHANGED
@@ -234,9 +234,11 @@ class VarID(AST):
234
234
  # If data has been populated (after operand checking), use that
235
235
  if hasattr(self, 'data') and self.data is not None:
236
236
  # Convert DataFrame to list of dictionaries
237
+ data_records = None
237
238
  try:
238
239
  if hasattr(self.data, 'to_dict'):
239
- result['data'] = self.data.to_dict('records')
240
+ data_records = self.data.to_dict('records')
241
+ result['data'] = data_records
240
242
  else:
241
243
  result['data'] = self.data
242
244
  except Exception:
@@ -246,15 +248,43 @@ class VarID(AST):
246
248
  except Exception:
247
249
  result['data'] = str(self.data)
248
250
  result['table'] = self.table
249
- result['interval'] = self.interval
251
+
252
+ # Interval handling: determine from data_type in data records
253
+ # According to DPM-XL spec, interval only applies to Number types
254
+ interval_value = False # default
255
+
256
+ # First check if type attribute is set (from semantic validation)
257
+ if hasattr(self, 'type') and self.type is not None:
258
+ from py_dpm.DataTypes.ScalarTypes import Number
259
+ if isinstance(self.type, Number):
260
+ interval_value = self.interval if self.interval is not None else False
261
+ else:
262
+ interval_value = None
263
+ # Otherwise, infer from data_type in data records
264
+ elif data_records and len(data_records) > 0 and 'data_type' in data_records[0]:
265
+ data_type = data_records[0]['data_type']
266
+ # Map database data types to determine if numeric
267
+ # Numeric types: 'i' (integer), 'r' (decimal), 'm' (monetary), 'p' (percentage)
268
+ # Non-numeric types: 'b' (boolean), 's' (string), 'e' (enumeration/item), etc.
269
+ numeric_data_types = {'i', 'r', 'm', 'p', 'INT', 'DEC', 'MON', 'PER'}
270
+ if data_type in numeric_data_types:
271
+ interval_value = self.interval if self.interval is not None else False
272
+ else:
273
+ interval_value = None
274
+ else:
275
+ # No type info available - use explicit interval or default to False
276
+ interval_value = self.interval if self.interval is not None else False
277
+
278
+ result['interval'] = interval_value
250
279
  else:
251
280
  # Use original structure for unexpanded VarID
281
+ # When there's no data (no semantic validation), default interval to False
252
282
  result.update({
253
283
  'table': self.table,
254
284
  'rows': self.rows,
255
285
  'cols': self.cols,
256
286
  'sheets': self.sheets,
257
- 'interval': self.interval,
287
+ 'interval': self.interval if self.interval is not None else False,
258
288
  'default': self.default,
259
289
  'is_table_group': self.is_table_group
260
290
  })
@@ -710,6 +740,40 @@ class RenameNode(AST):
710
740
  }
711
741
 
712
742
 
743
+ class SubOp(AST):
744
+ """
745
+ AST Object for the Sub operator. Filters a recordset based on a property substitution.
746
+
747
+ :parameter operand: Recordset to be filtered
748
+ :parameter property_code: Property code to substitute
749
+ :parameter value: Value to substitute (can be a literal, select, or itemReference)
750
+ """
751
+
752
+ def __init__(self, operand, property_code, value):
753
+ super().__init__()
754
+ self.operand = operand
755
+ self.property_code = property_code
756
+ self.value = value
757
+
758
+ def __str__(self):
759
+ return "<AST(name='{name}', operand={operand}, property_code='{property_code}', value={value})>".format(
760
+ name=self.__class__.__name__,
761
+ operand=self.operand,
762
+ property_code=self.property_code,
763
+ value=self.value
764
+ )
765
+
766
+ __repr__ = __str__
767
+
768
+ def toJSON(self):
769
+ return {
770
+ 'class_name': self.__class__.__name__,
771
+ 'operand': self.operand,
772
+ 'property_code': self.property_code,
773
+ 'value': self.value
774
+ }
775
+
776
+
713
777
  class PropertyReference(AST):
714
778
 
715
779
  def __init__(self, code):
py_dpm/AST/ASTTemplate.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from py_dpm.AST.ASTObjects import AggregationOp, BinOp, ComplexNumericOp, CondExpr, Constant, Dimension, FilterOp, GetOp, GroupingClause, \
2
- OperationRef, ParExpr, PersistentAssignment, PreconditionItem, PropertyReference, RenameOp, Scalar, Set, Start, TemporaryAssignment, \
2
+ OperationRef, ParExpr, PersistentAssignment, PreconditionItem, PropertyReference, RenameOp, Scalar, Set, Start, SubOp, TemporaryAssignment, \
3
3
  TimeShiftOp, UnaryOp, VarID, VarRef, WhereClauseOp, WithExpression
4
4
  from py_dpm.AST.ASTVisitor import NodeVisitor
5
5
 
@@ -84,6 +84,10 @@ class ASTTemplate(NodeVisitor):
84
84
  def visit_GetOp(self, node: GetOp):
85
85
  self.visit(node.operand)
86
86
 
87
+ def visit_SubOp(self, node: SubOp):
88
+ self.visit(node.operand)
89
+ self.visit(node.value)
90
+
87
91
  def visit_PreconditionItem(self, node: PreconditionItem):
88
92
  pass
89
93
 
@@ -400,6 +400,18 @@ class MLGeneration(ASTTemplate):
400
400
  )
401
401
  self.session.add(new_operand_ref)
402
402
 
403
+ def visit_SubOp(self, node: SubOp):
404
+ setattr(node, "op", "sub")
405
+ operand_node = self.create_operation_node(node)
406
+ setattr(node.operand, "parent", operand_node)
407
+ setattr(node.operand, "argument", "operand")
408
+ self.visit(node.operand)
409
+
410
+ # Visit the value (can be literal, select, or itemReference)
411
+ setattr(node.value, "parent", operand_node)
412
+ setattr(node.value, "argument", "value")
413
+ self.visit(node.value)
414
+
403
415
  def visit_PreconditionItem(self, node: PreconditionItem):
404
416
  operand_node = self.create_operation_node(node, is_leaf=True)
405
417
  operand_reference = "PreconditionItem" # "$_{}".format(node.value)