waroca commited on
Commit
750e278
·
verified ·
1 Parent(s): 4e2c730

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +139 -128
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
- /* Card wrapper for gr.Group */
29
- .card-wrapper {
30
- background: var(--bg-secondary, #1c1c1e) !important;
31
  border: 1px solid var(--border-color, rgba(255,255,255,0.08)) !important;
32
- border-radius: 12px !important;
33
  padding: 1.25rem !important;
34
- margin-bottom: 0.75rem !important;
35
- transition: all 0.15s ease !important;
36
  }
37
- .card-wrapper:hover {
38
- border-color: var(--border-color-strong, rgba(255,255,255,0.12)) !important;
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
- /* Dataset card content */
41
  .dataset-title {
42
- font-size: 1rem !important;
43
  font-weight: 600 !important;
44
- margin: 0 0 0.25rem 0 !important;
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.5rem 0 !important;
51
  font-family: 'SF Mono', Monaco, monospace !important;
52
  }
53
  .dataset-desc {
54
- font-size: 0.8125rem !important;
55
  color: var(--text-secondary, #a1a1a6) !important;
56
  line-height: 1.5 !important;
57
- margin: 0 0 0.75rem 0 !important;
58
  }
59
- .dataset-price {
 
 
 
60
  font-size: 0.875rem !important;
61
  font-weight: 600 !important;
 
 
 
62
  color: var(--text-primary, #f5f5f7) !important;
63
- margin: 0 !important;
64
  }
65
- .dataset-price.free {
 
66
  color: #34c759 !important;
67
  }
 
 
 
 
 
 
68
  .login-hint {
69
- font-size: 0.75rem !important;
70
  color: var(--text-tertiary, #6e6e73) !important;
71
  font-style: italic !important;
72
- margin: 0.5rem 0 0 0 !important;
 
 
 
 
73
  }
74
  """
75
 
@@ -384,18 +411,98 @@ def main():
384
 
385
  # Catalog Tab
386
  with gr.Tab("Catalog", id="catalog"):
387
- catalog_container = gr.HTML()
388
-
389
- # Subscribe section (visible when logged in)
390
- gr.HTML('<div style="margin-top: 1.5rem;"></div>')
391
- with gr.Row():
392
- dataset_dropdown = gr.Dropdown(
393
- label="Select a dataset to subscribe",
394
- choices=[],
395
- interactive=True,
396
- scale=3
397
- )
398
- subscribe_btn = gr.Button("Subscribe", variant="primary", scale=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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