import xmlrpc.client import time import sys # 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 search_draft_records(self): """搜索所有状态为 draft 的 alpha.datasets 记录""" try: ids = self.models.execute_kw( self.db, self.uid, self.password, 'alpha.datasets', 'search', [[('status', '=', 'draft')]] ) print(f"[INFO] 找到 {len(ids)} 条 draft 记录: {ids}") return ids except xmlrpc.client.Fault as e: print(f"[ERROR] XML-RPC Fault 搜索记录失败: {e.faultCode} - {e.faultString}") return [] except Exception as e: print(f"[ERROR] 搜索 draft 记录失败: {e}") return [] def get_record_status(self, record_id): """获取单条记录的状态""" try: records = self.models.execute_kw( self.db, self.uid, self.password, 'alpha.datasets', 'read', [[record_id], ['status', 'result_message', 'datasets_id', 'name']] ) if records: return records[0] return None except xmlrpc.client.Fault as e: print(f"[ERROR] XML-RPC Fault 获取记录 {record_id} 状态失败: {e.faultCode} - {e.faultString}") return None except Exception as e: print(f"[ERROR] 获取记录 {record_id} 状态失败: {e}") return None def trigger_btn_get_datasets(self, record_id): """ 调用 btn_get_datasets 方法 返回: (success, message) """ try: result = self.models.execute_kw( self.db, self.uid, self.password, 'alpha.datasets', 'btn_get_datasets', [[record_id]] ) # 处理不同的返回值 if result is True: print(f"[INFO] 记录 {record_id} 的 btn_get_datasets 调用成功") return True, "调用成功" elif isinstance(result, dict) and result.get('type') == 'ir.actions.client': # 返回的是 notification 字典 message = result.get('params', {}).get('message', '') msg_type = result.get('params', {}).get('type', 'info') if msg_type == 'danger': print(f"[WARN] 记录 {record_id} 返回错误通知: {message}") return False, message else: print(f"[INFO] 记录 {record_id} 返回通知: {message}") return True, message else: print(f"[INFO] 记录 {record_id} 的 btn_get_datasets 调用完成,返回: {result}") return True, "调用成功" except xmlrpc.client.Fault as e: error_msg = f"XML-RPC Fault: {e.faultCode} - {e.faultString}" print(f"[ERROR] 调用记录 {record_id} 的 btn_get_datasets 失败: {error_msg}") return False, error_msg except Exception as e: error_msg = f"Exception: {str(e)}" print(f"[ERROR] 调用记录 {record_id} 的 btn_get_datasets 失败: {error_msg}") return False, error_msg def wait_for_status_change(self, record_id, check_interval=5, max_wait_minutes=30): """ 轮询等待记录状态变为 done 或 failed 返回: (status, result_message) """ max_attempts = (max_wait_minutes * 60) // check_interval attempt = 0 while attempt < max_attempts: record = self.get_record_status(record_id) if not record: return 'unknown', '无法获取记录状态' status = record.get('status') result_message = record.get('result_message', '') datasets_id = record.get('datasets_id', 'N/A') name = record.get('name', 'N/A') if status in ['done', 'failed']: print(f"[INFO] 记录 {record_id} (name={name}, datasets_id={datasets_id}) 状态变为: {status}") if status == 'failed': print(f"[WARN] 失败原因: {result_message}") return status, result_message print(f"[INFO] 记录 {record_id} 当前状态: {status},等待 {check_interval} 秒后重试...") time.sleep(check_interval) attempt += 1 print(f"[WARN] 记录 {record_id} 等待超时 ({max_wait_minutes} 分钟)") return 'timeout', '等待超时' def main(): """主函数""" print("=" * 60) print("Odoo 批量处理 draft 数据集脚本") print("=" * 60) # 初始化客户端 client = OdooClient(ODOO_URL, DB_NAME, USERNAME, PASSWORD) # 统计信息 total_processed = 0 success_count = 0 failed_count = 0 timeout_count = 0 while True: # 搜索所有 draft 记录 draft_ids = client.search_draft_records() if not draft_ids: print("[INFO] 没有更多 draft 记录,处理完成!") break print(f"\n[INFO] 开始处理 {len(draft_ids)} 条 draft 记录...") for record_id in draft_ids: print(f"\n--- 处理记录 ID: {record_id} ---") # 再次确认状态(防止在搜索后状态被改变) current_record = client.get_record_status(record_id) if not current_record: print(f"[ERROR] 无法获取记录 {record_id} 的信息,跳过") failed_count += 1 total_processed += 1 continue if current_record.get('status') != 'draft': print(f"[INFO] 记录 {record_id} 状态已不是 draft (当前: {current_record.get('status')}),跳过") total_processed += 1 continue datasets_id = current_record.get('datasets_id', 'N/A') name = current_record.get('name', 'N/A') print(f"[INFO] 数据集名称: {name}") print(f"[INFO] 数据集 ID: {datasets_id}") # 触发 btn_get_datasets success, message = client.trigger_btn_get_datasets(record_id) if not success: print(f"[ERROR] 触发失败: {message},跳过记录 {record_id}") failed_count += 1 total_processed += 1 # 更新记录状态为 failed(如果触发失败) try: client.models.execute_kw( client.db, client.uid, client.password, 'alpha.datasets', 'write', [[record_id], { 'status': 'failed', 'result_message': f'Trigger failed: {message}' }] ) except Exception as e: print(f"[WARN] 无法更新记录状态: {e}") continue # 等待状态变化 status, result_msg = client.wait_for_status_change(record_id) if status == 'done': success_count += 1 print(f"[SUCCESS] 记录 {record_id} 处理成功") elif status == 'failed': failed_count += 1 print(f"[FAILED] 记录 {record_id} 处理失败: {result_msg}") elif status == 'timeout': timeout_count += 1 failed_count += 1 print(f"[TIMEOUT] 记录 {record_id} 处理超时") else: failed_count += 1 print(f"[UNKNOWN] 记录 {record_id} 状态未知: {status}") total_processed += 1 # 短暂延迟,避免请求过快 time.sleep(1) # 输出统计 print("\n" + "=" * 60) print("处理完成统计:") print(f" 总处理记录数: {total_processed}") print(f" 成功: {success_count}") print(f" 失败: {failed_count - timeout_count}") print(f" 超时: {timeout_count}") 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)