dwancin commited on
Commit
f447215
·
verified ·
1 Parent(s): 1ae4775

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +288 -0
app.py ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import random
3
+ import gradio as gr
4
+ import time
5
+
6
+ max_results = 200
7
+
8
+ # Cache to store details
9
+ cache = {}
10
+
11
+ # List of possible templates
12
+ component_templates = [
13
+ "All templates",
14
+ "SimpleDropdown",
15
+ "SimpleTextbox",
16
+ "SimpleImage",
17
+ "Audio",
18
+ "BarPlot",
19
+ "Button",
20
+ "Chatbot",
21
+ "ClearButton",
22
+ "Checkbox",
23
+ "CheckboxGroup",
24
+ "Code",
25
+ "ColorPicker",
26
+ "DataFrame",
27
+ "DownloadButton",
28
+ "DuplicateButton",
29
+ "Gallery",
30
+ "HTML",
31
+ "FileExplorer",
32
+ "Image",
33
+ "JSON",
34
+ "Label",
35
+ "LinePlot",
36
+ "LoginButton",
37
+ "LogoutButton",
38
+ "Markdown",
39
+ "Textbox",
40
+ "DateTime",
41
+ "Dropdown",
42
+ "Model3D",
43
+ "File",
44
+ "HighlightedText",
45
+ "AnnotatedImage",
46
+ "CheckboxGroup",
47
+ "Number",
48
+ "Plot",
49
+ "Radio",
50
+ "ScatterPlot",
51
+ "Slider",
52
+ "Timer",
53
+ "UploadButton",
54
+ "Video",
55
+ "ImageEditor",
56
+ "ParamViewer",
57
+ "MultimodalTextbox",
58
+ "NativePlot",
59
+ "Accordion",
60
+ "Column",
61
+ "Row",
62
+ "Group",
63
+ "Tabs",
64
+ "Tab",
65
+ "TabItem"
66
+ ]
67
+
68
+ # List of possible sort options
69
+ sort_by_options = [
70
+ "Most likes",
71
+ "Recently created",
72
+ "Recently updated"
73
+ ]
74
+
75
+ # List of possible background colors
76
+ background_colors = [
77
+ 'var(--color-blue-300)',
78
+ 'var(--color-red-300)',
79
+ 'var(--color-yellow-200)',
80
+ 'var(--color-green-200)',
81
+ 'var(--color-orange-200)'
82
+ ]
83
+
84
+ def fetch_space_details(subdomain):
85
+ """
86
+ Function to fetch details for a specific component using its subdomain.
87
+ Uses cache to avoid redundant API requests.
88
+ """
89
+ if subdomain in cache:
90
+ return cache[subdomain] # Return cached data
91
+
92
+ try:
93
+ api_url = f"https://huggingface.co/api/spaces/by-subdomain/{subdomain}"
94
+ response = requests.get(api_url)
95
+ if response.status_code == 200:
96
+ data = response.json()
97
+ cache[subdomain] = data # Cache the result
98
+ return data
99
+ else:
100
+ return None
101
+ except Exception as e:
102
+ raise gr.Error(f"Error fetching details for {subdomain}: {e}")
103
+ return None
104
+
105
+ def fetch_results(search_query, sort_query, template_query):
106
+ """
107
+ Function to fetch results from API based on search term
108
+ """
109
+ try:
110
+ api_url = f"https://gradio-custom-component-gallery-backend.hf.space/components?name_or_tags={search_query}"
111
+ response = requests.get(api_url)
112
+ results = response.json()
113
+
114
+ num_results = len(results)
115
+
116
+ if num_results < 1:
117
+ raise gr.Error("No results found")
118
+ else:
119
+ print(f"Received {num_results} results")
120
+
121
+ # Fetch detailed data for each component with caching
122
+ detailed_results = []
123
+ for component in results[:max_results]:
124
+ subdomain = component.get('subdomain', '')
125
+ details = fetch_space_details(subdomain)
126
+ if details:
127
+ component['createdAt'] = details.get('createdAt', 'N/A')
128
+ component['lastModified'] = details.get('lastModified', 'N/A')
129
+ detailed_results.append(component)
130
+
131
+ # Sort results based on the selected sort option
132
+ if sort_query == "Recently created":
133
+ detailed_results.sort(key=lambda x: x.get('createdAt', ''), reverse=True)
134
+ elif sort_query == "Recently updated":
135
+ detailed_results.sort(key=lambda x: x.get('lastModified', ''), reverse=True)
136
+ elif sort_query == "Most likes":
137
+ detailed_results.sort(key=lambda x: x.get('likes', 0), reverse=True)
138
+
139
+ updates = []
140
+
141
+ # Loop through the results returned from the API
142
+ for index, component in enumerate(detailed_results):
143
+ id = component.get('id', 'N/A')
144
+ name = component.get('name', 'N/A')
145
+ author = component.get('author', 'N/A')
146
+ tags = component.get('tags', '')
147
+ version = component.get('version', 'N/A')
148
+ template = component.get('template', '')
149
+ description = component.get('description', 'No description available')
150
+ subdomain = component.get('subdomain', '')
151
+ likes = component.get('likes', 0)
152
+ created_at = component.get('createdAt', 'N/A')
153
+ last_modified = component.get('lastModified', 'N/A')
154
+
155
+ # Select a random background color for this component
156
+ background_color = random.choice(background_colors)
157
+
158
+ # Create the HTML
159
+ html = f'''
160
+ <a href="https://huggingface.co/spaces/{id}" target=”_blank”>
161
+ <div style="width: 290px; height: 160px; padding: 15px; background-color: {background_color}; border: 1px solid var(--border-color-primary); border-radius: 12px; box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1); font-family: Arial, sans-serif; position: relative;">
162
+ <!-- Title -->
163
+ <div style="font-weight: var(--weight-bold); font-size: var(--text-lg); color: var(--neutral-950);">
164
+ {name}
165
+ </div>
166
+
167
+ <!-- Description -->
168
+ <div style="height: 72px; font-size: var(--text-md); color: var(--neutral-900); margin-top: 5px;">
169
+ {description}
170
+ </div>
171
+
172
+ <!-- Author -->
173
+ <div style="font-size: var(--text-sm); color: var(--accordion-text-color); margin-top: 10px; background-color: var(--background-fill-secondary); display: inline-block; padding: 5px 10px; border-radius: var(--radius-xl);">
174
+ @{author}
175
+ </div>
176
+
177
+ <!-- Like button with heart and count -->
178
+ <div style="position: absolute; top: var(--spacing-xl); right: var(--spacing-xl); display: flex; align-items: center; justify-content: center; background-color: var(--background-fill-secondary); color: var(--accordion-text-color); padding: 5px 10px; border-radius: var(--radius-xl);">
179
+ <div style="font-size: var(--text-sm); color: var(--accordion-text-color); padding-right: 3px;">&#x2661;</div> <!-- Heart icon -->
180
+ <div style="font-size: var(--text-md); color: var(--accordion-text-color); margin-right: 5px;">{likes}</div>
181
+ </div>
182
+ </div>
183
+ </a>
184
+ '''
185
+
186
+ # Update the results with fetched data
187
+ result_row_update = gr.update(
188
+ visible=(
189
+ True if template_query == component_templates[0]
190
+ else True if template_query == template
191
+ else False
192
+ )
193
+ )
194
+ result_update = gr.update(
195
+ value=html,
196
+ visible=(
197
+ True if template_query == component_templates[0]
198
+ else True if template_query == template
199
+ else False
200
+ )
201
+ )
202
+
203
+ # Collect the updates for each result
204
+ updates.extend([result_row_update, result_update])
205
+
206
+ # Hide the remaining rows (if there are fewer results than max_results)
207
+ for i in range(len(detailed_results), max_results):
208
+ updates.extend([
209
+ gr.update(visible=False), # result_row
210
+ gr.update(visible=False), # result
211
+ ])
212
+
213
+ # Ensure the results area is visible
214
+ results_area_update = gr.update(visible=True)
215
+ updates.append(results_area_update)
216
+
217
+ hide_spinner = gr.update(visible=False)
218
+
219
+ return updates
220
+
221
+ except Exception as e:
222
+ raise gr.Error(f"{e}")
223
+
224
+ def hide_component():
225
+ return gr.update(visible=False)
226
+
227
+ # Gradio setup
228
+ with gr.Blocks(
229
+ css="""
230
+ a {text-decoration-line: none !important;}
231
+ .form {border: none !important;}
232
+ .search-row {display: flex; align-items: center !important; gap: 0 !important; margin: 24px 0px 0px 0px;}
233
+ .result-row {min-width: 292px !important; max-width: 200px !important;}
234
+ .result-column {min-width: 292px !important; max-width: 200px !important;}
235
+ .search-input {background: var(--background-fill-secondary) !important;}
236
+ .sort-input {background: var(--background-fill-secondary) !important;}
237
+ .template-input {background: var(--background-fill-secondary) !important;}
238
+ .result {overflow: hidden;}
239
+ .result:hover {scale: 1.004; opacity: 0.9;}
240
+ .spinner {width: var(--size-40); height: var(--size-40); border: var(--size-5) solid var(--border-color-primary); border-top: var(--size-5) solid var(--border-color-accent); border-radius: 50%; animation: spin 1s linear infinite;}
241
+ @keyframes spin {0% {transform: rotate(0deg); } 100% { transform: rotate(360deg); }}
242
+ .spinner-container {display: flex; flex-direction: column; justify-content: center; align-items: center; width: 100%; height: 100vh; opacity: 0; animation: fadeIn 0.5s forwards;}
243
+ .loading-text {margin-top: var(--spacing-xl); color: var(--body-text-color); font-size: var(--text-xxl); animation: fadeIn 1s 0.2s forwards; opacity: 0;}
244
+ @keyframes fadeIn {to { opacity: 1; }}
245
+ """
246
+ ) as demo:
247
+ result_rows = []
248
+ results = []
249
+
250
+ with gr.Column():
251
+ with gr.Row(elem_classes="panel-row", variant="panel"):
252
+ with gr.Column(elem_classes="search-column"):
253
+ with gr.Row(elem_classes="search-row"):
254
+ search_input = gr.Textbox(placeholder="Search", elem_classes="search-input", show_label=False, max_lines=1, scale=1)
255
+ search_button = gr.Button("Search", scale=0)
256
+ with gr.Column(elem_classes="filter-column"):
257
+ with gr.Row(elem_classes="filter-row"):
258
+ sort_input = gr.Dropdown(label="Sort by", value=sort_by_options[0], choices=sort_by_options, elem_classes="sort-input", interactive=True)
259
+ template_input = gr.Dropdown(label="Template", value=component_templates[0], choices=component_templates, elem_classes="template-input", interactive=True)
260
+
261
+ with gr.Row(elem_classes="loading") as loading_area:
262
+ gr.Markdown("")
263
+ spinner = gr.HTML('<div class="spinner-container"><div class="spinner"></div><p class="loading-text">Loading, please wait...</p></div>')
264
+ gr.Markdown("")
265
+
266
+ with gr.Row(elem_classes="results") as results_area:
267
+ for i in range(max_results):
268
+ with gr.Row(visible=False, elem_classes="result-row") as result_row:
269
+ with gr.Column(elem_classes="result-column"):
270
+ result = gr.HTML("", elem_classes="result")
271
+ result_rows.append(result_row)
272
+ results.append(result)
273
+
274
+ # Prepare output array for each element
275
+ outputs = []
276
+ for i in range(max_results):
277
+ outputs.extend([result_rows[i], results[i]])
278
+ outputs.append(results_area)
279
+
280
+ # Use gr.on to trigger the fetch_results function on multiple events
281
+ gr.on(
282
+ triggers=[search_input.submit, search_button.click, sort_input.change, template_input.change, demo.load],
283
+ fn=fetch_results,
284
+ inputs=[search_input, sort_input, template_input],
285
+ outputs=outputs
286
+ ).success(fn=hide_component, inputs=None, outputs=loading_area)
287
+
288
+ demo.launch()