Add library_stats aggregate query for the sidebar summary
New LibraryStats (unique cards owned, total copies, deck count, and per-color copy counts including colorless) via Catalog::library_stats, tested against the fixture catalog. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,7 @@ use std::path::Path;
|
||||
use rusqlite::Connection;
|
||||
|
||||
pub use error::{CoreError, Result};
|
||||
pub use model::{CardRef, ColorSet, Deck, DeckEntry, LibraryCard, LibraryFilter};
|
||||
pub use model::{CardRef, ColorSet, Deck, DeckEntry, LibraryCard, LibraryFilter, LibraryStats};
|
||||
pub use search::NameIndex;
|
||||
|
||||
/// A handle to an open catalog: the library db with the reference db attached.
|
||||
|
||||
@@ -6,7 +6,7 @@ use rusqlite::{params, OptionalExtension};
|
||||
|
||||
use crate::db::REF_DB;
|
||||
use crate::error::Result;
|
||||
use crate::model::{CardRef, ColorSet, LibraryCard};
|
||||
use crate::model::{CardRef, ColorSet, LibraryCard, LibraryStats, WUBRG};
|
||||
use crate::Catalog;
|
||||
|
||||
/// Map a row shaped `(oracle_id, quantity_owned, location, name, mana_value,
|
||||
@@ -94,6 +94,56 @@ impl Catalog {
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Aggregate stats about the owned library, for the sidebar summary.
|
||||
pub fn library_stats(&self) -> Result<LibraryStats> {
|
||||
let unique_cards: i64 = self.conn.query_row(
|
||||
"SELECT COUNT(*) FROM library_cards WHERE quantity_owned > 0",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)?;
|
||||
let total_copies: i64 = self.conn.query_row(
|
||||
"SELECT COALESCE(SUM(quantity_owned), 0) FROM library_cards",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)?;
|
||||
let deck_count: i64 = self
|
||||
.conn
|
||||
.query_row("SELECT COUNT(*) FROM decks", [], |r| r.get(0))?;
|
||||
|
||||
let mut color_counts = [0i64; 5];
|
||||
let mut colorless_count = 0i64;
|
||||
{
|
||||
let mut stmt = self.conn.prepare(&format!(
|
||||
"SELECT oc.colors, lc.quantity_owned
|
||||
FROM library_cards lc
|
||||
JOIN {REF_DB}.oracle_cards oc ON oc.oracle_id = lc.oracle_id
|
||||
WHERE lc.quantity_owned > 0"
|
||||
))?;
|
||||
let rows =
|
||||
stmt.query_map([], |r| Ok((r.get::<_, i64>(0)? as u8, r.get::<_, i64>(1)?)))?;
|
||||
for row in rows {
|
||||
let (colors, qty) = row?;
|
||||
if colors == 0 {
|
||||
colorless_count += qty;
|
||||
} else {
|
||||
for (i, (bit, _)) in WUBRG.iter().enumerate() {
|
||||
if colors & bit != 0 {
|
||||
color_counts[i] += qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LibraryStats {
|
||||
unique_cards,
|
||||
total_copies,
|
||||
deck_count,
|
||||
color_counts,
|
||||
colorless_count,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the free-text storage location for a card (creates the library row if
|
||||
/// absent). Empty string clears it.
|
||||
pub fn set_location(&self, oracle_id: &str, location: &str) -> Result<()> {
|
||||
@@ -157,6 +207,25 @@ mod tests {
|
||||
assert_eq!(sol.card.image_uri, "http://img/sol-ring");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn library_stats_aggregates_counts_and_colors() {
|
||||
let fx = test_support::make();
|
||||
let cat = &fx.catalog;
|
||||
cat.add_owned("llanowar-elves", 2).unwrap(); // G
|
||||
cat.add_owned("deathrite-shaman", 1).unwrap(); // BG
|
||||
cat.add_owned("sol-ring", 3).unwrap(); // colorless
|
||||
cat.create_deck("Deck A", None).unwrap();
|
||||
cat.create_deck("Deck B", None).unwrap();
|
||||
|
||||
let stats = cat.library_stats().unwrap();
|
||||
assert_eq!(stats.unique_cards, 3);
|
||||
assert_eq!(stats.total_copies, 6);
|
||||
assert_eq!(stats.deck_count, 2);
|
||||
assert_eq!(stats.colorless_count, 3);
|
||||
// WUBRG order: W, U, B, R, G.
|
||||
assert_eq!(stats.color_counts, [0, 0, 1, 0, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn back_image_uri_is_plumbed_through() {
|
||||
let fx = test_support::make();
|
||||
|
||||
@@ -139,6 +139,22 @@ pub struct Deck {
|
||||
pub notes: String,
|
||||
}
|
||||
|
||||
/// Aggregate stats about the owned library, for a dashboard-style summary.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct LibraryStats {
|
||||
/// Distinct oracle cards owned (quantity_owned > 0).
|
||||
pub unique_cards: i64,
|
||||
/// Total physical copies owned, summed across all cards.
|
||||
pub total_copies: i64,
|
||||
pub deck_count: i64,
|
||||
/// Copies owned in each color, WUBRG order. A multicolor card's copies
|
||||
/// count toward every color it contains, so these can sum to more than
|
||||
/// `total_copies`.
|
||||
pub color_counts: [i64; 5],
|
||||
/// Copies owned with no color (colors == 0).
|
||||
pub colorless_count: i64,
|
||||
}
|
||||
|
||||
/// Criteria for searching the owned library. All fields are optional; an empty
|
||||
/// filter matches every owned card.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user