import re class DaxParser: """Class to parse DAX expressions and build dependency graphs.""" def __init__(self): """Initialize the DAX parser.""" # Regular expression to match measure references in DAX # This is a simplified pattern and may need refinement for complex DAX self.measure_ref_pattern = r'\[([^\]]+)\]' def extract_measure_references(self, dax_expression, all_measure_names): """ Extract references to other measures from a DAX expression. Args: dax_expression (str): The DAX expression to parse all_measure_names (list): List of all measure names in the model Returns: list: Names of measures referenced in the expression """ if not dax_expression: return [] # Find all potential measure references (anything in square brackets) potential_refs = re.findall(self.measure_ref_pattern, dax_expression) # Filter to only include actual measure names # This helps avoid false positives like column references measure_refs = [ref for ref in potential_refs if ref in all_measure_names] return list(set(measure_refs)) # Remove duplicates def build_call_graph(self, measures): """ Build a call graph of measure dependencies. Args: measures (list): List of measure dictionaries with 'name' and 'expression' keys Returns: dict: A graph representation with nodes and links """ # Extract all measure names all_measure_names = [measure['name'] for measure in measures] # Create nodes for the graph nodes = [{'id': measure['name'], 'expression': measure['expression']} for measure in measures] # Create links (dependencies) for the graph links = [] for measure in measures: source = measure['name'] expression = measure['expression'] # Find references to other measures in this expression references = self.extract_measure_references(expression, all_measure_names) # Add links for each reference for target in references: links.append({ 'source': source, 'target': target }) return { 'nodes': nodes, 'links': links }