feat: add community_categories table + SnipeCommunityStore publish/fetch methods
Migration 004 creates community_categories (platform, category_id, name, full_path, source_product, published_at) with a unique constraint on (platform, category_id) and a name index. SnipeCommunityStore gains publish_categories() (upsert batch) and fetch_categories() (ordered by name, configurable limit) to support Snipe's community category federation.
This commit is contained in:
parent
f7bf121aef
commit
a2c768c635
2 changed files with 98 additions and 0 deletions
|
|
@ -0,0 +1,19 @@
|
|||
-- 004_community_categories.sql
|
||||
-- MIT License
|
||||
-- Shared eBay category tree published by credentialed Snipe instances.
|
||||
-- Credentialless instances pull from this table during refresh().
|
||||
-- Privacy: only public eBay category metadata (IDs, names, paths) — no user data.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS community_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
platform TEXT NOT NULL DEFAULT 'ebay',
|
||||
category_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
full_path TEXT NOT NULL,
|
||||
source_product TEXT NOT NULL DEFAULT 'snipe',
|
||||
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (platform, category_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_community_cat_name
|
||||
ON community_categories (platform, name);
|
||||
|
|
@ -172,3 +172,82 @@ class SnipeCommunityStore(SharedStore):
|
|||
return cur.fetchone()[0]
|
||||
finally:
|
||||
self._db.putconn(conn)
|
||||
|
||||
def publish_categories(
|
||||
self,
|
||||
categories: list[tuple[str, str, str]],
|
||||
platform: str = "ebay",
|
||||
) -> int:
|
||||
"""Upsert a batch of eBay leaf categories into the shared community table.
|
||||
|
||||
Args:
|
||||
categories: List of (category_id, name, full_path) tuples.
|
||||
platform: Source auction platform (default "ebay").
|
||||
|
||||
Returns:
|
||||
Number of rows upserted.
|
||||
"""
|
||||
if not categories:
|
||||
return 0
|
||||
conn = self._db.getconn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.executemany(
|
||||
"""
|
||||
INSERT INTO community_categories
|
||||
(platform, category_id, name, full_path, source_product)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
ON CONFLICT (platform, category_id)
|
||||
DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
full_path = EXCLUDED.full_path,
|
||||
source_product = EXCLUDED.source_product,
|
||||
published_at = NOW()
|
||||
""",
|
||||
[
|
||||
(platform, cid, name, path, self._source_product)
|
||||
for cid, name, path in categories
|
||||
],
|
||||
)
|
||||
conn.commit()
|
||||
return len(categories)
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
log.warning(
|
||||
"Failed to publish %d categories to community store",
|
||||
len(categories), exc_info=True,
|
||||
)
|
||||
raise
|
||||
finally:
|
||||
self._db.putconn(conn)
|
||||
|
||||
def fetch_categories(
|
||||
self,
|
||||
platform: str = "ebay",
|
||||
limit: int = 500,
|
||||
) -> list[tuple[str, str, str]]:
|
||||
"""Fetch community-contributed eBay categories.
|
||||
|
||||
Args:
|
||||
platform: Source auction platform (default "ebay").
|
||||
limit: Maximum rows to return.
|
||||
|
||||
Returns:
|
||||
List of (category_id, name, full_path) tuples ordered by name.
|
||||
"""
|
||||
conn = self._db.getconn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT category_id, name, full_path
|
||||
FROM community_categories
|
||||
WHERE platform = %s
|
||||
ORDER BY name
|
||||
LIMIT %s
|
||||
""",
|
||||
(platform, limit),
|
||||
)
|
||||
return [(row[0], row[1], row[2]) for row in cur.fetchall()]
|
||||
finally:
|
||||
self._db.putconn(conn)
|
||||
|
|
|
|||
Loading…
Reference in a new issue