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()