client/server updates to use the newly added lists

just bare-bones changes for now, no list management, and some hardcoding
of list id 0 in places where code is showing something based on the
owned list of cards
This commit is contained in:
Gered 2016-07-24 15:09:30 -04:00
parent 4b2bc86866
commit 20ab3c0693
14 changed files with 588 additions and 325 deletions

View file

@ -6,6 +6,7 @@
[webtools.reagent.bootstrap :as bs]
[webtools.reagent.components :refer [raw-html]]
[mtgcoll.common :refer [max-search-results]]
[mtgcoll.client.auth :as auth]
[mtgcoll.client.components.utils :refer [set-short-label symboled-markup th-sortable]]
[mtgcoll.client.components.inventory :refer [inventory]]
[mtgcoll.client.utils :refer [format-currency]]))
@ -49,13 +50,13 @@
(> num-results max-search-results))
(defvc card-list-table
[filters pager & [{:keys [no-owned-highlight?] :as options}]]
[list-id filters pager & [{:keys [no-owned-highlight?] :as options}]]
(let [sort-settings (r/atom
{:sort-by :name
:ascending? true})]
(fn [filters pager]
(let [cards (view-cursor :cards filters (:sort-by @sort-settings) (:ascending? @sort-settings) (:page @pager) (:page-size @pager))
card-count (view-cursor :count-of-cards filters)
(fn [list-id filters pager]
(let [cards (view-cursor :cards list-id (auth/get-username) filters (:sort-by @sort-settings) (:ascending? @sort-settings) (:page @pager) (:page-size @pager))
card-count (view-cursor :count-of-cards list-id (auth/get-username) filters)
num-pages (min (js/Math.ceil (/ @card-count (:page-size @pager)))
(max-pages @pager))]
(if (vc/loading? cards)
@ -76,8 +77,9 @@
[th-sortable sort-settings :inventory "Inventory"]]]
[:tbody
(map
(fn [{:keys [id name set_code set_name mana_cost type power toughness rarity paper_price online_price owned_count] :as card}]
(let [owned? (> owned_count 0)]
(fn [{:keys [id name set_code set_name mana_cost type power toughness rarity paper_price online_price quantity] :as card}]
(let [quantity (or quantity 0)
owned? (> quantity 0)]
^{:key id}
[:tr {:class (if (and (not no-owned-highlight?) owned?) "warning")}
[:td [card-link id name :block-element? true]]
@ -89,8 +91,8 @@
[:td rarity]
[:td (format-currency paper_price true)]
[:td (format-currency online_price true)]
[:td [inventory id
{:num-owned owned_count
[:td [inventory id list-id
{:num-owned quantity
:button-size "xsmall"
:button-style (if owned? "primary")}]]]))
@cards)]]

View file

@ -13,15 +13,15 @@
["online" "near mint" "lightly played" "moderately played" "heavily played" "damaged"])
(defn on-add-card
[card-id quality foil?]
[card-id quality foil? list-id]
(ajax/POST (->url "/collection/add")
:params {:card-id card-id :quality quality :foil foil?}
:params {:card-id card-id :quality quality :foil foil? :list-id list-id}
:on-error #(set-error! "Server error while adding card to inventory.")))
(defn on-remove-card
[card-id quality foil?]
[card-id quality foil? list-id]
(ajax/POST (->url "/collection/remove")
:params {:card-id card-id :quality quality :foil foil?}
:params {:card-id card-id :quality quality :foil foil? :list-id list-id}
:on-error #(set-error! "Server error while adding card to inventory.")))
(defn can-modify-inventory?
@ -30,7 +30,7 @@
(auth/authenticated?)))
(defvc inventory-management
[card-id]
[card-id list-id]
(let [inventory (view-cursor :owned-card card-id)
inventory (group-by :quality @inventory)
colspan (if (can-modify-inventory?) 2 1)
@ -72,10 +72,10 @@
[:td.col-sm-3
[bs/ButtonGroup {:justified true}
[bs/ButtonGroup
[bs/Button {:bsStyle "success" :on-click #(on-add-card card-id quality false)}
[bs/Button {:bsStyle "success" :on-click #(on-add-card card-id quality false list-id)}
[bs/Glyphicon {:glyph "plus"}]]]
[bs/ButtonGroup
[bs/Button {:bsStyle "danger" :disabled (= 0 non-foil-quantity) :on-click #(on-remove-card card-id quality false)}
[bs/Button {:bsStyle "danger" :disabled (= 0 non-foil-quantity) :on-click #(on-remove-card card-id quality false list-id)}
[bs/Glyphicon {:glyph "minus"}]]]]])
;; foil
[:td {:class quantity-class}
@ -87,21 +87,21 @@
[:td.col-sm-3
[bs/ButtonGroup {:justified true}
[bs/ButtonGroup
[bs/Button {:bsStyle "success" :on-click #(on-add-card card-id quality true)}
[bs/Button {:bsStyle "success" :on-click #(on-add-card card-id quality true list-id)}
[bs/Glyphicon {:glyph "plus"}]]]
[bs/ButtonGroup
[bs/Button {:bsStyle "danger" :disabled (= 0 foil-quantity) :on-click #(on-remove-card card-id quality true)}
[bs/Button {:bsStyle "danger" :disabled (= 0 foil-quantity) :on-click #(on-remove-card card-id quality true list-id)}
[bs/Glyphicon {:glyph "minus"}]]]]])]))
qualities))]]]))
(defn inventory
[card-id & [{:keys [num-owned owned? button-size button-style] :as opts}]]
[card-id list-id & [{:keys [num-owned owned? button-size button-style] :as opts}]]
[bs/OverlayTrigger {:placement "bottom"
:trigger "click"
:root-close true
:overlay (r/as-component
[bs/Popover {:class "inventory" :title "Card Inventory"}
[inventory-management card-id]])}
[inventory-management card-id list-id]])}
[bs/Button
(merge
{:block true}

View file

@ -16,7 +16,7 @@
(defroute "/all" [] (page/page [collection/all-cards-list]))
(defroute "/sets" [] (page/page [sets/sets-list]))
(defroute "/set/:code" [code] (page/page [sets/set-details code]))
(defroute "/card/:id" [id] (page/page [cards/card-details id]))
(defroute "/card/:id" [id] (page/page [cards/card-details id 0]))
(defroute "/stats" [] (page/page [stats/stats-page]))
(defroute "*" [] (page/barebones-page [:div "not found"]))

View file

@ -6,6 +6,7 @@
[webtools.cljs.utils :refer [->url pprint-json]]
[webtools.reagent.bootstrap :as bs]
[webtools.reagent.components :refer [->keyed-comps raw-html]]
[mtgcoll.client.auth :as auth]
[mtgcoll.client.components.cards :refer [card-image card-link]]
[mtgcoll.client.components.utils :refer [symbol-image set-heading set-label symboled-markup]]
[mtgcoll.client.components.inventory :refer [inventory]]
@ -27,8 +28,8 @@
(->keyed-comps components)])
(defvc card-details
[id]
(let [card (view-cursor :full-card-info id)
[id list-id]
(let [card (view-cursor :full-card-info id list-id (auth/get-username))
variations (view-cursor :card-variations id)
pricing (view-cursor :card-pricing id)
printings (view-cursor :card-printings (:card_name @card))]
@ -48,10 +49,11 @@
[:a {:href (->url "#/set/" (:set_code @card))}
[:small [set-heading (:set_code @card) (:set_name @card)]]]
[:div.pull-right
[inventory id
{:num-owned (:owned_count @card)
:button-size "large"
:button-style (if (> (:owned_count @card) 0) "primary")}]]]
(let [quantity (or (:quantity @card) 0)]
[inventory id list-id
{:num-owned quantity
:button-size "large"
:button-style (if (> quantity 0) "primary")}])]]
[bs/Grid
{:class "card" :fluid true}
[bs/Col {:sm 6 :class "card-image"}

View file

@ -16,13 +16,14 @@
(defn all-cards-list
[]
(let [active-search-filters (r/cursor all-cards-search-filters [:active-filters])
pager (r/cursor all-cards-search-filters [:pager])]
pager (r/cursor all-cards-search-filters [:pager])
list-id 0]
(fn []
(set-active-breadcrumb! :all)
[:div
[bs/PageHeader "All Cards"]
[s/search-filter-selector all-cards-search-filters]
[card-list-table @active-search-filters pager]])))
[card-list-table list-id @active-search-filters pager]])))
;;;
@ -33,11 +34,12 @@
[]
(let [fixed-filters [{:field :owned? :value true :comparison :=}]
active-search-filters (r/cursor owned-cards-search-filters [:active-filters])
pager (r/cursor owned-cards-search-filters [:pager])]
pager (r/cursor owned-cards-search-filters [:pager])
list-id 0]
(s/apply-search-filters! owned-cards-search-filters fixed-filters)
(fn []
(set-active-breadcrumb! :owned)
[:div
[bs/PageHeader "Owned Cards"]
[s/search-filter-selector owned-cards-search-filters {:fixed-active-filters fixed-filters}]
[card-list-table @active-search-filters pager {:no-owned-highlight? true}]])))
[card-list-table list-id @active-search-filters pager {:no-owned-highlight? true}]])))

View file

@ -61,12 +61,13 @@
[set-code]
(let [fixed-filters [{:field :set-code :value set-code :comparison :=}]
active-search-filters (r/cursor set-cards-search-filters [:active-filters])
pager (r/cursor set-cards-search-filters [:pager])]
pager (r/cursor set-cards-search-filters [:pager])
list-id 0]
(s/apply-search-filters! set-cards-search-filters fixed-filters)
(fn []
[:div.set-cards-list
[s/search-filter-selector set-cards-search-filters {:fixed-active-filters fixed-filters}]
[card-list-table @active-search-filters pager]])))
[card-list-table list-id @active-search-filters pager]])))
(defvc set-details
[set-code]

View file

@ -6,6 +6,7 @@
[webtools.reagent.bootstrap :as bs]
[webtools.reagent.components :refer [->keyed-comps]]
[webtools.cljs.utils :refer [->url]]
[mtgcoll.client.auth :as auth]
[mtgcoll.client.page :refer [set-active-breadcrumb!]]
[mtgcoll.client.utils :refer [format-number format-currency get-field-value]]
[mtgcoll.client.components.utils :refer [set-short-label]]
@ -84,8 +85,8 @@
:backgroundColor (vec bg-colors)}]})
(defvc widget-color-totals
[online? & [{:keys [width] :as options}]]
(let [color-totals (view-cursor :stats/color-totals online?)]
[online? list-id & [{:keys [width] :as options}]]
(let [color-totals (view-cursor :stats/color-totals online? list-id (auth/get-username))]
(if-not (vc/loading? color-totals)
[bs/Col {:sm (or width 12) :class "widget"}
[:div.title "Card Colors"]
@ -97,8 +98,8 @@
{:vertical-legend? true}]])))
(defvc widget-basic-type-totals
[online? & [{:keys [width] :as options}]]
(let [basic-type-totals (view-cursor :stats/basic-type-totals online?)]
[online? list-id & [{:keys [width] :as options}]]
(let [basic-type-totals (view-cursor :stats/basic-type-totals online? list-id (auth/get-username))]
(if-not (vc/loading? basic-type-totals)
[bs/Col {:sm (or width 12) :class "widget"}
[:div.title "Basic Card Types"]
@ -133,8 +134,8 @@
data)]]]))
(defvc widget-most-owned-sets
[online? & [options]]
(let [data (view-cursor :stats/most-owned-sets online?)]
[online? list-id & [options]]
(let [data (view-cursor :stats/most-owned-sets online? list-id (auth/get-username))]
(if-not (vc/loading? data)
[table-statistics
"Cards Owned By Set"
@ -145,8 +146,8 @@
options])))
(defvc widget-most-copies-of-card
[online? & [options]]
(let [data (view-cursor :stats/most-copies-of-card online?)]
[online? list-id & [options]]
(let [data (view-cursor :stats/most-copies-of-card online? list-id (auth/get-username))]
(if-not (vc/loading? data)
[table-statistics
"Most Copies Owned"
@ -158,8 +159,8 @@
options])))
(defvc widget-most-nonland-copies-of-card
[online? & [options]]
(let [data (view-cursor :stats/most-nonland-copies-of-card online?)]
[online? list-id & [options]]
(let [data (view-cursor :stats/most-nonland-copies-of-card online? list-id (auth/get-username))]
(if-not (vc/loading? data)
[table-statistics
"Most Non-land Copies Owned"
@ -171,8 +172,8 @@
options])))
(defvc widget-most-valuable-cards
[online? pricing-source & [options]]
(let [data (view-cursor :stats/most-valuable-cards online? pricing-source)]
[online? list-id pricing-source & [options]]
(let [data (view-cursor :stats/most-valuable-cards online? pricing-source list-id (auth/get-username))]
(if-not (vc/loading? data)
[table-statistics
"Most Valuable Cards"
@ -184,8 +185,8 @@
options])))
(defvc widget-rarity-totals
[online? & [options]]
(let [data (view-cursor :stats/card-rarity-totals online?)]
[online? list-id & [options]]
(let [data (view-cursor :stats/card-rarity-totals online? list-id (auth/get-username))]
(if-not (vc/loading? data)
[table-statistics
"Cards Owned By Rarity"
@ -195,8 +196,8 @@
options])))
(defvc widget-agg-price-stats
[online? pricing-source & [{:keys [width] :as options}]]
(let [agg-price-stats (view-cursor :stats/agg-price-stats online? pricing-source)
[online? list-id pricing-source & [{:keys [width] :as options}]]
(let [agg-price-stats (view-cursor :stats/agg-price-stats online? pricing-source list-id (auth/get-username))
min-price (:min_price @agg-price-stats)
max-price (:max_price @agg-price-stats)
avg-price (:avg_price @agg-price-stats)
@ -209,15 +210,16 @@
[big-currency-statistic "Median Card Value" median-price]])))
(defvc widget-summary-stats
[online? pricing-source & [{:keys [width] :as options}]]
(let [agg-price-stats (view-cursor :stats/agg-price-stats online? pricing-source)
owned-total (view-cursor :stats/owned-total online?)
owned-foil-total (view-cursor :stats/owned-foil-total online?)
distinct-owned-total (view-cursor :stats/distinct-owned-total online?)
total-sets-owned-from (view-cursor :stats/total-sets-owned-from online?)
total-sets-owned-all-from (view-cursor :stats/total-sets-owned-all-from online?)
total-price (view-cursor :stats/total-price online? pricing-source)
num-worth-over-1-dollar (view-cursor :stats/num-cards-worth-over-1-dollar online? pricing-source)]
[online? list-id pricing-source & [{:keys [width] :as options}]]
(let [username (auth/get-username)
agg-price-stats (view-cursor :stats/agg-price-stats online? pricing-source list-id username)
owned-total (view-cursor :stats/owned-total online? list-id username)
owned-foil-total (view-cursor :stats/owned-foil-total online? list-id username)
distinct-owned-total (view-cursor :stats/distinct-owned-total online? list-id username)
total-sets-owned-from (view-cursor :stats/total-sets-owned-from online? list-id username)
total-sets-owned-all-from (view-cursor :stats/total-sets-owned-all-from online? list-id username)
total-price (view-cursor :stats/total-price online? pricing-source list-id username)
num-worth-over-1-dollar (view-cursor :stats/num-cards-worth-over-1-dollar online? pricing-source list-id username)]
[bs/Col {:sm (or width 12) :class "widget"}
[widget-row [big-number-statistic "Owned" @owned-total]]
[widget-row [big-number-statistic "Unique" @distinct-owned-total]]
@ -239,7 +241,8 @@
[]
(let [pricing-sources (view-cursor :pricing-sources)
online? (:online? @settings)
pricing-source (:pricing-source @settings)]
pricing-source (:pricing-source @settings)
list-id 0]
(set-active-breadcrumb! :stats)
(if (and (not (vc/loading? pricing-sources))
(nil? (:pricing-source @settings)))
@ -268,12 +271,12 @@
[bs/Grid {:fluid true :class "statistics"}
[widget-row
[bs/Col {:sm 4}
[widget-row [widget-summary-stats online? pricing-source]]
[widget-row [widget-rarity-totals online?]]
[widget-row [widget-most-owned-sets online?]]]
[widget-row [widget-summary-stats online? list-id pricing-source]]
[widget-row [widget-rarity-totals online? list-id]]
[widget-row [widget-most-owned-sets online? list-id]]]
[bs/Col {:sm 8}
[widget-row [widget-color-totals online?]]
[widget-row [widget-basic-type-totals online?]]
[widget-row [widget-most-valuable-cards online? pricing-source {:width 10}]]
[widget-row [widget-most-copies-of-card online? {:width 10}]]
[widget-row [widget-most-nonland-copies-of-card online? {:width 10}]]]]]]))
[widget-row [widget-color-totals online? list-id]]
[widget-row [widget-basic-type-totals online? list-id]]
[widget-row [widget-most-valuable-cards online? list-id pricing-source {:width 10}]]
[widget-row [widget-most-copies-of-card online? list-id {:width 10}]]
[widget-row [widget-most-nonland-copies-of-card online? list-id {:width 10}]]]]]]))

View file

@ -1,37 +1,43 @@
(ns mtgcoll.models.collection
(:require
[clojure.java.jdbc :as jdbc]
[views.sql.core :refer [vexec! with-view-transaction]]
[mtgcoll.db :refer [db]]
[mtgcoll.views.core :refer [view-system]]))
(defn update-collection!
[card-id quality foil? quantity-change]
[card-id quality foil? list-id user-id quantity-change]
;; written assuming postgresql server is _not_ 9.5+ (so, without access to UPSERT functionality)
(with-view-transaction
view-system
[dt @db]
(let [num-updates (first
(vexec! view-system dt
["update collection
set quantity = quantity + ?
where card_id = ? and
quality = ? and
foil = ?"
quantity-change card-id quality foil?]))]
(if (= 0 num-updates)
(first
(vexec! view-system dt
["insert into collection
(card_id, quality, quantity, foil)
values
(?, ?, ?, ?)"
card-id quality quantity-change foil?]))
num-updates))))
(let [list-id (int list-id)
public-only? (nil? user-id)]
(with-view-transaction
view-system
[dt @db]
(if-not (first (jdbc/query dt ["select count(*) from lists where id = ? and is_public in (true, ?)" list-id public-only?]))
(throw (new Exception (str "Not authorized to update list:" list-id)))
(let [num-updates (first
(vexec! view-system dt
["update collection
set quantity = quantity + ?
where card_id = ? and
quality = ? and
foil = ? and
list_id = ?"
quantity-change card-id quality foil? list-id]))]
(if (= 0 num-updates)
(first
(vexec! view-system dt
["insert into collection
(card_id, quality, quantity, foil, list_id)
values
(?, ?, ?, ?, ?)"
card-id quality quantity-change foil? list-id]))
num-updates))))))
(defn add-to-collection!
[card-id quality foil?]
(update-collection! card-id quality foil? 1))
[card-id quality foil? list-id user-id]
(update-collection! card-id quality foil? list-id user-id 1))
(defn remove-from-collection!
[card-id quality foil?]
(update-collection! card-id quality foil? -1))
[card-id quality foil? list-id user-id]
(update-collection! card-id quality foil? list-id user-id -1))

