From cd2827791f4af66204581491ed00bb6d9778cd53 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 1 Apr 2025 11:44:40 +1300 Subject: [PATCH] Rework stats display --- pug/includes/layout.pug | 64 ++++++++++++++++++++++++++++++++++++----- pug/tag_grid.pug | 13 +++++++-- routes/app.js | 33 +++++++++++---------- 3 files changed, 86 insertions(+), 24 deletions(-) diff --git a/pug/includes/layout.pug b/pug/includes/layout.pug index 1c8fd72..6285c08 100644 --- a/pug/includes/layout.pug +++ b/pug/includes/layout.pug @@ -53,9 +53,11 @@ html outline: 1px solid black; } body.themed.theme-system.overflow-y-scroll(hx-boost="true") - header.s-topbar + header.s-topbar.ps-sticky.t0 .s-topbar--container.wmx9 - .s-topbar--logo BC Explorer + .s-topbar--logo + != icons.get("compass-solid", 24) + .ml4 BC Explorer .fl-grow1 nav ul.s-navigation @@ -78,7 +80,7 @@ html block view div - .ps-sticky.t24.d-flex.fd-column.g12 + .ps-sticky.d-flex.fd-column.g12(style="top: 80px") if filter .s-sidebarwidget.s-sidebarwidget__blue.d-flex.ai-center.gx16.jc-space-between.p8.pl16 != icons.get("search") @@ -87,8 +89,56 @@ html a.s-btn.s-notice--btn(href=and({filter: null, filter_field: null})) Clear .s-sidebarwidget.wmn3 .s-sidebarwidget--header Collection + a.s-sidebarwidget--action Refresh table.s-sidebarwidget--content.s-sidebarwidget__items - each stat in count - tr.s-sidebarwidget--item - th= stat[0] - td= stat[1] + tr.s-sidebarwidget--item + th items + td= count.total + tr.s-sidebarwidget--item + th runtime + td= count.runtime + tr.s-sidebarwidget--item + th format + td + = count.albums + span.fc-black-400= ` albums` + span.fc-black-250= ` / ` + = count.singles + span.fc-black-400= ` singles` + tr.s-sidebarwidget--item + th price + td + = count.free + span.fc-black-400= ` free` + span.fc-black-250= ` / ` + = count.paid + span.fc-black-400= ` paid` + tr.s-sidebarwidget--item + th tracks + td + = count.tracks + span.pl8.fc-black-250= ` / ` + span.fc-black-400 avg #{count.avgTracks} + tr.s-sidebarwidget--item + th tags + td + = count.tags + span.pl8.fc-black-250= ` / ` + span.fc-black-400 avg #{count.avgTags} + span.fc-black-250= ` / ` + span.fc-black-400 lonely #{count.lonelyTags} + tr.s-sidebarwidget--item + th value + td + = `${count.displayCurrencySymbol}${count.value} ` + span.fc-black-400 #{count.displayCurrency} + tr.s-sidebarwidget--item + th diversity + //- supernova red-500, warm yellow-500, hot orange-500 + //- 0-9 black, 10-99 yellow, 100-999 orange, 1000+ red + td.w100 + .s-progress.d-grid.g2.h4.mtn6(style=`grid-template-columns: ${count.taste.map(t => t + "fr").join(" ")}`).bg-white.fc-black-400.fs-fine.lh-xxl + .s-progress--bar.bg-black-400(title=`${count.taste[0]} labels with <20 fans`)= count.taste[0] + .s-progress--bar.bg-yellow-400(title=`${count.taste[1]} labels with 20-199 fans`)= count.taste[1] + .s-progress--bar.bg-orange-400(title=`${count.taste[2]} labels with 200-1999 fans`)= count.taste[2] + .s-progress--bar.bg-red-400(title=`${count.taste[3]} labels with >2000 fans`)= count.taste[3] diff --git a/pug/tag_grid.pug b/pug/tag_grid.pug index 8a857b7..5583f21 100644 --- a/pug/tag_grid.pug +++ b/pug/tag_grid.pug @@ -1,19 +1,28 @@ extends includes/layout.pug block view - .mx-auto.w100.wmx11.fs-body1#content(style="cursor: default") script | var items = != JSON.stringify(items) + .mx-auto.w100.wmx11.fs-body1 + if !items.length + .d-flex.jc-center + .s-notice.d-flex.ai-center.p8.gx16.pl16(role="status") + != icons.get("info-circle") + div Tag data needs to be downloaded. This will take a while. + button.s-btn.s-btn__outlined Download now + + #content(style="cursor: default") script. setTimeout(() => { const content = document.getElementById("content") content.style.height = `${Math.round(Math.min(content.clientWidth, window.innerHeight)*0.8)}px` + const dark = window.matchMedia?.("(prefers-color-scheme: dark)").matches WordCloud(content, { list: items, fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI Adjusted", "Segoe UI", "Liberation Sans", sans-serif', fontWeight: "bold", - color: "random-dark", + color: dark ? "random-light": "random-dark", minSize: 4, gridSize: Math.round(content.clientWidth / 200), weightFactor: size => size * content.clientWidth / 180, diff --git a/routes/app.js b/routes/app.js index 1ed42de..f05bc2a 100644 --- a/routes/app.js +++ b/routes/app.js @@ -102,22 +102,25 @@ router.get("/", defineEventHandler({ const locals = { items: prepared.all(params), query, - count: [ - ["total", db.prepare("SELECT count(*) FROM item").pluck().get()], - ["runtime", db.prepare("SELECT iif(sum(duration) > 86400, cast(total(duration)/86400 AS INTEGER) || 'd ' || cast(total(duration)/3600%24 AS INTEGER) || 'h', cast(total(duration)/3600 AS INTEGER) || 'h') FROM track").pluck().get()], - ["albums", db.prepare("SELECT count(*) FROM item WHERE item_type = 'album'").pluck().get()], - ["singles", db.prepare("SELECT count(*) FROM item WHERE item_type = 'track'").pluck().get()], - ["free", db.prepare("SELECT count(*) FROM item WHERE price = 0").pluck().get()], - ["paid", db.prepare("SELECT count(*) FROM item WHERE price > 0").pluck().get()], - ["tracks", db.prepare("SELECT count(*) FROM track").pluck().get()], - ["avg tracks", Math.round(db.prepare("SELECT avg(count) FROM (SELECT count(*) AS count FROM track INNER JOIN item USING (item_id) WHERE item_type = 'album' GROUP BY item_id)").pluck().get()*10)/10], - ["tags", db.prepare("SELECT count(*) FROM item_tag").pluck().get()], - ["avg tags", Math.round(db.prepare("SELECT avg(count) FROM (SELECT count(*) AS count FROM item_tag GROUP BY item_id)").pluck().get()*10)/10], - ["lonely tags", db.prepare("SELECT count(*) FROM (SELECT tag FROM item_tag GROUP BY tag HAVING count(*) = 1)").pluck().get()], - ["value", displayCurrencySymbol + Math.round(select("item", ["currency", "price"]).all().map(c => { + count: { + total: db.prepare("SELECT count(*) FROM item").pluck().get(), + runtime: db.prepare("SELECT iif(sum(duration) > 86400, cast(total(duration)/86400 AS INTEGER) || 'd ' || cast(total(duration)/3600%24 AS INTEGER) || 'h', cast(total(duration)/3600 AS INTEGER) || 'h') FROM track").pluck().get(), + albums: db.prepare("SELECT count(*) FROM item WHERE item_type = 'album'").pluck().get(), + singles: db.prepare("SELECT count(*) FROM item WHERE item_type = 'track'").pluck().get(), + free: db.prepare("SELECT count(*) FROM item WHERE price = 0").pluck().get(), + paid: db.prepare("SELECT count(*) FROM item WHERE price > 0").pluck().get(), + tracks: db.prepare("SELECT count(*) FROM track").pluck().get(), + avgTracks: Math.round(db.prepare("SELECT avg(count) FROM (SELECT count(*) AS count FROM track INNER JOIN item USING (item_id) WHERE item_type = 'album' GROUP BY item_id)").pluck().get()*10)/10, + tags: db.prepare("SELECT count(*) FROM item_tag").pluck().get(), + avgTags: Math.round(db.prepare("SELECT avg(count) FROM (SELECT count(*) AS count FROM item_tag GROUP BY item_id)").pluck().get()*10)/10, + lonelyTags: db.prepare("SELECT count(*) FROM (SELECT tag FROM item_tag GROUP BY tag HAVING count(*) = 1)").pluck().get(), + value: Math.round(select("item", ["currency", "price"]).all().map(c => { return (currencyExchange.get(c.currency) || 0.6) * c.price / (currencyExchange.get(displayCurrency) || 1) / 10 - }).reduce((a, c) => a + c, 0)) * 10 + " " + displayCurrency] - ] + }).reduce((a, c) => a + c, 0)) * 10, + displayCurrency, + displayCurrencySymbol, + taste: db.prepare("with popularity (a) as (select avg(also_collected_count) from item group by band_url) select sum(iif(a >= 0 and a < 20, 1, 0)) as cold, sum(iif(a >= 20 and a < 200, 1, 0)) as warm, sum(iif(a >= 200 and a < 2000, 1, 0)) as hot, sum(iif(a >= 2000, 1, 0)) as supernova from popularity").raw().get() + } } if (mode === "artist_grid") { loadPreviews(locals, "band_name", 4, whereClause, filter_field, filter)