new file: Compare-PBI-Data.spec
modified: Evaluate_Result/admin_example_1.xlsx modified: Evaluate_Result/admin_example_2.xlsx new file: __pycache__/compareData.cpython-313.pyc new file: __pycache__/config.cpython-313.pyc new file: __pycache__/getDataFromAS.cpython-313.pyc new file: __pycache__/getQueries.cpython-313.pyc modified: app.py new file: build/Compare-PBI-Data/Analysis-00.toc new file: build/Compare-PBI-Data/Compare-PBI-Data.pkg new file: build/Compare-PBI-Data/EXE-00.toc new file: build/Compare-PBI-Data/PKG-00.toc new file: build/Compare-PBI-Data/PYZ-00.pyz new file: build/Compare-PBI-Data/PYZ-00.toc new file: build/Compare-PBI-Data/base_library.zip new file: build/Compare-PBI-Data/generated-2c83337f9fff21d32f0901febbdb8eec9b843eb92a216cf8171391083f1c5046.ico new file: build/Compare-PBI-Data/localpycs/pyimod01_archive.pyc new file: build/Compare-PBI-Data/localpycs/pyimod02_importers.pyc new file: build/Compare-PBI-Data/localpycs/pyimod03_ctypes.pyc new file: build/Compare-PBI-Data/localpycs/pyimod04_pywin32.pyc new file: build/Compare-PBI-Data/localpycs/struct.pyc new file: build/Compare-PBI-Data/warn-Compare-PBI-Data.txt new file: build/Compare-PBI-Data/xref-Compare-PBI-Data.html new file: build_exe.py modified: config.py new file: dist/Compare-PBI-Data.exe new file: dist/Evaluate_Result/admin_example_1.xlsx new file: dist/Evaluate_Result/admin_example_2.xlsx new file: dist/Export_Json/example.json modified: getDataFromAS.py modified: getQueries.py new file: icon.jpg modified: requirements.txt deleted: run.py modified: templates/index.html
This commit is contained in:
parent
c104fe51ed
commit
da0502c369
39
Compare-PBI-Data.spec
Normal file
39
Compare-PBI-Data.spec
Normal file
@ -0,0 +1,39 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['app.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('templates', 'templates'), ('MS.NET_Package', 'MS.NET_Package'), ('Export_Json', 'Export_Json'), ('Evaluate_Result', 'Evaluate_Result')],
|
||||
hiddenimports=['pythonnet', 'clr', 'pandas', 'openpyxl', 'flask', 'signal', 'werkzeug.serving'],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=['runtime_hook.py'],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='Compare-PBI-Data',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['icon.jpg'],
|
||||
)
|
Binary file not shown.
Binary file not shown.
BIN
__pycache__/compareData.cpython-313.pyc
Normal file
BIN
__pycache__/compareData.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/config.cpython-313.pyc
Normal file
BIN
__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/getDataFromAS.cpython-313.pyc
Normal file
BIN
__pycache__/getDataFromAS.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/getQueries.cpython-313.pyc
Normal file
BIN
__pycache__/getQueries.cpython-313.pyc
Normal file
Binary file not shown.
128
app.py
128
app.py
@ -1,14 +1,18 @@
|
||||
from flask import Flask, request, render_template, redirect, url_for
|
||||
from flask import Flask, request, render_template, redirect, url_for, Response
|
||||
import os
|
||||
import threading
|
||||
import config
|
||||
from getDataFromAS import ASDataFetcher
|
||||
from getQueries import getQueries_From_json
|
||||
from compareData import Comparator
|
||||
import time
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
result = ""
|
||||
result_buffer = []
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
@ -22,10 +26,38 @@ def update_config():
|
||||
setattr(config, key, value)
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@app.route('/stream')
|
||||
def stream():
|
||||
def generate():
|
||||
global result_buffer
|
||||
last_index = 0
|
||||
while True:
|
||||
if len(result_buffer) > last_index:
|
||||
# 发送新的消息,一次只发送一条,确保前端有足够时间处理
|
||||
import json
|
||||
json_data = json.dumps({"message": result_buffer[last_index]})
|
||||
yield f"data: {json_data}\n\n"
|
||||
last_index += 1
|
||||
time.sleep(0.2) # 每发送一条消息后短暂休眠,给前端处理时间
|
||||
else:
|
||||
time.sleep(0.1) # 如果没有新消息,短暂休眠,避免过度占用CPU
|
||||
|
||||
return Response(generate(), mimetype='text/event-stream')
|
||||
|
||||
@app.route('/exit', methods=['POST'])
|
||||
def exit_app():
|
||||
# 关闭应用程序的函数
|
||||
func = request.environ.get('werkzeug.server.shutdown')
|
||||
if func is None:
|
||||
raise RuntimeError('Not running with the Werkzeug Server')
|
||||
func()
|
||||
return 'Server shutting down...'
|
||||
|
||||
@app.route('/run', methods=['POST'])
|
||||
def run():
|
||||
global result
|
||||
global result, result_buffer
|
||||
result = ""
|
||||
result_buffer = []
|
||||
|
||||
# 拼接导出页面查询json文件夹路径
|
||||
json_folder_path = os.path.join(config.CURRENT_DIR_PATH, "Export_Json")
|
||||
@ -37,12 +69,29 @@ def run():
|
||||
if not os.path.exists(result_folder_path):
|
||||
os.makedirs(result_folder_path)
|
||||
|
||||
try:
|
||||
message = "---*********************************************************---\n"
|
||||
message += " 开始读取json\n"
|
||||
message += "---*********************************************************---\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
# 拼接导出页面查询json文件路径
|
||||
export_page_json_path_1 = os.path.join(json_folder_path, config.EXPORT_PAGE_JSON_NAME_1)
|
||||
export_page_json_path_2 = os.path.join(json_folder_path, config.EXPORT_PAGE_JSON_NAME_2)
|
||||
export_page_json_path_1 = os.path.join(json_folder_path, config.EXPORT_PAGE_JSON_NAME_1)
|
||||
export_page_json_path_2 = os.path.join(json_folder_path, config.EXPORT_PAGE_JSON_NAME_2)
|
||||
# 读取json文件中的查询
|
||||
queries_1 = getQueries_From_json(export_page_json_path_1)
|
||||
queries_2 = getQueries_From_json(export_page_json_path_2)
|
||||
queries_1 = getQueries_From_json(export_page_json_path_1)
|
||||
queries_2 = getQueries_From_json(export_page_json_path_2)
|
||||
|
||||
message = "\n---*********************************************************---\n"
|
||||
message += " 成功读取json\n"
|
||||
message += "---*********************************************************---\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
except Exception as e:
|
||||
message = f"错误提示: {e}\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
return redirect(url_for('index'))
|
||||
|
||||
# 创建 ASDataFetcher 实例
|
||||
fetcher_1 = ASDataFetcher(
|
||||
@ -78,16 +127,20 @@ def run():
|
||||
|
||||
# 定义线程任务
|
||||
def run_fetcher(fetcher, print_lock):
|
||||
global result
|
||||
global result, result_buffer
|
||||
try:
|
||||
fetcher.writeToExcel(print_lock)
|
||||
with print_lock:
|
||||
result += f"\n-------------------------\n{fetcher.json_name} 查询完成\n"
|
||||
result += "\n".join(fetcher.log_messages) + "\n"
|
||||
message = f"\n-------------------------\n{fetcher.json_name} 查询完成\n"
|
||||
message += "\n".join(fetcher.log_messages) + "\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
except Exception as e:
|
||||
with print_lock:
|
||||
result += f"错误提示: {e}\n"
|
||||
result += "\n".join(fetcher.log_messages) + "\n"
|
||||
message = f"错误提示: {e}\n"
|
||||
message += "\n".join(fetcher.log_messages) + "\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
return
|
||||
|
||||
# 创建锁
|
||||
@ -98,9 +151,11 @@ def run():
|
||||
thread_2 = threading.Thread(target=run_fetcher, args=(fetcher_2, print_lock))
|
||||
|
||||
try:
|
||||
result += "---*********************************************************---\n"
|
||||
result += " 开始查询\n"
|
||||
result += "---*********************************************************---\n"
|
||||
message = "---*********************************************************---\n"
|
||||
message += " 开始查询\n"
|
||||
message += "---*********************************************************---\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
|
||||
# 启动线程
|
||||
thread_1.start()
|
||||
@ -110,29 +165,48 @@ def run():
|
||||
thread_1.join()
|
||||
thread_2.join()
|
||||
|
||||
result += "\n---*********************************************************---\n"
|
||||
result += " 查询完成\n"
|
||||
result += "---*********************************************************---\n"
|
||||
message = "\n---*********************************************************---\n"
|
||||
message += " 查询完成\n"
|
||||
message += "---*********************************************************---\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
|
||||
except Exception as e:
|
||||
result += f"错误提示: {e}\n"
|
||||
message = f"错误提示: {e}\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
return redirect(url_for('index'))
|
||||
|
||||
try:
|
||||
result += "\n---------------------------------------------------------------\n"
|
||||
result += " 开始比较\n"
|
||||
result += "---------------------------------------------------------------\n"
|
||||
message = "\n---------------------------------------------------------------\n"
|
||||
message += " 开始比较\n"
|
||||
message += "---------------------------------------------------------------\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
|
||||
comparator = Comparator(fetcher_1.result_full_excel_name, fetcher_2.result_full_excel_name)
|
||||
comparator.compare_ExcelFiles()
|
||||
result += "\n".join(comparator.log_messages) + "\n"
|
||||
result += "\n---------------------------------------------------------------\n"
|
||||
result += " 比较完成\n"
|
||||
result += "---------------------------------------------------------------\n"
|
||||
message = "\n".join(comparator.log_messages) + "\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
|
||||
message = "\n---------------------------------------------------------------\n"
|
||||
message += " 比较完成\n"
|
||||
message += "---------------------------------------------------------------\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
except Exception as e:
|
||||
result += f"错误提示: {e}\n"
|
||||
message = f"错误提示: {e}\n"
|
||||
result += message
|
||||
result_buffer.append(message)
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
def open_browser():
|
||||
"""在启动 Flask 服务器后自动打开浏览器。"""
|
||||
webbrowser.open_new("http://127.0.0.1:5555/")
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
# 启动自动打开浏览器的线程
|
||||
threading.Timer(1, open_browser).start()
|
||||
app.run(debug=True, threaded=True,port=5555)
|
||||
|
14262
build/Compare-PBI-Data/Analysis-00.toc
Normal file
14262
build/Compare-PBI-Data/Analysis-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/Compare-PBI-Data/Compare-PBI-Data.pkg
Normal file
BIN
build/Compare-PBI-Data/Compare-PBI-Data.pkg
Normal file
Binary file not shown.
7798
build/Compare-PBI-Data/EXE-00.toc
Normal file
7798
build/Compare-PBI-Data/EXE-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
7776
build/Compare-PBI-Data/PKG-00.toc
Normal file
7776
build/Compare-PBI-Data/PKG-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/Compare-PBI-Data/PYZ-00.pyz
Normal file
BIN
build/Compare-PBI-Data/PYZ-00.pyz
Normal file
Binary file not shown.
6485
build/Compare-PBI-Data/PYZ-00.toc
Normal file
6485
build/Compare-PBI-Data/PYZ-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/Compare-PBI-Data/base_library.zip
Normal file
BIN
build/Compare-PBI-Data/base_library.zip
Normal file
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
BIN
build/Compare-PBI-Data/localpycs/pyimod01_archive.pyc
Normal file
BIN
build/Compare-PBI-Data/localpycs/pyimod01_archive.pyc
Normal file
Binary file not shown.
BIN
build/Compare-PBI-Data/localpycs/pyimod02_importers.pyc
Normal file
BIN
build/Compare-PBI-Data/localpycs/pyimod02_importers.pyc
Normal file
Binary file not shown.
BIN
build/Compare-PBI-Data/localpycs/pyimod03_ctypes.pyc
Normal file
BIN
build/Compare-PBI-Data/localpycs/pyimod03_ctypes.pyc
Normal file
Binary file not shown.
BIN
build/Compare-PBI-Data/localpycs/pyimod04_pywin32.pyc
Normal file
BIN
build/Compare-PBI-Data/localpycs/pyimod04_pywin32.pyc
Normal file
Binary file not shown.
BIN
build/Compare-PBI-Data/localpycs/struct.pyc
Normal file
BIN
build/Compare-PBI-Data/localpycs/struct.pyc
Normal file
Binary file not shown.
398
build/Compare-PBI-Data/warn-Compare-PBI-Data.txt
Normal file
398
build/Compare-PBI-Data/warn-Compare-PBI-Data.txt
Normal file
File diff suppressed because one or more lines are too long
81153
build/Compare-PBI-Data/xref-Compare-PBI-Data.html
Normal file
81153
build/Compare-PBI-Data/xref-Compare-PBI-Data.html
Normal file
File diff suppressed because it is too large
Load Diff
85
build_exe.py
Normal file
85
build_exe.py
Normal file
@ -0,0 +1,85 @@
|
||||
import PyInstaller.__main__
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# 获取当前脚本所在的目录路径
|
||||
CURRENT_DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 定义需要包含的文件夹
|
||||
folders_to_include = [
|
||||
'Export_Json',
|
||||
'Evaluate_Result',
|
||||
'MS.NET_Package',
|
||||
'templates'
|
||||
]
|
||||
|
||||
# 确保文件夹存在
|
||||
for folder in folders_to_include:
|
||||
folder_path = os.path.join(CURRENT_DIR_PATH, folder)
|
||||
if not os.path.exists(folder_path):
|
||||
os.makedirs(folder_path)
|
||||
|
||||
# 定义PyInstaller参数
|
||||
pyinstaller_args = [
|
||||
'app.py', # 主脚本
|
||||
'--name=Compare-PBI-Data', # 可执行文件名称
|
||||
'--onefile', # 打包成单个可执行文件
|
||||
'--windowed', # 不显示控制台窗口
|
||||
'--icon=icon.jpg', # 使用图标
|
||||
'--add-data=templates;templates', # 添加模板文件夹
|
||||
'--add-data=MS.NET_Package;MS.NET_Package', # 添加.NET包
|
||||
'--add-data=Export_Json;Export_Json', # 添加导出JSON文件夹
|
||||
'--add-data=Evaluate_Result;Evaluate_Result', # 添加评估结果文件夹
|
||||
'--hidden-import=pythonnet', # 添加隐藏导入
|
||||
'--hidden-import=clr', # 添加隐藏导入
|
||||
'--hidden-import=pandas', # 添加隐藏导入
|
||||
'--hidden-import=openpyxl', # 添加隐藏导入
|
||||
'--hidden-import=flask', # 添加隐藏导入
|
||||
'--hidden-import=signal', # 添加信号处理模块
|
||||
'--hidden-import=werkzeug.serving', # 添加werkzeug服务模块
|
||||
'--clean', # 清理临时文件
|
||||
'--runtime-hook=runtime_hook.py', # 添加运行时钩子
|
||||
]
|
||||
|
||||
# 创建运行时钩子文件
|
||||
with open('runtime_hook.py', 'w', encoding='utf-8') as f:
|
||||
f.write("""
|
||||
# 运行时钩子,用于确保应用程序可以正确关闭并创建必要的文件夹
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import config
|
||||
|
||||
# 确保正确处理SIGTERM信号
|
||||
def handle_sigterm(*args):
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGTERM, handle_sigterm)
|
||||
|
||||
# 获取应用程序所在的目录路径
|
||||
app_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 重新设置config模块中的CURRENT_DIR_PATH变量,确保使用正确的运行时路径
|
||||
config.CURRENT_DIR_PATH = app_dir
|
||||
|
||||
# 定义需要确保存在的文件夹
|
||||
folders_to_ensure = ['Export_Json', 'Evaluate_Result']
|
||||
|
||||
# 检查并创建必要的文件夹
|
||||
for folder in folders_to_ensure:
|
||||
folder_path = os.path.join(app_dir, folder)
|
||||
if not os.path.exists(folder_path):
|
||||
os.makedirs(folder_path)
|
||||
print(f'已创建文件夹: {folder_path}')
|
||||
""")
|
||||
|
||||
# 运行PyInstaller
|
||||
PyInstaller.__main__.run(pyinstaller_args)
|
||||
|
||||
# 删除临时的运行时钩子文件
|
||||
if os.path.exists('runtime_hook.py'):
|
||||
os.remove('runtime_hook.py')
|
||||
|
||||
print("打包完成!可执行文件位于 dist 文件夹中。")
|
||||
print("注意:运行可执行文件后,用户可以在Export_Json文件夹中放入文件,程序会将结果输出到Evaluate_Result文件夹。")
|
||||
print("程序可以通过界面上的'退出程序'按钮正常关闭。")
|
@ -2,6 +2,7 @@ import os
|
||||
|
||||
# 获取当前文件所在的文件夹路径
|
||||
CURRENT_DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
CURRENT_DIR_PATH_NET = os.path.dirname(os.path.abspath(__file__))
|
||||
# MS.NET包的路径
|
||||
NET_FOLDER = "MS.NET_Package"
|
||||
|
||||
@ -47,11 +48,11 @@ CUSTOMER_DATA_2 = "2"
|
||||
# --------------------------------------------------------------
|
||||
|
||||
desc_WORKSPACE = "Analysis Services的连接信息,如果是powerbi,则连接字符串中包含catalog"
|
||||
desc_USERNAME = "用户名"
|
||||
desc_USERNAME = "用户名 (如果是powerbi,则为邮箱,如果是AS,则为用户名或不填写)"
|
||||
desc_PASSWORD = "密码"
|
||||
desc_CATALOG = "如果是powerbi,则catalog为语义模型的名称,默认为None"
|
||||
desc_CATALOG = "如果是powerbi,则必填且catalog为语义模型的名称,默认为None"
|
||||
desc_EXPORT_PAGE_JSON_NAME = "导出页面查询的json文件的名称"
|
||||
desc_IS_ADMIN = "是否是管理员方式运行,如果是,则为True,否则为False"
|
||||
desc_ROLE = "角色名称"
|
||||
desc_ROLE = "角色名称。如果ADMIN为True,则不填,否则必填"
|
||||
desc_EFFECTIVE_USERNAME = "username()获取字段"
|
||||
desc_CUSTOMER_DATA = "customerdata()获取字段"
|
BIN
dist/Compare-PBI-Data.exe
vendored
Normal file
BIN
dist/Compare-PBI-Data.exe
vendored
Normal file
Binary file not shown.
BIN
dist/Evaluate_Result/admin_example_1.xlsx
vendored
Normal file
BIN
dist/Evaluate_Result/admin_example_1.xlsx
vendored
Normal file
Binary file not shown.
BIN
dist/Evaluate_Result/admin_example_2.xlsx
vendored
Normal file
BIN
dist/Evaluate_Result/admin_example_2.xlsx
vendored
Normal file
Binary file not shown.
1
dist/Export_Json/example.json
vendored
Normal file
1
dist/Export_Json/example.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":"1.1.0","events":[{"name":"User Action","component":"Report Canvas","start":"2024-12-21T17:14:40.987Z","id":"515c279a49002a814996","metrics":{"sourceLabel":"UserAction_StartedMonitoring"}},{"name":"User Action","component":"Report Canvas","start":"2024-12-21T17:14:42.623Z","id":"79b174c0d02ca69ada00","metrics":{"sourceLabel":"UserAction_Refresh"}},{"name":"Visual Container Lifecycle","start":"2024-12-21T17:14:42.629Z","end":"2024-12-21T17:14:42.682Z","id":"24c30acf43d0ab30dd40-b004a31d5ccd6a4a5385","metrics":{"status":1,"visualTitle":"Table","visualId":"24c30acf43d0ab30dd40","visualType":"tableEx","initialLoad":false}},{"name":"Resolve Parameters","start":"2024-12-21T17:14:42.630Z","end":"2024-12-21T17:14:42.631Z","id":"865f086f-a553-4057-9963-f386c6ea310f","parentId":"24c30acf43d0ab30dd40-b004a31d5ccd6a4a5385"},{"name":"Query","start":"2024-12-21T17:14:42.631Z","end":"2024-12-21T17:14:42.662Z","id":"f374c5d4-aeae-49e4-a63f-a086480fbad1","parentId":"24c30acf43d0ab30dd40-b004a31d5ccd6a4a5385"},{"name":"Render","start":"2024-12-21T17:14:42.661Z","end":"2024-12-21T17:14:42.682Z","id":"bf797551-b336-43c1-8a29-eda4f83f224e","parentId":"24c30acf43d0ab30dd40-b004a31d5ccd6a4a5385"},{"name":"Data View Transform","start":"2024-12-21T17:14:42.670Z","end":"2024-12-21T17:14:42.673Z","id":"2b79f57f-9353-48e9-afe8-0c1f446f8636","parentId":"bf797551-b336-43c1-8a29-eda4f83f224e"},{"name":"Visual Update Async","start":"2024-12-21T17:14:42.673Z","end":"2024-12-21T17:14:42.682Z","id":"ca7f9db7-b9c5-4739-aced-6a8a5560f0a8","parentId":"bf797551-b336-43c1-8a29-eda4f83f224e"},{"name":"Visual Update","start":"2024-12-21T17:14:42.673Z","end":"2024-12-21T17:14:42.673Z","id":"29d7c7a6-ef0d-416a-973f-cf4ddacedc8a","parentId":"bf797551-b336-43c1-8a29-eda4f83f224e"},{"name":"Execute Semantic Query","component":"DSE","start":"2024-12-21T17:14:42.649Z","end":"2024-12-21T17:14:42.656Z","id":"47aeb947-80d7-4096-9f2d-3b16c8b5b5e9","parentId":"f374c5d4-aeae-49e4-a63f-a086480fbad1"},{"name":"Query Generation","start":"2024-12-21T17:14:42.633Z","end":"2024-12-21T17:14:42.634Z","id":"4fa46d04-aa1d-46ea-933b-49002a24c309","parentId":"f374c5d4-aeae-49e4-a63f-a086480fbad1"},{"name":"Query Pending","start":"2024-12-21T17:14:42.648Z","end":"2024-12-21T17:14:42.649Z","id":"178f1281-20eb-4c78-a3d1-bdc9f6735a7c","parentId":"f374c5d4-aeae-49e4-a63f-a086480fbad1"},{"name":"Parse Query Result","start":"2024-12-21T17:14:42.658Z","end":"2024-12-21T17:14:42.660Z","id":"33e79f0c-bc13-4951-a18e-6fdbd767e4f8","parentId":"f374c5d4-aeae-49e4-a63f-a086480fbad1"},{"name":"Execute DAX Query","component":"DSE","start":"2024-12-21T17:14:42.651Z","end":"2024-12-21T17:14:42.655Z","id":"e458b6a8-0b2b-474a-a8f1-294856878497","parentId":"47aeb947-80d7-4096-9f2d-3b16c8b5b5e9","metrics":{"QueryText":"DEFINE\r\n\tVAR __DS0Core = \r\n\t\tSUMMARIZECOLUMNS(\r\n\t\t\tROLLUPADDISSUBTOTAL('financials'[Country], \"IsGrandTotalRowTotal\"),\r\n\t\t\t\"Sumv_Sales\", CALCULATE(SUM('financials'[ Sales]))\r\n\t\t)\r\n\r\n\tVAR __DS0PrimaryWindowed = \r\n\t\tTOPN(502, __DS0Core, [IsGrandTotalRowTotal], 0, 'financials'[Country], 1)\r\n\r\nEVALUATE\r\n\t__DS0PrimaryWindowed\r\n\r\nORDER BY\r\n\t[IsGrandTotalRowTotal] DESC, 'financials'[Country]","RowCount":6}},{"name":"Execute Query","component":"AS","start":"2024-12-21T17:14:42.653Z","end":"2024-12-21T17:14:42.657Z","id":"3BC9FA84-D491-49ED-B2A1-7DC0D4F32A4C","parentId":"e458b6a8-0b2b-474a-a8f1-294856878497"},{"name":"Serialize Rowset","component":"AS","start":"2024-12-21T17:14:42.657Z","end":"2024-12-21T17:14:42.657Z","id":"57BE528D-04A5-4696-A0AF-3327CD0CE702","parentId":"3BC9FA84-D491-49ED-B2A1-7DC0D4F32A4C"}],"sessionId":"14d9da6f-0492-428d-bcff-ece9e2ac26f6"}
|
@ -2,9 +2,9 @@ import re
|
||||
import pandas as pd
|
||||
import clr
|
||||
import os
|
||||
from config import CURRENT_DIR_PATH, NET_FOLDER
|
||||
from config import CURRENT_DIR_PATH_NET, NET_FOLDER
|
||||
|
||||
folder = os.path.join(CURRENT_DIR_PATH, NET_FOLDER)
|
||||
folder = os.path.join(CURRENT_DIR_PATH_NET, NET_FOLDER)
|
||||
clr.AddReference(
|
||||
folder
|
||||
+ r"\Microsoft.AnalysisServices.AdomdClient\v4.0_15.0.0.0__89845dcd8080cc91\Microsoft.AnalysisServices.AdomdClient.DLL"
|
||||
|
@ -4,15 +4,19 @@ import pandas as pd
|
||||
|
||||
def getQueries_From_json(json_name):
|
||||
# 打开并读取JSON文件
|
||||
with open(json_name + ".json", "r", encoding="utf-8-sig") as file:
|
||||
data = json.load(file)
|
||||
# 从JSON数据中提取所有名为"Execute DAX Query"的事件中的QueryText
|
||||
queries = [
|
||||
event["metrics"]["QueryText"]
|
||||
for event in data["events"]
|
||||
if event["name"] == "Execute DAX Query"
|
||||
]
|
||||
return queries
|
||||
try:
|
||||
with open(json_name + ".json", "r", encoding="utf-8-sig") as file:
|
||||
data = json.load(file)
|
||||
# 从JSON数据中提取所有名为"Execute DAX Query"的事件中的QueryText
|
||||
queries = [
|
||||
event["metrics"]["QueryText"]
|
||||
for event in data["events"]
|
||||
if event["name"] == "Execute DAX Query"
|
||||
]
|
||||
return queries
|
||||
except Exception as e:
|
||||
print(f"读取JSON文件错误: {e},文件路径: {json_name}.json")
|
||||
raise
|
||||
|
||||
|
||||
def getQueries_From_excel(excel_name):
|
||||
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
112
run.py
112
run.py
@ -1,112 +0,0 @@
|
||||
import os
|
||||
import threading
|
||||
from compareData import Comparator
|
||||
from getDataFromAS import ASDataFetcher
|
||||
from getQueries import getQueries_From_json
|
||||
import config
|
||||
|
||||
# 拼接导出页面查询json文件夹路径
|
||||
json_folder_path = os.path.join(config.CURRENT_DIR_PATH, "Export_Json")
|
||||
# 拼接查询结果excel文件夹路径
|
||||
result_folder_path = os.path.join(config.CURRENT_DIR_PATH, "Evaluate_Result")
|
||||
# 检查文件夹是否存在,如果不存在则创建它
|
||||
if not os.path.exists(json_folder_path):
|
||||
os.makedirs(json_folder_path)
|
||||
if not os.path.exists(result_folder_path):
|
||||
os.makedirs(result_folder_path)
|
||||
|
||||
# 拼接导出页面查询json文件路径
|
||||
export_page_json_path_1 = os.path.join(json_folder_path, config.EXPORT_PAGE_JSON_NAME_1)
|
||||
export_page_json_path_2 = os.path.join(json_folder_path, config.EXPORT_PAGE_JSON_NAME_2)
|
||||
# 读取json文件中的查询
|
||||
queries_1 = getQueries_From_json(export_page_json_path_1)
|
||||
queries_2 = getQueries_From_json(export_page_json_path_2)
|
||||
|
||||
# 创建 ASDataFetcher 实例
|
||||
fetcher_1 = ASDataFetcher(
|
||||
workspace=config.WORKSPACE_1,
|
||||
username=config.USERNAME_1,
|
||||
password=config.PASSWORD_1,
|
||||
queries=queries_1,
|
||||
json_name=config.EXPORT_PAGE_JSON_NAME_1,
|
||||
result_folder_path=result_folder_path,
|
||||
isAdmin=config.IS_ADMIN_1,
|
||||
catalog=config.CATALOG_1,
|
||||
effective_username=config.EFFECTIVE_USERNAME_1,
|
||||
customdata=config.CUSTOMER_DATA_1,
|
||||
role=config.ROLE_1,
|
||||
model_number=1,
|
||||
)
|
||||
|
||||
# 创建 ASDataFetcher 实例
|
||||
fetcher_2 = ASDataFetcher(
|
||||
workspace=config.WORKSPACE_2,
|
||||
username=config.USERNAME_2,
|
||||
password=config.PASSWORD_2,
|
||||
queries=queries_2,
|
||||
json_name=config.EXPORT_PAGE_JSON_NAME_2,
|
||||
result_folder_path=result_folder_path,
|
||||
isAdmin=config.IS_ADMIN_2,
|
||||
catalog=config.CATALOG_2,
|
||||
effective_username=config.EFFECTIVE_USERNAME_2,
|
||||
customdata=config.CUSTOMER_DATA_2,
|
||||
role=config.ROLE_2,
|
||||
model_number=2,
|
||||
)
|
||||
|
||||
# 定义线程任务
|
||||
def run_fetcher(fetcher, print_lock):
|
||||
fetcher.writeToExcel(print_lock)
|
||||
|
||||
# 创建锁
|
||||
print_lock = threading.Lock()
|
||||
|
||||
# 创建线程
|
||||
thread_1 = threading.Thread(target=run_fetcher, args=(fetcher_1, print_lock))
|
||||
thread_2 = threading.Thread(target=run_fetcher, args=(fetcher_2, print_lock))
|
||||
|
||||
try:
|
||||
print(
|
||||
f"""
|
||||
---*********************************************************---
|
||||
开始查询
|
||||
"""
|
||||
)
|
||||
# 启动线程
|
||||
thread_1.start()
|
||||
thread_2.start()
|
||||
|
||||
# 等待线程完成
|
||||
thread_1.join()
|
||||
thread_2.join()
|
||||
|
||||
print(
|
||||
f"""
|
||||
查询完成
|
||||
---*********************************************************---
|
||||
"""
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"错误提示: {e}")
|
||||
|
||||
try:
|
||||
print(
|
||||
"""
|
||||
---------------------------------------------------------------
|
||||
开始比较
|
||||
---------------------------------------------------------------
|
||||
"""
|
||||
)
|
||||
comparator = Comparator(
|
||||
fetcher_1.result_full_excel_name, fetcher_2.result_full_excel_name
|
||||
)
|
||||
comparator.compare_ExcelFiles()
|
||||
print(
|
||||
"""
|
||||
----------------------------------------------------------------
|
||||
比较完成
|
||||
----------------------------------------------------------------
|
||||
"""
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"错误提示: {e}")
|
@ -3,9 +3,11 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>数据对比</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="shortcut icon" href="https:/chenwuzhu.cn/upload/IMG_0495.JPG">
|
||||
<style>
|
||||
.container {
|
||||
height: 100vh;
|
||||
height: 98vh;
|
||||
display: flex;
|
||||
}
|
||||
.left {
|
||||
@ -16,10 +18,13 @@
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
margin-right: 10px;
|
||||
}
|
||||
.config {
|
||||
flex: 1; /* 上半部分 */
|
||||
display: inline-table;
|
||||
display: unset;
|
||||
flex-direction: row;
|
||||
}
|
||||
.config-content {
|
||||
@ -40,6 +45,9 @@
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
.description {
|
||||
color: gray;
|
||||
@ -54,6 +62,10 @@
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
background-color: white;
|
||||
padding: 2px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.config-keys {
|
||||
display: grid;
|
||||
@ -68,6 +80,55 @@
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
width: 98%;
|
||||
}
|
||||
input[type="text"] {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
h1, h2 {
|
||||
color: #333;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 8px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
.social-icons {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
.social-icons a {
|
||||
color: #333;
|
||||
font-size: 24px;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
.social-icons a:hover {
|
||||
color: #4CAF50;
|
||||
}
|
||||
pre {
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@ -97,15 +158,110 @@
|
||||
{% endfor %}
|
||||
<div class="buttons">
|
||||
<button type="submit">保存配置</button>
|
||||
<button type="submit" formaction="{{ url_for('run') }}">运行</button>
|
||||
<button type="button" id="runButton">运行</button>
|
||||
<button type="button" id="exitButton" style="background-color: #f44336;">退出程序</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result">
|
||||
<div class="social-icons">
|
||||
<a href="https://chenwuzhu.cn" target="_blank" title="微信"><i class="fab fa-weixin"></i></a>
|
||||
<a href="https://git.chenwuzhu.cn/chenwu/Compare-PBI-Data" target="_blank" title="Git"><i class="fab fa-github"></i></a>
|
||||
</div>
|
||||
<h1>运行结果</h1>
|
||||
<pre>{{ result }}</pre>
|
||||
<pre id="resultOutput">{{ result }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('runButton').addEventListener('click', function() {
|
||||
// 清空结果区域
|
||||
document.getElementById('resultOutput').textContent = '';
|
||||
|
||||
// 获取表单数据
|
||||
const form = document.querySelector('form');
|
||||
const formData = new FormData(form);
|
||||
|
||||
// 创建EventSource连接
|
||||
const eventSource = new EventSource('/stream');
|
||||
// 监听消息事件
|
||||
eventSource.onmessage = function(event) {
|
||||
const resultOutput = document.getElementById('resultOutput');
|
||||
// 解析JSON数据并显示消息内容
|
||||
try {
|
||||
const jsonData = JSON.parse(event.data);
|
||||
if (jsonData.message) {
|
||||
resultOutput.textContent += jsonData.message + '\n\n';
|
||||
resultOutput.scrollTop = resultOutput.scrollHeight; // 自动滚动到底部
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析JSON数据失败:', error);
|
||||
// 如果解析失败,则直接显示原始数据
|
||||
resultOutput.textContent += event.data + '\n\n';
|
||||
resultOutput.scrollTop = resultOutput.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听错误事件
|
||||
eventSource.onerror = function() {
|
||||
eventSource.close();
|
||||
};
|
||||
|
||||
// 发送运行请求
|
||||
fetch('{{ url_for("run") }}', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).catch(function(error) {
|
||||
console.error('Error:', error);
|
||||
eventSource.close();
|
||||
});
|
||||
|
||||
// 添加一个特殊的消息监听器,用于检测处理完成
|
||||
const checkCompletion = function() {
|
||||
try {
|
||||
// 尝试解析JSON数据
|
||||
const jsonData = JSON.parse(event.data);
|
||||
// 检测到结束分隔符时关闭连接
|
||||
if (jsonData.message && jsonData.message.includes('---*********************************************************---')) {
|
||||
setTimeout(function() {
|
||||
eventSource.close();
|
||||
}, 500);
|
||||
} else {
|
||||
setTimeout(checkCompletion, 500);
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果解析失败,检查原始数据
|
||||
if (event.data.includes('---*********************************************************---')) {
|
||||
setTimeout(function() {
|
||||
eventSource.close();
|
||||
}, 500);
|
||||
} else {
|
||||
setTimeout(checkCompletion, 500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(checkCompletion, 1000);
|
||||
});
|
||||
|
||||
// 添加退出按钮的事件监听器
|
||||
document.getElementById('exitButton').addEventListener('click', function() {
|
||||
if (confirm('确定要退出程序吗?')) {
|
||||
fetch('{{ url_for("exit_app") }}', {
|
||||
method: 'POST'
|
||||
}).then(function(response) {
|
||||
window.close();
|
||||
// 如果window.close()不起作用(在某些浏览器中可能被阻止),显示一条消息
|
||||
setTimeout(function() {
|
||||
alert('程序已关闭,请手动关闭此窗口。');
|
||||
}, 1000);
|
||||
}).catch(function(error) {
|
||||
console.error('Error:', error);
|
||||
alert('关闭程序时出错,请手动关闭窗口。');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user