| import gradio as gr |
| import pandas as pd |
|
|
| |
| DATA_URL = "hf://datasets/Ichlibitiche/roasterdb-specialty-coffee-sample/roasterdb_sample.csv" |
| df = pd.read_csv(DATA_URL) |
|
|
| DISPLAY_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", |
| } |
|
|
| def choices(col): |
| vals = df[col].dropna().astype(str) |
| vals = sorted(v for v in vals.unique() if v.strip() and v != "Unknown") |
| return ["All"] + vals |
|
|
| PRICE_MAX = float(df["price_value"].max()) |
|
|
| def explore(roaster, country, process, roast, flavor, max_price): |
| d = 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 < PRICE_MAX: |
| d = d[d["price_value"].fillna(PRICE_MAX + 1) <= max_price] |
|
|
| summary = ( |
| f"**{len(d)} coffees** from **{d['source_roaster'].nunique()} roasters** match — " |
| f"out of {len(df)} records in the free sample. " |
| f"The full RoasterDB has **8,000+ products from 280+ roasters**." |
| ) |
| table = d[list(DISPLAY_COLS)].rename(columns=DISPLAY_COLS).sort_values("Price (USD)") |
| return summary, table |
|
|
| with gr.Blocks(title="RoasterDB Sample Explorer") as demo: |
| gr.Markdown("# ☕ RoasterDB: Specialty Coffee Sample Explorer") |
| gr.Markdown(f"""Explore **{len(df)} verified specialty coffees from {df['source_roaster'].nunique()} artisan roasters** — tasting notes normalized to the **SCA Flavor Wheel**, with a verifiable source URL on every record. |
| |
| --- |
| ### 🌐 This is the free sample. The full RoasterDB has 8,000+ products from 280+ roasters. |
| * **🔗 Official Portal (full dataset, $99 snapshot):** [roasterdb.net](https://specialty-coffee-roasterdb.pages.dev) |
| * **🤗 Free Sample Dataset (Download CSV):** [Ichlibitiche/roasterdb-specialty-coffee-sample](https://huggingface.co/datasets/Ichlibitiche/roasterdb-specialty-coffee-sample) |
| * **🏆 Kaggle Dataset:** [RoasterDB Specialty Coffee Sample](https://www.kaggle.com/datasets/ahtiticheamine/roasterdb-specialty-coffee-sample) |
| * **🔄 Live Self-Serve Scraping:** [Specialty Coffee Roaster Scraper on Apify](https://apify.com/dataengineered/specialty-coffee-roaster-scraper) |
| ---""") |
|
|
| with gr.Row(): |
| roaster_dd = gr.Dropdown(choices=choices("source_roaster"), value="All", label="Roaster") |
| country_dd = gr.Dropdown(choices=choices("origin_country"), value="All", label="Origin Country") |
| process_dd = gr.Dropdown(choices=choices("process_method"), value="All", label="Process Method") |
| roast_dd = gr.Dropdown(choices=choices("roast_level"), value="All", label="Roast Level") |
| with gr.Row(): |
| flavor_tb = gr.Textbox(label="SCA Flavor Search", placeholder="e.g. Berry, Chocolate, Floral, Peach...") |
| price_sl = gr.Slider(minimum=0, maximum=PRICE_MAX, value=PRICE_MAX, step=1, label="Max Price (USD)") |
|
|
| btn = gr.Button("Explore Coffees", variant="primary") |
|
|
| out_text = gr.Markdown() |
| out_table = gr.Dataframe(label="Matching Coffees", wrap=True) |
|
|
| inputs = [roaster_dd, country_dd, process_dd, roast_dd, flavor_tb, price_sl] |
| btn.click(fn=explore, inputs=inputs, outputs=[out_text, out_table]) |
| demo.load(fn=explore, inputs=inputs, outputs=[out_text, out_table]) |
|
|
| gr.Markdown("""--- |
| *Sample data © RoasterDB under CC BY-NC 4.0. Full dataset commercially licensed at [roasterdb.net](https://specialty-coffee-roasterdb.pages.dev) · contact RoasterDB@proton.me*""") |
|
|
| demo.launch() |
|
|