run_uat_s1_review_fact_seed.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. #!/usr/bin/env python3
  2. """Seed tenant-scoped contract review / flow / product design facts for S1 KPI pipeline.
  3. Writes only business tables. S1 rebuild must extract STG and compute KPI day values.
  4. """
  5. from __future__ import annotations
  6. import argparse
  7. import json
  8. from datetime import datetime, timedelta
  9. from typing import Any
  10. from apply_sql_file import connect
  11. DEFAULT_TENANT = 838257186181189
  12. PREFIX = "UAT-CR-"
  13. PD_PREFIX = "UAT-PD-"
  14. PROCESS_PREFIX = "UAT-PROC-"
  15. DELIVERY_PREFIX = "UAT-DEL-"
  16. STAGES = (
  17. (1, "意见评审"),
  18. (2, "意见反馈"),
  19. (3, "二次评审"),
  20. (4, "领导意见"),
  21. (5, "合同盖章"),
  22. )
  23. DEPTS = (
  24. ("LAW", "法律事务部"),
  25. ("PRE_SALES", "技术售前组"),
  26. ("MPS", "综合主计划"),
  27. ("TEST", "试验站"),
  28. )
  29. def seed(tenant_id: int, dry_run: bool) -> dict[str, Any]:
  30. today = datetime.now().replace(microsecond=0)
  31. reviews = 14
  32. result: dict[str, Any] = {
  33. "tenantId": tenant_id,
  34. "dryRun": dry_run,
  35. "reviews": reviews,
  36. "insertedReviews": 0,
  37. "insertedFlows": 0,
  38. "insertedDesigns": 0,
  39. }
  40. conn = connect()
  41. try:
  42. conn.begin()
  43. with conn.cursor() as cur:
  44. cur.execute(
  45. "DELETE FROM ado_contract_review_flow WHERE tenant_id=%s AND ReviewBillNo LIKE %s",
  46. (tenant_id, f"{PREFIX}%"),
  47. )
  48. cur.execute(
  49. "DELETE FROM ado_contract_review WHERE tenant_id=%s AND BillNo LIKE %s",
  50. (tenant_id, f"{PREFIX}%"),
  51. )
  52. for branch_prefix in (PROCESS_PREFIX, DELIVERY_PREFIX):
  53. cur.execute(
  54. "DELETE FROM ado_contract_review_flow WHERE tenant_id=%s AND ReviewBillNo LIKE %s",
  55. (tenant_id, f"{branch_prefix}%"),
  56. )
  57. cur.execute(
  58. "DELETE FROM ado_contract_review WHERE tenant_id=%s AND BillNo LIKE %s",
  59. (tenant_id, f"{branch_prefix}%"),
  60. )
  61. cur.execute(
  62. "DELETE FROM ado_product_design WHERE tenant_id=%s AND BillNo LIKE %s",
  63. (tenant_id, f"{PD_PREFIX}%"),
  64. )
  65. if dry_run:
  66. conn.rollback()
  67. result["wouldDelete"] = True
  68. return result
  69. for index in range(reviews):
  70. day = today - timedelta(days=reviews - 1 - index)
  71. create_time = day.replace(hour=8, minute=0, second=0)
  72. update_time = create_time + timedelta(hours=18 + (index % 6))
  73. bill_no = f"{PREFIX}A-{index + 1:04d}"
  74. cur.execute(
  75. """
  76. INSERT INTO ado_contract_review
  77. (BillNo, Title, CustomerName, CustomerNo, ResponsibleAccount, ResponsibleName,
  78. CurrentStage, FlowStatus, CreateUser, CreateTime, UpdateUser, UpdateTime,
  79. IsActive, tenant_id)
  80. VALUES
  81. (%s, %s, %s, %s, %s, %s, 5, 'completed', %s, %s, %s, %s, 1, %s)
  82. """,
  83. (
  84. bill_no,
  85. f"UAT合同评审 {index + 1:04d}",
  86. f"UAT客户{(index % 4) + 1}",
  87. f"UAT-CUS-{(index % 4) + 1:03d}",
  88. f"uat.reviewer{(index % 3) + 1}",
  89. f"评审人{(index % 3) + 1}",
  90. f"uat.reviewer{(index % 3) + 1}",
  91. create_time,
  92. f"uat.reviewer{(index % 3) + 1}",
  93. update_time,
  94. tenant_id,
  95. ),
  96. )
  97. review_id = int(cur.lastrowid)
  98. result["insertedReviews"] += 1
  99. cursor_time = create_time
  100. for stage_no, stage_name in STAGES:
  101. stage_hours = 2 + (stage_no % 3)
  102. if stage_no == 1:
  103. for seq, (dept_no, dept_name) in enumerate(DEPTS, 1):
  104. start = cursor_time + timedelta(hours=seq - 1)
  105. complete = start + timedelta(hours=1, minutes=20 + seq * 5)
  106. cur.execute(
  107. """
  108. INSERT INTO ado_contract_review_flow
  109. (ReviewRecID, ReviewBillNo, StageNo, StageName, Department, DeptNo, Seq,
  110. ReviewerAccount, ReviewerName, StartTime, CompleteTime, ActualDays,
  111. NodeStatus, tenant_id)
  112. VALUES
  113. (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'approved', %s)
  114. """,
  115. (
  116. review_id, bill_no, stage_no, stage_name, dept_name, dept_no, seq,
  117. f"uat.{dept_no.lower()}", dept_name, start, complete,
  118. round((complete - start).total_seconds() / 86400, 4),
  119. tenant_id,
  120. ),
  121. )
  122. result["insertedFlows"] += 1
  123. cursor_time = cursor_time + timedelta(hours=stage_hours)
  124. else:
  125. start = cursor_time
  126. complete = start + timedelta(hours=stage_hours)
  127. cur.execute(
  128. """
  129. INSERT INTO ado_contract_review_flow
  130. (ReviewRecID, ReviewBillNo, StageNo, StageName, Department, DeptNo, Seq,
  131. ReviewerAccount, ReviewerName, StartTime, CompleteTime, ActualDays,
  132. NodeStatus, tenant_id)
  133. VALUES
  134. (%s, %s, %s, %s, %s, 'MPS', 1, %s, %s, %s, %s, %s, 'approved', %s)
  135. """,
  136. (
  137. review_id, bill_no, stage_no, stage_name, stage_name,
  138. f"uat.stage{stage_no}", stage_name, start, complete,
  139. round((complete - start).total_seconds() / 86400, 4),
  140. tenant_id,
  141. ),
  142. )
  143. result["insertedFlows"] += 1
  144. cursor_time = complete
  145. for branch_index, (branch_prefix, branch_name) in enumerate(
  146. ((PROCESS_PREFIX, "工艺评审"), (DELIVERY_PREFIX, "交期评审")),
  147. 1,
  148. ):
  149. branch_bill_no = f"{branch_prefix}A-{index + 1:04d}"
  150. branch_update = create_time + timedelta(hours=10 + branch_index * 4 + index % 5)
  151. cur.execute(
  152. """
  153. INSERT INTO ado_contract_review
  154. (BillNo, Title, CustomerName, CustomerNo, ResponsibleAccount, ResponsibleName,
  155. CurrentStage, FlowStatus, CreateUser, CreateTime, UpdateUser, UpdateTime,
  156. IsActive, tenant_id)
  157. VALUES
  158. (%s,%s,%s,%s,%s,%s,5,'completed',%s,%s,%s,%s,1,%s)
  159. """,
  160. (
  161. branch_bill_no,
  162. f"UAT{branch_name} {index + 1:04d}",
  163. f"UAT客户{(index % 4) + 1}",
  164. f"UAT-CUS-{(index % 4) + 1:03d}",
  165. f"uat.{branch_name}{(index % 3) + 1}",
  166. f"{branch_name}人{(index % 3) + 1}",
  167. f"uat.{branch_name}{(index % 3) + 1}",
  168. create_time,
  169. f"uat.{branch_name}{(index % 3) + 1}",
  170. branch_update,
  171. tenant_id,
  172. ),
  173. )
  174. branch_review_id = int(cur.lastrowid)
  175. result["insertedReviews"] += 1
  176. branch_cursor = create_time
  177. for stage_no, stage_name in STAGES:
  178. stage_hours = 1 + branch_index + stage_no % 4
  179. if stage_no == 1:
  180. for seq, (dept_no, dept_name) in enumerate(DEPTS, 1):
  181. start = branch_cursor + timedelta(minutes=30 * (seq - 1))
  182. complete = start + timedelta(hours=stage_hours, minutes=seq * 7)
  183. cur.execute(
  184. """
  185. INSERT INTO ado_contract_review_flow
  186. (ReviewRecID,ReviewBillNo,StageNo,StageName,Department,DeptNo,Seq,
  187. ReviewerAccount,ReviewerName,StartTime,CompleteTime,ActualDays,
  188. NodeStatus,tenant_id)
  189. VALUES
  190. (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,'approved',%s)
  191. """,
  192. (
  193. branch_review_id, branch_bill_no, stage_no, stage_name,
  194. dept_name, dept_no, seq, f"uat.{dept_no.lower()}",
  195. dept_name, start, complete,
  196. round((complete - start).total_seconds() / 86400, 4),
  197. tenant_id,
  198. ),
  199. )
  200. result["insertedFlows"] += 1
  201. branch_cursor += timedelta(hours=stage_hours)
  202. else:
  203. start = branch_cursor
  204. complete = start + timedelta(hours=stage_hours)
  205. cur.execute(
  206. """
  207. INSERT INTO ado_contract_review_flow
  208. (ReviewRecID,ReviewBillNo,StageNo,StageName,Department,DeptNo,Seq,
  209. ReviewerAccount,ReviewerName,StartTime,CompleteTime,ActualDays,
  210. NodeStatus,tenant_id)
  211. VALUES
  212. (%s,%s,%s,%s,%s,'MPS',1,%s,%s,%s,%s,%s,'approved',%s)
  213. """,
  214. (
  215. branch_review_id, branch_bill_no, stage_no, stage_name, stage_name,
  216. f"uat.{branch_prefix.lower()}.stage{stage_no}", stage_name,
  217. start, complete,
  218. round((complete - start).total_seconds() / 86400, 4),
  219. tenant_id,
  220. ),
  221. )
  222. result["insertedFlows"] += 1
  223. branch_cursor = complete
  224. if index == 0:
  225. cur.execute("SELECT COALESCE(MAX(Id), 0) AS max_id FROM ado_product_design")
  226. design_base = int(cur.fetchone()["max_id"])
  227. design_id = design_base + index + 1
  228. draw_start = create_time + timedelta(days=1)
  229. draw_end = draw_start + timedelta(hours=20 + index % 8)
  230. plan_end = draw_start + timedelta(hours=24)
  231. cur.execute(
  232. """
  233. INSERT INTO ado_product_design
  234. (Id, BillNo, ContractNo, ProductKind, DesignLeadAccount, DesignLeadName,
  235. DrawingNo, DrawingPlanStart, DrawingPlanEnd, DrawingDesignCycle,
  236. DrawingActualStart, DrawingActualEnd, Applicant, ApplyDate,
  237. ProductModel, ItemNum, ProductName, Qty, CreateUser, CreateTime,
  238. UpdateUser, UpdateTime, IsActive, tenant_id)
  239. VALUES
  240. (%s, %s, %s, 1, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
  241. %s, %s, %s, 1, %s, %s, %s, %s, 1, %s)
  242. """,
  243. (
  244. design_id,
  245. f"{PD_PREFIX}A-{index + 1:04d}",
  246. bill_no,
  247. f"uat.design{(index % 2) + 1}",
  248. f"设计人{(index % 2) + 1}",
  249. f"DWG-{index + 1:04d}",
  250. draw_start,
  251. plan_end,
  252. int((draw_end - draw_start).total_seconds() // 3600),
  253. draw_start,
  254. draw_end,
  255. f"uat.reviewer{(index % 3) + 1}",
  256. create_time,
  257. f"UAT-MODEL-{(index % 5) + 1}",
  258. f"UAT-ITEM-{(index % 5) + 1:03d}",
  259. f"UAT产品{index + 1}",
  260. f"uat.design{(index % 2) + 1}",
  261. create_time,
  262. f"uat.design{(index % 2) + 1}",
  263. draw_end,
  264. tenant_id,
  265. ),
  266. )
  267. result["insertedDesigns"] += 1
  268. conn.commit()
  269. except Exception:
  270. conn.rollback()
  271. raise
  272. finally:
  273. conn.close()
  274. return result
  275. def main() -> None:
  276. parser = argparse.ArgumentParser()
  277. parser.add_argument("--tenant", type=int, default=DEFAULT_TENANT)
  278. parser.add_argument("--dry-run", action="store_true")
  279. args = parser.parse_args()
  280. print(json.dumps(seed(args.tenant, args.dry_run), ensure_ascii=False, indent=2, default=str))
  281. if __name__ == "__main__":
  282. main()