Spaces:
Sleeping
Sleeping
File size: 7,441 Bytes
1a3167b 657765e 1a3167b 657765e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | import sqlite3
import os
import shutil
import pandas as pd
from datetime import datetime
DB_PATH = os.environ.get("DB_PATH", "pickldd.db")
UPLOADS_DIR = "uploads"
SORT_OPTIONS = ["β Overall", "π Buy Again", "π Crunchiness", "π¬ Sourness", "π§ Garlic", "πΆοΈ Spiciness", "π Reviews"]
_SORT_COLS = {
"β Overall": "avg_overall",
"π Buy Again": "buy_again_pct",
"π Crunchiness": "avg_crunch",
"π¬ Sourness": "avg_sour",
"π§ Garlic": "avg_garlic",
"πΆοΈ Spiciness": "avg_spicy",
"π Reviews": "review_count",
}
def init_db():
os.makedirs(UPLOADS_DIR, exist_ok=True)
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS reviews (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pickle_name TEXT NOT NULL,
brand TEXT DEFAULT '',
overall INTEGER NOT NULL CHECK(overall BETWEEN 1 AND 10),
crunchiness INTEGER NOT NULL CHECK(crunchiness BETWEEN 1 AND 10),
sourness INTEGER NOT NULL CHECK(sourness BETWEEN 1 AND 10),
garlic INTEGER NOT NULL CHECK(garlic BETWEEN 1 AND 10),
spiciness INTEGER NOT NULL DEFAULT 5,
buy_again INTEGER NOT NULL DEFAULT 1,
review_text TEXT DEFAULT '',
photo_path TEXT,
created_at TEXT DEFAULT (datetime('now'))
)
""")
# Non-destructive migrations for existing databases
for col, defn in [
("spiciness", "INTEGER NOT NULL DEFAULT 5"),
("buy_again", "INTEGER NOT NULL DEFAULT 1"),
]:
try:
conn.execute(f"ALTER TABLE reviews ADD COLUMN {col} {defn}")
except sqlite3.OperationalError:
pass
conn.execute("CREATE INDEX IF NOT EXISTS idx_reviews_name ON reviews(pickle_name)")
conn.execute("CREATE INDEX IF NOT EXISTS idx_reviews_brand ON reviews(brand)")
conn.commit()
conn.close()
def save_photo(tmp_path, pickle_name):
if not tmp_path:
return None
try:
ext = os.path.splitext(tmp_path)[1] or ".jpg"
safe = "".join(c for c in pickle_name if c.isalnum() or c in "-_ ")[:30].strip()
dest = os.path.join(UPLOADS_DIR, f"{datetime.now():%Y%m%d_%H%M%S}_{safe}{ext}")
shutil.copy2(tmp_path, dest)
return dest
except Exception:
return None
def insert_review(pickle_name, brand, overall, crunchiness, sourness, garlic,
spiciness, buy_again, review_text, photo_path):
conn = sqlite3.connect(DB_PATH)
try:
conn.execute(
"""INSERT INTO reviews
(pickle_name, brand, overall, crunchiness, sourness, garlic,
spiciness, buy_again, review_text, photo_path)
VALUES (?,?,?,?,?,?,?,?,?,?)""",
(
pickle_name.strip(),
(brand or "").strip(),
int(overall), int(crunchiness), int(sourness), int(garlic), int(spiciness),
1 if buy_again else 0,
(review_text or "").strip(),
photo_path,
),
)
conn.commit()
finally:
conn.close()
def _query_pickle_profiles(sort_by=None, name_filter="", brand_filter=""):
conn = sqlite3.connect(DB_PATH)
df = pd.read_sql_query(
"""
SELECT
pickle_name,
COALESCE(NULLIF(TRIM(brand), ''), 'β') AS brand,
ROUND(AVG(CAST(overall AS REAL)), 1) AS avg_overall,
ROUND(AVG(CAST(crunchiness AS REAL)), 1) AS avg_crunch,
ROUND(AVG(CAST(sourness AS REAL)), 1) AS avg_sour,
ROUND(AVG(CAST(garlic AS REAL)), 1) AS avg_garlic,
ROUND(AVG(CAST(spiciness AS REAL)), 1) AS avg_spicy,
ROUND(AVG(CAST(buy_again AS REAL)) * 100, 0) AS buy_again_pct,
COUNT(*) AS review_count
FROM reviews
WHERE (:name = '' OR LOWER(pickle_name) LIKE '%' || LOWER(:name) || '%')
AND (:brand = '' OR LOWER(COALESCE(brand,'')) LIKE '%' || LOWER(:brand) || '%')
GROUP BY LOWER(TRIM(pickle_name)), LOWER(TRIM(COALESCE(brand, '')))
""",
conn,
params={"name": name_filter or "", "brand": brand_filter or ""},
)
conn.close()
if df.empty:
return df
sort_col = _SORT_COLS.get(sort_by, "avg_overall")
return df.sort_values(sort_col, ascending=False).reset_index(drop=True)
def _query_leaderboard(sort_by="β Overall"):
df = _query_pickle_profiles(sort_by=sort_by)
if df.empty:
return df
df.insert(0, "rank", range(1, len(df) + 1))
return df
def get_analytics():
profiles = _query_pickle_profiles()
conn = sqlite3.connect(DB_PATH)
row = conn.execute("""
SELECT
COUNT(*) AS total_reviews,
COUNT(DISTINCT LOWER(TRIM(pickle_name))) AS total_pickles,
ROUND(AVG(CAST(crunchiness AS REAL)), 1) AS avg_crunch,
ROUND(AVG(CAST(sourness AS REAL)), 1) AS avg_sour,
ROUND(AVG(CAST(garlic AS REAL)), 1) AS avg_garlic,
ROUND(AVG(CAST(buy_again AS REAL)) * 100, 0) AS buy_again_pct
FROM reviews
""").fetchone()
conn.close()
total = int(row[0]) if row[0] else 0
total_pickles = int(row[1]) if row[1] else 0
avg_crunch = float(row[2]) if row[2] is not None else 0.0
avg_sour = float(row[3]) if row[3] is not None else 0.0
avg_garlic = float(row[4]) if row[4] is not None else 0.0
buy_again_pct = float(row[5]) if row[5] is not None else 0.0
def _label(r):
return f"{r['pickle_name']} ({r['brand']})" if r["brand"] != "β" else r["pickle_name"]
if profiles.empty:
return total, total_pickles, "β", "β", avg_crunch, avg_sour, avg_garlic, buy_again_pct
highest_rated = _label(profiles.iloc[0])
most_reviewed = _label(profiles.sort_values("review_count", ascending=False).iloc[0])
return total, total_pickles, highest_rated, most_reviewed, avg_crunch, avg_sour, avg_garlic, buy_again_pct
def get_pickle_choices():
df = _query_pickle_profiles()
if df.empty:
return []
choices = []
for _, row in df.iterrows():
b = row["brand"]
label = f"{row['pickle_name']} β {b}" if b != "β" else row["pickle_name"]
choices.append((label, f"{row['pickle_name']}|||{b}"))
return choices
def get_recent_reviews_df(limit=20):
conn = sqlite3.connect(DB_PATH)
df = pd.read_sql_query(
"""
SELECT pickle_name, brand, overall, crunchiness, sourness, garlic, spiciness, buy_again,
review_text, SUBSTR(created_at, 1, 10) AS date
FROM reviews ORDER BY id DESC LIMIT :limit
""",
conn,
params={"limit": limit},
)
conn.close()
return df
def get_top_pickles_df(n=8):
df = _query_pickle_profiles()
if df.empty:
return pd.DataFrame({"Pickle": [], "Avg Score": [], "Theme": []})
top = df.head(n).copy()
top["Pickle"] = top.apply(
lambda r: r["pickle_name"] + (f" ({r['brand']})" if r["brand"] != "β" else ""), axis=1
)
top["Theme"] = "Pickle"
return top[["Pickle", "avg_overall", "Theme"]].rename(columns={"avg_overall": "Avg Score"})
|