Add brand history tracking for new/dropped brands detection
- Add first_seen_at/last_seen_at columns to brands table - Create brand_history table for event tracking (added, dropped, returned) - Add dispensary_brand_stats view for dashboard 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
57
backend/migrations/027_brand_history_tracking.sql
Normal file
57
backend/migrations/027_brand_history_tracking.sql
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
-- Migration 027: Brand History Tracking
|
||||||
|
-- Add first_seen_at and last_seen_at to brands table for history tracking
|
||||||
|
-- Also add dispensary_id since brands is currently store_id linked
|
||||||
|
|
||||||
|
-- Add timestamp columns to brands
|
||||||
|
ALTER TABLE brands ADD COLUMN IF NOT EXISTS first_seen_at TIMESTAMPTZ DEFAULT NOW();
|
||||||
|
ALTER TABLE brands ADD COLUMN IF NOT EXISTS last_seen_at TIMESTAMPTZ DEFAULT NOW();
|
||||||
|
ALTER TABLE brands ADD COLUMN IF NOT EXISTS dispensary_id INTEGER REFERENCES dispensaries(id);
|
||||||
|
|
||||||
|
-- Add index for tracking dropped brands
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_brands_last_seen ON brands(last_seen_at DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_brands_dispensary ON brands(dispensary_id);
|
||||||
|
|
||||||
|
-- Create brand_history table for detailed tracking
|
||||||
|
CREATE TABLE IF NOT EXISTS brand_history (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
dispensary_id INTEGER NOT NULL REFERENCES dispensaries(id) ON DELETE CASCADE,
|
||||||
|
brand_name VARCHAR(255) NOT NULL,
|
||||||
|
event_type VARCHAR(20) NOT NULL, -- 'added', 'dropped', 'returned'
|
||||||
|
event_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
product_count INTEGER, -- number of products from this brand at event time
|
||||||
|
metadata JSONB
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_brand_history_dispensary ON brand_history(dispensary_id, event_at DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_brand_history_brand ON brand_history(brand_name, event_at DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_brand_history_event ON brand_history(event_type, event_at DESC);
|
||||||
|
|
||||||
|
-- Create view for brand stats per dispensary
|
||||||
|
CREATE OR REPLACE VIEW dispensary_brand_stats AS
|
||||||
|
SELECT
|
||||||
|
d.id AS dispensary_id,
|
||||||
|
COALESCE(d.dba_name, d.name) AS dispensary_name,
|
||||||
|
COUNT(DISTINCT p.brand) FILTER (WHERE p.last_seen_at >= NOW() - INTERVAL '7 days') AS current_brands,
|
||||||
|
COUNT(DISTINCT p.brand) AS total_brands_ever,
|
||||||
|
(
|
||||||
|
SELECT COUNT(DISTINCT brand_name)
|
||||||
|
FROM brand_history bh
|
||||||
|
WHERE bh.dispensary_id = d.id
|
||||||
|
AND bh.event_type = 'added'
|
||||||
|
AND bh.event_at >= NOW() - INTERVAL '7 days'
|
||||||
|
) AS new_brands_7d,
|
||||||
|
(
|
||||||
|
SELECT COUNT(DISTINCT brand_name)
|
||||||
|
FROM brand_history bh
|
||||||
|
WHERE bh.dispensary_id = d.id
|
||||||
|
AND bh.event_type = 'dropped'
|
||||||
|
AND bh.event_at >= NOW() - INTERVAL '7 days'
|
||||||
|
) AS dropped_brands_7d
|
||||||
|
FROM dispensaries d
|
||||||
|
LEFT JOIN products p ON p.dispensary_id = d.id
|
||||||
|
GROUP BY d.id, d.dba_name, d.name;
|
||||||
|
|
||||||
|
-- Grant permissions
|
||||||
|
GRANT SELECT ON brand_history TO scraper;
|
||||||
|
GRANT INSERT, UPDATE ON brand_history TO scraper;
|
||||||
|
GRANT SELECT ON dispensary_brand_stats TO scraper;
|
||||||
Reference in New Issue
Block a user