Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

187 linhas
6.1 KiB

  1. #!/usr/bin/env python3
  2. """Generate Liquibase seed SQL from 工埸產品保質期 Excel (joExpiry.xlsx).
  3. Stores both chilled defaultDays and minus18Days.
  4. useMinus18 = 1 only when the row has no non-18 shelf life (frozen-only).
  5. Flip useMinus18 in DB later for items that should print -18 days.
  6. Example:
  7. py scripts/generate_item_default_shelf_life_liquibase.py
  8. py scripts/generate_item_default_shelf_life_liquibase.py "C:\\Users\\Administrator\\Downloads\\joExpiry.xlsx"
  9. """
  10. from __future__ import annotations
  11. import argparse
  12. import datetime as dt
  13. from pathlib import Path
  14. import openpyxl
  15. PREFERRED_SHEET = "XXXXXXXX"
  16. OUT_SQL = Path(
  17. "src/main/resources/db/changelog/changes/20260820_item_default_shelf_life/"
  18. "05_seed_item_default_shelf_life_minus18_flag.sql"
  19. )
  20. def sql_str(value: str | None) -> str:
  21. if value is None:
  22. return "NULL"
  23. return "'" + value.replace("\\", "\\\\").replace("'", "''") + "'"
  24. def sql_int(value: int | None) -> str:
  25. return "NULL" if value is None else str(value)
  26. def norm_temp(raw) -> str | None:
  27. if raw is None:
  28. return None
  29. s = str(raw).strip().replace("℃", "").replace("°C", "").replace(" ", "")
  30. if not s or s.lower() in ("n/a", "none", "null", "-", "nan"):
  31. return None
  32. return s
  33. def is_minus18(temp: str | None) -> bool:
  34. return temp in ("-18", "-18.0")
  35. def to_days(raw) -> int | None:
  36. if raw is None or raw == "":
  37. return None
  38. if isinstance(raw, str) and raw.strip().lower() in ("n/a", "-", "none", "null"):
  39. return None
  40. try:
  41. n = int(round(float(raw)))
  42. except (TypeError, ValueError):
  43. return None
  44. return n if n > 0 else None
  45. def pick_days(t1, d1, t2, d2) -> tuple[int | None, str | None, int | None]:
  46. """Return (chilledDays, chilledTemp, minus18Days)."""
  47. chilled_days = None
  48. chilled_temp = None
  49. minus18_days = None
  50. for temp, days in ((norm_temp(t1), to_days(d1)), (norm_temp(t2), to_days(d2))):
  51. if not temp or not days:
  52. continue
  53. if is_minus18(temp):
  54. if minus18_days is None:
  55. minus18_days = days
  56. elif chilled_days is None:
  57. chilled_days = days
  58. chilled_temp = temp
  59. return chilled_days, chilled_temp, minus18_days
  60. def load_rows(xlsx: Path) -> tuple[str, list[dict]]:
  61. wb = openpyxl.load_workbook(xlsx, data_only=True)
  62. sheet_name = PREFERRED_SHEET if PREFERRED_SHEET in wb.sheetnames else wb.sheetnames[0]
  63. ws = wb[sheet_name]
  64. kept: list[dict] = []
  65. seen: set[str] = set()
  66. for row in ws.iter_rows(min_row=4, values_only=True):
  67. code_raw = row[3]
  68. if not code_raw:
  69. continue
  70. code = str(code_raw).strip().upper()
  71. if not code or code in seen:
  72. continue
  73. chilled, storage, minus18 = pick_days(row[5], row[6], row[7], row[8])
  74. if chilled is None and minus18 is None:
  75. continue
  76. seen.add(code)
  77. use_minus18 = 1 if chilled is None else 0
  78. name = str(row[4]).strip() if row[4] else ""
  79. cat = str(row[2]).strip() if row[2] else ""
  80. remark = f"{cat} {name}".strip()[:255]
  81. kept.append(
  82. {
  83. "itemCode": code,
  84. "defaultDays": chilled,
  85. "minus18Days": minus18,
  86. "useMinus18": use_minus18,
  87. "storageC": "-18" if use_minus18 else storage,
  88. "remarks": remark,
  89. }
  90. )
  91. return sheet_name, kept
  92. def render_sql(sheet_name: str, kept: list[dict], source_name: str) -> str:
  93. today = dt.date.today().isoformat()
  94. n_flag = sum(1 for r in kept if r["useMinus18"] == 1)
  95. lines = [
  96. "--liquibase formatted sql",
  97. "",
  98. "--changeset fpsms:seed_item_default_shelf_life_minus18_flag",
  99. f"--comment: Upsert joExpiry.xlsx sheet {sheet_name}: defaultDays + minus18Days + useMinus18 flag",
  100. f"-- generated {today}; rows={len(kept)} useMinus18=1 (frozen-only default)={n_flag}",
  101. "INSERT INTO `item_default_shelf_life`",
  102. "(`created`, `createdBy`, `version`, `modified`, `modifiedBy`, `deleted`,",
  103. " `itemCode`, `defaultDays`, `minus18Days`, `useMinus18`, `openedDays`, `storageC`, `remarks`)",
  104. "VALUES",
  105. ]
  106. value_rows = []
  107. for r in kept:
  108. value_rows.append(
  109. "("
  110. + ", ".join(
  111. [
  112. "NOW()",
  113. "'system'",
  114. "0",
  115. "NOW()",
  116. "'system'",
  117. "0",
  118. sql_str(r["itemCode"]),
  119. sql_int(r["defaultDays"]),
  120. sql_int(r["minus18Days"]),
  121. str(r["useMinus18"]),
  122. "NULL",
  123. sql_str(r["storageC"]),
  124. sql_str(r["remarks"]),
  125. ]
  126. )
  127. + ")"
  128. )
  129. lines.append(",\n".join(value_rows))
  130. lines.append("ON DUPLICATE KEY UPDATE")
  131. lines.append(" `defaultDays` = VALUES(`defaultDays`),")
  132. lines.append(" `minus18Days` = VALUES(`minus18Days`),")
  133. lines.append(" `useMinus18` = VALUES(`useMinus18`),")
  134. lines.append(" `openedDays` = VALUES(`openedDays`),")
  135. lines.append(" `storageC` = VALUES(`storageC`),")
  136. lines.append(" `remarks` = VALUES(`remarks`),")
  137. lines.append(" `modified` = NOW(),")
  138. lines.append(" `modifiedBy` = 'system',")
  139. lines.append(" `deleted` = 0;")
  140. lines.append("")
  141. return "\n".join(lines)
  142. def main() -> None:
  143. parser = argparse.ArgumentParser()
  144. parser.add_argument(
  145. "xlsx",
  146. nargs="?",
  147. default=str(Path.home() / "Downloads" / "joExpiry.xlsx"),
  148. )
  149. args = parser.parse_args()
  150. xlsx = Path(args.xlsx)
  151. if not xlsx.is_file():
  152. raise SystemExit(f"Excel not found: {xlsx}")
  153. sheet_name, kept = load_rows(xlsx)
  154. sql = render_sql(sheet_name, kept, xlsx.name)
  155. OUT_SQL.parent.mkdir(parents=True, exist_ok=True)
  156. OUT_SQL.write_text(sql, encoding="utf-8")
  157. n_flag = sum(1 for r in kept if r["useMinus18"] == 1)
  158. print(f"sheet={sheet_name} rows={len(kept)} useMinus18=1={n_flag} useMinus18=0={len(kept) - n_flag}")
  159. print(f"wrote {OUT_SQL}")
  160. if __name__ == "__main__":
  161. main()