serializer.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. from pathlib import Path
  2. from app.schema import DocumentProduct, CartOrderInfo, CartSessionItemOptions
  3. def typeset_flat_list(item_key: str, item_list: list[str | int]) -> str:
  4. """Convert flat list to string in the following format:
  5. => key: ["a", "b", 10, 44]
  6. """
  7. flat_list = []
  8. for s in item_list:
  9. flat_list.append(f'"{s}"')
  10. item = f" {item_key}: [{', '.join(flat_list)}]"
  11. return item
  12. def convert_pydantic_product_to_text(doc: DocumentProduct) -> str:
  13. """Convert a Pydantic model object (Product) into a text document
  14. ready to be written to disk.
  15. """
  16. doc_dict = doc.model_dump()
  17. doc_dict_meta = doc_dict["meta"]
  18. # -- options
  19. options = []
  20. if doc.meta.options:
  21. options.append("options:")
  22. if doc_dict_meta["options"]["size"]:
  23. size = typeset_flat_list("size", doc_dict_meta["options"]["size"])
  24. else:
  25. size = " size: "
  26. if doc_dict_meta["options"]["style"]:
  27. style = typeset_flat_list("style", doc_dict_meta["options"]["style"])
  28. else:
  29. style = " style: "
  30. options.append(size)
  31. options.append(style)
  32. if len(options):
  33. options = "\n".join(options)
  34. # -- inventory
  35. inventory = ["inventory:"]
  36. for item in doc_dict_meta["inventory"]:
  37. unit = []
  38. if item["product_variation_id"]:
  39. unit.append(f" - product_variation_id: \"{item['product_variation_id']}\"")
  40. else:
  41. unit.append(" - product_variation_id:")
  42. if item["size"]:
  43. unit.append(f" size: \"{item['size']}\"")
  44. else:
  45. unit.append(" size:")
  46. if item["style"]:
  47. unit.append(f" style: \"{item['style']}\"")
  48. else:
  49. unit.append(" style:")
  50. unit.append(f" amount: {item['amount']}")
  51. unit = "\n".join(unit)
  52. inventory.append(unit)
  53. inventory = "\n".join(inventory)
  54. # -- assemble txt_meta
  55. txt_meta = (
  56. "---",
  57. f"type: \"{doc_dict_meta['type']}\"",
  58. f"title: \"{doc_dict_meta['title']}\"",
  59. f"template: \"{doc_dict_meta['template']}\"",
  60. f"code: \"{doc_dict_meta['code']}\"",
  61. f"product_id: \"{doc_dict_meta['product_id']}\"",
  62. f"price: {doc_dict_meta['price']}",
  63. f"weight: {doc_dict_meta['weight']}",
  64. f"customs_tariff_code: \"{doc_dict_meta['customs_tariff_code']}\"",
  65. f"origin_country_iso: \"{doc_dict_meta['origin_country_iso']}\"",
  66. f"category: \"{doc_dict_meta['category']}\"",
  67. )
  68. if len(options):
  69. txt_meta = txt_meta + (f"{options}",)
  70. txt_meta = txt_meta + (f"{inventory}",)
  71. # --
  72. txt_blocks = []
  73. for block in doc_dict["blocks"]:
  74. if block["type"] == "text":
  75. txt_blocks.append(block["value"])
  76. elif block["type"] == "image":
  77. caption = ""
  78. if block["caption"]:
  79. caption = f"caption: \"{block['caption']}\""
  80. url_p = Path(block["url"])
  81. url = f"{url_p.stem}{url_p.suffix}"
  82. block_img = (
  83. f"type: \"{block['type']}\"",
  84. f'url: "{url}"',
  85. f"{caption}",
  86. )
  87. block_img = "\n".join(block_img)
  88. txt_blocks.append(block_img)
  89. txt_meta = "\n".join(txt_meta)
  90. txt_blocks = "\n---\n".join(txt_blocks)
  91. txt_doc = "\n---\n".join((txt_meta, txt_blocks))
  92. txt_doc = f"{txt_doc}\n"
  93. return txt_doc
  94. def format_yaml_multiline(text: str, indent: bool = False) -> str:
  95. """
  96. Manually indent multiline values for YAML key.
  97. """
  98. lines = text.splitlines()
  99. if indent:
  100. lines = [f"{line.rjust(len(line) +4)}" for line in lines]
  101. lines = "\n".join(lines)
  102. return lines
  103. def typeset_order_content(
  104. items_table, currency: str, subtotal: int, shipping: int, total: int
  105. ) -> str:
  106. """Return a multiline string of the orders' content (list of product
  107. items, price, etc.). Useful for when sending out an email.
  108. """
  109. content_order_txt = []
  110. for i in items_table:
  111. item_txt = f"- {i.quantity}x {i.title}, {currency} {i.price}\n"
  112. content_order_txt.append(item_txt)
  113. subtotal_txt = f"\nSubtotal {currency} {subtotal}\n"
  114. shipping_txt = f"Shipping {currency} {shipping}\n"
  115. total_txt = f"Total {currency} {total}"
  116. content_order_txt.append(subtotal_txt)
  117. content_order_txt.append(shipping_txt)
  118. content_order_txt.append(total_txt)
  119. return "".join(content_order_txt)
  120. def typeset_order_shipping_info(shipping_info):
  121. """Return a multiline string of the shipping info, useful for when
  122. sending out an email.
  123. """
  124. shipping_info_txt = []
  125. if shipping_info.first_name:
  126. shipping_info_txt.append(
  127. f"{shipping_info.first_name} {shipping_info.last_name}\n"
  128. )
  129. else:
  130. shipping_info_txt.append(f"{shipping_info.last_name}\n")
  131. address_full = f"{shipping_info.address}, {shipping_info.address_no}"
  132. shipping_info_txt.append(f"{address_full}\n")
  133. if shipping_info.address_extra:
  134. shipping_info_txt.append(f"{shipping_info.address_extra}\n")
  135. shipping_info_txt.append(f"{shipping_info.postal_code} {shipping_info.city}\n")
  136. shipping_info_txt.append(f"{shipping_info.country}")
  137. if shipping_info.phone_number:
  138. shipping_info_txt.append(f"{shipping_info.phone_number}")
  139. if shipping_info.note:
  140. note = format_yaml_multiline(shipping_info.note)
  141. shipping_info_txt.append(f"\n\n{note}")
  142. shipping_info_txt = "".join(shipping_info_txt)
  143. return shipping_info_txt
  144. def format_order_items(items: list[CartOrderInfo | dict[str, str | int]]) -> str:
  145. """
  146. """
  147. items_table = []
  148. for i in items:
  149. if type(i) is dict:
  150. i['options'] = CartSessionItemOptions(size=None, style=None)
  151. i = CartOrderInfo(**i)
  152. item_title = i.title
  153. if i.options.style:
  154. item_title += f" - {i.options.style.capitalize()}"
  155. if i.options.size:
  156. item_title += f" Size {i.options.size.upper()}"
  157. item = (
  158. f" - code: '{i.code}'\n"
  159. f" title: '{item_title}'\n"
  160. f" quantity: '{i.quantity}'\n"
  161. f" price: '{i.price}'\n"
  162. )
  163. items_table.append(item)
  164. items_table = "".join(items_table)
  165. return items_table
  166. def format_order_shipping_info(shipping_info) -> str:
  167. """
  168. """
  169. note = ""
  170. if shipping_info.note:
  171. note = f"|\n{format_yaml_multiline(shipping_info.note, indent=True)}"
  172. shipping_info_table = (
  173. f" first_name: '{shipping_info.first_name or ''}'\n"
  174. f" last_name: '{shipping_info.last_name}'\n"
  175. f" address: '{shipping_info.address}'\n"
  176. f" address_no: '{shipping_info.address_no}'\n"
  177. f" address_extra: '{shipping_info.address_extra or ''}'\n"
  178. f" postal_code: '{shipping_info.postal_code}'\n"
  179. f" city: '{shipping_info.city}'\n"
  180. f" country: '{shipping_info.country}'\n"
  181. f" note: '{note}'\n"
  182. f" email: '{shipping_info.email}'\n"
  183. f" phone_number: '{shipping_info.phone_number or ''}'\n"
  184. )
  185. return shipping_info_table