diff --git a/__pycache__/dax_parser.cpython-313.pyc b/__pycache__/dax_parser.cpython-313.pyc index 9910f5d..d5ffba0 100644 Binary files a/__pycache__/dax_parser.cpython-313.pyc and b/__pycache__/dax_parser.cpython-313.pyc differ diff --git a/__pycache__/model_connector.cpython-313.pyc b/__pycache__/model_connector.cpython-313.pyc index 0fed43c..db4bc91 100644 Binary files a/__pycache__/model_connector.cpython-313.pyc and b/__pycache__/model_connector.cpython-313.pyc differ diff --git a/app.py b/app.py index 9e2e48e..c493458 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,7 @@ from flask import Flask, render_template, jsonify, request import json import os +import requests from model_connector import ModelConnector from dax_parser import DaxParser @@ -56,6 +57,41 @@ def get_call_graph(): except Exception as e: return jsonify({'success': False, 'message': str(e)}) +@app.route('/format_dax', methods=['POST']) +def format_dax(): + """Proxy for DAX formatter service.""" + try: + dax_code = request.json.get('daxCode') + if not dax_code: + return jsonify({'success': False, 'message': 'No DAX code provided'}) + + # 调用新的DAX格式化API + payload = { + 'dax': dax_code, + 'callerApp': 'https://www.daxformatter.com/', + 'callerVersion': '0.5.3', + 'listSeparator': ',', + 'decimalSeparator': '.', + 'maxLineLength': 0 + } + + response = requests.post('https://daxformatter.azurewebsites.net/api/DaxTokenFormat/', + json=payload) + + if response.status_code != 200: + return jsonify({'success': False, 'message': 'DAX formatting service error'}) + + # 解析返回的JSON + formatted_data = response.json() + + # 检查是否有错误 + if formatted_data.get('errors') and len(formatted_data['errors']) > 0: + return jsonify({'success': False, 'message': 'DAX formatting error: ' + str(formatted_data['errors'])}) + + return jsonify({'success': True, 'formattedTokens': formatted_data['formatted']}) + except Exception as e: + return jsonify({'success': False, 'message': str(e)}) + if __name__ == '__main__': app.run(debug=True) diff --git a/model_connector.py b/model_connector.py index 07393f5..1f8639e 100644 --- a/model_connector.py +++ b/model_connector.py @@ -1,5 +1,5 @@ from sys import path -path.append('C:\\Users\\ChenwuServise\\OneDrive - AZCollaboration\\Desktop\\GIT\\PBI-Measure-CallGraph\\MSNET') +path.append('C:\\Users\\Chenw\\Documents\\Trae\\PBI-Measure-CallGraph\\MSNET') import pyadomd import pandas as pd diff --git a/requirements.txt b/requirements.txt index c7a3cf0..33c6d25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ Flask==2.3.3 pyodbc==4.0.39 pandas==2.2.3 +requests==2.31.0 +beautifulsoup4==4.12.2 diff --git a/static/css/dax-highlight.css b/static/css/dax-highlight.css new file mode 100644 index 0000000..ec6015d --- /dev/null +++ b/static/css/dax-highlight.css @@ -0,0 +1,41 @@ +/* DAX语法高亮样式 */ +#measure-expression { + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + background-color: #f8f8f8; + padding: 10px; + border-radius: 4px; + overflow: auto; + line-height: 1.5; + white-space: pre-wrap; +} + +/* 关键字 */ +.dax-keyword { + color: #0000ff; + font-weight: bold; +} + +/* 函数 */ +.dax-function { + color: #795e26; +} + +/* 括号 */ +.dax-parenthesis { + color: #000000; +} + +/* 数字 */ +.dax-number { + color: #098658; +} + +/* 字符串 */ +.dax-string { + color: #a31515; +} + +/* 普通文本 */ +.dax-normal { + color: #000000; +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 2c5d5cd..e8cfe67 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,6 +5,8 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Power BI Measure Call Graph</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> + <link rel="stylesheet" href="{{ url_for('static', filename='css/dax-highlight.css') }}"> + <!-- D3.js for visualization --> <script src="https://d3js.org/d3.v7.min.js"></script> <script> @@ -13,31 +15,85 @@ const daxCode = measureExpression.textContent; try { - const formData = new FormData(); - formData.append('fx', daxCode); - formData.append('r', 'US'); - - const response = await fetch('https://www.daxformatter.com', { + // 调用后端API进行格式化 + const response = await fetch('/format_dax', { method: 'POST', - body: formData + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ daxCode: daxCode }) }); if (!response.ok) { throw new Error('格式化请求失败'); } - const result = await response.text(); - // 解析返回的HTML,提取格式化后的DAX代码 - const parser = new DOMParser(); - const doc = parser.parseFromString(result, 'text/html'); - const formattedDax = doc.querySelector('#formatted-dax').textContent; + const result = await response.json(); - measureExpression.textContent = formattedDax; + if (!result.success) { + throw new Error(result.message || '格式化失败'); + } + + // 处理新的格式化结果(带有标记类型的数组) + const formattedHtml = applyDaxSyntaxHighlighting(result.formattedTokens); + measureExpression.innerHTML = formattedHtml; } catch (error) { console.error('格式化DAX时出错:', error); alert('格式化DAX时出错,请检查网络连接或稍后重试'); } } + + // 将格式化的DAX标记转换为HTML并应用语法高亮 + function applyDaxSyntaxHighlighting(formattedTokens) { + if (!formattedTokens || formattedTokens.length === 0) { + return ''; + } + + let html = ''; + + // 处理每一行的标记 + formattedTokens.forEach(line => { + line.forEach(token => { + const text = token.string; + const type = token.type || 'Normal'; + + // 根据标记类型应用不同的CSS类 + switch(type) { + case 'Keyword': + html += `<span class="dax-keyword">${escapeHtml(text)}</span>`; + break; + case 'Function': + html += `<span class="dax-function">${escapeHtml(text)}</span>`; + break; + case 'Parenthesis': + html += `<span class="dax-parenthesis">${escapeHtml(text)}</span>`; + break; + case 'Number': + html += `<span class="dax-number">${escapeHtml(text)}</span>`; + break; + case 'String': + html += `<span class="dax-string">${escapeHtml(text)}</span>`; + break; + default: + html += escapeHtml(text); + } + }); + html += '\n'; // 添加换行 + }); + + return html; + } + + // 转义HTML特殊字符 + function escapeHtml(text) { + return text + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + </script> </head> <body>