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>
116 lines
4.3 KiB
SQL
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);
|