mariagrandury commited on
Commit
dd7df46
Β·
1 Parent(s): 8e3b19a

extract constants, make urls shorts and clickable

Browse files
constants.py ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Shared constants for the NLP resources application.
3
+ Contains lists of countries, languages, domains, tasks, and utility functions.
4
+ """
5
+
6
+ import re
7
+ from urllib.parse import urlparse
8
+
9
+ # Countries where Spanish is spoken
10
+ COUNTRIES = [
11
+ "Spain",
12
+ "Mexico",
13
+ "Argentina",
14
+ "Colombia",
15
+ "Peru",
16
+ "Venezuela",
17
+ "Chile",
18
+ "Ecuador",
19
+ "Guatemala",
20
+ "Cuba",
21
+ "Bolivia",
22
+ "Dominican Republic",
23
+ "Honduras",
24
+ "Paraguay",
25
+ "El Salvador",
26
+ "Nicaragua",
27
+ "Costa Rica",
28
+ "Panama",
29
+ "Uruguay",
30
+ "Puerto Rico",
31
+ ]
32
+
33
+ # Languages relevant to Spanish NLP
34
+ LANGUAGES = [
35
+ "spanish",
36
+ "catalan",
37
+ "basque",
38
+ "galician",
39
+ "guarani",
40
+ "quechua",
41
+ "aymara",
42
+ "nauhatl",
43
+ "mapudungun",
44
+ ]
45
+
46
+ # NLP tasks
47
+ TASKS = [
48
+ "text classification",
49
+ "sentiment analysis",
50
+ "named entity recognition",
51
+ "part-of-speech tagging",
52
+ "question answering",
53
+ "text summarization",
54
+ "machine translation",
55
+ "language modeling",
56
+ "text generation",
57
+ "information extraction",
58
+ "semantic similarity",
59
+ "natural language inference",
60
+ ]
61
+
62
+ # Domains for datasets
63
+ DOMAINS = [
64
+ "clinical",
65
+ "legal",
66
+ "financial",
67
+ "scientific",
68
+ "news",
69
+ "social media",
70
+ "literature",
71
+ "general",
72
+ ]
73
+
74
+ # Dataset types
75
+ DATASET_TYPES = ["pretraining", "benchmark", "supervised fine-tuning", "alignment"]
76
+
77
+ # Event types
78
+ EVENT_TYPES = ["workshop", "talk", "AMA", "round table"]
79
+
80
+ # Initiative types
81
+ INITIATIVE_TYPES = [
82
+ "project",
83
+ "event",
84
+ "research group",
85
+ "community",
86
+ "research institute",
87
+ "non-profit",
88
+ "OS company",
89
+ ]
90
+
91
+ # Technical levels (for events)
92
+ TECHNICAL_LEVELS = ["1", "2", "3", "4", "5"]
93
+
94
+
95
+ def format_url_for_display(url: str) -> str:
96
+ """
97
+ Format URL for display in tables - show only the meaningful part.
98
+
99
+ Args:
100
+ url: Full URL string
101
+
102
+ Returns:
103
+ Shortened, readable version of the URL
104
+ """
105
+ if not url or not url.strip():
106
+ return ""
107
+
108
+ url = url.strip()
109
+
110
+ # Remove protocol
111
+ if url.startswith(("http://", "https://")):
112
+ url = url.split("://", 1)[1]
113
+
114
+ # Special handling for common domains
115
+ if "huggingface.co" in url:
116
+ # Extract the meaningful part after huggingface.co
117
+ if "/datasets/" in url:
118
+ return url.split("/datasets/")[-1]
119
+ elif "/models/" in url:
120
+ return url.split("/models/")[-1]
121
+ elif "/collections/" in url:
122
+ return url.split("/collections/")[-1]
123
+ else:
124
+ # Return everything after huggingface.co/
125
+ parts = url.split("huggingface.co/")
126
+ return parts[-1] if len(parts) > 1 else url
127
+
128
+ elif "github.com" in url:
129
+ # Extract repo name (owner/repo)
130
+ parts = url.split("github.com/")
131
+ if len(parts) > 1:
132
+ repo_path = parts[-1].split("/")
133
+ if len(repo_path) >= 2:
134
+ return f"{repo_path[0]}/{repo_path[1]}"
135
+ return url
136
+
137
+ elif "zenodo.org" in url:
138
+ # Extract record ID
139
+ if "/record/" in url:
140
+ return f"zenodo:{url.split('/record/')[-1].split('/')[0]}"
141
+ return url
142
+
143
+ elif "arxiv.org" in url:
144
+ # Extract arXiv ID
145
+ if "/abs/" in url:
146
+ return f"arXiv:{url.split('/abs/')[-1]}"
147
+ elif "/pdf/" in url:
148
+ return f"arXiv:{url.split('/pdf/')[-1].replace('.pdf', '')}"
149
+ return url
150
+
151
+ elif "youtube.com" in url or "youtu.be" in url:
152
+ # Extract video ID or title if available
153
+ if "watch?v=" in url:
154
+ video_id = url.split("watch?v=")[-1].split("&")[0]
155
+ return f"YouTube:{video_id[:8]}..."
156
+ elif "youtu.be/" in url:
157
+ video_id = url.split("youtu.be/")[-1].split("?")[0]
158
+ return f"YouTube:{video_id[:8]}..."
159
+ return url
160
+
161
+ else:
162
+ # For other URLs, try to extract domain and path
163
+ try:
164
+ parsed = urlparse(
165
+ f"https://{url}" if not url.startswith(("http://", "https://")) else url
166
+ )
167
+ domain = parsed.netloc
168
+ path = parsed.path.strip("/")
169
+
170
+ if path:
171
+ # Show domain + first part of path
172
+ path_parts = path.split("/")
173
+ if len(path_parts) > 0 and path_parts[0]:
174
+ return f"{domain}/{path_parts[0]}"
175
+
176
+ return domain
177
+ except:
178
+ # Fallback: limit length
179
+ return url[:30] + "..." if len(url) > 30 else url
180
+
181
+
182
+ def make_url_clickable(url: str, display_text: str = None) -> str:
183
+ """
184
+ Convert URL to clickable HTML link.
185
+
186
+ Args:
187
+ url: Full URL
188
+ display_text: Text to display for the link (optional)
189
+
190
+ Returns:
191
+ HTML link string
192
+ """
193
+ if not url or not url.strip():
194
+ return ""
195
+
196
+ url = url.strip()
197
+
198
+ # Ensure URL has protocol
199
+ if not url.startswith(("http://", "https://")):
200
+ url = f"https://{url}"
201
+
202
+ # Use provided display text or format the URL
203
+ text = display_text if display_text else format_url_for_display(url)
204
+
205
+ return f'<a target="_blank" href="{url}" style="color: var(--link-text-color); text-decoration: underline; text-decoration-style: dotted;">{text}</a>'
206
+
207
+
208
+ def get_column_display_names():
209
+ """
210
+ Return mapping of column names to pretty display names.
211
+
212
+ Returns:
213
+ Dictionary mapping column names to display names
214
+ """
215
+ return {
216
+ # Common fields
217
+ "name": "Name",
218
+ "submitted_by": "Submitted By",
219
+ "date_submitted": "Date Submitted",
220
+ # Dataset fields
221
+ "github_url": "GitHub",
222
+ "huggingface_url": "HF Dataset",
223
+ "zenodo_url": "Zenodo",
224
+ "paper_url": "Paper",
225
+ "website_url": "Website",
226
+ "dataset_type": "Type",
227
+ "task": "Tasks",
228
+ "domain": "Domain",
229
+ "countries": "Countries",
230
+ "languages": "Languages",
231
+ # Model fields
232
+ "familia": "Family",
233
+ "available_sizes": "Sizes (B)",
234
+ "hf_collection_url": "HF Collection",
235
+ # Event fields
236
+ "titulo": "Title",
237
+ "ponente": "Speaker",
238
+ "bio": "Bio",
239
+ "tipo": "Type",
240
+ "etiquetas": "Tags",
241
+ "tema": "Topic",
242
+ "nivel_tecnico": "Tech Level",
243
+ "fecha": "Date",
244
+ "youtube": "YouTube",
245
+ # Shared task fields
246
+ "conference_name": "Conference",
247
+ "workshop_date": "Workshop Date",
248
+ "registration_deadline": "Registration",
249
+ "data_available_date": "Data Available",
250
+ "submission_deadline": "Submission",
251
+ "more_info_url": "More Info",
252
+ # Initiative fields
253
+ "type": "Type",
254
+ }
255
+
256
+
257
+ def format_dataframe_for_display(df, url_columns=None, hide_columns=None):
258
+ """
259
+ Format a DataFrame for better display in Gradio tables with clickable URLs.
260
+
261
+ Args:
262
+ df: Pandas DataFrame
263
+ url_columns: List of column names that contain URLs
264
+ hide_columns: List of column names to hide
265
+
266
+ Returns:
267
+ Formatted DataFrame
268
+ """
269
+ if df.empty:
270
+ return df
271
+
272
+ # Make a copy to avoid modifying original
273
+ display_df = df.copy()
274
+
275
+ # Hide specified columns
276
+ if hide_columns:
277
+ display_df = display_df.drop(
278
+ columns=[col for col in hide_columns if col in display_df.columns]
279
+ )
280
+
281
+ # Format URL columns with clickable links
282
+ if url_columns:
283
+ for col in url_columns:
284
+ if col in display_df.columns:
285
+ display_df[col] = display_df[col].apply(
286
+ lambda x: make_url_clickable(x) if x and str(x).strip() else ""
287
+ )
288
+
289
+ # Ensure first column content doesn't wrap (for name/title columns)
290
+ first_col = display_df.columns[0] if len(display_df.columns) > 0 else None
291
+ if first_col:
292
+ # Keep full text but ensure it displays in a single line (no wrapping)
293
+ # Replace line breaks and excessive whitespace to ensure single line display
294
+ display_df[first_col] = display_df[first_col].apply(
295
+ lambda x: str(x).replace("\n", " ").replace("\r", " ").strip() if x else ""
296
+ )
297
+
298
+ # Rename columns to pretty names
299
+ column_names = get_column_display_names()
300
+ display_df = display_df.rename(columns=column_names)
301
+
302
+ return display_df
303
+
304
+
305
+ def format_dataframe_for_html_display(df, url_columns=None, hide_columns=None):
306
+ """
307
+ Format a DataFrame for HTML display with clickable links.
308
+
309
+ Args:
310
+ df: Pandas DataFrame
311
+ url_columns: List of column names that contain URLs
312
+ hide_columns: List of column names to hide
313
+
314
+ Returns:
315
+ HTML string representation of the DataFrame
316
+ """
317
+ if df.empty:
318
+ return "<p>No data available</p>"
319
+
320
+ # Make a copy to avoid modifying original
321
+ display_df = df.copy()
322
+
323
+ # Hide specified columns
324
+ if hide_columns:
325
+ display_df = display_df.drop(
326
+ columns=[col for col in hide_columns if col in display_df.columns]
327
+ )
328
+
329
+ # Format URL columns with clickable links
330
+ if url_columns:
331
+ for col in url_columns:
332
+ if col in display_df.columns:
333
+ display_df[col] = display_df[col].apply(
334
+ lambda x: make_url_clickable(x) if x and str(x).strip() else ""
335
+ )
336
+
337
+ # Ensure first column content doesn't wrap (for name/title columns)
338
+ first_col = display_df.columns[0] if len(display_df.columns) > 0 else None
339
+ if first_col:
340
+ # Keep full text but ensure it displays in a single line (no wrapping)
341
+ # Replace line breaks and excessive whitespace to ensure single line display
342
+ display_df[first_col] = display_df[first_col].apply(
343
+ lambda x: str(x).replace("\n", " ").replace("\r", " ").strip() if x else ""
344
+ )
345
+
346
+ # Rename columns to pretty names
347
+ column_names = get_column_display_names()
348
+ display_df = display_df.rename(columns=column_names)
349
+
350
+ # Convert to HTML with custom styling
351
+ html = display_df.to_html(
352
+ escape=False, # Allow HTML in cells
353
+ index=False, # Don't show row indices
354
+ classes="dataframe-table",
355
+ table_id="resources-table",
356
+ )
357
+
358
+ # Add custom CSS styling
359
+ styled_html = f"""
360
+ <style>
361
+ .dataframe-table {{
362
+ border-collapse: collapse;
363
+ margin: 25px 0;
364
+ font-size: 0.9em;
365
+ font-family: sans-serif;
366
+ min-width: 400px;
367
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
368
+ width: 100%;
369
+ }}
370
+ .dataframe-table thead tr {{
371
+ background-color: #009879;
372
+ color: #ffffff;
373
+ text-align: left;
374
+ }}
375
+ .dataframe-table th,
376
+ .dataframe-table td {{
377
+ padding: 12px 15px;
378
+ border: 1px solid #dddddd;
379
+ }}
380
+ .dataframe-table tbody tr {{
381
+ border-bottom: 1px solid #dddddd;
382
+ }}
383
+ .dataframe-table tbody tr:nth-of-type(even) {{
384
+ background-color: #f3f3f3;
385
+ }}
386
+ .dataframe-table tbody tr:hover {{
387
+ background-color: #f5f5f5;
388
+ }}
389
+ .dataframe-table a {{
390
+ color: #009879;
391
+ text-decoration: none;
392
+ }}
393
+ .dataframe-table a:hover {{
394
+ text-decoration: underline;
395
+ }}
396
+ </style>
397
+ {html}
398
+ """
399
+
400
+ return styled_html
datasets_resource.py CHANGED
@@ -4,6 +4,16 @@ import gradio as gr
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
 
 
 
 
 
 
 
 
 
 
7
  # Dataset configuration
8
  DATASET_NAME = "somosnlp/recursos-pln-es"
9
  CONFIG_NAME = "datasets"
@@ -169,27 +179,56 @@ def create_all_tab():
169
  show_label=False,
170
  )
171
 
172
- # Load initial data
173
- initial_df = load_data()
 
 
 
 
 
 
 
 
 
 
 
 
174
 
 
175
  table = gr.Dataframe(
176
- value=initial_df,
177
  label=RESOURCE_TITLE,
178
  show_label=False,
179
  interactive=False,
180
- wrap=True,
 
181
  )
182
 
183
  # Connect search functionality
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  search_box.change(
185
- fn=lambda query: search_and_filter_data(initial_df, query),
186
  inputs=search_box,
187
  outputs=table,
188
  )
189
 
190
  # Refresh button to reload data
191
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
192
- refresh_btn.click(fn=lambda: load_data(), outputs=table)
193
 
194
  return table
195
 
@@ -215,12 +254,7 @@ def create_contribute_tab():
215
 
216
  dataset_type_input = gr.Dropdown(
217
  label="Dataset Type *",
218
- choices=[
219
- "pretraining",
220
- "benchmark",
221
- "supervised fine-tuning",
222
- "alignment",
223
- ],
224
  info="Type of dataset (required)",
225
  multiselect=False,
226
  )
@@ -262,18 +296,13 @@ def create_contribute_tab():
262
  # Multi-select fields
263
  task_input = gr.CheckboxGroup(
264
  label="Tasks",
265
- choices=[
266
- "language modeling",
267
- "text classification",
268
- "text generation",
269
- "summarization",
270
- ],
271
  info="What tasks is this dataset suitable for?",
272
  )
273
 
274
  domain_input = gr.CheckboxGroup(
275
  label="Domain",
276
- choices=["legal", "clinical"],
277
  info="Specific domains covered by the dataset",
278
  )
279
 
@@ -531,12 +560,7 @@ def create_edit_tab():
531
  name_input = gr.Textbox(label="Name *", placeholder="Dataset name")
532
  dataset_type_input = gr.Dropdown(
533
  label="Dataset Type *",
534
- choices=[
535
- "pretraining",
536
- "benchmark",
537
- "supervised fine-tuning",
538
- "alignment",
539
- ],
540
  value="benchmark",
541
  )
542
 
@@ -563,73 +587,22 @@ def create_edit_tab():
563
 
564
  task_input = gr.CheckboxGroup(
565
  label="Tasks",
566
- choices=[
567
- "text classification",
568
- "sentiment analysis",
569
- "named entity recognition",
570
- "part-of-speech tagging",
571
- "question answering",
572
- "text summarization",
573
- "machine translation",
574
- "language modeling",
575
- "text generation",
576
- "information extraction",
577
- "semantic similarity",
578
- "natural language inference",
579
- ],
580
  )
581
 
582
  domain_input = gr.CheckboxGroup(
583
  label="Domain",
584
- choices=[
585
- "clinical",
586
- "legal",
587
- "financial",
588
- "scientific",
589
- "news",
590
- "social media",
591
- "literature",
592
- "general",
593
- ],
594
  )
595
 
596
  countries_input = gr.CheckboxGroup(
597
  label="Countries",
598
- choices=[
599
- "Spain",
600
- "Mexico",
601
- "Argentina",
602
- "Colombia",
603
- "Peru",
604
- "Venezuela",
605
- "Chile",
606
- "Ecuador",
607
- "Guatemala",
608
- "Cuba",
609
- "Bolivia",
610
- "Dominican Republic",
611
- "Honduras",
612
- "Paraguay",
613
- "El Salvador",
614
- "Nicaragua",
615
- "Costa Rica",
616
- "Panama",
617
- "Uruguay",
618
- "Puerto Rico",
619
- ],
620
  )
621
 
622
  languages_input = gr.CheckboxGroup(
623
  label="Languages",
624
- choices=[
625
- "spanish",
626
- "catalan",
627
- "basque",
628
- "galician",
629
- "guarani",
630
- "quechua",
631
- "aymara",
632
- ],
633
  )
634
 
635
  update_btn = gr.Button("πŸ’Ύ Update Entry", variant="primary")
 
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
7
+ from constants import (
8
+ COUNTRIES,
9
+ DATASET_TYPES,
10
+ DOMAINS,
11
+ LANGUAGES,
12
+ TASKS,
13
+ format_dataframe_for_display,
14
+ format_dataframe_for_html_display,
15
+ )
16
+
17
  # Dataset configuration
18
  DATASET_NAME = "somosnlp/recursos-pln-es"
19
  CONFIG_NAME = "datasets"
 
179
  show_label=False,
180
  )
181
 
182
+ # Load and format initial data with clickable links
183
+ def get_formatted_data():
184
+ df = load_data()
185
+ return format_dataframe_for_display(
186
+ df,
187
+ url_columns=[
188
+ "github_url",
189
+ "huggingface_url",
190
+ "zenodo_url",
191
+ "paper_url",
192
+ "website_url",
193
+ ],
194
+ hide_columns=["date_submitted"],
195
+ )
196
 
197
+ # Use Dataframe component with HTML rendering enabled
198
  table = gr.Dataframe(
199
+ value=get_formatted_data(),
200
  label=RESOURCE_TITLE,
201
  show_label=False,
202
  interactive=False,
203
+ wrap=False, # Disable wrapping to show full text in single lines
204
+ datatype="markdown", # Enable HTML rendering
205
  )
206
 
207
  # Connect search functionality
208
+ def search_and_format(query):
209
+ initial_df = load_data()
210
+ filtered_df = search_and_filter_data(initial_df, query)
211
+ return format_dataframe_for_display(
212
+ filtered_df,
213
+ url_columns=[
214
+ "github_url",
215
+ "huggingface_url",
216
+ "zenodo_url",
217
+ "paper_url",
218
+ "website_url",
219
+ ],
220
+ hide_columns=["date_submitted"],
221
+ )
222
+
223
  search_box.change(
224
+ fn=search_and_format,
225
  inputs=search_box,
226
  outputs=table,
227
  )
228
 
229
  # Refresh button to reload data
230
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
231
+ refresh_btn.click(fn=get_formatted_data, outputs=table)
232
 
233
  return table
234
 
 
254
 
255
  dataset_type_input = gr.Dropdown(
256
  label="Dataset Type *",
257
+ choices=DATASET_TYPES,
 
 
 
 
 
258
  info="Type of dataset (required)",
259
  multiselect=False,
260
  )
 
296
  # Multi-select fields
297
  task_input = gr.CheckboxGroup(
298
  label="Tasks",
299
+ choices=TASKS,
 
 
 
 
 
300
  info="What tasks is this dataset suitable for?",
301
  )
302
 
303
  domain_input = gr.CheckboxGroup(
304
  label="Domain",
305
+ choices=DOMAINS,
306
  info="Specific domains covered by the dataset",
307
  )
308
 
 
560
  name_input = gr.Textbox(label="Name *", placeholder="Dataset name")
561
  dataset_type_input = gr.Dropdown(
562
  label="Dataset Type *",
563
+ choices=DATASET_TYPES,
 
 
 
 
 
564
  value="benchmark",
565
  )
566
 
 
587
 
588
  task_input = gr.CheckboxGroup(
589
  label="Tasks",
590
+ choices=TASKS,
 
 
 
 
 
 
 
 
 
 
 
 
 
591
  )
592
 
593
  domain_input = gr.CheckboxGroup(
594
  label="Domain",
595
+ choices=DOMAINS,
 
 
 
 
 
 
 
 
 
596
  )
597
 
598
  countries_input = gr.CheckboxGroup(
599
  label="Countries",
600
+ choices=COUNTRIES,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
  )
602
 
603
  languages_input = gr.CheckboxGroup(
604
  label="Languages",
605
+ choices=LANGUAGES,
 
 
 
 
 
 
 
 
606
  )
607
 
608
  update_btn = gr.Button("πŸ’Ύ Update Entry", variant="primary")
events_resource.py CHANGED
@@ -4,6 +4,13 @@ import gradio as gr
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
 
 
 
 
 
 
 
7
  # Dataset configuration
8
  DATASET_NAME = "somosnlp/recursos-pln-es"
9
  CONFIG_NAME = "events"
@@ -176,27 +183,44 @@ def create_all_tab():
176
  show_label=False,
177
  )
178
 
179
- # Load initial data
180
- initial_df = load_data()
 
 
 
 
 
 
181
 
 
182
  table = gr.Dataframe(
183
- value=initial_df,
184
  label=RESOURCE_TITLE,
185
  show_label=False,
186
  interactive=False,
187
- wrap=True,
 
188
  )
189
 
190
  # Connect search functionality
 
 
 
 
 
 
 
 
 
191
  search_box.change(
192
- fn=lambda query: search_and_filter_data(initial_df, query),
193
  inputs=search_box,
194
  outputs=table,
195
  )
196
 
197
  # Refresh button to reload data
198
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
199
- refresh_btn.click(fn=lambda: load_data(), outputs=table)
200
 
201
  return table
202
 
@@ -235,7 +259,7 @@ def create_contribute_tab():
235
 
236
  tipo_input = gr.Dropdown(
237
  label="Tipo *",
238
- choices=["workshop", "talk", "AMA", "round table"],
239
  info="Type of event (required)",
240
  multiselect=False,
241
  )
@@ -254,7 +278,7 @@ def create_contribute_tab():
254
 
255
  nivel_tecnico_input = gr.Dropdown(
256
  label="Nivel TΓ©cnico *",
257
- choices=["1", "2", "3", "4", "5"],
258
  info="Technical level from 1 (beginner) to 5 (expert) (required)",
259
  multiselect=False,
260
  )
@@ -467,13 +491,13 @@ def create_edit_tab():
467
  bio_input = gr.Textbox(label="Bio *", lines=2, placeholder="Speaker bio")
468
  tipo_input = gr.Dropdown(
469
  label="Tipo *",
470
- choices=["workshop", "talk", "AMA", "round table"],
471
  value="talk",
472
  )
473
  etiquetas_input = gr.Textbox(label="Etiquetas *", placeholder="Tags")
474
  tema_input = gr.Textbox(label="Tema *", placeholder="Topic")
475
  nivel_tecnico_input = gr.Dropdown(
476
- label="Nivel TΓ©cnico *", choices=["1", "2", "3", "4", "5"], value="3"
477
  )
478
  fecha_input = gr.Textbox(label="Fecha *", placeholder="DD/MM/YYYY")
479
  youtube_input = gr.Textbox(
 
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
7
+ from constants import (
8
+ EVENT_TYPES,
9
+ TECHNICAL_LEVELS,
10
+ format_dataframe_for_display,
11
+ format_dataframe_for_html_display,
12
+ )
13
+
14
  # Dataset configuration
15
  DATASET_NAME = "somosnlp/recursos-pln-es"
16
  CONFIG_NAME = "events"
 
183
  show_label=False,
184
  )
185
 
186
+ # Load and format initial data with clickable links
187
+ def get_formatted_data():
188
+ df = load_data()
189
+ return format_dataframe_for_display(
190
+ df,
191
+ url_columns=["youtube"],
192
+ hide_columns=["date_submitted"],
193
+ )
194
 
195
+ # Use Dataframe component with HTML rendering enabled
196
  table = gr.Dataframe(
197
+ value=get_formatted_data(),
198
  label=RESOURCE_TITLE,
199
  show_label=False,
200
  interactive=False,
201
+ wrap=False, # Disable wrapping to show full text in single lines
202
+ datatype="markdown", # Enable HTML rendering
203
  )
204
 
205
  # Connect search functionality
206
+ def search_and_format(query):
207
+ initial_df = load_data()
208
+ filtered_df = search_and_filter_data(initial_df, query)
209
+ return format_dataframe_for_display(
210
+ filtered_df,
211
+ url_columns=["youtube"],
212
+ hide_columns=["date_submitted"],
213
+ )
214
+
215
  search_box.change(
216
+ fn=search_and_format,
217
  inputs=search_box,
218
  outputs=table,
219
  )
220
 
221
  # Refresh button to reload data
222
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
223
+ refresh_btn.click(fn=get_formatted_data, outputs=table)
224
 
225
  return table
226
 
 
259
 
260
  tipo_input = gr.Dropdown(
261
  label="Tipo *",
262
+ choices=EVENT_TYPES,
263
  info="Type of event (required)",
264
  multiselect=False,
265
  )
 
278
 
279
  nivel_tecnico_input = gr.Dropdown(
280
  label="Nivel TΓ©cnico *",
281
+ choices=TECHNICAL_LEVELS,
282
  info="Technical level from 1 (beginner) to 5 (expert) (required)",
283
  multiselect=False,
284
  )
 
491
  bio_input = gr.Textbox(label="Bio *", lines=2, placeholder="Speaker bio")
492
  tipo_input = gr.Dropdown(
493
  label="Tipo *",
494
+ choices=EVENT_TYPES,
495
  value="talk",
496
  )
497
  etiquetas_input = gr.Textbox(label="Etiquetas *", placeholder="Tags")
498
  tema_input = gr.Textbox(label="Tema *", placeholder="Topic")
499
  nivel_tecnico_input = gr.Dropdown(
500
+ label="Nivel TΓ©cnico *", choices=TECHNICAL_LEVELS, value="3"
501
  )
502
  fecha_input = gr.Textbox(label="Fecha *", placeholder="DD/MM/YYYY")
503
  youtube_input = gr.Textbox(
initiatives_resource.py CHANGED
@@ -4,6 +4,14 @@ import gradio as gr
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
 
 
 
 
 
 
 
 
7
  # Dataset configuration
8
  DATASET_NAME = "somosnlp/recursos-pln-es"
9
  CONFIG_NAME = "initiatives"
@@ -147,27 +155,44 @@ def create_all_tab():
147
  show_label=False,
148
  )
149
 
150
- # Load initial data
151
- initial_df = load_data()
 
 
 
 
 
 
152
 
 
153
  table = gr.Dataframe(
154
- value=initial_df,
155
  label=RESOURCE_TITLE,
156
  show_label=False,
157
  interactive=False,
158
- wrap=True,
 
159
  )
160
 
161
  # Connect search functionality
 
 
 
 
 
 
 
 
 
162
  search_box.change(
163
- fn=lambda query: search_and_filter_data(initial_df, query),
164
  inputs=search_box,
165
  outputs=table,
166
  )
167
 
168
  # Refresh button to reload data
169
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
170
- refresh_btn.click(fn=lambda: load_data(), outputs=table)
171
 
172
  return table
173
 
@@ -193,15 +218,7 @@ def create_contribute_tab():
193
 
194
  type_input = gr.Dropdown(
195
  label="Type *",
196
- choices=[
197
- "project",
198
- "event",
199
- "research group",
200
- "community",
201
- "research institute",
202
- "non-profit",
203
- "OS company",
204
- ],
205
  info="Type of initiative (required)",
206
  multiselect=False,
207
  )
@@ -430,53 +447,16 @@ def create_edit_tab():
430
  name_input = gr.Textbox(label="Name *", placeholder="Initiative name")
431
  type_input = gr.Dropdown(
432
  label="Type *",
433
- choices=[
434
- "project",
435
- "event",
436
- "research group",
437
- "community",
438
- "research institute",
439
- "non-profit",
440
- "OS company",
441
- ],
442
  value="project",
443
  )
444
  countries_input = gr.CheckboxGroup(
445
  label="Countries *",
446
- choices=[
447
- "Spain",
448
- "Mexico",
449
- "Argentina",
450
- "Colombia",
451
- "Peru",
452
- "Venezuela",
453
- "Chile",
454
- "Ecuador",
455
- "Guatemala",
456
- "Cuba",
457
- "Bolivia",
458
- "Dominican Republic",
459
- "Honduras",
460
- "Paraguay",
461
- "El Salvador",
462
- "Nicaragua",
463
- "Costa Rica",
464
- "Panama",
465
- "Uruguay",
466
- "Puerto Rico",
467
- ],
468
  )
469
  languages_input = gr.CheckboxGroup(
470
  label="Languages *",
471
- choices=[
472
- "spanish",
473
- "catalan",
474
- "basque",
475
- "galician",
476
- "guarani",
477
- "quechua",
478
- "aymara",
479
- ],
480
  )
481
  website_url_input = gr.Textbox(
482
  label="Website URL *", placeholder="https://..."
 
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
7
+ from constants import (
8
+ COUNTRIES,
9
+ INITIATIVE_TYPES,
10
+ LANGUAGES,
11
+ format_dataframe_for_display,
12
+ format_dataframe_for_html_display,
13
+ )
14
+
15
  # Dataset configuration
16
  DATASET_NAME = "somosnlp/recursos-pln-es"
17
  CONFIG_NAME = "initiatives"
 
155
  show_label=False,
156
  )
157
 
158
+ # Load and format initial data with clickable links
159
+ def get_formatted_data():
160
+ df = load_data()
161
+ return format_dataframe_for_display(
162
+ df,
163
+ url_columns=["website_url"],
164
+ hide_columns=["date_submitted"],
165
+ )
166
 
167
+ # Use Dataframe component with HTML rendering enabled
168
  table = gr.Dataframe(
169
+ value=get_formatted_data(),
170
  label=RESOURCE_TITLE,
171
  show_label=False,
172
  interactive=False,
173
+ wrap=False, # Disable wrapping to show full text in single lines
174
+ datatype="markdown", # Enable HTML rendering
175
  )
176
 
177
  # Connect search functionality
178
+ def search_and_format(query):
179
+ initial_df = load_data()
180
+ filtered_df = search_and_filter_data(initial_df, query)
181
+ return format_dataframe_for_display(
182
+ filtered_df,
183
+ url_columns=["website_url"],
184
+ hide_columns=["date_submitted"],
185
+ )
186
+
187
  search_box.change(
188
+ fn=search_and_format,
189
  inputs=search_box,
190
  outputs=table,
191
  )
192
 
193
  # Refresh button to reload data
194
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
195
+ refresh_btn.click(fn=get_formatted_data, outputs=table)
196
 
197
  return table
198
 
 
218
 
219
  type_input = gr.Dropdown(
220
  label="Type *",
221
+ choices=INITIATIVE_TYPES,
 
 
 
 
 
 
 
 
222
  info="Type of initiative (required)",
223
  multiselect=False,
224
  )
 
447
  name_input = gr.Textbox(label="Name *", placeholder="Initiative name")
448
  type_input = gr.Dropdown(
449
  label="Type *",
450
+ choices=INITIATIVE_TYPES,
 
 
 
 
 
 
 
 
451
  value="project",
452
  )
453
  countries_input = gr.CheckboxGroup(
454
  label="Countries *",
455
+ choices=COUNTRIES,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  )
457
  languages_input = gr.CheckboxGroup(
458
  label="Languages *",
459
+ choices=LANGUAGES,
 
 
 
 
 
 
 
 
460
  )
461
  website_url_input = gr.Textbox(
462
  label="Website URL *", placeholder="https://..."
models_resource.py CHANGED
@@ -4,6 +4,8 @@ import gradio as gr
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
 
 
7
  # Dataset configuration
8
  DATASET_NAME = "somosnlp/recursos-pln-es"
9
  CONFIG_NAME = "models"
@@ -160,27 +162,43 @@ def create_all_tab():
160
  show_label=False,
161
  )
162
 
163
- # Load initial data
164
- initial_df = load_data()
 
 
 
 
 
 
165
 
166
  table = gr.Dataframe(
167
- value=initial_df,
168
  label=RESOURCE_TITLE,
169
  show_label=False,
170
  interactive=False,
171
  wrap=True,
 
172
  )
173
 
174
  # Connect search functionality
 
 
 
 
 
 
 
 
 
175
  search_box.change(
176
- fn=lambda query: search_and_filter_data(initial_df, query),
177
  inputs=search_box,
178
  outputs=table,
179
  )
180
 
181
  # Refresh button to reload data
182
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
183
- refresh_btn.click(fn=lambda: load_data(), outputs=table)
184
 
185
  return table
186
 
@@ -256,22 +274,20 @@ def create_contribute_tab():
256
  )
257
 
258
 
259
-
260
  def search_entries(query: str) -> pd.DataFrame:
261
  """Search for entries by familia or URL."""
262
  if not query.strip():
263
  return pd.DataFrame()
264
-
265
  df = load_data()
266
  if df.empty:
267
  return df
268
-
269
  # Search in familia and hf_collection_url columns
270
- mask = (
271
- df['familia'].str.contains(query, case=False, na=False) |
272
- df['hf_collection_url'].str.contains(query, case=False, na=False)
273
- )
274
-
275
  return df[mask]
276
 
277
 
@@ -279,72 +295,85 @@ def load_entry_for_edit(selected_entry: str) -> tuple:
279
  """Load a specific entry for editing."""
280
  if not selected_entry:
281
  return ("",) * 5 # Return empty values for all fields
282
-
283
  df = load_data()
284
  if df.empty:
285
  return ("",) * 5
286
-
287
  # Find the entry by familia or hf_collection_url
288
- entry = df[df['familia'] == selected_entry].iloc[0] if (df['familia'] == selected_entry).any() else df[df['hf_collection_url'] == selected_entry].iloc[0]
289
-
 
 
 
 
290
  return (
291
- entry['familia'],
292
- entry['available_sizes'],
293
- entry['hf_collection_url'],
294
- entry['website_url'],
295
- entry['paper_url']
296
  )
297
 
298
 
299
  def update_entry(
300
  original_identifier: str,
301
- familia: str, available_sizes: str, hf_collection_url: str,
302
- website_url: str, paper_url: str, profile: gr.OAuthProfile | None
 
 
 
 
303
  ):
304
  """Update an existing entry."""
305
  if not profile:
306
  return "❌ Please log in to edit entries."
307
-
308
  username = profile.username
309
  if not username:
310
  return "❌ Could not get username from profile."
311
-
312
  if not original_identifier:
313
  return "❌ No entry selected to edit."
314
-
315
  if not hf_collection_url.strip():
316
  return "❌ Hugging Face Collection URL is required."
317
-
318
  # Validate URLs
319
- for url_field, url_value in [("Hugging Face Collection URL", hf_collection_url),
320
- ("Website URL", website_url), ("Paper URL", paper_url)]:
 
 
 
321
  if url_value.strip() and not validate_url(url_value):
322
  return f"❌ Invalid {url_field}. Please provide a valid URL."
323
-
324
  # Validate available_sizes format
325
  if available_sizes.strip() and not validate_sizes(available_sizes):
326
  return "❌ Invalid available sizes format. Use comma-separated numbers (e.g., '0.1, 1.3, 7, 14')."
327
-
328
  try:
329
  # Load existing dataset
330
  existing_dataset = load_dataset(DATASET_NAME, CONFIG_NAME, split="train")
331
  existing_df = existing_dataset.to_pandas()
332
-
333
  # Find and update the entry
334
- mask = (existing_df['familia'] == original_identifier) | (existing_df['hf_collection_url'] == original_identifier)
 
 
335
  if not mask.any():
336
  return f"❌ Entry '{original_identifier}' not found."
337
-
338
  # Update the entry
339
  current_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
340
-
341
- existing_df.loc[mask, 'familia'] = familia
342
- existing_df.loc[mask, 'available_sizes'] = available_sizes
343
- existing_df.loc[mask, 'hf_collection_url'] = hf_collection_url
344
- existing_df.loc[mask, 'website_url'] = website_url
345
- existing_df.loc[mask, 'paper_url'] = paper_url
346
- existing_df.loc[mask, 'date_submitted'] = current_time
347
-
348
  # Convert back to Dataset and push to hub
349
  updated_dataset = Dataset.from_pandas(existing_df)
350
  updated_dataset.push_to_hub(
@@ -352,104 +381,131 @@ def update_entry(
352
  config_name=CONFIG_NAME,
353
  commit_message=f"Update model entry: {familia or hf_collection_url} (edited by {username})",
354
  )
355
-
356
  return f"βœ… Successfully updated '{familia or hf_collection_url}'!"
357
-
358
  except Exception as e:
359
  return f"❌ Error updating entry: {str(e)}"
360
 
361
 
362
-
363
  def create_edit_tab():
364
  """Create the edit tab for modifying existing entries."""
365
  with gr.TabItem("✏️ Edit", id=f"{RESOURCE_TYPE}_edit"):
366
  gr.Markdown(f"### Edit Existing {RESOURCE_TITLE}")
367
  gr.Markdown("Please log in to edit entries:")
368
  login_button = gr.LoginButton(elem_id=f"{RESOURCE_TYPE}-edit-oauth-button")
369
-
370
  gr.Markdown("Search for an entry to edit:")
371
-
372
  with gr.Row():
373
  search_input = gr.Textbox(
374
  label="Search by familia or collection URL",
375
  placeholder="Enter model familia or Hugging Face collection URL...",
376
- scale=3
377
  )
378
  search_btn = gr.Button("πŸ” Search", scale=1)
379
-
380
  search_results = gr.Dropdown(
381
- label="Select entry to edit",
382
- choices=[],
383
- interactive=True
384
  )
385
-
386
  gr.Markdown("---")
387
  gr.Markdown("**Edit the selected entry:**")
388
-
389
  with gr.Column(visible=False) as edit_form:
390
- hf_collection_url_input = gr.Textbox(label="Hugging Face Collection URL *", placeholder="https://huggingface.co/collections/...")
391
- familia_input = gr.Textbox(label="Familia", placeholder="e.g., BERT, GPT, T5...")
 
 
 
 
 
392
  available_sizes_input = gr.Textbox(
393
  label="Available Sizes (in B parameters)",
394
  placeholder="e.g., 0.1, 1.3, 7, 14",
395
- info="Comma-separated list of model sizes in billions of parameters"
 
 
 
 
 
 
396
  )
397
- website_url_input = gr.Textbox(label="Website URL", placeholder="https://...")
398
- paper_url_input = gr.Textbox(label="Paper URL", placeholder="https://arxiv.org/...")
399
-
400
  update_btn = gr.Button("πŸ’Ύ Update Entry", variant="primary")
401
  result_msg = gr.Markdown()
402
-
403
  # Store the original identifier for updating
404
  original_identifier_state = gr.State("")
405
-
406
  def search_and_update_dropdown(query):
407
  results_df = search_entries(query)
408
  if results_df.empty:
409
  return gr.Dropdown(choices=[], value=None)
410
  else:
411
  # Use familia if available, otherwise use hf_collection_url
412
- choices = [entry if entry else url for entry, url in zip(results_df['familia'].fillna(''), results_df['hf_collection_url'])]
 
 
 
 
 
 
413
  return gr.Dropdown(choices=choices, value=None)
414
-
415
  def load_entry_and_show_form(selected_entry):
416
  if not selected_entry:
417
  return (gr.Column(visible=False), "", *[("",) * 5])
418
-
419
  entry_data = load_entry_for_edit(selected_entry)
420
  return (gr.Column(visible=True), selected_entry, *entry_data)
421
-
422
  # Event handlers
423
  search_btn.click(
424
  fn=search_and_update_dropdown,
425
  inputs=[search_input],
426
- outputs=[search_results]
427
  )
428
-
429
  search_results.change(
430
  fn=load_entry_and_show_form,
431
  inputs=[search_results],
432
  outputs=[
433
- edit_form, original_identifier_state,
434
- familia_input, available_sizes_input, hf_collection_url_input,
435
- website_url_input, paper_url_input
436
- ]
 
 
 
 
437
  )
438
-
439
  update_btn.click(
440
  fn=update_entry,
441
  inputs=[
442
  original_identifier_state,
443
- familia_input, available_sizes_input, hf_collection_url_input,
444
- website_url_input, paper_url_input
 
 
 
445
  ],
446
- outputs=[result_msg]
447
  )
448
-
449
  return (
450
- search_input, search_btn, search_results, edit_form,
451
- familia_input, available_sizes_input, hf_collection_url_input,
452
- website_url_input, paper_url_input, update_btn, result_msg
 
 
 
 
 
 
 
 
453
  )
454
 
455
 
 
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
7
+ from constants import format_dataframe_for_display, format_dataframe_for_html_display
8
+
9
  # Dataset configuration
10
  DATASET_NAME = "somosnlp/recursos-pln-es"
11
  CONFIG_NAME = "models"
 
162
  show_label=False,
163
  )
164
 
165
+ # Load and format initial data with clickable links
166
+ def get_formatted_data():
167
+ df = load_data()
168
+ return format_dataframe_for_display(
169
+ df,
170
+ url_columns=["hf_collection_url", "website_url", "paper_url"],
171
+ hide_columns=["date_submitted"],
172
+ )
173
 
174
  table = gr.Dataframe(
175
+ value=get_formatted_data(),
176
  label=RESOURCE_TITLE,
177
  show_label=False,
178
  interactive=False,
179
  wrap=True,
180
+ datatype="markdown",
181
  )
182
 
183
  # Connect search functionality
184
+ def search_and_format(query):
185
+ initial_df = load_data()
186
+ filtered_df = search_and_filter_data(initial_df, query)
187
+ return format_dataframe_for_display(
188
+ filtered_df,
189
+ url_columns=["hf_collection_url", "website_url", "paper_url"],
190
+ hide_columns=["date_submitted"],
191
+ )
192
+
193
  search_box.change(
194
+ fn=search_and_format,
195
  inputs=search_box,
196
  outputs=table,
197
  )
198
 
199
  # Refresh button to reload data
200
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
201
+ refresh_btn.click(fn=get_formatted_data, outputs=table)
202
 
203
  return table
204
 
 
274
  )
275
 
276
 
 
277
  def search_entries(query: str) -> pd.DataFrame:
278
  """Search for entries by familia or URL."""
279
  if not query.strip():
280
  return pd.DataFrame()
281
+
282
  df = load_data()
283
  if df.empty:
284
  return df
285
+
286
  # Search in familia and hf_collection_url columns
287
+ mask = df["familia"].str.contains(query, case=False, na=False) | df[
288
+ "hf_collection_url"
289
+ ].str.contains(query, case=False, na=False)
290
+
 
291
  return df[mask]
292
 
293
 
 
295
  """Load a specific entry for editing."""
296
  if not selected_entry:
297
  return ("",) * 5 # Return empty values for all fields
298
+
299
  df = load_data()
300
  if df.empty:
301
  return ("",) * 5
302
+
303
  # Find the entry by familia or hf_collection_url
304
+ entry = (
305
+ df[df["familia"] == selected_entry].iloc[0]
306
+ if (df["familia"] == selected_entry).any()
307
+ else df[df["hf_collection_url"] == selected_entry].iloc[0]
308
+ )
309
+
310
  return (
311
+ entry["familia"],
312
+ entry["available_sizes"],
313
+ entry["hf_collection_url"],
314
+ entry["website_url"],
315
+ entry["paper_url"],
316
  )
317
 
318
 
319
  def update_entry(
320
  original_identifier: str,
321
+ familia: str,
322
+ available_sizes: str,
323
+ hf_collection_url: str,
324
+ website_url: str,
325
+ paper_url: str,
326
+ profile: gr.OAuthProfile | None,
327
  ):
328
  """Update an existing entry."""
329
  if not profile:
330
  return "❌ Please log in to edit entries."
331
+
332
  username = profile.username
333
  if not username:
334
  return "❌ Could not get username from profile."
335
+
336
  if not original_identifier:
337
  return "❌ No entry selected to edit."
338
+
339
  if not hf_collection_url.strip():
340
  return "❌ Hugging Face Collection URL is required."
341
+
342
  # Validate URLs
343
+ for url_field, url_value in [
344
+ ("Hugging Face Collection URL", hf_collection_url),
345
+ ("Website URL", website_url),
346
+ ("Paper URL", paper_url),
347
+ ]:
348
  if url_value.strip() and not validate_url(url_value):
349
  return f"❌ Invalid {url_field}. Please provide a valid URL."
350
+
351
  # Validate available_sizes format
352
  if available_sizes.strip() and not validate_sizes(available_sizes):
353
  return "❌ Invalid available sizes format. Use comma-separated numbers (e.g., '0.1, 1.3, 7, 14')."
354
+
355
  try:
356
  # Load existing dataset
357
  existing_dataset = load_dataset(DATASET_NAME, CONFIG_NAME, split="train")
358
  existing_df = existing_dataset.to_pandas()
359
+
360
  # Find and update the entry
361
+ mask = (existing_df["familia"] == original_identifier) | (
362
+ existing_df["hf_collection_url"] == original_identifier
363
+ )
364
  if not mask.any():
365
  return f"❌ Entry '{original_identifier}' not found."
366
+
367
  # Update the entry
368
  current_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
369
+
370
+ existing_df.loc[mask, "familia"] = familia
371
+ existing_df.loc[mask, "available_sizes"] = available_sizes
372
+ existing_df.loc[mask, "hf_collection_url"] = hf_collection_url
373
+ existing_df.loc[mask, "website_url"] = website_url
374
+ existing_df.loc[mask, "paper_url"] = paper_url
375
+ existing_df.loc[mask, "date_submitted"] = current_time
376
+
377
  # Convert back to Dataset and push to hub
378
  updated_dataset = Dataset.from_pandas(existing_df)
379
  updated_dataset.push_to_hub(
 
381
  config_name=CONFIG_NAME,
382
  commit_message=f"Update model entry: {familia or hf_collection_url} (edited by {username})",
383
  )
384
+
385
  return f"βœ… Successfully updated '{familia or hf_collection_url}'!"
386
+
387
  except Exception as e:
388
  return f"❌ Error updating entry: {str(e)}"
389
 
390
 
 
391
  def create_edit_tab():
392
  """Create the edit tab for modifying existing entries."""
393
  with gr.TabItem("✏️ Edit", id=f"{RESOURCE_TYPE}_edit"):
394
  gr.Markdown(f"### Edit Existing {RESOURCE_TITLE}")
395
  gr.Markdown("Please log in to edit entries:")
396
  login_button = gr.LoginButton(elem_id=f"{RESOURCE_TYPE}-edit-oauth-button")
397
+
398
  gr.Markdown("Search for an entry to edit:")
399
+
400
  with gr.Row():
401
  search_input = gr.Textbox(
402
  label="Search by familia or collection URL",
403
  placeholder="Enter model familia or Hugging Face collection URL...",
404
+ scale=3,
405
  )
406
  search_btn = gr.Button("πŸ” Search", scale=1)
407
+
408
  search_results = gr.Dropdown(
409
+ label="Select entry to edit", choices=[], interactive=True
 
 
410
  )
411
+
412
  gr.Markdown("---")
413
  gr.Markdown("**Edit the selected entry:**")
414
+
415
  with gr.Column(visible=False) as edit_form:
416
+ hf_collection_url_input = gr.Textbox(
417
+ label="Hugging Face Collection URL *",
418
+ placeholder="https://huggingface.co/collections/...",
419
+ )
420
+ familia_input = gr.Textbox(
421
+ label="Familia", placeholder="e.g., BERT, GPT, T5..."
422
+ )
423
  available_sizes_input = gr.Textbox(
424
  label="Available Sizes (in B parameters)",
425
  placeholder="e.g., 0.1, 1.3, 7, 14",
426
+ info="Comma-separated list of model sizes in billions of parameters",
427
+ )
428
+ website_url_input = gr.Textbox(
429
+ label="Website URL", placeholder="https://..."
430
+ )
431
+ paper_url_input = gr.Textbox(
432
+ label="Paper URL", placeholder="https://arxiv.org/..."
433
  )
434
+
 
 
435
  update_btn = gr.Button("πŸ’Ύ Update Entry", variant="primary")
436
  result_msg = gr.Markdown()
437
+
438
  # Store the original identifier for updating
439
  original_identifier_state = gr.State("")
440
+
441
  def search_and_update_dropdown(query):
442
  results_df = search_entries(query)
443
  if results_df.empty:
444
  return gr.Dropdown(choices=[], value=None)
445
  else:
446
  # Use familia if available, otherwise use hf_collection_url
447
+ choices = [
448
+ entry if entry else url
449
+ for entry, url in zip(
450
+ results_df["familia"].fillna(""),
451
+ results_df["hf_collection_url"],
452
+ )
453
+ ]
454
  return gr.Dropdown(choices=choices, value=None)
455
+
456
  def load_entry_and_show_form(selected_entry):
457
  if not selected_entry:
458
  return (gr.Column(visible=False), "", *[("",) * 5])
459
+
460
  entry_data = load_entry_for_edit(selected_entry)
461
  return (gr.Column(visible=True), selected_entry, *entry_data)
462
+
463
  # Event handlers
464
  search_btn.click(
465
  fn=search_and_update_dropdown,
466
  inputs=[search_input],
467
+ outputs=[search_results],
468
  )
469
+
470
  search_results.change(
471
  fn=load_entry_and_show_form,
472
  inputs=[search_results],
473
  outputs=[
474
+ edit_form,
475
+ original_identifier_state,
476
+ familia_input,
477
+ available_sizes_input,
478
+ hf_collection_url_input,
479
+ website_url_input,
480
+ paper_url_input,
481
+ ],
482
  )
483
+
484
  update_btn.click(
485
  fn=update_entry,
486
  inputs=[
487
  original_identifier_state,
488
+ familia_input,
489
+ available_sizes_input,
490
+ hf_collection_url_input,
491
+ website_url_input,
492
+ paper_url_input,
493
  ],
494
+ outputs=[result_msg],
495
  )
496
+
497
  return (
498
+ search_input,
499
+ search_btn,
500
+ search_results,
501
+ edit_form,
502
+ familia_input,
503
+ available_sizes_input,
504
+ hf_collection_url_input,
505
+ website_url_input,
506
+ paper_url_input,
507
+ update_btn,
508
+ result_msg,
509
  )
510
 
511
 
shared_tasks_resource.py CHANGED
@@ -4,6 +4,8 @@ import gradio as gr
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
 
 
7
  # Dataset configuration
8
  DATASET_NAME = "somosnlp/recursos-pln-es"
9
  CONFIG_NAME = "shared_tasks"
@@ -176,27 +178,43 @@ def create_all_tab():
176
  show_label=False,
177
  )
178
 
179
- # Load initial data
180
- initial_df = load_data()
 
 
 
 
 
 
181
 
182
  table = gr.Dataframe(
183
- value=initial_df,
184
  label=RESOURCE_TITLE,
185
  show_label=False,
186
  interactive=False,
187
  wrap=True,
 
188
  )
189
 
190
  # Connect search functionality
 
 
 
 
 
 
 
 
 
191
  search_box.change(
192
- fn=lambda query: search_and_filter_data(initial_df, query),
193
  inputs=search_box,
194
  outputs=table,
195
  )
196
 
197
  # Refresh button to reload data
198
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
199
- refresh_btn.click(fn=lambda: load_data(), outputs=table)
200
 
201
  return table
202
 
@@ -289,22 +307,20 @@ def create_contribute_tab():
289
  )
290
 
291
 
292
-
293
  def search_entries(query: str) -> pd.DataFrame:
294
  """Search for entries by name or conference."""
295
  if not query.strip():
296
  return pd.DataFrame()
297
-
298
  df = load_data()
299
  if df.empty:
300
  return df
301
-
302
  # Search in name and conference_name columns
303
- mask = (
304
- df['name'].str.contains(query, case=False, na=False) |
305
- df['conference_name'].str.contains(query, case=False, na=False)
306
- )
307
-
308
  return df[mask]
309
 
310
 
@@ -312,81 +328,97 @@ def load_entry_for_edit(selected_entry: str) -> tuple:
312
  """Load a specific entry for editing."""
313
  if not selected_entry:
314
  return ("",) * 7 # Return empty values for all fields
315
-
316
  df = load_data()
317
  if df.empty:
318
  return ("",) * 7
319
-
320
  # Find the entry by name
321
- entry = df[df['name'] == selected_entry].iloc[0]
322
-
323
  return (
324
- entry['name'],
325
- entry['conference_name'],
326
- entry['workshop_date'],
327
- entry['registration_deadline'],
328
- entry['data_available_date'],
329
- entry['submission_deadline'],
330
- entry['more_info_url']
331
  )
332
 
333
 
334
  def update_entry(
335
  original_name: str,
336
- name: str, conference_name: str, workshop_date: str, registration_deadline: str,
337
- data_available_date: str, submission_deadline: str, more_info_url: str,
 
 
 
 
 
338
  profile: gr.OAuthProfile | None,
339
  ):
340
  """Update an existing entry."""
341
  if not profile:
342
  return "❌ Please log in to edit entries."
343
-
344
  username = profile.username
345
  if not username:
346
  return "❌ Could not get username from profile."
347
-
348
  if not original_name:
349
  return "❌ No entry selected to edit."
350
-
351
  # Validate required fields
352
- required_fields = [name, conference_name, workshop_date, registration_deadline,
353
- data_available_date, submission_deadline, more_info_url]
 
 
 
 
 
 
 
354
  if not all(field.strip() for field in required_fields):
355
  return "❌ All fields are required."
356
-
357
  # Validate URL
358
  if not validate_url(more_info_url):
359
  return "❌ Invalid URL. Please provide a valid URL."
360
-
361
  # Validate dates
362
- date_fields = [("Workshop Date", workshop_date), ("Registration Deadline", registration_deadline),
363
- ("Data Available Date", data_available_date), ("Submission Deadline", submission_deadline)]
 
 
 
 
364
  for field_name, date_value in date_fields:
365
  if not validate_date(date_value):
366
  return f"❌ Invalid {field_name}. Please use DD/MM/YYYY format."
367
-
368
  try:
369
  # Load existing dataset
370
  existing_dataset = load_dataset(DATASET_NAME, CONFIG_NAME, split="train")
371
  existing_df = existing_dataset.to_pandas()
372
-
373
  # Find and update the entry
374
- mask = existing_df['name'] == original_name
375
  if not mask.any():
376
  return f"❌ Entry '{original_name}' not found."
377
-
378
  # Update the entry
379
  current_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
380
-
381
- existing_df.loc[mask, 'name'] = name
382
- existing_df.loc[mask, 'conference_name'] = conference_name
383
- existing_df.loc[mask, 'workshop_date'] = workshop_date
384
- existing_df.loc[mask, 'registration_deadline'] = registration_deadline
385
- existing_df.loc[mask, 'data_available_date'] = data_available_date
386
- existing_df.loc[mask, 'submission_deadline'] = submission_deadline
387
- existing_df.loc[mask, 'more_info_url'] = more_info_url
388
- existing_df.loc[mask, 'date_submitted'] = current_time
389
-
390
  # Convert back to Dataset and push to hub
391
  updated_dataset = Dataset.from_pandas(existing_df)
392
  updated_dataset.push_to_hub(
@@ -394,109 +426,136 @@ def update_entry(
394
  config_name=CONFIG_NAME,
395
  commit_message=f"Update shared task entry: {name} (edited by {username})",
396
  )
397
-
398
  return f"βœ… Successfully updated '{name}'!"
399
-
400
  except Exception as e:
401
  return f"❌ Error updating entry: {str(e)}"
402
 
403
 
404
-
405
  def create_edit_tab():
406
  """Create the edit tab for modifying existing entries."""
407
  with gr.TabItem("✏️ Edit", id=f"{RESOURCE_TYPE}_edit"):
408
  gr.Markdown(f"### Edit Existing {RESOURCE_TITLE}")
409
  gr.Markdown("Please log in to edit entries:")
410
  login_button = gr.LoginButton(elem_id=f"{RESOURCE_TYPE}-edit-oauth-button")
411
-
412
  gr.Markdown("Search for an entry to edit:")
413
-
414
  with gr.Row():
415
  search_input = gr.Textbox(
416
  label="Search by name or conference",
417
  placeholder="Enter shared task name or conference name...",
418
- scale=3
419
  )
420
  search_btn = gr.Button("πŸ” Search", scale=1)
421
-
422
  search_results = gr.Dropdown(
423
- label="Select entry to edit",
424
- choices=[],
425
- interactive=True
426
  )
427
-
428
  gr.Markdown("---")
429
  gr.Markdown("**Edit the selected entry:**")
430
-
431
  with gr.Column(visible=False) as edit_form:
432
  name_input = gr.Textbox(label="Name *", placeholder="Shared task name")
433
- conference_name_input = gr.Textbox(label="Conference Name *", placeholder="Conference or workshop name")
434
-
 
 
435
  gr.Markdown("**Important Dates** (all required, format: DD/MM/YYYY)")
436
  with gr.Row():
437
- workshop_date_input = gr.Textbox(label="Workshop Date *", placeholder="DD/MM/YYYY")
438
- registration_deadline_input = gr.Textbox(label="Registration Deadline *", placeholder="DD/MM/YYYY")
 
 
 
 
439
  with gr.Row():
440
- data_available_date_input = gr.Textbox(label="Data Available Date *", placeholder="DD/MM/YYYY")
441
- submission_deadline_input = gr.Textbox(label="Submission Deadline *", placeholder="DD/MM/YYYY")
442
-
443
- more_info_url_input = gr.Textbox(label="More Info URL *", placeholder="https://...")
444
-
 
 
 
 
 
 
445
  update_btn = gr.Button("πŸ’Ύ Update Entry", variant="primary")
446
  result_msg = gr.Markdown()
447
-
448
  # Store the original name for updating
449
  original_name_state = gr.State("")
450
-
451
  def search_and_update_dropdown(query):
452
  results_df = search_entries(query)
453
  if results_df.empty:
454
  return gr.Dropdown(choices=[], value=None)
455
  else:
456
- choices = results_df['name'].tolist()
457
  return gr.Dropdown(choices=choices, value=None)
458
-
459
  def load_entry_and_show_form(selected_entry):
460
  if not selected_entry:
461
  return (gr.Column(visible=False), "", *[("",) * 7])
462
-
463
  entry_data = load_entry_for_edit(selected_entry)
464
  return (gr.Column(visible=True), selected_entry, *entry_data)
465
-
466
  # Event handlers
467
  search_btn.click(
468
  fn=search_and_update_dropdown,
469
  inputs=[search_input],
470
- outputs=[search_results]
471
  )
472
-
473
  search_results.change(
474
  fn=load_entry_and_show_form,
475
  inputs=[search_results],
476
  outputs=[
477
- edit_form, original_name_state,
478
- name_input, conference_name_input, workshop_date_input,
479
- registration_deadline_input, data_available_date_input,
480
- submission_deadline_input, more_info_url_input
481
- ]
 
 
 
 
 
482
  )
483
-
484
  update_btn.click(
485
  fn=update_entry,
486
  inputs=[
487
  original_name_state,
488
- name_input, conference_name_input, workshop_date_input,
489
- registration_deadline_input, data_available_date_input,
490
- submission_deadline_input, more_info_url_input,
 
 
 
 
491
  ],
492
- outputs=[result_msg]
493
  )
494
-
495
  return (
496
- search_input, search_btn, search_results, edit_form,
497
- name_input, conference_name_input, workshop_date_input,
498
- registration_deadline_input, data_available_date_input,
499
- submission_deadline_input, more_info_url_input, update_btn, result_msg
 
 
 
 
 
 
 
 
 
500
  )
501
 
502
 
 
4
  import pandas as pd
5
  from datasets import Dataset, load_dataset
6
 
7
+ from constants import format_dataframe_for_display, format_dataframe_for_html_display
8
+
9
  # Dataset configuration
10
  DATASET_NAME = "somosnlp/recursos-pln-es"
11
  CONFIG_NAME = "shared_tasks"
 
178
  show_label=False,
179
  )
180
 
181
+ # Load and format initial data with clickable links
182
+ def get_formatted_data():
183
+ df = load_data()
184
+ return format_dataframe_for_display(
185
+ df,
186
+ url_columns=["more_info_url"],
187
+ hide_columns=["date_submitted"],
188
+ )
189
 
190
  table = gr.Dataframe(
191
+ value=get_formatted_data(),
192
  label=RESOURCE_TITLE,
193
  show_label=False,
194
  interactive=False,
195
  wrap=True,
196
+ datatype="markdown",
197
  )
198
 
199
  # Connect search functionality
200
+ def search_and_format(query):
201
+ initial_df = load_data()
202
+ filtered_df = search_and_filter_data(initial_df, query)
203
+ return format_dataframe_for_display(
204
+ filtered_df,
205
+ url_columns=["more_info_url"],
206
+ hide_columns=["date_submitted"],
207
+ )
208
+
209
  search_box.change(
210
+ fn=search_and_format,
211
  inputs=search_box,
212
  outputs=table,
213
  )
214
 
215
  # Refresh button to reload data
216
  refresh_btn = gr.Button("πŸ”„ Refresh Data", variant="secondary")
217
+ refresh_btn.click(fn=get_formatted_data, outputs=table)
218
 
219
  return table
220
 
 
307
  )
308
 
309
 
 
310
  def search_entries(query: str) -> pd.DataFrame:
311
  """Search for entries by name or conference."""
312
  if not query.strip():
313
  return pd.DataFrame()
314
+
315
  df = load_data()
316
  if df.empty:
317
  return df
318
+
319
  # Search in name and conference_name columns
320
+ mask = df["name"].str.contains(query, case=False, na=False) | df[
321
+ "conference_name"
322
+ ].str.contains(query, case=False, na=False)
323
+
 
324
  return df[mask]
325
 
326
 
 
328
  """Load a specific entry for editing."""
329
  if not selected_entry:
330
  return ("",) * 7 # Return empty values for all fields
331
+
332
  df = load_data()
333
  if df.empty:
334
  return ("",) * 7
335
+
336
  # Find the entry by name
337
+ entry = df[df["name"] == selected_entry].iloc[0]
338
+
339
  return (
340
+ entry["name"],
341
+ entry["conference_name"],
342
+ entry["workshop_date"],
343
+ entry["registration_deadline"],
344
+ entry["data_available_date"],
345
+ entry["submission_deadline"],
346
+ entry["more_info_url"],
347
  )
348
 
349
 
350
  def update_entry(
351
  original_name: str,
352
+ name: str,
353
+ conference_name: str,
354
+ workshop_date: str,
355
+ registration_deadline: str,
356
+ data_available_date: str,
357
+ submission_deadline: str,
358
+ more_info_url: str,
359
  profile: gr.OAuthProfile | None,
360
  ):
361
  """Update an existing entry."""
362
  if not profile:
363
  return "❌ Please log in to edit entries."
364
+
365
  username = profile.username
366
  if not username:
367
  return "❌ Could not get username from profile."
368
+
369
  if not original_name:
370
  return "❌ No entry selected to edit."
371
+
372
  # Validate required fields
373
+ required_fields = [
374
+ name,
375
+ conference_name,
376
+ workshop_date,
377
+ registration_deadline,
378
+ data_available_date,
379
+ submission_deadline,
380
+ more_info_url,
381
+ ]
382
  if not all(field.strip() for field in required_fields):
383
  return "❌ All fields are required."
384
+
385
  # Validate URL
386
  if not validate_url(more_info_url):
387
  return "❌ Invalid URL. Please provide a valid URL."
388
+
389
  # Validate dates
390
+ date_fields = [
391
+ ("Workshop Date", workshop_date),
392
+ ("Registration Deadline", registration_deadline),
393
+ ("Data Available Date", data_available_date),
394
+ ("Submission Deadline", submission_deadline),
395
+ ]
396
  for field_name, date_value in date_fields:
397
  if not validate_date(date_value):
398
  return f"❌ Invalid {field_name}. Please use DD/MM/YYYY format."
399
+
400
  try:
401
  # Load existing dataset
402
  existing_dataset = load_dataset(DATASET_NAME, CONFIG_NAME, split="train")
403
  existing_df = existing_dataset.to_pandas()
404
+
405
  # Find and update the entry
406
+ mask = existing_df["name"] == original_name
407
  if not mask.any():
408
  return f"❌ Entry '{original_name}' not found."
409
+
410
  # Update the entry
411
  current_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
412
+
413
+ existing_df.loc[mask, "name"] = name
414
+ existing_df.loc[mask, "conference_name"] = conference_name
415
+ existing_df.loc[mask, "workshop_date"] = workshop_date
416
+ existing_df.loc[mask, "registration_deadline"] = registration_deadline
417
+ existing_df.loc[mask, "data_available_date"] = data_available_date
418
+ existing_df.loc[mask, "submission_deadline"] = submission_deadline
419
+ existing_df.loc[mask, "more_info_url"] = more_info_url
420
+ existing_df.loc[mask, "date_submitted"] = current_time
421
+
422
  # Convert back to Dataset and push to hub
423
  updated_dataset = Dataset.from_pandas(existing_df)
424
  updated_dataset.push_to_hub(
 
426
  config_name=CONFIG_NAME,
427
  commit_message=f"Update shared task entry: {name} (edited by {username})",
428
  )
429
+
430
  return f"βœ… Successfully updated '{name}'!"
431
+
432
  except Exception as e:
433
  return f"❌ Error updating entry: {str(e)}"
434
 
435
 
 
436
  def create_edit_tab():
437
  """Create the edit tab for modifying existing entries."""
438
  with gr.TabItem("✏️ Edit", id=f"{RESOURCE_TYPE}_edit"):
439
  gr.Markdown(f"### Edit Existing {RESOURCE_TITLE}")
440
  gr.Markdown("Please log in to edit entries:")
441
  login_button = gr.LoginButton(elem_id=f"{RESOURCE_TYPE}-edit-oauth-button")
442
+
443
  gr.Markdown("Search for an entry to edit:")
444
+
445
  with gr.Row():
446
  search_input = gr.Textbox(
447
  label="Search by name or conference",
448
  placeholder="Enter shared task name or conference name...",
449
+ scale=3,
450
  )
451
  search_btn = gr.Button("πŸ” Search", scale=1)
452
+
453
  search_results = gr.Dropdown(
454
+ label="Select entry to edit", choices=[], interactive=True
 
 
455
  )
456
+
457
  gr.Markdown("---")
458
  gr.Markdown("**Edit the selected entry:**")
459
+
460
  with gr.Column(visible=False) as edit_form:
461
  name_input = gr.Textbox(label="Name *", placeholder="Shared task name")
462
+ conference_name_input = gr.Textbox(
463
+ label="Conference Name *", placeholder="Conference or workshop name"
464
+ )
465
+
466
  gr.Markdown("**Important Dates** (all required, format: DD/MM/YYYY)")
467
  with gr.Row():
468
+ workshop_date_input = gr.Textbox(
469
+ label="Workshop Date *", placeholder="DD/MM/YYYY"
470
+ )
471
+ registration_deadline_input = gr.Textbox(
472
+ label="Registration Deadline *", placeholder="DD/MM/YYYY"
473
+ )
474
  with gr.Row():
475
+ data_available_date_input = gr.Textbox(
476
+ label="Data Available Date *", placeholder="DD/MM/YYYY"
477
+ )
478
+ submission_deadline_input = gr.Textbox(
479
+ label="Submission Deadline *", placeholder="DD/MM/YYYY"
480
+ )
481
+
482
+ more_info_url_input = gr.Textbox(
483
+ label="More Info URL *", placeholder="https://..."
484
+ )
485
+
486
  update_btn = gr.Button("πŸ’Ύ Update Entry", variant="primary")
487
  result_msg = gr.Markdown()
488
+
489
  # Store the original name for updating
490
  original_name_state = gr.State("")
491
+
492
  def search_and_update_dropdown(query):
493
  results_df = search_entries(query)
494
  if results_df.empty:
495
  return gr.Dropdown(choices=[], value=None)
496
  else:
497
+ choices = results_df["name"].tolist()
498
  return gr.Dropdown(choices=choices, value=None)
499
+
500
  def load_entry_and_show_form(selected_entry):
501
  if not selected_entry:
502
  return (gr.Column(visible=False), "", *[("",) * 7])
503
+
504
  entry_data = load_entry_for_edit(selected_entry)
505
  return (gr.Column(visible=True), selected_entry, *entry_data)
506
+
507
  # Event handlers
508
  search_btn.click(
509
  fn=search_and_update_dropdown,
510
  inputs=[search_input],
511
+ outputs=[search_results],
512
  )
513
+
514
  search_results.change(
515
  fn=load_entry_and_show_form,
516
  inputs=[search_results],
517
  outputs=[
518
+ edit_form,
519
+ original_name_state,
520
+ name_input,
521
+ conference_name_input,
522
+ workshop_date_input,
523
+ registration_deadline_input,
524
+ data_available_date_input,
525
+ submission_deadline_input,
526
+ more_info_url_input,
527
+ ],
528
  )
529
+
530
  update_btn.click(
531
  fn=update_entry,
532
  inputs=[
533
  original_name_state,
534
+ name_input,
535
+ conference_name_input,
536
+ workshop_date_input,
537
+ registration_deadline_input,
538
+ data_available_date_input,
539
+ submission_deadline_input,
540
+ more_info_url_input,
541
  ],
542
+ outputs=[result_msg],
543
  )
544
+
545
  return (
546
+ search_input,
547
+ search_btn,
548
+ search_results,
549
+ edit_form,
550
+ name_input,
551
+ conference_name_input,
552
+ workshop_date_input,
553
+ registration_deadline_input,
554
+ data_available_date_input,
555
+ submission_deadline_input,
556
+ more_info_url_input,
557
+ update_btn,
558
+ result_msg,
559
  )
560
 
561