Ichlibitiche's picture
Add WhiskyDB tab to sample explorers
7e320f8 verified
Raw
History Blame Contribute Delete
14.7 kB
import gradio as gr
import pandas as pd
# ── Load the three free samples from their public Hugging Face datasets ──
ROASTER_URL = "hf://datasets/Ichlibitiche/roasterdb-specialty-coffee-sample/roasterdb_sample.csv"
FLORA_URL = "hf://datasets/Ichlibitiche/floradb-houseplants-care-sample/floradb_sample.csv"
SUPP_URL = "hf://datasets/Ichlibitiche/suppdb-supplements-sample/suppdb_sample.csv"
WHISKY_BASE = "hf://datasets/Ichlibitiche/whiskydb-fine-spirits-sample/"
r_df = pd.read_csv(ROASTER_URL)
f_df = pd.read_csv(FLORA_URL)
s_df = pd.read_csv(SUPP_URL)
w_df = pd.read_csv(WHISKY_BASE + "spirits.csv")
w_dist_df = pd.read_csv(WHISKY_BASE + "distilleries.csv")
w_casks_df = pd.read_csv(WHISKY_BASE + "casks_taxonomy.csv")
w_flavors_df = pd.read_csv(WHISKY_BASE + "flavors_taxonomy.csv")
def choices(df, col):
vals = df[col].dropna().astype(str)
return ["All"] + sorted(v for v in vals.unique() if v.strip() and v != "Unknown")
# ── RoasterDB ──
R_COLS = {
"source_roaster": "Roaster",
"title": "Coffee",
"origin_country": "Origin",
"process_method": "Process",
"roast_level": "Roast",
"weight_grams": "Weight (g)",
"price_value": "Price (USD)",
"tasting_notes_sca_nodes": "SCA Flavor Notes",
"source_url": "Source URL",
}
R_PRICE_MAX = float(r_df["price_value"].max())
def r_explore(roaster, country, process, roast, flavor, max_price):
d = r_df
if roaster != "All":
d = d[d["source_roaster"] == roaster]
if country != "All":
d = d[d["origin_country"] == country]
if process != "All":
d = d[d["process_method"] == process]
if roast != "All":
d = d[d["roast_level"] == roast]
if flavor and flavor.strip():
d = d[d["tasting_notes_sca_nodes"].fillna("").str.contains(flavor.strip(), case=False, regex=False)]
if max_price < R_PRICE_MAX:
d = d[d["price_value"].fillna(R_PRICE_MAX + 1) <= max_price]
summary = (
f"**{len(d)} coffees** from **{d['source_roaster'].nunique()} roasters** match β€” "
f"out of {len(r_df)} in the free sample. The full RoasterDB has **8,000+ products from 280+ roasters**."
)
return summary, d[list(R_COLS)].rename(columns=R_COLS).sort_values("Price (USD)")
# ── FloraDB ──
F_COLS = {
"common_name": "Common Name",
"scientific_name": "Scientific Name",
"family": "Family",
"light_requirement_level": "Light",
"min_lux": "Min Lux",
"max_lux": "Max Lux",
"watering_frequency_days": "Water Every (days)",
"min_temp_celsius": "Min Β°C",
"max_temp_celsius": "Max Β°C",
"is_toxic_to_dogs": "Toxic: Dogs",
"is_toxic_to_cats": "Toxic: Cats",
"toxicity_status": "Toxicity Source",
"care_confidence": "Care Confidence",
"gbif_source_url": "GBIF Source",
}
F_PET_FILTERS = [
"All",
"Verified safe for dogs",
"Verified safe for cats",
"Toxic to dogs or cats",
"Toxicity unknown",
]
def f_explore(name_query, family, light, confidence, pet_filter):
d = f_df
if name_query and name_query.strip():
q = name_query.strip()
hit = (
d["common_name"].fillna("").str.contains(q, case=False, regex=False)
| d["scientific_name"].fillna("").str.contains(q, case=False, regex=False)
)
d = d[hit]
if family != "All":
d = d[d["family"] == family]
if light != "All":
d = d[d["light_requirement_level"] == light]
if confidence != "All":
d = d[d["care_confidence"] == confidence]
# Safety filters are conservative: "safe" requires a determined status, never unknown
if pet_filter == "Verified safe for dogs":
d = d[(d["is_toxic_to_dogs"] == 0) & (d["toxicity_status"] != "unknown")]
elif pet_filter == "Verified safe for cats":
d = d[(d["is_toxic_to_cats"] == 0) & (d["toxicity_status"] != "unknown")]
elif pet_filter == "Toxic to dogs or cats":
d = d[(d["is_toxic_to_dogs"] == 1) | (d["is_toxic_to_cats"] == 1)]
elif pet_filter == "Toxicity unknown":
d = d[d["toxicity_status"] == "unknown"]
summary = (
f"**{len(d)} plants** across **{d['family'].nunique()} families** match β€” "
f"out of {len(f_df)} in the free sample. "
f"The full FloraDB has **270 care plants + 891 ASPCA toxicity records + a 20,000+ species GBIF index**."
)
return summary, d[list(F_COLS)].rename(columns=F_COLS)
# ── SuppDB ──
S_COLS = {
"brand": "Brand",
"product_name": "Product",
"form_type": "Form",
"ingredient": "Ingredient",
"ingredient_form": "Ingredient Form",
"ingredient_category": "Category",
"amount_per_serving_mg": "Dose (mg/serving)",
"is_proprietary_blend": "Hidden in Blend",
"recommended_daily_mg": "NIH RDA (mg)",
"upper_safety_limit_mg": "Upper Limit (mg)",
"molecular_formula": "Formula",
"source_url": "NIH DSLD Label",
}
S_BLEND_FILTERS = [
"All",
"Disclosed doses only",
"Proprietary-blend (hidden dose) only",
]
def s_explore(ingredient_query, brand, category, form, blend_filter):
d = s_df
if ingredient_query and ingredient_query.strip():
q = ingredient_query.strip()
hit = (
d["ingredient"].fillna("").str.contains(q, case=False, regex=False)
| d["product_name"].fillna("").str.contains(q, case=False, regex=False)
)
d = d[hit]
if brand != "All":
d = d[d["brand"] == brand]
if category != "All":
d = d[d["ingredient_category"] == category]
if form != "All":
d = d[d["form_type"] == form]
if blend_filter == "Disclosed doses only":
d = d[d["is_proprietary_blend"] == 0]
elif blend_filter == "Proprietary-blend (hidden dose) only":
d = d[d["is_proprietary_blend"] == 1]
summary = (
f"**{len(d)} ingredient records** across **{d['product_id'].nunique()} products** "
f"from **{d['brand'].nunique()} brands** match β€” out of {len(s_df)} records in the free sample. "
f"The full SuppDB has **115,000+ ingredient records across 17,000+ products**."
)
return summary, d[list(S_COLS)].rename(columns=S_COLS)
# ── WhiskyDB ──
W_COLS = {
"name": "Spirit",
"type": "Type",
"age": "Age (years)",
"abv": "ABV %",
"volume_ml": "Volume (ml)",
"source_name": "Source",
"source_url": "Source URL",
}
def w_explore(query, spirit_type):
d = w_df
if query and query.strip():
d = d[d["name"].fillna("").str.contains(query.strip(), case=False, regex=False)]
if spirit_type != "All":
d = d[d["type"] == spirit_type]
summary = (
f"**{len(d)} spirits** match β€” out of {len(w_df)} in the free sample. "
f"The full WhiskyDB has **1,290+ spirits, 3,200+ distilleries & 20,000+ monthly auction-price benchmarks (2005 β†’ today)**."
)
return summary, d[list(W_COLS)].rename(columns=W_COLS)
# ── UI ──
with gr.Blocks(title="Dataset Sample Explorers") as demo:
gr.Markdown("# πŸ—‚οΈ Dataset Sample Explorers")
gr.Markdown("""Interactive explorers for the **free samples** of four curated commercial datasets. Every record carries a source URL so any fact can be re-verified. Full datasets are available at each official portal.""")
with gr.Tab("β˜• RoasterDB β€” Specialty Coffee"):
gr.Markdown(f"""**{len(r_df)} verified coffees from {r_df['source_roaster'].nunique()} artisan roasters**, tasting notes normalized to the **SCA Flavor Wheel**. Full dataset: **8,000+ products, 280+ roasters** β€” [roasterdb.net](https://specialty-coffee-roasterdb.pages.dev) Β· [πŸ€— sample dataset](https://huggingface.co/datasets/Ichlibitiche/roasterdb-specialty-coffee-sample) Β· [πŸ† Kaggle](https://www.kaggle.com/datasets/ahtiticheamine/roasterdb-specialty-coffee-sample) Β· [πŸ”„ Apify scraper](https://apify.com/dataengineered/specialty-coffee-roaster-scraper)""")
with gr.Row():
r_roaster = gr.Dropdown(choices=choices(r_df, "source_roaster"), value="All", label="Roaster")
r_country = gr.Dropdown(choices=choices(r_df, "origin_country"), value="All", label="Origin Country")
r_process = gr.Dropdown(choices=choices(r_df, "process_method"), value="All", label="Process Method")
r_roast = gr.Dropdown(choices=choices(r_df, "roast_level"), value="All", label="Roast Level")
with gr.Row():
r_flavor = gr.Textbox(label="SCA Flavor Search", placeholder="e.g. Berry, Chocolate, Floral, Peach...")
r_price = gr.Slider(minimum=0, maximum=R_PRICE_MAX, value=R_PRICE_MAX, step=1, label="Max Price (USD)")
r_btn = gr.Button("Explore Coffees", variant="primary")
r_text = gr.Markdown()
r_table = gr.Dataframe(label="Matching Coffees", wrap=True)
r_inputs = [r_roaster, r_country, r_process, r_roast, r_flavor, r_price]
r_btn.click(fn=r_explore, inputs=r_inputs, outputs=[r_text, r_table])
demo.load(fn=r_explore, inputs=r_inputs, outputs=[r_text, r_table])
with gr.Tab("🌿 FloraDB β€” Houseplants & Pet Toxicity"):
gr.Markdown(f"""**{len(f_df)} houseplants from {f_df['family'].nunique()} botanical families** β€” care as **quantitative metrics** (Lux, watering days, Β°C) with **ASPCA pet toxicity** on GBIF-verified names. Full dataset: **270 care plants + 891 toxicity records + 20,000+ species index** β€” [floradb](https://houseplants-botanical-floradb.pages.dev) Β· [πŸ€— sample dataset](https://huggingface.co/datasets/Ichlibitiche/floradb-houseplants-care-sample) Β· [πŸ† Kaggle](https://www.kaggle.com/datasets/ahtiticheamine/floradb-houseplants-care-sample) Β· [πŸ”„ Apify lookup](https://apify.com/dataengineered/houseplant-care-toxicity-lookup)""")
with gr.Row():
f_name = gr.Textbox(label="Plant Search", placeholder="e.g. Monstera, Pothos, Ficus...")
f_family = gr.Dropdown(choices=choices(f_df, "family"), value="All", label="Botanical Family")
f_light = gr.Dropdown(choices=choices(f_df, "light_requirement_level"), value="All", label="Light Requirement")
with gr.Row():
f_conf = gr.Dropdown(choices=choices(f_df, "care_confidence"), value="All", label="Care Confidence")
f_pet = gr.Dropdown(choices=F_PET_FILTERS, value="All", label="Pet Safety (ASPCA-based, conservative)")
f_btn = gr.Button("Explore Plants", variant="primary")
f_text = gr.Markdown()
f_table = gr.Dataframe(label="Matching Plants", wrap=True)
f_inputs = [f_name, f_family, f_light, f_conf, f_pet]
f_btn.click(fn=f_explore, inputs=f_inputs, outputs=[f_text, f_table])
demo.load(fn=f_explore, inputs=f_inputs, outputs=[f_text, f_table])
with gr.Tab("πŸ’Š SuppDB β€” Supplements & Nootropics"):
gr.Markdown(f"""**{len(s_df)} active-ingredient records from {s_df['product_id'].nunique()} real supplement products ({s_df['brand'].nunique()} brands)** β€” NIH DSLD labels with **mg-normalized doses**, **proprietary-blend transparency**, and **PubChem chemistry**. Full dataset: **17,000+ products, 115,000+ ingredient records** β€” [suppdb.net](https://supplements-nootropics-suppdb.pages.dev) Β· [πŸ€— sample dataset](https://huggingface.co/datasets/Ichlibitiche/suppdb-supplements-sample) Β· [πŸ† Kaggle](https://www.kaggle.com/datasets/ahtiticheamine/suppdb-supplements-sample)""")
with gr.Row():
s_query = gr.Textbox(label="Ingredient / Product Search", placeholder="e.g. Magnesium, Ashwagandha, Vitamin D...")
s_brand = gr.Dropdown(choices=choices(s_df, "brand"), value="All", label="Brand")
with gr.Row():
s_category = gr.Dropdown(choices=choices(s_df, "ingredient_category"), value="All", label="Ingredient Category")
s_form = gr.Dropdown(choices=choices(s_df, "form_type"), value="All", label="Product Form")
s_blend = gr.Dropdown(choices=S_BLEND_FILTERS, value="All", label="Proprietary-Blend Transparency")
s_btn = gr.Button("Explore Supplements", variant="primary")
s_text = gr.Markdown()
s_table = gr.Dataframe(label="Matching Ingredient Records", wrap=True)
s_inputs = [s_query, s_brand, s_category, s_form, s_blend]
s_btn.click(fn=s_explore, inputs=s_inputs, outputs=[s_text, s_table])
demo.load(fn=s_explore, inputs=s_inputs, outputs=[s_text, s_table])
with gr.Tab("πŸ₯ƒ WhiskyDB β€” Fine Spirits"):
gr.Markdown(f"""**{len(w_df)} spirits + {len(w_dist_df)} distilleries**, provenance-tracked from open public sources, with **fully open cask & flavor taxonomies** (CC BY 4.0). Full dataset: **1,290+ spirits, 3,200+ distilleries, 20,000+ monthly auction-price benchmarks (Nov 2005 β†’ today)** β€” [πŸ€— sample dataset](https://huggingface.co/datasets/Ichlibitiche/whiskydb-fine-spirits-sample) Β· [πŸ™ GitHub](https://github.com/WhiskyyDB/whisky-database) Β· [πŸ“§ get the full dataset](mailto:whiskeydn.kite979@simplelogin.com)""")
with gr.Row():
w_query = gr.Textbox(label="Spirit Search", placeholder="e.g. Lagavulin, Eagle Rare, Yamazaki...")
w_type = gr.Dropdown(choices=choices(w_df, "type"), value="All", label="Spirit Type")
w_btn = gr.Button("Explore Spirits", variant="primary")
w_text = gr.Markdown()
w_table = gr.Dataframe(label="Matching Spirits", wrap=True)
w_inputs = [w_query, w_type]
w_btn.click(fn=w_explore, inputs=w_inputs, outputs=[w_text, w_table])
demo.load(fn=w_explore, inputs=w_inputs, outputs=[w_text, w_table])
with gr.Accordion(f"🏭 Sample Distilleries ({len(w_dist_df)})", open=False):
gr.Dataframe(value=w_dist_df.rename(columns={"name": "Distillery", "country": "Country", "region": "Region", "source_url": "Source URL"})[["Distillery", "Country", "Region", "Source URL"]], wrap=True)
with gr.Accordion(f"πŸ›’οΈ Open Cask Taxonomy ({len(w_casks_df)} styles, CC BY 4.0)", open=False):
gr.Dataframe(value=w_casks_df, wrap=True)
with gr.Accordion(f"πŸ‘ƒ Open Flavor Taxonomy ({len(w_flavors_df)} descriptors, CC BY 4.0)", open=False):
gr.Dataframe(value=w_flavors_df, wrap=True)
gr.Markdown("""---
*Sample data CC BY-NC 4.0 (per-dataset attribution on each tab); WhiskyDB taxonomies CC BY 4.0. FloraDB toxicity data is informational, not veterinary advice; SuppDB is factual label data, not medical advice. Full datasets commercially licensed at each portal. Sister project: [πŸ”¬ INCIDB Skincare Dupe Finder](https://huggingface.co/spaces/Ichlibitiche/incidb-skincare-dupe-finder).*""")
demo.launch()