Yu-Gi-Oh! Deck Building and Card Inventory Management web interface written in Common Lisp, utilizing HTMX.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
11KB

  1. #|
  2. src/web/construct-decks.lisp
  3. Web Routes For Construct Decks
  4. TODO Pick one: /construct/ or /construct/ ?
  5. Features Requsted:
  6. - DONE Card Names
  7. - DONE Card Prices - I think we'll need to wire this up to the
  8. YGO-CC-ITEM. That requires rewriting that component to use the new
  9. YGO-SET instead of the CSV.
  10. - DONE Sort by Price - Honestly having trouble sorting this junk lmao!
  11. - Show only YGO-SET-ITEMS with inventory - see constructed-decks.lisp<models>
  12. |#
  13. (in-package #:cl-deck-builder2.web)
  14. (defparameter *current-constructed-deck* nil)
  15. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  16. (defun render-construct-deck (id tpl)
  17. (with-logged-in-user
  18. (let ((deck (first
  19. (select-constructed-decks
  20. (sxql:where (:= :id id))))))
  21. (when deck
  22. (render-with-env tpl
  23. (list :active "/construct"
  24. :deck deck
  25. :id (mito:object-id deck)
  26. :name (cl-deck-builder2.models.constructed-decks::deck-name-of deck)
  27. :saved-deck-list (select-constructed-decks)))))))
  28. ;; I think this takes a CAR and a CDR pair of YGO-SET ID and VARIANT ID
  29. ;; I'm pretty sure it's supposed to be finding YGO-SET-ITEMs though
  30. (defun selected-sets-to-deck-listing (cards)
  31. (let ((lst '()))
  32. (with-connection (db)
  33. (with-transaction
  34. (dolist (card cards (reverse lst))
  35. (destructuring-bind (set variant)
  36. card
  37. (let* ((set-id (cdr set))
  38. (variant-id (cdr variant))
  39. (set-item (first (ygo-select-set-item set-id variant-id)))
  40. (set (first (ygo-set (sxql:where (:= :id set-id)))))
  41. (variant (mito:find-dao 'variant-condition :id variant-id)))
  42. (with-slots ((passcode-id cl-deck-builder2.models.ygoprodeck.classes::passcode-id))
  43. set
  44. (push (list :deck-set set
  45. :condition variant
  46. :set-item set-item
  47. :card (ygo-card-by-passcode passcode-id))
  48. lst)))))))))
  49. ;; XXX Where does this go? What does this accomplish?
  50. (defun select-deck-as-plist (id)
  51. (let ((lst '()))
  52. (dolist (itm (retrieve-dao 'deck-item :deck-id id) (reverse lst))
  53. (push (list :deck-item itm) lst))))
  54. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  55. (defun default-constructed-deck-is-valid-to-pull ()
  56. (valid-pull-p
  57. (deck-to-pull-set-items-qty-as-alist *current-constructed-deck*)
  58. (deck-to-pull-desired-qty-as-alist *current-constructed-deck*)))
  59. (defun default-constructed-deck-invalid-qtys ()
  60. (find-any-invalid-qtys
  61. (deck-to-pull-set-items-qty-as-alist *current-constructed-deck*)
  62. (deck-to-pull-desired-qty-as-alist *current-constructed-deck*)))
  63. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  64. (defun store-combined-deck-as-current (original-deck selected-listing)
  65. (setf *current-constructed-deck*
  66. (loop for original in (reverse original-deck)
  67. for selected in selected-listing
  68. collect (apply #'make-instance 'constructed-deck-intermediate (append original selected)))))
  69. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  70. ;; We have to actually pull the inventory.
  71. ;; This is the old code but it's written for YDK-DECK object so maybe I'll write one for CONSTRUCTED-DECK-INTERMEDIATE.
  72. (defun pull-from-inventory (cdi-items)
  73. "Attempt to find inventory stock for every card in this deck, and if we are able to secure it, construct and insert a new PULLED-DECK into the database."
  74. (let ((counts (deck-to-pull-desired-qty-as-alist cdi-items)))
  75. (dolist (row counts)
  76. (decf (qty-of (car row)) (cdr row))
  77. ;; Update each one and save - wrapping this in a transaction causes issues with pulling multiple items.
  78. (save-dao (car row)))))
  79. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  80. (defroute ("/construct" :method :GET) ()
  81. "Constructed Deck View Main Route. Login Required."
  82. (v:info :construct "GET /construct")
  83. (with-logged-in-user
  84. (render-with-env #P"construct/index.html"
  85. (list :active "/construct"
  86. :saved-deck-list (select-constructed-decks)))))
  87. (defroute ("/construct/:id/view" :method :GET) (&key id)
  88. "Constructed Deck View deck by ID Route. Login Required."
  89. (v:info :construct "GET /construct/~d" id)
  90. (with-logged-in-user
  91. (render-construct-deck id #P"construct/index.html")))
  92. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  93. (defroute ("/construct/ygo-set-item" :method :GET) (&key _parsed)
  94. "Find the YGO-SET-ITEM associated with the |SET-ID| and |VARIANT-ID|
  95. TODO This needs a rewrite as it currently actually accepts multiple
  96. CARDs and operates on their component |SET-ID| and |VARIANT-ID| which
  97. is really not optimal. Ideally we should be handling this all in bulk
  98. with better routes."
  99. (v:info :cards "GET /cards/ygo-set-items ~a" _parsed)
  100. (with-logged-in-user
  101. (alexandria:if-let ((cards (query-param "cards" _parsed)))
  102. (dolist (card cards)
  103. (let ((|set-id| (query-param "set-id" card))
  104. (|variant-id| (query-param "variant-id" card)))
  105. (handler-case
  106. (ratify:with-parsed-forms
  107. ((:integer |set-id|)
  108. (:integer |variant-id|))
  109. (return
  110. (render-with-env #P"cards/variant-results.html"
  111. (list :id (gensym)
  112. :item (find |variant-id|
  113. (select-ygo-cc-item-variants |set-id|)
  114. :key (alexandria:compose #'mito:object-id #'variant-of))))))
  115. (ratify:combined-error (e)
  116. (flash-error (format nil "/cards/ygo-set-items => ~d:~d ~a~%" |set-id| |variant-id| e)))))))))
  117. (defroute ("/construct/:id/select-sets" :method :GET) (&key id)
  118. "Select the YDK-SET for the corresponding PASSCODEs. Use Deck ID from the deck builder."
  119. (v:info :construct "GET /construct/~d/select-sets" id)
  120. (with-logged-in-user
  121. (with-connection (db)
  122. (let* ((cards (mito:retrieve-dao 'deck-item :deck-id id))
  123. (sets (mapcar (alexandria:compose #'ygo-card-sets #'deck-passcode-of) cards))
  124. (variants (select-variant-condition)))
  125. (render-with-env #P"construct/select-sets.html"
  126. (list :sets (reverse sets)
  127. :deck-id id
  128. :variants variants))))))
  129. (defroute ("/construct/:id/select-sets" :method :POST) (&key id _parsed)
  130. "Constructed Decks Select Sets POST Route - Attempt to SUBTRACT-DESIRED-FROM-SET-ITEM-QTY-AS-ALIST, and if VALID-PULL-P, call PULL-FROM-INVENTORY. Otherwise, FLASH-ERROR to the user about Insufficient Inventory."
  131. (v:info :construct "POST /construct/~d/select-sets ~a" id _parsed)
  132. (with-logged-in-user
  133. (alexandria:if-let ((cards (query-param "cards" _parsed)))
  134. (let ((original-deck (select-deck-as-plist id))
  135. (selected-listing (selected-sets-to-deck-listing cards)))
  136. ;; Store the current constructed deck template
  137. (store-combined-deck-as-current original-deck selected-listing)
  138. ;; See if there are any issues with the current inventory
  139. (if (default-constructed-deck-is-valid-to-pull)
  140. (progn
  141. ;; Decrement the stock from the YGO-SET-ITEM
  142. (pull-from-inventory *current-constructed-deck*)
  143. ;; Create a new CONSTRUCTED-DECK based on *CURRENT-CONSTRUCTED-DECK*
  144. (cdi-pull-from-inventory *current-constructed-deck*)
  145. (flash-message (_ "Deck constructed!"))
  146. (render-with-env #P"construct/constructed-deck-list.html"
  147. (list :original-deck original-deck
  148. :deck-id id
  149. :total-price nil
  150. :table *current-constructed-deck*)))
  151. (let ((err (default-constructed-deck-invalid-qtys)))
  152. (with-connection (db)
  153. (flash-error (format nil (_ "Insufficient Inventory: <a href=\"/cards/by-passcode/~a\" target=\"_blank\">~a</a>")
  154. ;; TODO all this nesting again...
  155. (ygo-passcode-of err)
  156. (ygo-passcode-of err))))
  157. (redirect (format nil "/construct/~d/select-sets" id))))))))
  158. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  159. (defroute ("/construct/:id/deconstruct" :method :POST) (&key id _parsed)
  160. "Deconstruct deck. That is, increment the QTY of the the contents to of the CONSTRUCTED-DECK-AS-CDI-LIST."
  161. (v:info :construct "POST /construct/~d/deconstruct ~a" id _parsed)
  162. (with-logged-in-user
  163. (return-to-inventory
  164. (constructed-deck-as-cdi-list id)))
  165. (_ "Ok!"))
  166. (defroute ("/construct/:id/selected-sets" :method :GET) (&key id _parsed)
  167. "Query CONSTRUCTED-DECK ID for the YGO-SET-ITEMS it contains. We use CONSTRUCTED-DECK-AS-CDI-LIST again."
  168. (v:info :construct "GET /construct/~d/selected-sets ~a" id _parsed)
  169. (with-logged-in-user
  170. (let ((table (constructed-deck-as-cdi-list id)))
  171. (render-with-env #P"construct/constructed-deck-list.html"
  172. (list :table table)))))
  173. (defroute ("/construct/:id/update" :method :POST) (&key id |name| |sell-price| |sold| _parsed)
  174. "Update a CONSTRUCTED-DECK by ID"
  175. (v:info :construct "POST /construct/~d/update ~a" id _parsed)
  176. (with-logged-in-user
  177. (let ((found (find-dao 'constructed-deck :id id)))
  178. (when found
  179. (when |name| (setf (deck-name-of found) |name|))
  180. (when |sell-price| (setf (deck-sell-price-of found) |sell-price|))
  181. (when (string= |sold| "on") (setf (deck-sold found) 1))
  182. (update-dao found)
  183. (flash-message (_ "Deck Updated"))
  184. (redirect (format nil "/construct/~d/view" id))))))
  185. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  186. ;; This one looks like: Get all the cards in a deck id 13 and query all the sets using YGO-CARD-SETS
  187. ;; (let* ((cards (retrieve-dao 'deck-item :deck-id 13))
  188. ;; (all-sets (mapcar #'deck-passcode-of cards)))
  189. ;; (mapcar #'cl-deck-builder2.models.ygoprodeck.methods::ygo-card-sets
  190. ;; (reverse all-sets)))
  191. ;; (with-connection (db)
  192. ;; (with-transaction
  193. ;; (let ((v (mito:find-dao 'cl-deck-builder2.models.ygoprodeck.fields::variant-condition :name "Near Mint")))
  194. ;; (mapcar (lambda (set)
  195. ;; (mito:find-dao 'cl-deck-builder2.models.ygoprodeck.classes::ygo-set-item
  196. ;; :item set
  197. ;; :variant v))
  198. ;; *))))