Spaces:
Sleeping
Sleeping
| # SQLite product catalog and analytics storage. | |
| import json | |
| import os | |
| import sqlite3 | |
| import uuid | |
| from datetime import datetime | |
| from pathlib import Path | |
| from typing import Dict, List, Optional | |
| from app.models import DB_DIR | |
| class Database: | |
| def __init__(self): | |
| db_path_override = os.getenv("MADVERSE_DB_PATH") | |
| self.db_path = Path(db_path_override).resolve() if db_path_override else (DB_DIR / "adcraft.db") | |
| self.db_path.parent.mkdir(parents=True, exist_ok=True) | |
| print(f" [DATABASE] Initializing SQLite database: {self.db_path}") | |
| self._init_db() | |
| print(f" [DATABASE] Tables ready: products, generated_content, clicks") | |
| def _init_db(self): | |
| conn = sqlite3.connect(str(self.db_path)) | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS products ( | |
| id TEXT PRIMARY KEY, | |
| name TEXT NOT NULL, | |
| description TEXT, | |
| price TEXT, | |
| category TEXT, | |
| brand TEXT, | |
| image_paths TEXT, | |
| enhanced_image_path TEXT, | |
| created_at TEXT NOT NULL, | |
| updated_at TEXT NOT NULL | |
| ) | |
| """) | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS generated_content ( | |
| id TEXT PRIMARY KEY, | |
| product_id TEXT, | |
| content_type TEXT, | |
| language TEXT, | |
| content_json TEXT, | |
| pamphlet_path TEXT, | |
| product_image_path TEXT, | |
| created_at TEXT NOT NULL, | |
| FOREIGN KEY (product_id) REFERENCES products(id) | |
| ) | |
| """) | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS clicks ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| product_id TEXT, | |
| platform TEXT, | |
| source TEXT, | |
| clicked_at TEXT NOT NULL, | |
| FOREIGN KEY (product_id) REFERENCES products(id) | |
| ) | |
| """) | |
| conn.commit() | |
| conn.close() | |
| def _conn(self): | |
| conn = sqlite3.connect(str(self.db_path)) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| def create_product(self, name: str, description: str = "", | |
| price: str = "", category: str = "", | |
| brand: str = "", image_paths: list = None) -> str: | |
| product_id = str(uuid.uuid4())[:8] | |
| now = datetime.now().isoformat() | |
| print(f" [DB] Creating product: id={product_id} | name={name} | brand={brand} | category={category}") | |
| conn = self._conn() | |
| conn.execute( | |
| "INSERT INTO products VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", | |
| (product_id, name, description, price, category, brand, | |
| json.dumps(image_paths or []), None, now, now) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| print(f" [DB] Product created: {product_id} | Images: {len(image_paths or [])}") | |
| return product_id | |
| def get_product(self, product_id: str) -> Optional[dict]: | |
| conn = self._conn() | |
| row = conn.execute("SELECT * FROM products WHERE id = ?", (product_id,)).fetchone() | |
| conn.close() | |
| if row: | |
| d = dict(row) | |
| d["image_paths"] = json.loads(d["image_paths"]) | |
| return d | |
| return None | |
| def list_products(self) -> List[dict]: | |
| conn = self._conn() | |
| rows = conn.execute("SELECT * FROM products ORDER BY created_at DESC").fetchall() | |
| conn.close() | |
| products = [] | |
| for row in rows: | |
| d = dict(row) | |
| d["image_paths"] = json.loads(d["image_paths"]) | |
| products.append(d) | |
| return products | |
| def update_product(self, product_id: str, **kwargs) -> bool: | |
| """Update product fields. Only updates provided fields.""" | |
| conn = self._conn() | |
| product = conn.execute("SELECT * FROM products WHERE id = ?", (product_id,)).fetchone() | |
| if not product: | |
| conn.close() | |
| return False | |
| updatable = ["name", "description", "price", "category", "brand", | |
| "image_paths", "enhanced_image_path"] | |
| sets = [] | |
| values = [] | |
| for key in updatable: | |
| if key in kwargs: | |
| val = kwargs[key] | |
| if key == "image_paths" and isinstance(val, list): | |
| val = json.dumps(val) | |
| sets.append(f"{key} = ?") | |
| values.append(val) | |
| if not sets: | |
| conn.close() | |
| return False | |
| sets.append("updated_at = ?") | |
| values.append(datetime.now().isoformat()) | |
| values.append(product_id) | |
| conn.execute(f"UPDATE products SET {', '.join(sets)} WHERE id = ?", values) | |
| conn.commit() | |
| conn.close() | |
| print(f" [DB] Product updated: {product_id} | Fields: {list(kwargs.keys())}") | |
| return True | |
| def delete_product(self, product_id: str): | |
| conn = self._conn() | |
| conn.execute("DELETE FROM generated_content WHERE product_id = ?", (product_id,)) | |
| conn.execute("DELETE FROM clicks WHERE product_id = ?", (product_id,)) | |
| conn.execute("DELETE FROM products WHERE id = ?", (product_id,)) | |
| conn.commit() | |
| conn.close() | |
| def save_generated_content(self, product_id: str, content_type: str, | |
| language: str, content: dict, | |
| pamphlet_path: str = None, | |
| product_image_path: str = None) -> str: | |
| content_id = str(uuid.uuid4())[:8] | |
| now = datetime.now().isoformat() | |
| print(f" [DB] Saving content: id={content_id} | product={product_id} | type={content_type} | lang={language}") | |
| conn = self._conn() | |
| conn.execute( | |
| "INSERT INTO generated_content VALUES (?, ?, ?, ?, ?, ?, ?, ?)", | |
| (content_id, product_id, content_type, language, | |
| json.dumps(content, ensure_ascii=False), pamphlet_path, | |
| product_image_path, now) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| print(f" [DB] Content saved: {content_id}") | |
| return content_id | |
| def get_product_content(self, product_id: str) -> List[dict]: | |
| conn = self._conn() | |
| rows = conn.execute( | |
| "SELECT * FROM generated_content WHERE product_id = ? ORDER BY created_at DESC", | |
| (product_id,) | |
| ).fetchall() | |
| conn.close() | |
| results = [] | |
| for row in rows: | |
| d = dict(row) | |
| d["content_json"] = json.loads(d["content_json"]) | |
| results.append(d) | |
| return results | |
| def track_click(self, product_id: str, platform: str, source: str = ""): | |
| now = datetime.now().isoformat() | |
| print(f" [DB] Tracking click: product={product_id} | platform={platform} | source={source}") | |
| conn = self._conn() | |
| conn.execute( | |
| "INSERT INTO clicks (product_id, platform, source, clicked_at) VALUES (?, ?, ?, ?)", | |
| (product_id, platform, source, now) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| def get_analytics(self, product_id: str) -> dict: | |
| conn = self._conn() | |
| rows = conn.execute( | |
| "SELECT platform, COUNT(*) as count FROM clicks WHERE product_id = ? GROUP BY platform", | |
| (product_id,) | |
| ).fetchall() | |
| total = conn.execute( | |
| "SELECT COUNT(*) FROM clicks WHERE product_id = ?", (product_id,) | |
| ).fetchone()[0] | |
| conn.close() | |
| platforms = {row["platform"]: row["count"] for row in rows} | |
| return {"total_clicks": total, "by_platform": platforms} | |