View file

@ -9,11 +9,13 @@
(def collection-routes
(wrap-middleware
(routes
(POST "/collection/add" [card-id quality foil :as request]
(collection/add-to-collection! card-id quality foil)
(response/json {:status "ok"}))
(POST "/collection/add" [card-id quality foil list-id :as request]
(let [username (get-in request [:session :user :username])
result (collection/add-to-collection! card-id quality foil list-id username)]
(response/json {:status "ok"})))
(POST "/collection/remove" [card-id quality foil :as request]
(collection/remove-from-collection! card-id quality foil)
(response/json {:status "ok"})))
(POST "/collection/remove" [card-id quality foil list-id :as request]
(let [username (get-in request [:session :user :username])
result (collection/remove-from-collection! card-id quality foil list-id username)]
(response/json {:status "ok"}))))
(wrap-authenticated)))

View file

@ -1,10 +1,12 @@
(ns mtgcoll.views.core
(:require
[clojure.tools.logging :as log]
[views.reagent.sente.server :as vr]
[views.core :as views]
[views.sql.view :refer [view]]
[honeysql.format :as fmt]
[mtgcoll.db :refer [db]]
[mtgcoll.auth :as auth]
[mtgcoll.views.sente :refer [sente-socket]]
[mtgcoll.views.functions.cards :as cards]
[mtgcoll.views.functions.sets :as sets]
@ -56,11 +58,42 @@
#_(views/add-views! view-system views)
(defn view-auth-fn
[{:keys [view-id parameters] :as view-sig} subscriber-key context]
(let [request context
username (get-in request [:session :user :username])]
(if-not (auth/using-authorization?)
true
(case view-id
; views where the user-id parameter is always last
(:stats/owned-total :stats/owned-foil-total :stats/distinct-owned-total :stats/color-totals
:stats/basic-type-totals :stats/most-common-types :stats/total-sets-owned-from
:stats/total-sets-owned-all-from :stats/most-owned-sets :stats/most-copies-of-card
:stats/most-nonland-copies-of-card :stats/total-price :stats/agg-price-stats :stats/most-valuable-cards
:stats/num-cards-worth-over-1-dollar :stats/card-rarity-totals :full-card-info)
(= username (last parameters))
; views where the user-id parameter is second
(:cards :count-of-cards)
(= username (second parameters))
; assume otherwise that the view is not one that requires an auth check (no user-id parameter)
true))))
(defn view-on-unauth-fn
[{:keys [view-id parameters] :as view-sig} subscriber-key context]
(let [request context
user-profile (get-in request [:session :user])]
(log/warn "Unauthorized view subscription attempt: " view-id ", " parameters " - user profile: " user-profile)))
(defn init!
[]
(vr/init! view-system @sente-socket
{:views views
:use-default-sente-router? true}))
:use-default-sente-router? true
:auth-fn view-auth-fn
:on-unauth-fn view-on-unauth-fn}))
(defn shutdown!
[]

View file

@ -24,53 +24,59 @@
where c.id = ?" id])
(defn full-card-info
[id]
["select c.id,
c.set_code,
c.layout,
c.name as card_name,
c.mana_cost,
c.converted_mana_cost,
c.colors,
c.color_identity,
c.type,
c.supertypes,
c.types,
c.subtypes,
c.rarity,
c.text,
c.flavor,
c.artist,
c.number,
c.power,
c.toughness,
c.loyalty,
c.multiverseid,
c.image_name,
c.watermark,
c.border,
s.border as set_border,
c.timeshifted,
c.hand,
c.life,
c.reserved,
c.release_date,
c.starter,
s.name as set_name,
s.gatherer_code,
s.old_code,
s.magic_cards_info_code,
s.release_date as set_release_date,
s.type as set_type,
s.block,
s.online_only,
c.paper_price,
c.online_price,
c.owned_count,
c.owned_foil_count
from cards c
join sets s on c.set_code = s.code
where c.id = ?" id])
[id list-id user-id]
(let [list-id (int list-id)
public-only? (nil? user-id)]
["select c.id,
c.set_code,
c.layout,
c.name as card_name,
c.mana_cost,
c.converted_mana_cost,
c.colors,
c.color_identity,
c.type,
c.supertypes,
c.types,
c.subtypes,
c.rarity,
c.text,
c.flavor,
c.artist,
c.number,
c.power,
c.toughness,
c.loyalty,
c.multiverseid,
c.image_name,
c.watermark,
c.border,
s.border as set_border,
c.timeshifted,
c.hand,
c.life,
c.reserved,
c.release_date,
c.starter,
s.name as set_name,
s.gatherer_code,
s.old_code,
s.magic_cards_info_code,
s.release_date as set_release_date,
s.type as set_type,
s.block,
s.online_only,
c.paper_price,
c.online_price,
lc.quantity,
lc.foil_quantity
from cards c
join sets s on c.set_code = s.code
join lists l on l.id = ?
left join lists_card_quantities lc on (lc.card_id = c.id and lc.list_id = l.id)
where c.id = ?
and l.is_public in (true, ?)"
list-id id public-only?]))
(defn card-names
[id]
@ -164,20 +170,24 @@
:number (text-comparison-fn [:number] true)
:power (text-comparison-fn [:power] true)
:toughness (text-comparison-fn [:toughness] true)
:owned-count (numeric-comparison-fn [:owned_count])
:owned-foil-count (numeric-comparison-fn [:owned_foil_count])
:owned-count (numeric-comparison-fn [:quantity])
:owned-foil-count (numeric-comparison-fn [:foil_quantity])
:paper-price (numeric-comparison-fn [:paper_price])
:online-price (numeric-comparison-fn [:online_price])
:owned? (fn [value comparison]
(assert (= := comparison))
(case value
true [:> :owned_count 0]
false [:= :owned_count 0]))
true [:> :quantity 0]
false [:or
[:is :quantity nil]
[:= :quantity 0]]))
:owned-foil? (fn [value comparison]
(assert (= := comparison))
(case value
true [:> :owned_foil_count 0]
false [:= :owned_foil_count 0]))})
true [:> :foil_quantity 0]
false [:or
[:is :foil_quantity nil]
[:= :foil_quantity 0]]))})
(defn- filter->hsql-where-criteria
[{:keys [field value comparison]}]
@ -185,32 +195,38 @@
(f value comparison)))
(defn- base-card-search-query
[hsql-filters & [order-by]]
[list-id public-only? hsql-filters & [order-by]]
(let [q {:from [[(merge
{:select [:c.id
:c.name
:c.normalized_name
:c.set_code
[:s.name :set_name]
:c.mana_cost
:c.converted_mana_cost
:c.colors
:c.color_identity
:c.type
:c.rarity
:c.text
:c.flavor
:c.artist
:c.number
:c.power
:c.toughness
:c.loyalty
:c.paper_price
:c.online_price
:c.owned_count
:c.owned_foil_count]
:from [[:cards :c]]
:join [[:sets :s] [:= :c.set_code :s.code]]}
{:select [:c.id
:c.name
:c.normalized_name
:c.set_code
[:s.name :set_name]
:c.mana_cost
:c.converted_mana_cost
:c.colors
:c.color_identity
:c.type
:c.rarity
:c.text
:c.flavor
:c.artist
:c.number
:c.power
:c.toughness
:c.loyalty
:c.paper_price
:c.online_price
:lc.quantity
:lc.foil_quantity]
:from [[:cards :c]]
:join [[:sets :s] [:= :c.set_code :s.code]
[:lists :l] [:and
[:= :l.id list-id]
[:in :l.is_public [true public-only?]]]]
:left-join [[:lists_card_quantities :lc] [:and
[:= :c.id :lc.card_id]
[:= :lc.list_id list-id]]]}
(if order-by
{:order-by [order-by [:c.id]]}))
:cards_list]]}]
@ -219,8 +235,9 @@
q)))
(defn cards
[filters & [sort-by ascending? page-num page-size]]
(let [sort-by (case sort-by
[list-id user-id filters & [sort-by ascending? page-num page-size]]
(let [public-only? (nil? user-id)
sort-by (case sort-by
:name :name
:set :set_code
:mana-cost :converted_mana_cost
@ -228,10 +245,10 @@
:rarity :rarity
:paper-price :paper_price
:online-price :online_price
:inventory :owned_count
:inventory :quantity
:name)
sort-dir (if ascending? :asc :desc)
nulls (if (some #{sort-by} #{:converted_mana_cost :paper_price :online_price :owned_count})
nulls (if (some #{sort-by} #{:converted_mana_cost :paper_price :online_price :quantity})
(if-not ascending? :nulls-last :nulls-first))
page-size (or page-size 25)
page-size (if (> page-size 200) 200 page-size)
@ -241,6 +258,8 @@
page-num)
hsql-filters (mapv filter->hsql-where-criteria filters)
q (base-card-search-query
list-id
public-only?
hsql-filters
(if nulls
[sort-by sort-dir nulls]
@ -251,10 +270,11 @@
(hsql/format q)))
(defn count-of-cards
[filters]
(let [hsql-filters (mapv filter->hsql-where-criteria filters)
q (base-card-search-query hsql-filters)
[list-id user-id filters]
(let [public-only? (nil? user-id)
hsql-filters (mapv filter->hsql-where-criteria filters)
q (base-card-search-query list-id public-only? hsql-filters)
q (assoc q :select [:%count.*])]
(hsql/format q)))
#_(cards [{:field :name :value "z" :comparison :like}] :name true 10000 20)
#_(cards 0 nil [{:field :name :value "z" :comparison :like}] :name true 10000 20)

View file

@ -4,12 +4,12 @@
[card-id]
["select quality, quantity, foil
from collection
where card_id = ?"
where card_id = ? AND list_id = 0"
card-id])
(defn total-owned-of-card
[card-id]
["select count(*)
from collection
where card_id = ?"
where card_id = ? AND list_id = 0"
card-id])

View file

@ -18,7 +18,7 @@
select count(*)
from collection cl
join cards c on cl.card_id = c.id
where c.set_code = s.code and cl.quantity > 0
where c.set_code = s.code and cl.quantity > 0 and cl.list_id = 0
) as owned_count
from sets s
where s.code = ?" code])
@ -47,6 +47,6 @@
select count(*)
from collection cl
join cards c on cl.card_id = c.id
where c.set_code = s.code and cl.quantity > 0
where c.set_code = s.code and cl.quantity > 0 and cl.list_id = 0
) as owned_count
from sets s"])

View file

@ -1,228 +1,390 @@
(ns mtgcoll.views.functions.statistics)
(defn owned-total
[online?]
["select coalesce(sum(quantity), 0) as total
from collection
where online = ?"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select coalesce(sum(cl.quantity), 0) as total
from collection cl
join lists l on cl.list_id = l.id
where cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))"
online? list-id public-only?]))
(defn distinct-owned-total
[online?]
["select count(distinct c.id)
from cards c
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ?"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select count(distinct c.id)
from cards c
join collection cl on c.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))"
online? list-id public-only?]))
(defn owned-foil-total
[online?]
["select coalesce(sum(quantity), 0) as total
from collection
where online = ? and foil = true"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select coalesce(sum(cl.quantity), 0) as total
from collection cl
join lists l on cl.list_id = l.id
where cl.online = ?
and cl.foil = true
and cl.list_id = ?
and (l.is_public in (true, ?))"
online? list-id public-only?]))
(defn color-totals
[online?]
(let [online? (boolean online?)]
[online? list-id user-id]
;; TODO: i really dislike how this query is written ... try to clean this up at some point maybe?
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select
(
select sum(cl.quantity)
from cards c
join collection cl on c.id = cl.card_id
where c.color_identity like '%B%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.color_identity like '%B%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as black,
(
select sum(cl.quantity)
from cards c
join collection cl on c.id = cl.card_id
where c.color_identity like '%U%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.color_identity like '%U%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as blue,
(
select sum(cl.quantity)
from cards c
join collection cl on c.id = cl.card_id
where c.color_identity like '%G%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.color_identity like '%G%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as green,
(
select sum(cl.quantity)
from cards c
join collection cl on c.id = cl.card_id
where c.color_identity like '%R%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.color_identity like '%R%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as red,
(
select sum(cl.quantity)
from cards c
join collection cl on c.id = cl.card_id
where c.color_identity like '%W%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.color_identity like '%W%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as white,
(
select sum(cl.quantity)
from cards c
join collection cl on c.id = cl.card_id
where c.color_identity = '' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.color_identity = ''
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as colorless"
online? online? online? online? online? online?]))
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?]))
(defn basic-type-totals
[online?]
(let [online? (boolean online?)]
[online? list-id user-id]
;; TODO: i really dislike how this query is written ... try to clean this up at some point maybe?
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Artifact%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Artifact%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as artifacts,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Creature%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Creature%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as creatures,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Enchantment%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Enchantment%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as enchantments,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Instant%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Instant%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as instants,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Land%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Land%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as lands,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Planeswalker%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Planeswalker%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as planeswalkers,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Tribal%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Tribal%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as tribals,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where c.types like '%Sorcery%' and cl.quantity > 0 and cl.online = ?
join lists l on cl.list_id = l.id
where c.types like '%Sorcery%'
and cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as sorcerys"
online? online? online? online? online? online? online? online?]))
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?
online? list-id public-only?]))
(defn most-common-types
[online?]
["select *
from (
select sum(cl.quantity) as quantity, c.types
from cards c
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ?
group by c.types
) types_totals
order by quantity desc
limit 10"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select *
from (
select sum(cl.quantity) as quantity, c.types
from cards c
join collection cl on c.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
group by c.types
) types_totals
order by quantity desc
limit 10"
online? list-id public-only?]))
(defn total-sets-owned-from
[online?]
["select count(distinct c.set_code)
from cards c
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ?"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select count(distinct c.set_code)
from cards c
join collection cl on c.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))"
online? list-id public-only?]))
(defn total-sets-owned-all-from
[online?]
["select count(*)
from (
select
(
select count(*)
from cards
where set_code = s.code
) as num_in_set,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ? and c.set_code = s.code
) as owned_in_set,
s.code
from sets s
) as set_owned_counts
where set_owned_counts.num_in_set = set_owned_counts.owned_in_set"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select count(*)
from (
select
(
select count(*)
from cards
where set_code = s.code
) as num_in_set,
(
select coalesce(sum(cl.quantity), 0)
from cards c
join collection cl on c.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and c.set_code = s.code
and cl.list_id = ?
and (l.is_public in (true, ?))
) as owned_in_set,
s.code
from sets s
) as set_owned_counts
where set_owned_counts.num_in_set = set_owned_counts.owned_in_set"
online? list-id public-only?]))
(defn most-owned-sets
[online?]
["select *
from (
select sum(cl.quantity) as quantity, c.set_code
from cards c
join sets s on c.set_code = s.code
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ?
group by c.set_code
) sets_totals
order by quantity desc
limit 10"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select *
from (
select sum(cl.quantity) as quantity, c.set_code
from cards c
join sets s on c.set_code = s.code
join collection cl on c.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
group by c.set_code
) sets_totals
order by quantity desc
limit 10"
online? list-id public-only?]))
(defn most-copies-of-card
[online?]
["select quantity, c.id, c.name, c.set_code
from (
select sum(cl.quantity) as quantity, c2.id as card_id
from cards c2
join collection cl on c2.id = cl.card_id
where cl.quantity > 0 and cl.online = ?
group by c2.id
) copies_of_cards
join cards c on c.id = card_id
order by quantity desc
limit 10"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select quantity, c.id, c.name, c.set_code
from (
select sum(cl.quantity) as quantity, c2.id as card_id
from cards c2
join collection cl on c2.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
group by c2.id
) copies_of_cards
join cards c on c.id = card_id
order by quantity desc
limit 10"
online? list-id public-only?]))
(defn most-nonland-copies-of-card
[online?]
["select quantity, c.id, c.name, c.set_code
from (
select sum(cl.quantity) as quantity, c2.id as card_id
from cards c2
join collection cl on c2.id = cl.card_id
where cl.quantity > 0 and cl.online = ? and c2.type not like '%Land%'
group by c2.id
) copies_of_cards
join cards c on c.id = card_id
order by quantity desc
limit 10"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select quantity, c.id, c.name, c.set_code
from (
select sum(cl.quantity) as quantity, c2.id as card_id
from cards c2
join collection cl on c2.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and c2.type not like '%Land%'
and cl.list_id = ?
and (l.is_public in (true, ?))
group by c2.id
) copies_of_cards
join cards c on c.id = card_id
order by quantity desc
limit 10"
online? list-id public-only?]))
(defn total-price
[online? price-source]
(let [online? (boolean online?)]
[online? price-source list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select cast(coalesce(sum(sub_total), 0) as decimal(10, 2)) as total
from (
select (cl.quantity * cp.price) as sub_total
from cards c
join card_prices cp on c.id = cp.card_id
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ? and cp.online = ? and cp.source = ?
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cp.online = ?
and cp.source = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
) as sub_totals"
online? online? price-source]))
online? online? price-source list-id public-only?]))
(defn agg-price-stats
[online? price-source]
(let [online? (boolean online?)]
[online? price-source list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select cast(coalesce(min(cp.price), 0) as decimal(10, 2)) as min_price,
cast(coalesce(max(cp.price), 0) as decimal(10, 2)) as max_price,
cast(coalesce(avg(cp.price), 0) as decimal(10, 2)) as avg_price,
@ -230,37 +392,67 @@
from cards c
join card_prices cp on c.id = cp.card_id
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ? and cp.online = ? and cp.source = ?"
online? online? price-source]))
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cp.online = ?
and cp.source = ?
and cl.list_id = ?
and (l.is_public in (true, ?))"
online? online? price-source list-id public-only?]))
(defn most-valuable-cards
[online? price-source]
(let [online? (boolean online?)]
[online? price-source list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select c.id, c.name, c.set_code, cp.price
from cards c
join card_prices cp on c.id = cp.card_id
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ? and cp.online = ? and cp.source = ?
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cp.online = ?
and cp.source = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
order by cp.price desc
limit 10"
online? online? price-source]))
online? online? price-source list-id public-only?]))
(defn num-cards-worth-over-1-dollar
[online? price-source]
(let [online? (boolean online?)]
[online? price-source list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select coalesce(sum(cl.quantity), 0) as count
from cards c
join card_prices cp on c.id = cp.card_id
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ? and cp.online = ? and cp.source = ? and cp.price >= 1.0"
online? online? price-source]))
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cp.online = ?
and cp.source = ?
and cp.price >= 1.0
and cl.list_id = ?
and (l.is_public in (true, ?))"
online? online? price-source list-id public-only?]))
(defn card-rarity-totals
[online?]
["select coalesce(sum(cl.quantity), 0) as total, c.rarity
from cards c
join collection cl on c.id = cl.card_id
where cl.quantity > 0 and cl.online = ?
group by c.rarity
order by total desc"
(boolean online?)])
[online? list-id user-id]
(let [online? (boolean online?)
list-id (int list-id)
public-only? (nil? user-id)]
["select coalesce(sum(cl.quantity), 0) as total, c.rarity
from cards c
join collection cl on c.id = cl.card_id
join lists l on cl.list_id = l.id
where cl.quantity > 0
and cl.online = ?
and cl.list_id = ?
and (l.is_public in (true, ?))
group by c.rarity
order by total desc"
online? list-id public-only?]))