run_uat_s1_review_fact_seed.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. STAGES = (
  15. (1, "意见评审"),
  16. (2, "意见反馈"),
  17. (3, "二次评审"),
  18. (4, "领导意见"),
  19. (5, "合同盖章"),
  20. )
  21. DEPTS = (
  22. ("LAW", "法律事务部"),
  23. ("PRE_SALES", "技术售前组"),
  24. ("MPS", "综合主计划"),
  25. ("TEST", "试验站"),
  26. )
  27. def seed(tenant_id: int, dry_run: bool) -> dict[str, Any]:
  28. today = datetime.now().replace(microsecond=0)
  29. reviews = 14
  30. result: dict[str, Any] = {
  31. "tenantId": tenant_id,
  32. "dryRun": dry_run,
  33. "reviews": reviews,
  34. "insertedReviews": 0,
  35. "insertedFlows": 0,
  36. "insertedDesigns": 0,
  37. }
  38. conn = connect()
  39. try:
  40. conn.begin()
  41. with conn.cursor() as cur:
  42. cur.execute(
  43. "DELETE FROM ado_contract_review_flow WHERE tenant_id=%s AND ReviewBillNo LIKE %s",
  44. (tenant_id, f"{PREFIX}%"),
  45. )
  46. cur.execute(
  47. "DELETE FROM ado_contract_review WHERE tenant_id=%s AND BillNo LIKE %s",
  48. (tenant_id, f"{PREFIX}%"),
  49. )
  50. cur.execute(
  51. "DELETE FROM ado_product_design WHERE tenant_id=%s AND BillNo LIKE %s",
  52. (tenant_id, f"{PD_PREFIX}%"),
  53. )
  54. if dry_run:
  55. conn.rollback()
  56. result["wouldDelete"] = True
  57. return result
  58. for index in range(reviews):
  59. day = today - timedelta(days=reviews - 1 - index)
  60. create_time = day.replace(hour=8, minute=0, second=0)
  61. update_time = create_time + timedelta(hours=18 + (index % 6))
  62. bill_no = f"{PREFIX}A-{index + 1:04d}"
  63. cur.execute(
  64. """
  65. INSERT INTO ado_contract_review
  66. (BillNo, Title, CustomerName, CustomerNo, ResponsibleAccount, ResponsibleName,
  67. CurrentStage, FlowStatus, CreateUser, CreateTime, UpdateUser, UpdateTime,
  68. IsActive, tenant_id)
  69. VALUES
  70. (%s, %s, %s, %s, %s, %s, 5, 'completed', %s, %s, %s, %s, 1, %s)
  71. """,
  72. (
  73. bill_no,
  74. f"UAT合同评审 {index + 1:04d}",
  75. f"UAT客户{(index % 4) + 1}",
  76. f"UAT-CUS-{(index % 4) + 1:03d}",
  77. f"uat.reviewer{(index % 3) + 1}",
  78. f"评审人{(index % 3) + 1}",
  79. f"uat.reviewer{(index % 3) + 1}",
  80. create_time,
  81. f"uat.reviewer{(index % 3) + 1}",
  82. update_time,
  83. tenant_id,
  84. ),
  85. )
  86. review_id = int(cur.lastrowid)
  87. result["insertedReviews"] += 1
  88. cursor_time = create_time
  89. for stage_no, stage_name in STAGES:
  90. stage_hours = 2 + (stage_no % 3)
  91. if stage_no == 1:
  92. for seq, (dept_no, dept_name) in enumerate(DEPTS, 1):
  93. start = cursor_time + timedelta(hours=seq - 1)
  94. complete = start + timedelta(hours=1, minutes=20 + seq * 5)
  95. cur.execute(
  96. """
  97. INSERT INTO ado_contract_review_flow
  98. (ReviewRecID, ReviewBillNo, StageNo, StageName, Department, DeptNo, Seq,
  99. ReviewerAccount, ReviewerName, StartTime, CompleteTime, ActualDays,
  100. NodeStatus, tenant_id)
  101. VALUES
  102. (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'approved', %s)
  103. """,
  104. (
  105. review_id, bill_no, stage_no, stage_name, dept_name, dept_no, seq,
  106. f"uat.{dept_no.lower()}", dept_name, start, complete,
  107. round((complete - start).total_seconds() / 86400, 4),
  108. tenant_id,
  109. ),
  110. )
  111. result["insertedFlows"] += 1
  112. cursor_time = cursor_time + timedelta(hours=stage_hours)
  113. else:
  114. start = cursor_time
  115. complete = start + timedelta(hours=stage_hours)
  116. cur.execute(
  117. """
  118. INSERT INTO ado_contract_review_flow
  119. (ReviewRecID, ReviewBillNo, StageNo, StageName, Department, DeptNo, Seq,
  120. ReviewerAccount, ReviewerName, StartTime, CompleteTime, ActualDays,
  121. NodeStatus, tenant_id)
  122. VALUES
  123. (%s, %s, %s, %s, %s, 'MPS', 1, %s, %s, %s, %s, %s, 'approved', %s)
  124. """,
  125. (
  126. review_id, bill_no, stage_no, stage_name, stage_name,
  127. f"uat.stage{stage_no}", stage_name, start, complete,
  128. round((complete - start).total_seconds() / 86400, 4),
  129. tenant_id,
  130. ),
  131. )
  132. result["insertedFlows"] += 1
  133. cursor_time = complete
  134. if index == 0:
  135. cur.execute("SELECT COALESCE(MAX(Id), 0) AS max_id FROM ado_product_design")
  136. design_base = int(cur.fetchone()["max_id"])
  137. design_id = design_base + index + 1
  138. draw_start = create_time + timedelta(days=1)
  139. draw_end = draw_start + timedelta(hours=20 + index % 8)
  140. plan_end = draw_start + timedelta(hours=24)
  141. cur.execute(
  142. """
  143. INSERT INTO ado_product_design
  144. (Id, BillNo, ContractNo, ProductKind, DesignLeadAccount, DesignLeadName,
  145. DrawingNo, DrawingPlanStart, DrawingPlanEnd, DrawingDesignCycle,
  146. DrawingActualStart, DrawingActualEnd, Applicant, ApplyDate,
  147. ProductModel, ItemNum, ProductName, Qty, CreateUser, CreateTime,
  148. UpdateUser, UpdateTime, IsActive, tenant_id)
  149. VALUES
  150. (%s, %s, %s, 1, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
  151. %s, %s, %s, 1, %s, %s, %s, %s, 1, %s)
  152. """,
  153. (
  154. design_id,
  155. f"{PD_PREFIX}A-{index + 1:04d}",
  156. bill_no,
  157. f"uat.design{(index % 2) + 1}",
  158. f"设计人{(index % 2) + 1}",
  159. f"DWG-{index + 1:04d}",
  160. draw_start,
  161. plan_end,
  162. int((draw_end - draw_start).total_seconds() // 3600),
  163. draw_start,
  164. draw_end,
  165. f"uat.reviewer{(index % 3) + 1}",
  166. create_time,
  167. f"UAT-MODEL-{(index % 5) + 1}",
  168. f"UAT-ITEM-{(index % 5) + 1:03d}",
  169. f"UAT产品{index + 1}",
  170. f"uat.design{(index % 2) + 1}",
  171. create_time,
  172. f"uat.design{(index % 2) + 1}",
  173. draw_end,
  174. tenant_id,
  175. ),
  176. )
  177. result["insertedDesigns"] += 1
  178. conn.commit()
  179. except Exception:
  180. conn.rollback()
  181. raise
  182. finally:
  183. conn.close()
  184. return result
  185. def main() -> None:
  186. parser = argparse.ArgumentParser()
  187. parser.add_argument("--tenant", type=int, default=DEFAULT_TENANT)
  188. parser.add_argument("--dry-run", action="store_true")
  189. args = parser.parse_args()
  190. print(json.dumps(seed(args.tenant, args.dry_run), ensure_ascii=False, indent=2, default=str))
  191. if __name__ == "__main__":
  192. main()