Upload folder using huggingface_hub
Browse files
app.py
CHANGED
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
| 3 |
import gradio as gr
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
from components import subscriptions
|
| 6 |
-
from components.catalog import create_catalog_html, get_dataset_choices
|
| 7 |
import utils
|
| 8 |
import theme
|
| 9 |
import os
|
|
@@ -25,51 +24,79 @@ print("========================================")
|
|
| 25 |
def main():
|
| 26 |
# Custom CSS for dataset cards
|
| 27 |
custom_css = theme.css + """
|
| 28 |
-
/*
|
| 29 |
-
.card-
|
| 30 |
-
background: var(--bg-
|
| 31 |
border: 1px solid var(--border-color, rgba(255,255,255,0.08)) !important;
|
| 32 |
-
border-radius:
|
| 33 |
padding: 1.25rem !important;
|
| 34 |
-
margin-bottom:
|
| 35 |
-
transition: all 0.
|
| 36 |
}
|
| 37 |
-
.card-
|
| 38 |
-
border-color: var(--border-color-strong, rgba(255,255,255,0.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
}
|
| 40 |
-
/* Dataset card content */
|
| 41 |
.dataset-title {
|
| 42 |
-
font-size:
|
| 43 |
font-weight: 600 !important;
|
| 44 |
-
margin: 0
|
| 45 |
color: var(--text-primary, #f5f5f7) !important;
|
|
|
|
| 46 |
}
|
| 47 |
.dataset-id {
|
| 48 |
font-size: 0.75rem !important;
|
| 49 |
color: var(--text-tertiary, #6e6e73) !important;
|
| 50 |
-
margin: 0 0 0
|
| 51 |
font-family: 'SF Mono', Monaco, monospace !important;
|
| 52 |
}
|
| 53 |
.dataset-desc {
|
| 54 |
-
font-size: 0.
|
| 55 |
color: var(--text-secondary, #a1a1a6) !important;
|
| 56 |
line-height: 1.5 !important;
|
| 57 |
-
margin: 0 0
|
| 58 |
}
|
| 59 |
-
.
|
|
|
|
|
|
|
|
|
|
| 60 |
font-size: 0.875rem !important;
|
| 61 |
font-weight: 600 !important;
|
|
|
|
|
|
|
|
|
|
| 62 |
color: var(--text-primary, #f5f5f7) !important;
|
| 63 |
-
margin: 0 !important;
|
| 64 |
}
|
| 65 |
-
.
|
|
|
|
| 66 |
color: #34c759 !important;
|
| 67 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
.login-hint {
|
| 69 |
-
font-size: 0.
|
| 70 |
color: var(--text-tertiary, #6e6e73) !important;
|
| 71 |
font-style: italic !important;
|
| 72 |
-
margin: 0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
}
|
| 74 |
"""
|
| 75 |
|
|
@@ -384,18 +411,98 @@ def main():
|
|
| 384 |
|
| 385 |
# Catalog Tab
|
| 386 |
with gr.Tab("Catalog", id="catalog"):
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
|
| 400 |
# Subscriptions Tab
|
| 401 |
with gr.Tab("My Subscriptions", id="subscriptions"):
|
|
@@ -413,71 +520,12 @@ def main():
|
|
| 413 |
</div>
|
| 414 |
""")
|
| 415 |
|
| 416 |
-
# Store datasets for reference
|
| 417 |
-
datasets_cache = []
|
| 418 |
-
|
| 419 |
# Load user status
|
| 420 |
def load_user_status(profile: gr.OAuthProfile | None):
|
| 421 |
if profile:
|
| 422 |
return f"👤 Signed in as **{profile.username}**"
|
| 423 |
return "Sign in to subscribe to datasets"
|
| 424 |
|
| 425 |
-
# Load catalog and dropdown
|
| 426 |
-
def load_catalog_and_dropdown(profile: gr.OAuthProfile | None):
|
| 427 |
-
nonlocal datasets_cache
|
| 428 |
-
datasets_cache = utils.get_catalog() or []
|
| 429 |
-
username = profile.username if profile else None
|
| 430 |
-
|
| 431 |
-
# Generate catalog HTML
|
| 432 |
-
catalog_html = create_catalog_html(datasets_cache, username)
|
| 433 |
-
|
| 434 |
-
# Generate dropdown choices
|
| 435 |
-
if username:
|
| 436 |
-
choices = get_dataset_choices(datasets_cache, username)
|
| 437 |
-
else:
|
| 438 |
-
choices = []
|
| 439 |
-
|
| 440 |
-
return catalog_html, gr.update(choices=choices, value=None)
|
| 441 |
-
|
| 442 |
-
# Handle subscription
|
| 443 |
-
def handle_subscribe(dataset_choice, profile: gr.OAuthProfile | None, token: gr.OAuthToken | None):
|
| 444 |
-
if not profile:
|
| 445 |
-
return "Please sign in first to subscribe."
|
| 446 |
-
|
| 447 |
-
if not dataset_choice:
|
| 448 |
-
return "Please select a dataset from the dropdown."
|
| 449 |
-
|
| 450 |
-
# Find the dataset info
|
| 451 |
-
dataset_id = dataset_choice
|
| 452 |
-
dataset_info = next((d for d in datasets_cache if d.get('dataset_id') == dataset_id), None)
|
| 453 |
-
|
| 454 |
-
if not dataset_info:
|
| 455 |
-
return f"Dataset not found: {dataset_id}"
|
| 456 |
-
|
| 457 |
-
hf_token = token.token if token else None
|
| 458 |
-
plans = dataset_info.get("plans", [])
|
| 459 |
-
|
| 460 |
-
if plans:
|
| 461 |
-
plan = plans[0]
|
| 462 |
-
price_id = plan.get("stripe_price_id", "")
|
| 463 |
-
is_free = price_id in ["free", "0", 0]
|
| 464 |
-
|
| 465 |
-
if is_free:
|
| 466 |
-
result = utils.subscribe_free(dataset_id, profile.username, hf_token)
|
| 467 |
-
if "error" in result:
|
| 468 |
-
return f"Error: {result['error']}"
|
| 469 |
-
display_name = dataset_info.get('display_name', dataset_id)
|
| 470 |
-
return f"Your 24-hour DataPass for **{display_name}** is active! Go to My Subscriptions to get your access token."
|
| 471 |
-
else:
|
| 472 |
-
result = utils.create_checkout_session(dataset_id, profile.username, hf_token)
|
| 473 |
-
if "error" in result:
|
| 474 |
-
return f"Error: {result['error']}"
|
| 475 |
-
if "checkout_url" in result:
|
| 476 |
-
return f"[Click here to complete payment]({result['checkout_url']})"
|
| 477 |
-
return "Error creating checkout session."
|
| 478 |
-
|
| 479 |
-
return "No subscription plans available for this dataset."
|
| 480 |
-
|
| 481 |
# Load subscriptions
|
| 482 |
def load_subscriptions(profile: gr.OAuthProfile | None, token: gr.OAuthToken | None):
|
| 483 |
if not profile:
|
|
@@ -493,47 +541,10 @@ def main():
|
|
| 493 |
user_subs = utils.get_user_subscriptions(profile.username, hf_token)
|
| 494 |
return subscriptions.create_subscriptions_html(user_subs)
|
| 495 |
|
| 496 |
-
# Update button text based on selection
|
| 497 |
-
def update_button_text(dataset_choice):
|
| 498 |
-
if not dataset_choice:
|
| 499 |
-
return gr.update(value="Subscribe")
|
| 500 |
-
|
| 501 |
-
# Find the dataset info
|
| 502 |
-
dataset_info = next((d for d in datasets_cache if d.get('dataset_id') == dataset_choice), None)
|
| 503 |
-
if not dataset_info:
|
| 504 |
-
return gr.update(value="Subscribe")
|
| 505 |
-
|
| 506 |
-
plans = dataset_info.get("plans", [])
|
| 507 |
-
if plans:
|
| 508 |
-
plan = plans[0]
|
| 509 |
-
price_id = plan.get("stripe_price_id", "")
|
| 510 |
-
if price_id in ["free", "0", 0]:
|
| 511 |
-
return gr.update(value="Start Free Trial")
|
| 512 |
-
else:
|
| 513 |
-
price = plan.get("price", "10")
|
| 514 |
-
return gr.update(value=f"Subscribe - ${price}/mo")
|
| 515 |
-
|
| 516 |
-
return gr.update(value="Subscribe")
|
| 517 |
-
|
| 518 |
# Load on page load
|
| 519 |
demo.load(fn=load_user_status, outputs=[user_status])
|
| 520 |
-
demo.load(fn=load_catalog_and_dropdown, outputs=[catalog_container, dataset_dropdown])
|
| 521 |
demo.load(fn=load_subscriptions, outputs=[subscriptions_container])
|
| 522 |
|
| 523 |
-
# Update button on dropdown change
|
| 524 |
-
dataset_dropdown.change(
|
| 525 |
-
fn=update_button_text,
|
| 526 |
-
inputs=[dataset_dropdown],
|
| 527 |
-
outputs=[subscribe_btn]
|
| 528 |
-
)
|
| 529 |
-
|
| 530 |
-
# Subscribe button handler
|
| 531 |
-
subscribe_btn.click(
|
| 532 |
-
fn=handle_subscribe,
|
| 533 |
-
inputs=[dataset_dropdown],
|
| 534 |
-
outputs=[subscribe_status]
|
| 535 |
-
)
|
| 536 |
-
|
| 537 |
return demo
|
| 538 |
|
| 539 |
|
|
|
|
| 3 |
import gradio as gr
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
from components import subscriptions
|
|
|
|
| 6 |
import utils
|
| 7 |
import theme
|
| 8 |
import os
|
|
|
|
| 24 |
def main():
|
| 25 |
# Custom CSS for dataset cards
|
| 26 |
custom_css = theme.css + """
|
| 27 |
+
/* Dataset card styling */
|
| 28 |
+
.dataset-card-group {
|
| 29 |
+
background: var(--bg-card, #1c1c1e) !important;
|
| 30 |
border: 1px solid var(--border-color, rgba(255,255,255,0.08)) !important;
|
| 31 |
+
border-radius: 16px !important;
|
| 32 |
padding: 1.25rem !important;
|
| 33 |
+
margin-bottom: 1rem !important;
|
| 34 |
+
transition: all 0.2s ease !important;
|
| 35 |
}
|
| 36 |
+
.dataset-card-group:hover {
|
| 37 |
+
border-color: var(--border-color-strong, rgba(255,255,255,0.15)) !important;
|
| 38 |
+
box-shadow: 0 4px 20px rgba(0,0,0,0.15) !important;
|
| 39 |
+
}
|
| 40 |
+
.card-header-row {
|
| 41 |
+
margin-bottom: 0.5rem !important;
|
| 42 |
+
gap: 0.75rem !important;
|
| 43 |
+
}
|
| 44 |
+
.card-title-col {
|
| 45 |
+
min-width: 0 !important;
|
| 46 |
+
}
|
| 47 |
+
.card-price-col {
|
| 48 |
+
flex-shrink: 0 !important;
|
| 49 |
+
min-width: auto !important;
|
| 50 |
}
|
|
|
|
| 51 |
.dataset-title {
|
| 52 |
+
font-size: 1.125rem !important;
|
| 53 |
font-weight: 600 !important;
|
| 54 |
+
margin: 0 !important;
|
| 55 |
color: var(--text-primary, #f5f5f7) !important;
|
| 56 |
+
line-height: 1.3 !important;
|
| 57 |
}
|
| 58 |
.dataset-id {
|
| 59 |
font-size: 0.75rem !important;
|
| 60 |
color: var(--text-tertiary, #6e6e73) !important;
|
| 61 |
+
margin: 0.25rem 0 0 0 !important;
|
| 62 |
font-family: 'SF Mono', Monaco, monospace !important;
|
| 63 |
}
|
| 64 |
.dataset-desc {
|
| 65 |
+
font-size: 0.875rem !important;
|
| 66 |
color: var(--text-secondary, #a1a1a6) !important;
|
| 67 |
line-height: 1.5 !important;
|
| 68 |
+
margin: 0 0 1rem 0 !important;
|
| 69 |
}
|
| 70 |
+
.price-badge {
|
| 71 |
+
display: inline-block !important;
|
| 72 |
+
padding: 0.375rem 0.875rem !important;
|
| 73 |
+
border-radius: 20px !important;
|
| 74 |
font-size: 0.875rem !important;
|
| 75 |
font-weight: 600 !important;
|
| 76 |
+
}
|
| 77 |
+
.price-badge.paid {
|
| 78 |
+
background: var(--bg-tertiary, rgba(255,255,255,0.05)) !important;
|
| 79 |
color: var(--text-primary, #f5f5f7) !important;
|
|
|
|
| 80 |
}
|
| 81 |
+
.price-badge.free {
|
| 82 |
+
background: rgba(52, 199, 89, 0.15) !important;
|
| 83 |
color: #34c759 !important;
|
| 84 |
}
|
| 85 |
+
.card-footer-row {
|
| 86 |
+
padding-top: 0.75rem !important;
|
| 87 |
+
border-top: 1px solid var(--border-color, rgba(255,255,255,0.06)) !important;
|
| 88 |
+
gap: 0.75rem !important;
|
| 89 |
+
align-items: center !important;
|
| 90 |
+
}
|
| 91 |
.login-hint {
|
| 92 |
+
font-size: 0.8125rem !important;
|
| 93 |
color: var(--text-tertiary, #6e6e73) !important;
|
| 94 |
font-style: italic !important;
|
| 95 |
+
margin: 0 !important;
|
| 96 |
+
}
|
| 97 |
+
/* Make buttons fill available space in cards */
|
| 98 |
+
.card-footer-row .gr-button {
|
| 99 |
+
min-width: 140px !important;
|
| 100 |
}
|
| 101 |
"""
|
| 102 |
|
|
|
|
| 411 |
|
| 412 |
# Catalog Tab
|
| 413 |
with gr.Tab("Catalog", id="catalog"):
|
| 414 |
+
# Use gr.render to dynamically create cards with integrated buttons
|
| 415 |
+
@gr.render(triggers=[demo.load])
|
| 416 |
+
def render_catalog(profile: gr.OAuthProfile | None = None, token: gr.OAuthToken | None = None):
|
| 417 |
+
datasets = utils.get_catalog() or []
|
| 418 |
+
|
| 419 |
+
if not datasets:
|
| 420 |
+
gr.HTML("""
|
| 421 |
+
<div class="empty-state">
|
| 422 |
+
<div class="empty-state-icon">📦</div>
|
| 423 |
+
<div class="empty-state-title">No datasets available</div>
|
| 424 |
+
<div class="empty-state-text">Check back soon for new data products.</div>
|
| 425 |
+
</div>
|
| 426 |
+
""")
|
| 427 |
+
return
|
| 428 |
+
|
| 429 |
+
for dataset in datasets:
|
| 430 |
+
dataset_id = dataset.get('dataset_id', '')
|
| 431 |
+
display_name = dataset.get('display_name', dataset_id)
|
| 432 |
+
description = dataset.get('description', 'No description available.')
|
| 433 |
+
|
| 434 |
+
# Get pricing info
|
| 435 |
+
plans = dataset.get("plans", [])
|
| 436 |
+
is_free = False
|
| 437 |
+
price = "10"
|
| 438 |
+
if plans:
|
| 439 |
+
plan = plans[0]
|
| 440 |
+
price_id = plan.get("stripe_price_id", "")
|
| 441 |
+
if price_id in ["free", "0", 0]:
|
| 442 |
+
is_free = True
|
| 443 |
+
else:
|
| 444 |
+
price = plan.get("price", "10")
|
| 445 |
+
|
| 446 |
+
price_class = "free" if is_free else "paid"
|
| 447 |
+
price_text = "Free Trial" if is_free else f"${price}/mo"
|
| 448 |
+
button_text = "Start Free Trial" if is_free else f"Subscribe - ${price}/mo"
|
| 449 |
+
|
| 450 |
+
# Create card using gr.Group
|
| 451 |
+
with gr.Group(elem_classes="dataset-card-group"):
|
| 452 |
+
# Card header with title and price
|
| 453 |
+
with gr.Row(elem_classes="card-header-row"):
|
| 454 |
+
with gr.Column(scale=4, elem_classes="card-title-col"):
|
| 455 |
+
gr.HTML(f'''
|
| 456 |
+
<h3 class="dataset-title">{display_name}</h3>
|
| 457 |
+
<p class="dataset-id">{dataset_id}</p>
|
| 458 |
+
''')
|
| 459 |
+
with gr.Column(scale=1, min_width=100, elem_classes="card-price-col"):
|
| 460 |
+
gr.HTML(f'<span class="price-badge {price_class}">{price_text}</span>')
|
| 461 |
+
|
| 462 |
+
# Description
|
| 463 |
+
gr.HTML(f'<p class="dataset-desc">{description}</p>')
|
| 464 |
+
|
| 465 |
+
# Footer with action
|
| 466 |
+
with gr.Row(elem_classes="card-footer-row"):
|
| 467 |
+
if profile:
|
| 468 |
+
# User is logged in - show subscribe button
|
| 469 |
+
btn = gr.Button(
|
| 470 |
+
button_text,
|
| 471 |
+
variant="primary",
|
| 472 |
+
size="sm",
|
| 473 |
+
key=f"subscribe-btn-{dataset_id}"
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
# Define handler with frozen variables (critical for loops!)
|
| 477 |
+
def make_subscribe_handler(ds_id, ds_name, ds_free):
|
| 478 |
+
def handler(p: gr.OAuthProfile | None, t: gr.OAuthToken | None):
|
| 479 |
+
if not p:
|
| 480 |
+
return "Please sign in first to subscribe."
|
| 481 |
+
|
| 482 |
+
hf_token = t.token if t else None
|
| 483 |
+
|
| 484 |
+
if ds_free:
|
| 485 |
+
result = utils.subscribe_free(ds_id, p.username, hf_token)
|
| 486 |
+
if "error" in result:
|
| 487 |
+
return f"Error: {result['error']}"
|
| 488 |
+
return f"Your 24-hour DataPass for **{ds_name}** is active! Go to **My Subscriptions** to get your access token."
|
| 489 |
+
else:
|
| 490 |
+
result = utils.create_checkout_session(ds_id, p.username, hf_token)
|
| 491 |
+
if "error" in result:
|
| 492 |
+
return f"Error: {result['error']}"
|
| 493 |
+
if "checkout_url" in result:
|
| 494 |
+
return f"[Click here to complete payment]({result['checkout_url']})"
|
| 495 |
+
return "Error creating checkout session."
|
| 496 |
+
return handler
|
| 497 |
+
|
| 498 |
+
btn.click(
|
| 499 |
+
fn=make_subscribe_handler(dataset_id, display_name, is_free),
|
| 500 |
+
outputs=[subscribe_status],
|
| 501 |
+
key=f"subscribe-click-{dataset_id}"
|
| 502 |
+
)
|
| 503 |
+
else:
|
| 504 |
+
# User not logged in - show hint
|
| 505 |
+
gr.HTML('<p class="login-hint">Sign in with Hugging Face to subscribe</p>')
|
| 506 |
|
| 507 |
# Subscriptions Tab
|
| 508 |
with gr.Tab("My Subscriptions", id="subscriptions"):
|
|
|
|
| 520 |
</div>
|
| 521 |
""")
|
| 522 |
|
|
|
|
|
|
|
|
|
|
| 523 |
# Load user status
|
| 524 |
def load_user_status(profile: gr.OAuthProfile | None):
|
| 525 |
if profile:
|
| 526 |
return f"👤 Signed in as **{profile.username}**"
|
| 527 |
return "Sign in to subscribe to datasets"
|
| 528 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 529 |
# Load subscriptions
|
| 530 |
def load_subscriptions(profile: gr.OAuthProfile | None, token: gr.OAuthToken | None):
|
| 531 |
if not profile:
|
|
|
|
| 541 |
user_subs = utils.get_user_subscriptions(profile.username, hf_token)
|
| 542 |
return subscriptions.create_subscriptions_html(user_subs)
|
| 543 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 544 |
# Load on page load
|
| 545 |
demo.load(fn=load_user_status, outputs=[user_status])
|
|
|
|
| 546 |
demo.load(fn=load_subscriptions, outputs=[subscriptions_container])
|
| 547 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
return demo
|
| 549 |
|
| 550 |
|