crm_package_spec.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. """Contract / quote product package_spec codec: JSON array with legacy scalar support."""
  2. from __future__ import annotations
  3. import json
  4. from typing import Any, Iterable, List, Optional
  5. MAX_PACKAGE_SPECS_PER_LINE = 20
  6. def normalize_package_specs_list(values: Optional[Iterable[Any]]) -> List[str]:
  7. """Trim, drop empties, dedupe while preserving order."""
  8. out: List[str] = []
  9. seen = set()
  10. for raw in values or []:
  11. s = str(raw or "").strip()
  12. if not s or s in seen:
  13. continue
  14. seen.add(s)
  15. out.append(s)
  16. if len(out) >= MAX_PACKAGE_SPECS_PER_LINE:
  17. break
  18. return out
  19. def decode_package_specs(value: Any) -> List[str]:
  20. """Decode DB/API stored package_spec into a list of specs.
  21. - JSON array of strings → list (normalized)
  22. - legacy non-empty scalar → one-element list
  23. - empty / None → []
  24. """
  25. if value is None:
  26. return []
  27. if isinstance(value, (list, tuple)):
  28. return normalize_package_specs_list(value)
  29. text = str(value).strip()
  30. if not text:
  31. return []
  32. if text.startswith("["):
  33. try:
  34. parsed = json.loads(text)
  35. if isinstance(parsed, list):
  36. return normalize_package_specs_list(parsed)
  37. except (json.JSONDecodeError, TypeError, ValueError):
  38. pass
  39. return [text]
  40. def encode_package_specs(specs: Optional[Iterable[Any]]) -> str:
  41. """Encode list to JSON array string for DB; empty list → ''."""
  42. normalized = normalize_package_specs_list(specs)
  43. if not normalized:
  44. return ""
  45. return json.dumps(normalized, ensure_ascii=False)
  46. def resolve_package_specs_from_payload(data: Optional[dict]) -> List[str]:
  47. """Prefer packageSpecs / package_specs; fallback to scalar packageSpec / package_spec."""
  48. if not data:
  49. return []
  50. if "packageSpecs" in data and data.get("packageSpecs") is not None:
  51. return normalize_package_specs_list(data.get("packageSpecs"))
  52. if "package_specs" in data and data.get("package_specs") is not None:
  53. return normalize_package_specs_list(data.get("package_specs"))
  54. for key in ("packageSpec", "package_spec"):
  55. if key in data and data.get(key) is not None:
  56. return decode_package_specs(data.get(key))
  57. return []