import_all_templates.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. """
  2. 完整导入 daping 项目的所有模板
  3. 支持:
  4. 1. 提取 X11、Hplus、F2 Multi 的完整模板(包含关怀、健康、天气等)
  5. 2. 为 Ultra、Seeker、X11Ultra 创建复用关系(复用 X11 模板)
  6. """
  7. import json
  8. import re
  9. from pathlib import Path
  10. from datetime import datetime
  11. DAPING_PRE_DATA = Path(__file__).parent.parent.parent / "daping_rec_online_api" / "app" / "pre_data"
  12. def extract_templates_from_py(py_file: Path) -> list:
  13. """从 Python 文件中提取所有模板"""
  14. content = py_file.read_text(encoding='utf-8')
  15. templates = []
  16. # 匹配 value_xxx = """...""" 模式
  17. pattern = r'value_(\w+)\s*=\s*"""(.*?)"""'
  18. matches = re.findall(pattern, content, re.DOTALL)
  19. for template_type, template_body in matches:
  20. # 跳过不需要的模板
  21. if template_type in ['default', 'basic', 'w_basic', 'w_default', 'd_default']:
  22. continue
  23. # 提取 second_key
  24. key_pattern = rf'value_{template_type}_second_key\s*=\s*"([^"]+)"'
  25. key_match = re.search(key_pattern, content)
  26. redis_key = key_match.group(1) if key_match else ""
  27. # 清理模板类型名称
  28. clean_type = template_type
  29. if template_type.startswith('w_'):
  30. clean_type = template_type[2:]
  31. elif template_type.startswith('d_'):
  32. clean_type = template_type[2:]
  33. templates.append({
  34. "template_type": clean_type,
  35. "template_body": template_body.strip(),
  36. "redis_key": redis_key
  37. })
  38. return templates
  39. def generate_all_templates() -> list:
  40. """生成所有模板记录(包括复用关系)"""
  41. records = []
  42. template_id_counter = 1
  43. # 模板类型标准化映射
  44. TYPE_NORMALIZE = {
  45. "popular": "time_popular",
  46. "made": "time_custom",
  47. "custom": "time_custom",
  48. "season_in": "time_season",
  49. "season_storage": "time_season",
  50. "care": "time_care",
  51. "env": "time_environmental",
  52. "environmental": "time_environmental",
  53. "health": "time_health",
  54. "health2": "time_health_2",
  55. "weather": "time_weather",
  56. }
  57. # 模板类型描述
  58. TYPE_DESCRIPTIONS = {
  59. "common": "通用",
  60. "time_popular": "人气",
  61. "time_custom": "定制",
  62. "time_season": "季节",
  63. "time_care": "关怀",
  64. "time_environmental": "环保",
  65. "time_health": "健康",
  66. "time_health_2": "健康2-杀菌",
  67. "time_weather": "天气",
  68. }
  69. # 机型配置(与 daping 项目一致)
  70. MODEL_CONFIGS = [
  71. # X11 洗衣机
  72. {
  73. "model_name": "x11",
  74. "display_name": "X11",
  75. "model_id": "x11",
  76. "device_type": "wash",
  77. "zone_id": "2",
  78. "zone_name": "洗衣机",
  79. "file": "write_jinja_x11_down.py",
  80. },
  81. # X11 干衣机
  82. {
  83. "model_name": "x11",
  84. "display_name": "X11",
  85. "model_id": "x11",
  86. "device_type": "dry",
  87. "zone_id": "0",
  88. "zone_name": "干衣机",
  89. "file": "write_jinja_x11_up.py",
  90. },
  91. # Hplus 洗衣机
  92. {
  93. "model_name": "hplus",
  94. "display_name": "Hplus",
  95. "model_id": "hplus",
  96. "device_type": "wash",
  97. "zone_id": "3",
  98. "zone_name": "洗衣机",
  99. "file": "write_jinja_h_down.py",
  100. },
  101. # Hplus 干衣机
  102. {
  103. "model_name": "hplus",
  104. "display_name": "Hplus",
  105. "model_id": "hplus",
  106. "device_type": "dry",
  107. "zone_id": "0",
  108. "zone_name": "干衣机",
  109. "file": "write_jinja_h_up.py",
  110. },
  111. # F2 Multi 洗衣机
  112. {
  113. "model_name": "f2_multi",
  114. "display_name": "F2 Multi",
  115. "model_id": "f2_multi",
  116. "device_type": "wash",
  117. "zone_id": "1",
  118. "zone_name": "洗衣机",
  119. "file": "write_jinja_multi_down.py",
  120. },
  121. ]
  122. # 复用关系配置
  123. REUSE_CONFIGS = [
  124. # Ultra 洗衣机(复用 X11 洗涤)
  125. {
  126. "model_id": "ultra",
  127. "display_name": "Ultra",
  128. "device_type": "wash",
  129. "zone_id": "1",
  130. "zone_name": "洗衣机",
  131. "reuse_from_model": "x11",
  132. "reuse_from_device": "wash",
  133. },
  134. # Seeker 洗衣机(复用 X11 洗涤)
  135. {
  136. "model_id": "seeker",
  137. "display_name": "Seeker",
  138. "device_type": "wash",
  139. "zone_id": "2",
  140. "zone_name": "洗衣机",
  141. "reuse_from_model": "x11",
  142. "reuse_from_device": "wash",
  143. },
  144. # X11 Ultra 干衣机(复用 X11 干衣)
  145. {
  146. "model_id": "x11_ultra",
  147. "display_name": "X11 Ultra",
  148. "device_type": "dry",
  149. "zone_id": "24",
  150. "zone_name": "干衣机",
  151. "reuse_from_model": "x11",
  152. "reuse_from_device": "dry",
  153. },
  154. ]
  155. # 1. 处理独立模板(X11、Hplus、F2 Multi)
  156. print("\n" + "="*60)
  157. print("提取独立模板...")
  158. print("="*60)
  159. source_template_map = {} # 用于复用关系查找
  160. for config in MODEL_CONFIGS:
  161. file_path = DAPING_PRE_DATA / config["model_name"] / config["file"]
  162. if not file_path.exists():
  163. print(f"⚠ 文件不存在: {file_path}")
  164. continue
  165. print(f"\n处理: {config['display_name']} {config['zone_name']}")
  166. templates = extract_templates_from_py(file_path)
  167. if not templates:
  168. print(f" ⚠ 未找到模板")
  169. continue
  170. print(f" ✓ 找到 {len(templates)} 个模板")
  171. for tpl in templates:
  172. template_type = tpl["template_type"]
  173. # 标准化模板类型
  174. normalized_type = TYPE_NORMALIZE.get(template_type, template_type)
  175. if normalized_type.startswith("time_"):
  176. final_type = normalized_type
  177. else:
  178. final_type = f"time_{normalized_type}" if normalized_type != "common" else "common"
  179. description = TYPE_DESCRIPTIONS.get(final_type, final_type)
  180. # 生成模板ID
  181. tpl_id = f"{config['model_id']}_{config['device_type']}_{final_type}"
  182. template_id_counter += 1
  183. # 记录源模板位置(用于复用)
  184. source_key = f"{config['model_id']}_{config['device_type']}_{final_type}"
  185. source_template_map[source_key] = tpl_id
  186. record = {
  187. "id": tpl_id,
  188. "name": f"{config['display_name']}{config['zone_name']}-{description}",
  189. "category": f"{'洗涤' if config['device_type'] == 'wash' else '干衣'}",
  190. "device_type": config["device_type"],
  191. "machine_model": config["model_id"],
  192. "template_type": final_type,
  193. "reusable_from": "",
  194. "reuse_targets": [],
  195. "description": f"{config['display_name']} {config['zone_name']} {description}模板",
  196. "output_description": "语音播报脚本",
  197. "call_method": "",
  198. "input_variables": [
  199. {
  200. "name": "program_id",
  201. "label": "程序ID",
  202. "description": "洗涤/干衣程序ID",
  203. "data_type": "string",
  204. "required": True,
  205. "default": "15"
  206. }
  207. ],
  208. "template_body": tpl["template_body"],
  209. "created_at": datetime.utcnow().isoformat() + "Z",
  210. "updated_at": datetime.utcnow().isoformat() + "Z"
  211. }
  212. records.append(record)
  213. print(f" ✓ {record['name']} ({len(tpl['template_body'])} 字符)")
  214. # 2. 处理复用模板(Ultra、Seeker、X11Ultra)
  215. print("\n" + "="*60)
  216. print("创建复用模板...")
  217. print("="*60)
  218. for config in REUSE_CONFIGS:
  219. source_key = f"{config['reuse_from_model']}_{config['reuse_from_device']}"
  220. # 找出所有源模板
  221. source_tpl_ids = [
  222. tpl_id for key, tpl_id in source_template_map.items()
  223. if key.startswith(source_key)
  224. ]
  225. if not source_tpl_ids:
  226. print(f"⚠ 未找到 {config['display_name']} {config['zone_name']} 的源模板")
  227. continue
  228. print(f"\n处理: {config['display_name']} {config['zone_name']} (复用)")
  229. for source_tpl_id in source_tpl_ids:
  230. # 从源模板ID中提取模板类型
  231. parts = source_tpl_id.split("_")
  232. template_type = "_".join(parts[2:]) # 去掉 model_id 和 device_type
  233. description = TYPE_DESCRIPTIONS.get(template_type, template_type)
  234. # 生成复用模板ID
  235. tpl_id = f"{config['model_id']}_{config['device_type']}_{template_type}"
  236. template_id_counter += 1
  237. # 更新源模板的 reuse_targets
  238. for record in records:
  239. if record["id"] == source_tpl_id:
  240. if tpl_id not in record["reuse_targets"]:
  241. record["reuse_targets"].append(tpl_id)
  242. record = {
  243. "id": tpl_id,
  244. "name": f"{config['display_name']}{config['zone_name']}-{description}",
  245. "category": f"{'洗涤' if config['device_type'] == 'wash' else '干衣'}",
  246. "device_type": config["device_type"],
  247. "machine_model": config["model_id"],
  248. "template_type": template_type,
  249. "reusable_from": source_tpl_id,
  250. "reuse_targets": [],
  251. "description": f"{config['display_name']} {config['zone_name']} {description}模板(复用)",
  252. "output_description": "语音播报脚本",
  253. "call_method": "",
  254. "input_variables": [
  255. {
  256. "name": "program_id",
  257. "label": "程序ID",
  258. "description": "洗涤/干衣程序ID",
  259. "data_type": "string",
  260. "required": True,
  261. "default": "15"
  262. }
  263. ],
  264. "template_body": "", # 复用模板不需要存储正文
  265. "created_at": datetime.utcnow().isoformat() + "Z",
  266. "updated_at": datetime.utcnow().isoformat() + "Z"
  267. }
  268. records.append(record)
  269. print(f" ✓ {record['name']} (复用自 {source_tpl_id})")
  270. return records
  271. def main():
  272. output_file = Path(__file__).parent.parent / "backend" / "data" / "templates.json"
  273. print("="*60)
  274. print("开始从 daping 项目提取所有模板...")
  275. print("="*60)
  276. # 生成所有模板记录
  277. records = generate_all_templates()
  278. # 保存到 JSON
  279. output_file.parent.mkdir(parents=True, exist_ok=True)
  280. output_file.write_text(
  281. json.dumps(records, ensure_ascii=False, indent=2),
  282. encoding='utf-8'
  283. )
  284. # 统计信息
  285. type_stats = {}
  286. model_stats = {}
  287. reuse_count = sum(1 for r in records if r["reusable_from"])
  288. for r in records:
  289. type_stats[r["template_type"]] = type_stats.get(r["template_type"], 0) + 1
  290. model_stats[r["machine_model"]] = model_stats.get(r["machine_model"], 0) + 1
  291. print(f"\n" + "="*60)
  292. print(f"✓ 成功导入 {len(records)} 个模板")
  293. print(f" - 独立模板: {len(records) - reuse_count} 个")
  294. print(f" - 复用模板: {reuse_count} 个")
  295. print(f"\n按机型统计:")
  296. for model, count in sorted(model_stats.items()):
  297. print(f" - {model}: {count} 个")
  298. print(f"\n按模板类型统计:")
  299. for tpl_type, count in sorted(type_stats.items()):
  300. print(f" - {tpl_type}: {count} 个")
  301. print(f"\n输出文件: {output_file}")
  302. print("="*60)
  303. if __name__ == "__main__":
  304. main()