Yu-Gi-Oh! Deck Building and Card Inventory Management web interface written in Common Lisp, utilizing HTMX.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

209 řádky
7.7KB

  1. #|
  2. src/models/crystal-commerce-csv.lisp
  3. Crystal Commerce Search CSV Export / Import functionality.
  4. This code is so old. It dates back to May. I mistakenly assumed we
  5. would get access to Crystal Commerce or TCGPlayer API data.
  6. Instead, I based my initial database design off of the CSV export
  7. data. The CSV export data does not supply the following necessary
  8. fields: Passcode (Card Image), Variants (Condition).
  9. I learned July 15 that this design was insufficient and set out to
  10. replace it with something more robust. Within 2 weeks by the beginning
  11. of August I had conceptualized a new design, and by the second week of
  12. August, had implemented that design. That is currenly YGOPRODECK-2.
  13. There is some munging facility in here that I wrote while munging the
  14. YGOProDeck API data during the v2 rewrite.
  15. TODO This needs to be rewritten to match the new YGOProDeck v3
  16. API. Currently we scan in the Product Name, it could be split out to
  17. match the corresponding db entry.
  18. TODO Some time in October I became aware we have access to Crystal Commerce API now.
  19. https://crystal-service.readme.io/docs/get-started-with-the-admin-api
  20. https://crystal-service.readme.io/reference/get_api-v1-activity-logs
  21. |#
  22. (in-package #:cl-user)
  23. (defpackage #:cl-deck-builder2.models.crystal-commerce.csv
  24. (:use #:cl
  25. #:cl-deck-builder2.db
  26. #:cl-deck-builder2.models.generics
  27. #:cl-deck-builder2.models.crystal-commerce)
  28. (:import-from #:cl-deck-builder2.toolkit
  29. #:grouped)
  30. (:local-nicknames (#:v #:org.shirakumo.verbose))
  31. (:export #:*cc-csv-import-fields*
  32. #:*cc-csv-header-fields*
  33. #:csv-import-cc))
  34. (in-package #:cl-deck-builder2.models.crystal-commerce.csv)
  35. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  36. ;; CSV Import
  37. (defvar *csv* nil
  38. "The currently loaded CSV data.")
  39. (defvar *card-sets* nil
  40. "Data loaded from CARD-SET table. We use datafly for this one because
  41. mito conses a lot for CLOS. A PLIST is faster.")
  42. (defvar *cc-csv-import-fields*
  43. '("Product Name"
  44. "Category"
  45. "Total Qty"
  46. "Wishlists"
  47. "Buy Price"
  48. "Sell Price"
  49. "URL"
  50. "Barcode"
  51. "Manufacturer SKU"
  52. "Amazon ASIN"
  53. "MSRP"
  54. "Brand"
  55. "Weight"
  56. "Description"
  57. "Max Qty"
  58. "Domestic Only"
  59. "Tax Exempt")
  60. "Crystal Commerce header fields via CSV import. We transform this into *CC-CSV-HEADER-FIELDS*.
  61. SEE *CC-CSV-HEADER-FIELDS*.")
  62. (defvar *cc-csv-header-fields*
  63. (mapcar (lambda (s)
  64. (alexandria:make-keyword
  65. (string-upcase
  66. (substitute #\- #\Space s))))
  67. *cc-csv-import-fields*)
  68. "Transformed list of Keyword-ified header fields. That is, we take *CC-CSV-IMPORT-FIELDS* and transform the strings into keywords:
  69. \"Product Name\" => :PRODUCT-NAME.")
  70. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  71. (defun load-csv (maybe-pathname &optional (force nil))
  72. "Cache CSV data from MAYBE-PATHNAME which may be a PATHNAME or a STRING containing CSV data."
  73. (if (or force
  74. (null *csv*))
  75. (progn
  76. (v:info :cc-csv "Loading CSV from ~A~%"
  77. (typecase maybe-pathname
  78. (pathname maybe-pathname)
  79. (string 'STRING)))
  80. (setf *csv*
  81. ;; Skip the first header line
  82. (rest
  83. (cl-csv:read-csv maybe-pathname))))
  84. (v:info :cc-csv "Using previously loaded CSV data of length ~d~%" (length *csv*))))
  85. (defun load-card-sets ()
  86. "Cache CARD-SET info to match up from PRODUCT-NAME."
  87. (unless *card-sets*
  88. (with-datafly-connection (db)
  89. (with-datafly-transaction
  90. (setf *card-sets*
  91. (datafly:retrieve-all
  92. (sxql:select (:code :passcode)
  93. (sxql:from :ygo_set))))))))
  94. ;; TODO update this code with the new YGOProDeck stuff
  95. (defun normalize-product-name (name)
  96. "Given a PRODUCT-NAME, apply the extracted data from the string: Name,
  97. Set Code, Rarity, and Edition. Return a property list that can be
  98. passed to SXQL:SET=."
  99. (let ((normalized-name name))
  100. ;; XXX EW
  101. ;; Replace things like "Rare-" and "Common-" With "Rare" and "Common"
  102. (setf normalized-name (cl-ppcre:regex-replace "(\\w+)- " normalized-name "\\1"))
  103. ;; A few single cards is borked
  104. (setf normalized-name (cl-ppcre:regex-replace "MRD-EN134 - Unlimited" normalized-name "MRD-EN134 - Common - Unlimited"))
  105. (setf normalized-name (cl-ppcre:regex-replace "SXG3-ENE05" normalized-name "SGX3-ENE05"))
  106. (setf normalized-name (cl-ppcre:regex-replace "wCPP-EN002" normalized-name "WCPP-EN002"))
  107. ;; This set was borknd
  108. ;; Maybe not, maybe it just doesn't have a Limited/Unlimited status?
  109. ;; (setf normalized-name (cl-ppcre:regex-replace "TKN4-([^ ]+) - Super Rare" normalized-name "TKN4-\\1 - Super Rare - Unlimited"))
  110. ;; Replace things like Rare 1st Edition with Rare - 1st Edition
  111. (setf normalized-name (cl-ppcre:regex-replace "(Common|Rare) (1st|Limited|Unlimited)" normalized-name "\\1 - \\2"))
  112. (let ((parts (reverse
  113. (cl-ppcre:split " \\s?[-–] \\s?" normalized-name))))
  114. (case (length parts)
  115. (1 `(:name ,(first parts)))
  116. (2 `(:name ,(format nil "~{~A~^ - ~}" parts)))
  117. (3 `(:name ,(first parts)
  118. :code ,(second parts)
  119. :rarity ,(third parts)))
  120. (4 `(:name ,(first parts)
  121. :code ,(second parts)
  122. :rarity ,(third parts)
  123. :edition ,(fourth parts)))
  124. (5 `(:name ,(format nil "~{~A~^ - ~}" (subseq parts 0 2))
  125. :code ,(third parts)
  126. :rarity ,(fourth parts)
  127. :edition ,(fifth parts)))
  128. (6 `(:name ,(format nil "~{~A~^ - ~}" (subseq parts 0 3))
  129. :code ,(fourth parts)
  130. :rarity ,(fifth parts)
  131. :edition ,(sixth parts)))
  132. (otherwise (format t "Skipping data: ~a~%" name))))))
  133. ;; (defparameter *db-variants*
  134. ;; (let ((ht (make-hash-table)))
  135. ;; (loop for variant in (select-dao 'variant) do
  136. ;; (setf (gethash (variant-name-of variant) ht) variant))
  137. ;; ht))
  138. ;; TODO clean up the field names to auto generate
  139. ;;
  140. ;; TODO I added some additional fields, opt-qty reserved-qty condition language
  141. ;; I guess we will need one of each in the db...
  142. (defun csv-import-cc (csv)
  143. "Import the perfect data from Crystal Commerce. Do nothing with it but import it into the table."
  144. (load-csv csv)
  145. (v:info :cc-csv "CSV Loaded")
  146. ;; (load-card-sets)
  147. ;; (format t "Card sets loaded from DB~&")
  148. (let ((csv-items (seed-csv-items)))
  149. (pre-seed-db-items csv-items))
  150. (v:info :cc-csv "done.~&"))
  151. (defun seed-csv-items ()
  152. (let ((csv-items '()))
  153. (with-connection (db)
  154. (with-transaction
  155. (dolist (row *csv*)
  156. (let ((row-plist (mapcan #'list *cc-csv-header-fields* row)))
  157. (push (or (mito:find-dao 'cc-item :product-name (getf row-plist :product-name))
  158. (apply #'make-instance 'cc-item row-plist))
  159. csv-items)))))
  160. (v:info :cc-csv "CSV Items: ~d" (length csv-items))
  161. (unless (mito:dao-synced (car csv-items))
  162. (do-grouped-insert csv-items))))
  163. (defun pre-seed-db-items (cc-items)
  164. (let ((db-items '()))
  165. (with-connection (db)
  166. (with-transaction
  167. (dolist (cc-item cc-items)
  168. (dolist (variant (select-dao 'variant))
  169. (push (or (mito:find-dao 'ygo-cc-item :item-id (mito:object-id cc-item)
  170. :variant-id (mito:object-id variant))
  171. (make-instance 'ygo-cc-item
  172. :item cc-item
  173. :variant variant))
  174. db-items)))))
  175. (v:info :cc-csv "DB Items: ~d" (length db-items))
  176. (unless (mito:dao-synced (car db-items))
  177. (do-grouped-insert db-items))))