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]
|
return cur.fetchone()[0]
|
||||||
finally:
|
finally:
|
||||||
self._db.putconn(conn)
|
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