From cf784002ea8fc1788051dee8c2bdc9bbb2ce5428 Mon Sep 17 00:00:00 2001 From: wangzhiming <1115712903@qq.com> Date: Mon, 12 May 2025 13:50:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- API.py | 160 ++++++++++++++++++++++++++++++++++++++++++++---------- server.py | 96 +++++++++++++++++++++++++------- 2 files changed, 207 insertions(+), 49 deletions(-) diff --git a/API.py b/API.py index 3992078..3df876d 100644 --- a/API.py +++ b/API.py @@ -6,11 +6,32 @@ from datetime import timedelta import pymysql from sqlalchemy import create_engine from dateutil.relativedelta import relativedelta +import logging +import time +import os +total_start = time.time() +# 在文件开头添加日志配置 +def setup_logging(): + log_dir = os.path.join(os.path.dirname(__file__), 'logs') + os.makedirs(log_dir, exist_ok=True) + + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(os.path.join(log_dir, 'api.log')), + logging.StreamHandler() + ] + ) + +setup_logging() +logger = logging.getLogger(__name__) # 在API.py开头添加 import sys + # 替换原来的get_user_date函数 def get_user_date(): if len(sys.argv) > 1: @@ -43,7 +64,7 @@ engine = create_engine(f'mysql+pymysql://{username}:{password}@{host}:{port}/{da configPath = r'C:\AI trading\config\Rey\test_reinforcement' matlabPath = r'D:\Dropbox\Matlab\Rey\MATLAB' -# 修改为你的 Excel 文件路径 +# 修改为 Excel 文件路径 tempPath = r'C:\Users\24011\Documents\WeChat Files\wxid_k4ep58f81rx421\FileStorage\File\2025-04\tuigua_huangjijingshi - 副本\tuigua_huangjijingshi - 副本\皇极经世.xlsx' # ========== 加载 64 卦映射表 ========== @@ -58,14 +79,14 @@ Map64GuaOmit = Map64Gua[Map64Gua['change_omit'] == 0] # ========== 从数据库加载 24 节气数据 ========== def load_solar_terms(conn_params): """通过 SQLAlchemy 连接读取 solar_terms 表""" - sqlquery = 'SELECT * FROM solar_terms' + sqlquery = "SELECT * FROM solar_terms WHERE As_Of_Date > '1744-01-01'" df = pd.read_sql(sqlquery, engine) # 尝试将 As_Of_Date 转为 datetime,如果失败就变成 NaT df['As_Of_Date'] = pd.to_datetime(df['As_Of_Date'], errors='coerce') # 过滤掉早于 1900-01-01 或转换失败的日期 - df = df[df['As_Of_Date'] >= pd.Timestamp('1900-01-01')] + df = df[df['As_Of_Date'] >= pd.Timestamp('1780-01-01')] df = df.dropna(subset=['As_Of_Date']) # df['As_Of_Date'] = df['As_Of_Date'] df = df.sort_values('As_Of_Date') @@ -102,14 +123,51 @@ def get_user_date(): print("日期格式不正确,请使用YYYY-MM-DD格式", file=sys.stderr) sys.exit(1) # 加载数据并标记重要节气 +load_start = time.time() solar_terms = load_solar_terms(engine) +logger.info(f"节气加载完成,耗时: {time.time() - load_start:.2f}秒") important_terms = ['冬至', '雨水', '谷雨', '夏至', '处暑', '霜降'] solar_terms['isImportant'] = solar_terms['Solar_Terms'].isin(important_terms).astype(int) Map24Jieqi = solar_terms.copy() # ========== 读取CSV文件并处理每个日期的卦象 ========== +read_start = time.time() csv_path = r'C:\Users\24011\Documents\WeChat Files\wxid_k4ep58f81rx421\FileStorage\File\2025-04\tuigua_huangjijingshi - 副本\tuigua_huangjijingshi - 副本\python1\2020 - 2030年每天卦.csv' table = pd.read_csv(csv_path) +logger.info(f"读取csv文件完成,耗时: {time.time() - read_start:.2f}秒") +try: + piwen_path = r'C:\Users\24011\Documents\WeChat Files\wxid_k4ep58f81rx421\FileStorage\File\2025-04\tuigua_huangjijingshi - 副本\tuigua_huangjijingshi - 副本\python1\批文.xlsx' + piwen_data = pd.read_excel(piwen_path) + + # 打印列名用于调试 + print("批文文件列名:", piwen_data.columns.tolist(), file=sys.stderr) + + # 检查必要的列是否存在 + if not all(col in piwen_data.columns for col in ['卦名', '命卦批文', '八个字基本盘']): + print("批文文件缺少必要列,请检查Excel文件列名", file=sys.stderr) + piwen_data = pd.DataFrame(columns=['卦名', '命卦批文', '八个字基本盘']) +except Exception as e: + print(f"读取批文文件失败: {str(e)}", file=sys.stderr) + piwen_data = pd.DataFrame(columns=['卦名', '命卦批文', '八个字基本盘']) + +def get_piwen_info(trigram_name): + """根据卦名获取批文和基本盘信息""" + if not isinstance(trigram_name, str): + trigram_name = str(trigram_name) + + try: + match = piwen_data[piwen_data['卦名'] == trigram_name] + if not match.empty: + return { + 'piwen': match.iloc[0]['命卦批文'], + 'basic_info': match.iloc[0]['八个字基本盘'] + } + else: + print(f"未找到卦名 {trigram_name} 的批文信息", file=sys.stderr) + return {'piwen': '暂无批文信息', 'basic_info': '暂无基本盘信息'} + except Exception as e: + print(f"获取批文信息出错: {str(e)}", file=sys.stderr) + return {'piwen': '暂无批文信息', 'basic_info': '暂无基本盘信息'} # 获取用户输入 kDate = get_user_date() #print("\n计算日期:", kDate.strftime('%Y-%m-%d')) @@ -125,16 +183,23 @@ import json # 导入 JSON 库 if __name__ == "__main__": print("=== 调试开始 ===", file=sys.stderr) # 打印到 stderr 不会干扰 stdout 的 JSON - kDate = get_user_date() try: + input_start = time.time() + kDate = get_user_date() + logger.info(f"获取用户输入完成,耗时: {time.time() - input_start:.2f}秒") + # 计算卦象 + calc_start = time.time() Gua1Hour, Gua1Day, Gua1Month, Gua1Year, GuaLuck = guaCalc_huangjijingshi( Map64Gua, Map24Jieqi, kDate ) + logger.info(f"卦象计算完成,耗时: {time.time() - calc_start:.2f}秒") # # 计算10年前(当前日期减10年) # Yearpre10 = kDate.replace(year=kDate.year - 10) # # 计算10年后(当前日期加10年) # Yearpast10 = kDate.replace(year=kDate.year + 10) # 或者使用relativedelta(更精确处理闰年等情况) + # 计算年份卦象 + year_start = time.time() Yearpre10 = datetime(2010, 1, 1) Yearpast10 = datetime(2030, 1, 1,) @@ -154,9 +219,12 @@ if __name__ == "__main__": }) yearGuaMap = pd.DataFrame(year_gua_list) + logger.info(f"年份卦象计算完成,耗时: {time.time() - year_start:.2f}秒") #========== 计算年份吉凶 ========== + # 计算年份吉凶 + luck_start = time.time() LuckYear = luckCalc_huangjijingshi(Map64Gua, yearGuaMap, GuaLuck) - + logger.info(f"吉凶计算完成,耗时: {time.time() - luck_start:.2f}秒") # # 检查 Gua1Day 的类型并正确处理 # if isinstance(Gua1Day, pd.DataFrame): # # 如果是 DataFrame,提取第一行 @@ -171,26 +239,34 @@ if __name__ == "__main__": # raise ValueError("Gua1Day 的类型不支持,必须是 DataFrame、Series 或 dict") def format_gua_data(gua_data): """通用格式化卦象数据的函数""" - if isinstance(gua_data, (pd.DataFrame, pd.Series)): - data = gua_data.iloc[0] if isinstance(gua_data, pd.DataFrame) else gua_data - return { - 'id': int(data.get('id', 0)), - 'trigram': str(data.get('trigram', '')), - 'yaoAll': str(data.get('yaoAll', '')), - 'yao1': int(data.get('yao1', 0)), - 'yao2': int(data.get('yao2', 0)), - 'yao3': int(data.get('yao3', 0)), - 'yao4': int(data.get('yao4', 0)), - 'yao5': int(data.get('yao5', 0)), - 'yao6': int(data.get('yao6', 0)), - 'value_2binary': int(data.get('value_2binary', 0)), - 'change_omit': int(data.get('change_omit', 0)), - 'type': str(data.get('Type', 'unknown')) - } + if isinstance(gua_data, pd.DataFrame): + # DataFrame类型 - 取第一行转为字典 + data = gua_data.iloc[0].to_dict() + elif isinstance(gua_data, pd.Series): + # Series类型 - 直接转为字典 + data = gua_data.to_dict() elif isinstance(gua_data, dict): - return gua_data + # 已经是字典类型 + data = gua_data else: + # 未知类型返回空字典 return {} + + # 统一处理字典数据 + return { + 'id': int(data.get('id', 0)), + 'trigram': str(data.get('trigram', '')), + 'yaoAll': str(data.get('yaoAll', '')), + 'yao1': int(data.get('yao1', 0)), + 'yao2': int(data.get('yao2', 0)), + 'yao3': int(data.get('yao3', 0)), + 'yao4': int(data.get('yao4', 0)), + 'yao5': int(data.get('yao5', 0)), + 'yao6': int(data.get('yao6', 0)), + 'value_2binary': int(data.get('value_2binary', 0)), + 'change_omit': int(data.get('change_omit', 0)), + 'type': str(data.get('Type', 'unknown')) + } # 构建结果字典,确保所有值是 Python 原生类型 # result = { @@ -210,29 +286,53 @@ if __name__ == "__main__": # 'type': str(day_data.get('Type', 'day')) # 如果没有值,使用默认值 'day' # } # } - # 构建完整结果 + # 构建完整结果 + build_start = time.time() result = { 'date': kDate.strftime('%Y-%m-%d'), - 'year_gua': format_gua_data(Gua1Year), - 'month_gua': format_gua_data(Gua1Month), - 'day_gua': format_gua_data(Gua1Day), - 'hour_gua': format_gua_data(Gua1Hour), - 'luck_gua': format_gua_data(GuaLuck), + 'year_gua': { + **format_gua_data(Gua1Year), + **get_piwen_info(format_gua_data(Gua1Year).get('trigram', '')) + }, + 'month_gua': { + **format_gua_data(Gua1Month), + **get_piwen_info(format_gua_data(Gua1Month).get('trigram', '')) + }, + 'day_gua': { + **format_gua_data(Gua1Day), + **get_piwen_info(format_gua_data(Gua1Day).get('trigram', '')) + }, + 'hour_gua': { + **format_gua_data(Gua1Hour), + **get_piwen_info(format_gua_data(Gua1Hour).get('trigram', '')) + }, + 'luck_gua': { + **format_gua_data(GuaLuck), + **get_piwen_info(format_gua_data(GuaLuck).get('trigram', '')) + }, 'luck_years': [ { 'year': row['Year'], 'trigram': row['trigram'], - 'yaoAll': row['yaoAll'] + 'yaoAll': row['yaoAll'], + **get_piwen_info(row['trigram']) } for _, row in LuckYear.iterrows() ] if isinstance(LuckYear, pd.DataFrame) else [] } + logger.info(f"结果构建完成,耗时: {time.time() - build_start:.2f}秒") + # 在打印 JSON 前检查内容 print("=== 要输出的 JSON 内容 ===", file=sys.stderr) print(result, file=sys.stderr) # 输出 JSON 格式的结果 # print(json.dumps(result, ensure_ascii=False, indent=4)) # 添加 indent 参数更好查看格式 + # 输出结果 + output_start = time.time() print(json.dumps(result, ensure_ascii=False, indent=2)) - + logger.info(f"结果输出完成,耗时: {time.time() - output_start:.2f}秒") + + total_time = time.time() - total_start + logger.info(f"=== API 执行完成,总耗时: {total_time:.2f}秒 ===") except Exception as e: import traceback traceback.print_exc(file=sys.stderr) # 打印错误堆栈,帮助调试 diff --git a/server.py b/server.py index 98c5213..ac3ca5a 100644 --- a/server.py +++ b/server.py @@ -4,60 +4,118 @@ import sys from datetime import datetime import os import json +# 在server.py顶部添加 +import pandas as pd +import logging +from logging.handlers import RotatingFileHandler +import time + app = Flask(__name__) +# 在Flask应用初始化后添加 +@app.before_first_request +def load_batch_data(): + # 读取批文Excel文件 + batch_path = r'C:\Users\24011\Documents\WeChat Files\wxid_k4ep58f81rx421\FileStorage\File\2025-04\tuigua_huangjijingshi - 副本\tuigua_huangjijingshi - 副本\python1\批文.xlsx' + app.batch_data = pd.read_excel(batch_path) + + # 在 Flask 应用初始化后添加日志配置 +def setup_logging(): + # 创建日志目录(如果不存在) + log_dir = os.path.join(os.path.dirname(__file__), 'logs') + os.makedirs(log_dir, exist_ok=True) + + # 配置根日志 + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + RotatingFileHandler( + os.path.join(log_dir, 'app.log'), + maxBytes=1024*1024*5, # 5MB + backupCount=5 + ), + logging.StreamHandler() + ] + ) + +# 在应用初始化后调用 +setup_logging() +logger = logging.getLogger(__name__) @app.route('/') def home(): return render_template('index.html') @app.route('/calculate', methods=['POST']) def calculate(): + start_time = time.time() + logger.info("开始处理计算请求") + date_str = request.form.get('date') - time_str = request.form.get('time', '00:00:00') # 默认为00:00:00 + time_str = request.form.get('time', '00:00:00') try: # 验证日期和时间格式 - datetime.strptime(date_str, '%Y-%m-%d') # 先单独验证日期 - datetime.strptime(time_str, '%H:%M:%S') # 再验证时间 - - # 组合日期和时间 + datetime.strptime(date_str, '%Y-%m-%d') + datetime.strptime(time_str, '%H:%M:%S') datetime_str = f"{date_str} {time_str}" api_path = os.path.join(os.path.dirname(__file__), 'API.py') + logger.info(f"准备调用API.py,参数: {datetime_str}") + + api_start = time.time() result = subprocess.run( [sys.executable, api_path, datetime_str], capture_output=True, - text=True, # 确保输出是文本 + text=True, check=True ) + api_time = time.time() - api_start + logger.info(f"API.py执行完成,耗时: {api_time:.2f}秒") - # 打印完整输出用于调试 - print("=== API.py 原始输出 ===") - print("stdout:", result.stdout) - print("stderr:", result.stderr) + logger.debug("=== API.py 原始输出 ===") + logger.debug(f"stdout: {result.stdout[:200]}...") # 只记录前200字符 + logger.debug(f"stderr: {result.stderr[:200]}...") try: + json_start = time.time() json_output = json.loads(result.stdout) + json_time = time.time() - json_start + logger.info(f"JSON解析完成,耗时: {json_time:.2f}秒") + + total_time = time.time() - start_time + logger.info(f"请求处理完成,总耗时: {total_time:.2f}秒") return jsonify({'success': True, 'result': json_output}) + except json.JSONDecodeError as e: - print("JSON 解析失败:", e) - print("原始输出:", result.stdout) + error_msg = f'JSON解析失败: {str(e)}' + logger.error(error_msg) + logger.error(f"原始输出: {result.stdout[:500]}") return jsonify({ 'success': False, - 'error': f'JSON解析失败: {str(e)}', - 'raw_output': result.stdout # 返回原始输出用于调试 + 'error': error_msg, + 'raw_output': result.stdout[:500] }) - except ValueError: - return jsonify({'success': False, 'error': '无效的日期格式'}) + except ValueError as e: + error_msg = f'无效的日期格式: {str(e)}' + logger.error(error_msg) + return jsonify({'success': False, 'error': error_msg}) + except subprocess.CalledProcessError as e: + error_msg = f'计算失败: {e.stderr}' + logger.error(error_msg) + logger.error(f"stdout: {e.stdout[:500]}") return jsonify({ 'success': False, - 'error': f'计算失败: {e.stderr}', - 'stdout': e.stdout + 'error': error_msg, + 'stdout': e.stdout[:500] }) + except Exception as e: - return jsonify({'success': False, 'error': str(e)}) + error_msg = f'服务器错误: {str(e)}' + logger.error(error_msg, exc_info=True) + return jsonify({'success': False, 'error': error_msg}) if __name__ == '__main__': app.run(debug=True,host='0.0.0.0',port=5000) \ No newline at end of file