Yu-Gi-Oh! Deck Building and Card Inventory Management web interface written in Common Lisp, utilizing HTMX.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

259 lignes
10KB

  1. #|
  2. src/models/ygoprodeck-2.lisp
  3. Yu-Gi-Oh! Pro Deck Database Interface v2
  4. TODO Documentation
  5. |#
  6. (in-package #:cl-deck-builder2.models.ygoprodeck.methods)
  7. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  8. (defmacro ygo-card (&body body)
  9. `(with-includes
  10. 'ygo-card
  11. (mito:includes
  12. 'ygo-card-archetype
  13. 'ygo-card-attribute
  14. 'ygo-card-frame-type
  15. 'ygo-card-race
  16. 'ygo-card-type)
  17. ,@body))
  18. (defmacro ygo-set (&body body)
  19. `(with-includes 'ygo-set
  20. (mito:includes
  21. 'ygo-card
  22. 'ygo-set-code
  23. 'ygo-set-edition
  24. 'ygo-set-name
  25. 'ygo-set-rarity
  26. 'ygo-set-rarity-code)
  27. ;; (mito:includes
  28. ;; 'ygo-card-name)
  29. ,@body))
  30. (defun ygo-set-by-id (id)
  31. (first (ygo-set (sxql:where (:= :id id)))))
  32. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  33. (defmethod split-code ((set-code ygo-set-code))
  34. (cl-ppcre:split "-" (name-of set-code)))
  35. (defmethod ygo-card-sets ((obj ygo-card))
  36. (ygo-card-sets (ygo-passcode-of obj)))
  37. (defmethod ygo-card-sets ((passcode integer))
  38. ;; If alternate IDs exist, just mush them in
  39. (let ((alt-id (has-alternative-artwork passcode)))
  40. (when alt-id
  41. ;; XXX Why the long accessor name here? How can we get this accessor? Maybe I have to specify it manually.
  42. (with-slots ((alternate-id cl-deck-builder2.models.ygoprodeck.classes::alternate-id)) alt-id
  43. (setf passcode alternate-id)))
  44. (let ((sets
  45. (ygo-set
  46. ;; (sxql:left-join :ygo_card_name :on (:= :ygo-card.name-id :ygo-card-name.id))
  47. (sxql:where (:= :passcode-id passcode))
  48. (sxql:order-by :price :desc))))
  49. sets)))
  50. ;; Only showing cards with inventory
  51. ;; (with-connection (db)
  52. ;; (mito:select-dao 'cl-deck-builder2.models.ygoprodeck.classes::ygo-set-item
  53. ;; (sxql:where (:and (:in :item_id
  54. ;; (sxql:select :id
  55. ;; (sxql:from :ygo_set)
  56. ;; (sxql:where (:= :passcode-id 89631139))))
  57. ;; (:> :qty 0)))))
  58. ;; (mapcar (alexandria:compose #'cl-deck-builder2.models.ygoprodeck2::ygo-card-sets #'deck-passcode-of)
  59. ;; (retrieve-dao 'deck-item :deck-id 5))
  60. ;; (reduce #'+ (mapcar (alexandria:compose #'read-from-string #'cl-deck-builder2.models.ygoprodeck2::ygo-price-of)
  61. ;; (cl-deck-builder2.models.ygoprodeck2::ygo-card-sets (deck-passcode-of (find-dao 'deck-item)))))
  62. ;; price of a deck by id using #'first of the ygo-card-sets
  63. ;;
  64. ;; (reduce '+ (mapcar (alexandria:compose #'read-from-string #'cl-deck-builder2.models.ygoprodeck.classes::ygo-price-of)
  65. ;; (mapcar #'first
  66. ;; (mapcar (alexandria:compose #'cl-deck-builder2.models.ygoprodeck.methods::ygo-card-sets #'deck-passcode-of)
  67. ;; (retrieve-dao 'deck-item :deck-id 308)))))
  68. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  69. (defun ygo-card-by-name (name &optional (test :=))
  70. (let ((clauses (case test
  71. (:= (sxql:where (:= :name name)))
  72. (:like (sxql:where (:like :name (format nil "%~a%" name)))))))
  73. (ygo-card clauses)))
  74. (defun ygo-card-by-passcode (passcode)
  75. (first (ygo-card (sxql:where (:= :id passcode)))))
  76. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  77. (defun ygo-set-by-code (name &optional (test :=))
  78. (let ((clauses (case test
  79. (:= (sxql:where (:= :name name)))
  80. (:like (sxql:where (:like :name (format nil "%~a%" name)))))))
  81. (ygo-set
  82. (sxql:where (:in :code-id
  83. (sxql:select :id
  84. (sxql:from (sxql:make-sql-symbol
  85. (mito.class.table:table-name
  86. (find-class 'ygo-set-code))))
  87. clauses))))))
  88. (defun ygo-set-by-name (name &optional (test :=))
  89. (let ((clauses (case test
  90. (:= (sxql:where (:= :set_name name)))
  91. (:like (sxql:where (:like :set_name (format nil "%~a%" name)))))))
  92. (ygo-set
  93. (sxql:where (:in :code-id
  94. (sxql:select :id
  95. (sxql:from (sxql:make-sql-symbol
  96. (mito.class.table:table-name
  97. (find-class 'ygo-set-name))))
  98. clauses))))))
  99. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  100. (defun ygo-select-set-item (item-id variant-id)
  101. "Helper function. Select a single YGO-SET-ITEM by ITEM-ID and VARIANT-ID."
  102. (mito:select-dao 'ygo-set-item
  103. (mito:includes 'ygo-set 'variant-condition)
  104. (sxql:where (:and (:= :item-id item-id)
  105. (:= :variant-id variant-id)))))
  106. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  107. ;; https://lispcookbook.github.io/cl-cookbook/clos.html#pretty-printing
  108. ;;
  109. ;; (defmethod print-object ((obj person) stream)
  110. ;; (print-unreadable-object (obj stream :type t)
  111. ;; (with-accessors ((name name)
  112. ;; (lisper lisper))
  113. ;; obj
  114. ;; (format stream "~a, lisper: ~a" name lisper))))
  115. ;;
  116. ;; (defmethod print-object ((obj person) stream)
  117. ;; (print-unreadable-object (obj stream :type t)
  118. ;; (format stream "~a, lisper: ~a" (name obj) (lisper obj))))
  119. ;;
  120. ;; (defmethod print-object ((obj person) stream)
  121. ;; (print-unreadable-object (obj stream :type t :identity t)))
  122. ;;
  123. ;; Caution: trying to access a slot that is not bound by default will lead to an error. Use slot-boundp.
  124. (defmethod print-object ((obj ygo-card) stream)
  125. (print-unreadable-object (obj stream :type t)
  126. (let ((args))
  127. (when (ygo-passcode-of obj)
  128. (push (ygo-passcode-of obj) args))
  129. (ignore-errors
  130. (with-accessors ((name name-of))
  131. obj
  132. (when (and (slot-boundp obj 'name)
  133. (slot-value obj 'name))
  134. (push (name-of name) args))))
  135. (when args
  136. (format stream "~{~a~^ - ~}" (reverse args))))))
  137. (defmethod print-object ((obj ygo-set) stream)
  138. (print-unreadable-object (obj stream :type t)
  139. (let ((args))
  140. (when (mito:object-id obj)
  141. (push (mito:object-id obj) args))
  142. (ignore-errors
  143. (with-accessors ((passcode ygo-passcode-of)
  144. (name name-of)
  145. (code ygo-code-of)
  146. (rarity ygo-rarity-of)
  147. (edition ygo-edition-of))
  148. obj
  149. (when (and (slot-boundp obj 'passcode)
  150. (slot-value obj 'passcode))
  151. (push (ygo-passcode-of passcode) args))
  152. (when (and (slot-boundp obj 'code)
  153. (slot-value obj 'code))
  154. (push (ygo-set-code-of code) args))
  155. (when (and (slot-boundp obj 'rarity)
  156. (slot-value obj 'rarity))
  157. (push (ygo-rarity-of rarity) args))
  158. (when (and (slot-boundp obj 'edition)
  159. (slot-value obj 'edition))
  160. (push (ygo-edition-of edition) args))
  161. (when (and (slot-boundp obj 'name)
  162. (slot-value obj 'name))
  163. (push (name-of name) args))))
  164. (when args
  165. (format stream "~{~a~^ - ~}" (reverse args))))))
  166. (defun ygo-card-names-from-list (&rest args)
  167. (with-connection (db)
  168. (with-transaction
  169. (mito:retrieve-by-sql
  170. (sxql:select (:ygo_card.id :name)
  171. (sxql:from :ygo_card)
  172. (sxql:left-join :ygo_card_name :on (:= :ygo_card.name_id :ygo_card_name.id))
  173. (sxql:where (:in :ygo_card_name.id
  174. (sxql:select :name_id
  175. (sxql:from :ygo_card)
  176. (sxql:where (:in :id args))))))))))
  177. (defun ygo-card-names-by-deck-id (id)
  178. (with-connection (db)
  179. (with-transaction
  180. (mito:retrieve-by-sql
  181. (sxql:select (:ygo_card.id :name)
  182. (sxql:from :ygo_card)
  183. (sxql:left-join :ygo_card_name :on (:= :ygo_card.name_id :ygo_card_name.id))
  184. (sxql:where (:in :ygo_card_name.id
  185. (sxql:select :name_id
  186. (sxql:from :ygo_card)
  187. (sxql:where (:in :id
  188. (sxql:select :passcode
  189. (sxql:from :deck_item)
  190. (sxql:where (:= :deck_id id)))))))))))))
  191. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  192. (defun count-all-dao ()
  193. (let ((class-list (append (registered-classes (registry) :cl-deck-builder2.models.ygoprodeck.fields)
  194. (registered-classes (registry) :cl-deck-builder2.models.ygoprodeck.classes)))
  195. (lst '()))
  196. (with-connection (db)
  197. (with-transaction
  198. (dolist (class class-list lst)
  199. (push (list class (mito:count-dao class)) lst))))))
  200. ;; If you have altnerative artwork and you are the main card, youll get a list with length > 1
  201. ;; If you have alternative artwork and you are an alternative artwork card, you'll get the main list with length 1
  202. (defmethod has-alternative-artwork ((id integer))
  203. (find-dao 'ygo-alternative-artwork :passcode-id id))
  204. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  205. ;; So in the CSV importer I had a function PRE-SEED-DB-ITEMS that
  206. ;; would copy CC-ITEM x5 VARIANT-CONDITION to seed something like 250k
  207. ;; items in the db. We're just going to duplicate the logic here.
  208. ;;
  209. ;; INPUT: (select-dao 'ygo-set)
  210. (defun pre-seed-db-items (ygo-sets)
  211. (let ((variants (select-dao 'variant-condition)))
  212. (with-connection (db)
  213. (with-transaction
  214. (dolist (itm ygo-sets)
  215. (dolist (variant variants)
  216. (mito:create-dao 'ygo-set-item :item-id (mito:object-id itm)
  217. :variant-id (mito:object-id variant))))
  218. (mito:count-dao 'ygo-set-item)))))
  219. (defun set-ygo-set-item-qty (amt)
  220. "Helper function. Set the QTY of every YGO-SET-ITEM to AMT."
  221. (with-connection (db)
  222. (with-transaction
  223. (mito:execute-sql
  224. (sxql:update (sxql:make-sql-symbol
  225. (mito.class.table:table-name
  226. (find-class 'ygo-set-item)))
  227. (sxql:set= :qty amt))))))