discgod/server/schema.sql
type-two 1945c19fb3 discgod: API-token Discogs seller-inventory tracker
Lean MV3 extension + standalone single-file ingest server that pulls watched
sellers' full inventories from the Discogs API into discogs_full (mp_* schema),
tracking price changes and sales over time. Successor to pliceclogs/marketwatcher;
runs alongside the legacy plice server on a separate port (5057).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 19:54:33 +10:00

116 lines
4.3 KiB
SQL

-- discgod schema (reference copy — the server applies this itself via
-- discgod_ingest.ensure_schema() on startup). Additive: it reuses the existing
-- marketwatcher mp_* tables verbatim and adds one watchlist table.
--
-- Apply manually if you like: psql discogs_full -f schema.sql
-- Sellers ever seen, with latest reputation stats.
CREATE TABLE IF NOT EXISTS mp_seller (
seller_id BIGINT PRIMARY KEY,
username TEXT NOT NULL,
rating NUMERIC(3,1),
rating_percent NUMERIC(5,2),
ratings_count INTEGER,
ships_from TEXT,
first_seen TIMESTAMPTZ DEFAULT now(),
last_seen TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_mp_seller_username ON mp_seller (username);
-- One row per marketplace listing (unique physical copy). release_id joins
-- straight to your XML `release` table.
CREATE TABLE IF NOT EXISTS mp_listing (
item_id BIGINT PRIMARY KEY,
release_id BIGINT NOT NULL,
seller_id BIGINT,
seller_username TEXT,
title TEXT,
label TEXT,
cat_no TEXT,
media_condition TEXT,
sleeve_condition TEXT,
notes TEXT,
price_value NUMERIC(12,2),
price_currency TEXT,
shipping_value NUMERIC(12,2),
converted_value NUMERIC(12,2),
have_count INTEGER,
want_count INTEGER,
ships_from TEXT,
status TEXT NOT NULL DEFAULT 'active', -- active | gone
gone_at TIMESTAMPTZ,
first_seen TIMESTAMPTZ DEFAULT now(),
last_seen TIMESTAMPTZ DEFAULT now(),
times_seen INTEGER DEFAULT 1,
last_crawl_id TEXT
);
CREATE INDEX IF NOT EXISTS idx_mp_listing_release ON mp_listing (release_id);
CREATE INDEX IF NOT EXISTS idx_mp_listing_seller ON mp_listing (seller_username, status);
CREATE INDEX IF NOT EXISTS idx_mp_listing_seen ON mp_listing (last_seen DESC);
-- Append-only state-change history per listing.
-- event: listed | price_change | condition_change | gone | relisted
CREATE TABLE IF NOT EXISTS mp_listing_event (
id BIGSERIAL PRIMARY KEY,
item_id BIGINT NOT NULL,
release_id BIGINT,
seller_username TEXT,
event TEXT NOT NULL,
old_price NUMERIC(12,2),
new_price NUMERIC(12,2),
currency TEXT,
details JSONB,
recorded_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_mp_event_item ON mp_listing_event (item_id, recorded_at DESC);
CREATE INDEX IF NOT EXISTS idx_mp_event_type ON mp_listing_event (event, recorded_at DESC);
CREATE INDEX IF NOT EXISTS idx_mp_event_release ON mp_listing_event (release_id);
-- Inventory-size time series per seller. source = 'api_inventory' for discgod
-- (the authoritative pagination total from the Discogs API).
CREATE TABLE IF NOT EXISTS mp_seller_snapshot (
id BIGSERIAL PRIMARY KEY,
seller_id BIGINT,
username TEXT NOT NULL,
total_listings BIGINT,
source TEXT NOT NULL,
filter_key TEXT,
filter_params JSONB,
captured_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_mp_snapshot_series
ON mp_seller_snapshot (username, source, filter_key, captured_at DESC);
-- Provenance: every captured inventory page.
CREATE TABLE IF NOT EXISTS mp_page_log (
id BIGSERIAL PRIMARY KEY,
url TEXT,
page_type TEXT, -- 'api_inventory' for discgod
seller_page TEXT,
params JSONB,
page INTEGER,
total_results BIGINT,
item_count INTEGER,
item_ids BIGINT[],
new_items INTEGER,
price_changes INTEGER,
crawl_id TEXT,
scraped_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_mp_page_log_time ON mp_page_log (scraped_at DESC);
-- discgod watchlist: who to auto-crawl. Shared by the extension popup and the
-- headless cron crawler.
CREATE TABLE IF NOT EXISTS discgod_seller_watch (
username TEXT PRIMARY KEY,
seller_id BIGINT,
enabled BOOLEAN DEFAULT TRUE,
note TEXT,
filter_params JSONB,
added_at TIMESTAMPTZ DEFAULT now(),
last_crawled_at TIMESTAMPTZ,
last_listing_count INTEGER,
last_status TEXT
);
CREATE INDEX IF NOT EXISTS idx_discgod_watch_enabled ON discgod_seller_watch (enabled);