Files
alpha_tools/rpc_fetch_final_prompt/rpc_fetch_final_prompt.py
T
2026-04-01 15:48:02 +08:00

420 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import xmlrpc.client
import os
import sys
import time
from datetime import datetime, timedelta
# Odoo 连接配置
ODOO_URL = "http://192.168.31.41:32000"
DB_NAME = "quantify"
USERNAME = "rpc"
PASSWORD = "aaaAAA111"
class OdooClient:
def __init__(self, url, db, username, password):
self.url = url
self.db = db
self.username = username
self.password = password
self.uid = None
self.models = None
self._connect()
def _connect(self):
"""建立 XML-RPC 连接"""
try:
common = xmlrpc.client.ServerProxy(f"{self.url}/xmlrpc/2/common")
self.uid = common.authenticate(self.db, self.username, self.password, {})
if not self.uid:
raise Exception("认证失败,请检查用户名和密码")
self.models = xmlrpc.client.ServerProxy(f"{self.url}/xmlrpc/2/object")
print(f"[INFO] 连接成功,UID: {self.uid}")
except Exception as e:
print(f"[ERROR] 连接失败: {e}")
sys.exit(1)
def get_today_date_str(self):
"""获取今天的日期字符串 (YYYY-MM-DD)"""
return datetime.now().strftime('%Y-%m-%d')
def get_today_start(self):
"""获取今天的开始时间 (YYYY-MM-DD 00:00:00)"""
return datetime.now().strftime('%Y-%m-%d 00:00:00')
def get_today_end(self):
"""获取今天的结束时间 (YYYY-MM-DD 23:59:59)"""
return datetime.now().strftime('%Y-%m-%d 23:59:59')
def get_tomorrow_start(self):
"""获取明天的开始时间 (YYYY-MM-DD 00:00:00)"""
tomorrow = datetime.now() + timedelta(days=1)
return tomorrow.strftime('%Y-%m-%d 00:00:00')
# ==================== 步骤1: 清理历史数据 ====================
def cancel_old_non_terminal_records(self):
"""
步骤1: 取消非今天的非终态记录
将状态不是 done/failed/cancel 且创建日期 < 今天的记录设为 cancel
"""
today_start = self.get_today_start()
# 搜索条件:状态不是终态,且创建日期小于今天开始时间
domain = [
('status', 'not in', ['done', 'failed', 'cancel']),
('create_date', '<', today_start)
]
try:
record_ids = self.models.execute_kw(
self.db, self.uid, self.password,
'alpha.idea', 'search',
[domain]
)
if not record_ids:
print(f"[INFO] 没有需要取消的历史记录")
return 0
print(f"[INFO] 找到 {len(record_ids)} 条历史记录需要取消(创建日期 < {self.get_today_date_str()})")
# 批量更新状态为 cancel
self.models.execute_kw(
self.db, self.uid, self.password,
'alpha.idea', 'write',
[record_ids, {'status': 'cancel'}]
)
print(f"[INFO] 成功将 {len(record_ids)} 条历史记录状态更新为 cancel")
return len(record_ids)
except xmlrpc.client.Fault as e:
print(f"[ERROR] XML-RPC Fault 取消记录失败: {e.faultCode} - {e.faultString}")
return 0
except Exception as e:
print(f"[ERROR] 取消记录失败: {e}")
return 0
# ==================== 通用方法: 获取指定状态的记录 ====================
def fetch_records_by_status(self, status, fields=None):
"""
获取今天指定状态的记录
:param status: 状态值,如 'draft', 'fetched_data', 'generated_prompt'
:param fields: 需要读取的字段列表,默认读取 ['id', 'name', 'status']
"""
today_start = self.get_today_start()
tomorrow_start = self.get_tomorrow_start()
if fields is None:
fields = ['id', 'name', 'status']
# 使用日期范围查询当天的记录
domain = [
('create_date', '>=', today_start),
('create_date', '<', tomorrow_start),
('status', '=', status)
]
try:
record_ids = self.models.execute_kw(
self.db, self.uid, self.password,
'alpha.idea', 'search',
[domain]
)
if not record_ids:
print(f"[INFO] 没有找到今天的 {status} 状态记录")
return []
print(f"[INFO] 找到 {len(record_ids)} 条今天的 {status} 状态记录")
records = self.models.execute_kw(
self.db, self.uid, self.password,
'alpha.idea', 'read',
[record_ids, fields]
)
return records
except xmlrpc.client.Fault as e:
print(f"[ERROR] XML-RPC Fault 获取 {status} 记录失败: {e.faultCode} - {e.faultString}")
return []
except Exception as e:
print(f"[ERROR] 获取 {status} 记录失败: {e}")
return []
# ==================== 通用方法: 调用按钮并等待状态变化 ====================
def call_button_and_wait(self, record_id, button_name, target_statuses, check_interval=5, max_wait_minutes=30):
"""
调用指定按钮并等待状态变为目标状态
:param record_id: 记录ID
:param button_name: 按钮方法名,如 'btn_check_and_fetch_data'
:param target_statuses: 目标状态列表,如 ['generated_prompt', 'failed']
:param check_interval: 检查间隔(秒)
:param max_wait_minutes: 最大等待时间(分钟)
:return: (最终状态, 结果消息)
"""
# 调用按钮
try:
result = self.models.execute_kw(
self.db, self.uid, self.password,
'alpha.idea', button_name,
[[record_id]]
)
print(f"[INFO] 记录 {record_id} 的 {button_name} 已调用")
except xmlrpc.client.Fault as e:
error_msg = f"XML-RPC Fault: {e.faultCode} - {e.faultString}"
print(f"[ERROR] 调用 {button_name} 失败: {error_msg}")
return 'failed', error_msg
except Exception as e:
error_msg = f"Exception: {str(e)}"
print(f"[ERROR] 调用 {button_name} 失败: {error_msg}")
return 'failed', error_msg
# 等待状态变化
return self.wait_for_status_change(record_id, target_statuses, check_interval, max_wait_minutes)
def wait_for_status_change(self, record_id, target_statuses, check_interval=5, max_wait_minutes=30):
"""
轮询等待记录状态变为目标状态之一
:return: (current_status, result_message)
"""
max_attempts = (max_wait_minutes * 60) // check_interval
attempt = 0
# 获取初始状态
record = self.get_record_status(record_id)
if not record:
return 'unknown', '无法获取记录状态'
current_status = record.get('status')
print(f"[INFO] 记录 {record_id} 初始状态: {current_status}")
while attempt < max_attempts:
if current_status in target_statuses:
print(f"[INFO] 记录 {record_id} 状态变为目标状态: {current_status}")
return current_status, record.get('result_message', '')
print(f"[INFO] 记录 {record_id} 当前状态: {current_status},等待 {check_interval} 秒后重试...")
time.sleep(check_interval)
attempt += 1
record = self.get_record_status(record_id)
if record:
current_status = record.get('status')
else:
print(f"[WARN] 无法获取记录 {record_id} 状态")
print(f"[WARN] 记录 {record_id} 等待超时 ({max_wait_minutes} 分钟)")
return current_status, '等待超时'
def get_record_status(self, record_id):
"""获取单条记录的状态和 result_message"""
try:
records = self.models.execute_kw(
self.db, self.uid, self.password,
'alpha.idea', 'read',
[[record_id], ['status', 'result_message', 'name']]
)
if records:
return records[0]
return None
except Exception as e:
print(f"[ERROR] 获取记录 {record_id} 状态失败: {e}")
return None
# ==================== 步骤2: 处理 draft 记录 ====================
def process_draft_records(self):
"""
步骤2: 处理今天的 draft 状态记录
调用 btn_check_and_fetch_data,等待状态变为 fetched_data 或 failed
"""
print("\n[步骤2] 处理今天的 draft 记录...")
print("-" * 40)
draft_records = self.fetch_records_by_status('draft')
if not draft_records:
print("[INFO] 没有需要处理的 draft 记录")
return 0
success_count = 0
failed_count = 0
for record in draft_records:
record_id = record.get('id')
name = record.get('name')
print(f"\n处理 draft 记录: {name} (ID: {record_id})")
# 调用 btn_check_and_fetch_data,等待状态变为 fetched_data 或 failed
target_statuses = ['fetched_data', 'failed', 'cancel', 'done']
final_status, message = self.call_button_and_wait(
record_id,
'btn_check_and_fetch_data',
target_statuses,
check_interval=5,
max_wait_minutes=30
)
if final_status == 'fetched_data':
print(f"[SUCCESS] 记录 {record_id} 成功变为 fetched_data 状态")
success_count += 1
else:
print(f"[FAILED] 记录 {record_id} 最终状态为 {final_status},失败原因: {message}")
failed_count += 1
print(f"\n[步骤2完成] 成功: {success_count}, 失败: {failed_count}")
return success_count
# ==================== 步骤3: 处理 fetched_data 记录 ====================
def process_fetched_data_records(self):
"""
步骤3: 处理今天的 fetched_data 状态记录
调用 btn_generate_final_prompt,等待状态变为 generated_prompt 或 failed
"""
print("\n[步骤3] 处理今天的 fetched_data 记录...")
print("-" * 40)
fetched_records = self.fetch_records_by_status('fetched_data')
if not fetched_records:
print("[INFO] 没有需要处理的 fetched_data 记录")
return 0
success_count = 0
failed_count = 0
for record in fetched_records:
record_id = record.get('id')
name = record.get('name')
print(f"\n处理 fetched_data 记录: {name} (ID: {record_id})")
# 调用 btn_generate_final_prompt,等待状态变为 generated_prompt 或 failed
target_statuses = ['generated_prompt', 'failed', 'cancel', 'done']
final_status, message = self.call_button_and_wait(
record_id,
'btn_generate_final_prompt',
target_statuses,
check_interval=5,
max_wait_minutes=30
)
if final_status == 'generated_prompt':
print(f"[SUCCESS] 记录 {record_id} 成功变为 generated_prompt 状态")
success_count += 1
else:
print(f"[FAILED] 记录 {record_id} 最终状态为 {final_status},失败原因: {message}")
failed_count += 1
print(f"\n[步骤3完成] 成功: {success_count}, 失败: {failed_count}")
return success_count
# ==================== 步骤4: 下载 final_prompt ====================
def download_final_prompts(self):
"""
步骤4: 获取今天的 generated_prompt 状态记录,下载 final_prompt 到本地文件
"""
print("\n[步骤4] 下载今天的 final_prompt...")
print("-" * 40)
generated_records = self.fetch_records_by_status('generated_prompt', ['id', 'name', 'final_prompt'])
if not generated_records:
print("[INFO] 没有需要下载的 generated_prompt 记录")
return 0
# 创建当天日期文件夹
today_folder = self.get_today_date_str()
output_dir = os.path.join(os.getcwd(), today_folder)
print(f"[INFO] 文件将保存到: {output_dir}")
success_count = 0
failed_count = 0
for record in generated_records:
record_id = record.get('id')
name = record.get('name')
final_prompt = record.get('final_prompt', '')
print(f"\n处理记录: {name} (ID: {record_id})")
# 检查 final_prompt 是否为空
if not final_prompt or not final_prompt.strip():
print(f"[WARN] 记录 {name} 的 final_prompt 为空,跳过")
failed_count += 1
continue
# 保存文件
if self.save_prompt_to_md(name, final_prompt, output_dir):
success_count += 1
else:
failed_count += 1
print(f"\n[步骤4完成] 成功保存: {success_count}, 失败: {failed_count}")
return success_count
def save_prompt_to_md(self, name, final_prompt, output_dir):
"""将 final_prompt 保存为 Markdown 文件"""
try:
os.makedirs(output_dir, exist_ok=True)
filename = f"{name}.md"
filepath = os.path.join(output_dir, filename)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(final_prompt)
print(f"[SUCCESS] 已保存文件: {filepath}")
return True
except Exception as e:
print(f"[ERROR] 保存文件失败 {name}.md: {e}")
return False
def main():
"""主函数"""
print("=" * 60)
print("Odoo Alpha Idea 处理脚本")
print(f"执行时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
# 初始化客户端
client = OdooClient(ODOO_URL, DB_NAME, USERNAME, PASSWORD)
# 步骤1: 清理历史数据(创建日期 < 今天的非终态记录设为 cancel)
print("\n[步骤1] 清理历史数据...")
print("-" * 40)
canceled_count = client.cancel_old_non_terminal_records()
print(f"[步骤1完成] 已取消 {canceled_count} 条历史记录")
# 步骤2: 处理今天的 draft 记录
draft_success = client.process_draft_records()
# 步骤3: 处理今天的 fetched_data 记录
fetched_success = client.process_fetched_data_records()
# 步骤4: 下载今天的 final_prompt
downloaded_count = client.download_final_prompts()
# 输出统计
print("\n" + "=" * 60)
print("处理完成统计:")
print(f" 步骤1 - 取消的历史记录: {canceled_count}")
print(f" 步骤2 - 处理的 draft 记录: {draft_success}")
print(f" 步骤3 - 处理的 fetched_data 记录: {fetched_success}")
print(f" 步骤4 - 下载的 prompt 文件: {downloaded_count}")
print(f" 文件保存路径: {os.path.join(os.getcwd(), client.get_today_date_str())}")
print("=" * 60)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[INFO] 用户中断脚本")
sys.exit(0)
except Exception as e:
print(f"[ERROR] 脚本异常: {e}")
sys.exit(1)