Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,27 +1,41 @@
|
|
1 |
import gradio as gr
|
2 |
-
import json
|
3 |
import pandas as pd
|
|
|
4 |
import os
|
5 |
-
import shutil
|
6 |
import datetime
|
7 |
|
8 |
# Paths
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
ADMIN_CODE = os.getenv("ADMIN_CODE", "")
|
15 |
|
16 |
-
# Init data file
|
17 |
-
if not os.path.exists(DATA_FILE) or os.path.getsize(DATA_FILE) == 0:
|
18 |
-
with open(DATA_FILE, "w") as f:
|
19 |
-
json.dump([], f)
|
20 |
-
|
21 |
-
# Country list
|
22 |
ALL_COUNTRIES = sorted(list(set([
|
23 |
"United States of America", "United Kingdom", "India", "France", "Germany", "Canada", "Australia", "Japan", "Brazil", "Mexico",
|
24 |
-
] + [ # Full list
|
25 |
"Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Argentina", "Armenia", "Austria", "Azerbaijan",
|
26 |
"Bangladesh", "Belgium", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bulgaria", "Cambodia",
|
27 |
"Chile", "China", "Colombia", "Croatia", "Cuba", "Czech Republic", "Denmark", "Dominican Republic", "Ecuador",
|
@@ -33,178 +47,103 @@ ALL_COUNTRIES = sorted(list(set([
|
|
33 |
"Zambia", "Zimbabwe"
|
34 |
])))
|
35 |
|
36 |
-
|
37 |
-
|
38 |
-
def backup_data():
|
39 |
-
os.makedirs(BACKUP_DIR, exist_ok=True)
|
40 |
-
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
41 |
-
backup_file = os.path.join(BACKUP_DIR, f'teamup_data_backup_{timestamp}.json')
|
42 |
-
shutil.copy(DATA_FILE, backup_file)
|
43 |
-
|
44 |
-
# Save data safely
|
45 |
-
|
46 |
-
def atomic_save(data, path):
|
47 |
-
tmp_path = path + ".tmp"
|
48 |
-
with open(tmp_path, "w") as f:
|
49 |
-
json.dump(data, f, indent=2)
|
50 |
-
os.replace(tmp_path, path)
|
51 |
-
|
52 |
-
# Submit profile
|
53 |
|
|
|
54 |
def submit_profile(name, discord, city, country, address, looking, onlinecheck, languages, laptop, robot, skills, describe3, experience, idea):
|
55 |
if not discord or not city or not country or not laptop or not robot:
|
56 |
return "β Please fill in all required fields."
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
break
|
86 |
-
else:
|
87 |
-
data.append({
|
88 |
-
"Name": name,
|
89 |
-
"Discord": discord,
|
90 |
-
"City": city,
|
91 |
-
"Country": country,
|
92 |
-
"Address": address,
|
93 |
-
"Looking for Team": looking,
|
94 |
-
"Onlinecheck": onlinecheck,
|
95 |
-
"Languages": languages if isinstance(languages, list) else [languages],
|
96 |
-
"Laptop": laptop,
|
97 |
-
"Robot": robot,
|
98 |
-
"Skills": skills,
|
99 |
-
"Describe3": describe3,
|
100 |
-
"Experience": experience,
|
101 |
-
"Project Idea": idea
|
102 |
-
})
|
103 |
-
|
104 |
-
try:
|
105 |
-
atomic_save(data, DATA_FILE)
|
106 |
-
backup_data()
|
107 |
-
return "β
Profile saved!"
|
108 |
-
except Exception as e:
|
109 |
-
return f"β Failed to save: {e}"
|
110 |
-
|
111 |
-
# Filter dropdowns
|
112 |
-
|
113 |
-
def update_city_filter(country):
|
114 |
-
with open(DATA_FILE, "r") as f:
|
115 |
-
data = json.load(f)
|
116 |
-
df = pd.DataFrame(data)
|
117 |
-
|
118 |
-
if country != "All":
|
119 |
-
df = df[df["Country"].str.title() == country.title()]
|
120 |
-
|
121 |
-
cities = sorted(set(df["City"].dropna().unique()))
|
122 |
-
return gr.update(choices=["All"] + cities, value="All")
|
123 |
-
|
124 |
-
def update_dropdown_choices():
|
125 |
-
with open(DATA_FILE, "r") as f:
|
126 |
-
data = json.load(f)
|
127 |
-
df = pd.DataFrame(data)
|
128 |
-
|
129 |
-
country_choices = sorted(df["Country"].dropna().unique()) if "Country" in df else []
|
130 |
-
city_choices = sorted(df["City"].dropna().unique()) if "City" in df else []
|
131 |
-
language_set = set()
|
132 |
-
if "Languages" in df:
|
133 |
-
for lang_list in df["Languages"].dropna():
|
134 |
-
if isinstance(lang_list, list):
|
135 |
-
language_set.update(lang_list)
|
136 |
-
elif isinstance(lang_list, str):
|
137 |
-
language_set.update(lang_list.split(", "))
|
138 |
-
return (
|
139 |
-
gr.update(choices=["All"] + list(country_choices), value="All"),
|
140 |
-
gr.update(choices=["All"] + list(city_choices), value="All"),
|
141 |
-
gr.update(choices=["All"] + sorted(language_set), value="All")
|
142 |
-
)
|
143 |
-
|
144 |
-
# Filter table
|
145 |
-
|
146 |
def filter_by_fields(selected_country, selected_city, selected_language):
|
147 |
-
|
148 |
-
|
|
|
149 |
|
150 |
-
if
|
151 |
return "<p>No data available.</p>"
|
152 |
|
153 |
-
df =
|
154 |
-
|
155 |
-
df["
|
156 |
-
df["City"] = df["City"].astype(str).str.strip().str.title()
|
157 |
-
df["Languages"] = df["Languages"].apply(lambda x: ", ".join(x) if isinstance(x, list) else str(x)).str.strip().str.lower()
|
158 |
|
159 |
if selected_country != "All":
|
160 |
-
df = df[df["Country"] == selected_country.
|
161 |
if selected_city != "All":
|
162 |
-
df = df[df["City"] == selected_city.
|
163 |
if selected_language != "All":
|
164 |
-
df = df[df["Languages"].str.contains(selected_language.
|
165 |
|
166 |
if df.empty:
|
167 |
return "<p>No participants match your filters.</p>"
|
168 |
|
169 |
-
|
170 |
-
|
|
|
|
|
|
|
171 |
|
172 |
-
html = '<h3 style="margin-top:20px;">\U0001f50d Find your matching Teammates & Register your team <a href="https://forms.gle/gJEMGD4CEA2emhD18" target="_blank">here</a></h3>'
|
173 |
-
html += "<table style='width:100%; border-collapse: collapse;'>"
|
174 |
-
html += "<tr>" + "".join(f"<th style='border: 1px solid #ccc; padding: 6px;'>{col}</th>" for col in df.columns) + "</tr>"
|
175 |
for _, row in df.iterrows():
|
176 |
-
html += "<tr>"
|
177 |
-
|
178 |
-
|
179 |
-
|
|
|
|
|
|
|
|
|
180 |
html += "</table>"
|
181 |
return html
|
182 |
|
183 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
184 |
|
|
|
185 |
def delete_by_discord(discord, code):
|
186 |
if code != ADMIN_CODE:
|
187 |
return "β Invalid admin code."
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
return f"ποΈ Deleted user with Discord: {discord}"
|
194 |
|
195 |
-
# CSV export
|
196 |
-
|
197 |
-
def download_csv(code):
|
198 |
-
if code != ADMIN_CODE:
|
199 |
-
raise gr.Error("β Invalid admin code.")
|
200 |
-
with open(DATA_FILE, "r") as f:
|
201 |
-
data = json.load(f)
|
202 |
-
df = pd.DataFrame(data)
|
203 |
-
csv_path = os.path.join("data", "teamup_export.csv")
|
204 |
-
df.to_csv(csv_path, index=False)
|
205 |
-
return csv_path
|
206 |
-
|
207 |
-
# Interface
|
208 |
with gr.Blocks(css=".gr-dropdown { max-height: 100px; overflow-y: auto; font-size: 12px; }") as demo:
|
209 |
gr.Markdown("# π LeRobot Worldwide Hackathon - Team-Up Dashboard")
|
210 |
|
@@ -217,7 +156,7 @@ with gr.Blocks(css=".gr-dropdown { max-height: 100px; overflow-y: auto; font-siz
|
|
217 |
address = gr.Text(label="Address (optional)")
|
218 |
looking = gr.Radio(["Yes", "No"], label="π Looking for a team?")
|
219 |
onlinecheck = gr.Radio(["Participate Online", "Join a Local Hackathon"], label="π I will...")
|
220 |
-
languages = gr.CheckboxGroup(choices=
|
221 |
laptop = gr.Text(label="π» Laptop Setup *")
|
222 |
robot = gr.Text(label="π€ Robot Setup *")
|
223 |
skills = gr.Text(label="π§ Skills (comma separated)")
|
@@ -228,47 +167,23 @@ with gr.Blocks(css=".gr-dropdown { max-height: 100px; overflow-y: auto; font-siz
|
|
228 |
status = gr.Textbox(label="", interactive=False)
|
229 |
|
230 |
with gr.Column():
|
231 |
-
gr.Markdown("\n\nπ― 2. Choose your preferences to find your teammates")
|
232 |
country_filter = gr.Dropdown(label="Filter by Country", choices=["All"] + ALL_COUNTRIES, value="All")
|
233 |
city_filter = gr.Dropdown(label="Filter by City", choices=["All"], value="All")
|
234 |
-
language_filter = gr.Dropdown(label="Filter by Language", choices=["All"
|
235 |
table_html = gr.HTML(label="Matching Participants")
|
236 |
|
237 |
-
demo.load(fn=filter_by_fields, inputs=[country_filter, city_filter, language_filter], outputs=[table_html])
|
238 |
-
demo.load(fn=update_dropdown_choices, outputs=[country_filter, city_filter, language_filter])
|
239 |
-
|
240 |
country_filter.change(fn=update_city_filter, inputs=[country_filter], outputs=[city_filter])
|
241 |
-
|
242 |
-
|
243 |
-
language_filter.change(fn=filter_by_fields, inputs=[country_filter, city_filter, language_filter], outputs=[table_html])
|
244 |
|
245 |
-
submit_btn.click(
|
246 |
-
|
247 |
-
inputs=[name, discord, city, country, address, looking, onlinecheck, languages, laptop, robot, skills, describe3, experience, idea],
|
248 |
-
outputs=[status]
|
249 |
-
).then(
|
250 |
-
fn=update_dropdown_choices,
|
251 |
-
inputs=[],
|
252 |
-
outputs=[country_filter, city_filter, language_filter]
|
253 |
-
).then(
|
254 |
-
fn=filter_by_fields,
|
255 |
-
inputs=[country_filter, city_filter, language_filter],
|
256 |
-
outputs=[table_html]
|
257 |
-
)
|
258 |
|
259 |
-
gr.Markdown("---\n### π‘οΈ Admin Panel
|
260 |
admin_discord = gr.Text(label="Discord Username")
|
261 |
admin_code = gr.Text(label="Admin Code", type="password")
|
262 |
del_btn = gr.Button("Delete Profile")
|
263 |
del_status = gr.Textbox(label="Status", interactive=False)
|
264 |
del_btn.click(delete_by_discord, inputs=[admin_discord, admin_code], outputs=[del_status])
|
265 |
|
266 |
-
|
267 |
-
export_code = gr.Text(label="Admin Code", type="password")
|
268 |
-
download_btn = gr.Button("Generate and Download CSV")
|
269 |
-
download_file = gr.File(label="CSV Export", interactive=False)
|
270 |
-
download_btn.click(fn=download_csv, inputs=[export_code], outputs=[download_file])
|
271 |
-
|
272 |
-
if __name__ == "__main__":
|
273 |
-
demo.launch()
|
274 |
-
|
|
|
1 |
import gradio as gr
|
|
|
2 |
import pandas as pd
|
3 |
+
import sqlite3
|
4 |
import os
|
|
|
5 |
import datetime
|
6 |
|
7 |
# Paths
|
8 |
+
DB_FILE = "data/teamup.db"
|
9 |
+
os.makedirs("data", exist_ok=True)
|
10 |
+
|
11 |
+
# Initialize database
|
12 |
+
conn = sqlite3.connect(DB_FILE)
|
13 |
+
c = conn.cursor()
|
14 |
+
c.execute('''
|
15 |
+
CREATE TABLE IF NOT EXISTS teamup (
|
16 |
+
discord TEXT PRIMARY KEY,
|
17 |
+
name TEXT,
|
18 |
+
city TEXT,
|
19 |
+
country TEXT,
|
20 |
+
address TEXT,
|
21 |
+
looking TEXT,
|
22 |
+
onlinecheck TEXT,
|
23 |
+
languages TEXT,
|
24 |
+
laptop TEXT,
|
25 |
+
robot TEXT,
|
26 |
+
skills TEXT,
|
27 |
+
describe3 TEXT,
|
28 |
+
experience TEXT,
|
29 |
+
idea TEXT
|
30 |
+
)
|
31 |
+
''')
|
32 |
+
conn.commit()
|
33 |
+
conn.close()
|
34 |
|
35 |
ADMIN_CODE = os.getenv("ADMIN_CODE", "")
|
36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
ALL_COUNTRIES = sorted(list(set([
|
38 |
"United States of America", "United Kingdom", "India", "France", "Germany", "Canada", "Australia", "Japan", "Brazil", "Mexico",
|
|
|
39 |
"Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Argentina", "Armenia", "Austria", "Azerbaijan",
|
40 |
"Bangladesh", "Belgium", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bulgaria", "Cambodia",
|
41 |
"Chile", "China", "Colombia", "Croatia", "Cuba", "Czech Republic", "Denmark", "Dominican Republic", "Ecuador",
|
|
|
47 |
"Zambia", "Zimbabwe"
|
48 |
])))
|
49 |
|
50 |
+
LANGUAGES = ["English", "French", "Spanish", "German", "Portuguese", "Chinese", "Arabic", "Hindi"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
|
52 |
+
# Insert or update profile
|
53 |
def submit_profile(name, discord, city, country, address, looking, onlinecheck, languages, laptop, robot, skills, describe3, experience, idea):
|
54 |
if not discord or not city or not country or not laptop or not robot:
|
55 |
return "β Please fill in all required fields."
|
56 |
|
57 |
+
lang_str = ", ".join(languages) if isinstance(languages, list) else languages
|
58 |
+
|
59 |
+
conn = sqlite3.connect(DB_FILE)
|
60 |
+
c = conn.cursor()
|
61 |
+
c.execute("""
|
62 |
+
INSERT INTO teamup (discord, name, city, country, address, looking, onlinecheck, languages, laptop, robot, skills, describe3, experience, idea)
|
63 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
64 |
+
ON CONFLICT(discord) DO UPDATE SET
|
65 |
+
name=excluded.name,
|
66 |
+
city=excluded.city,
|
67 |
+
country=excluded.country,
|
68 |
+
address=excluded.address,
|
69 |
+
looking=excluded.looking,
|
70 |
+
onlinecheck=excluded.onlinecheck,
|
71 |
+
languages=excluded.languages,
|
72 |
+
laptop=excluded.laptop,
|
73 |
+
robot=excluded.robot,
|
74 |
+
skills=excluded.skills,
|
75 |
+
describe3=excluded.describe3,
|
76 |
+
experience=excluded.experience,
|
77 |
+
idea=excluded.idea
|
78 |
+
""", (discord, name, city.title(), country.title(), address, looking, onlinecheck, lang_str.lower(), laptop, robot, skills, describe3, experience, idea))
|
79 |
+
conn.commit()
|
80 |
+
conn.close()
|
81 |
+
return "β
Profile saved!"
|
82 |
+
|
83 |
+
# Filter and return HTML table
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
def filter_by_fields(selected_country, selected_city, selected_language):
|
85 |
+
conn = sqlite3.connect(DB_FILE)
|
86 |
+
df = pd.read_sql_query("SELECT * FROM teamup", conn)
|
87 |
+
conn.close()
|
88 |
|
89 |
+
if df.empty:
|
90 |
return "<p>No data available.</p>"
|
91 |
|
92 |
+
df["City"] = df["city"].str.title()
|
93 |
+
df["Country"] = df["country"].str.title()
|
94 |
+
df["Languages"] = df["languages"]
|
|
|
|
|
95 |
|
96 |
if selected_country != "All":
|
97 |
+
df = df[df["Country"] == selected_country.title()]
|
98 |
if selected_city != "All":
|
99 |
+
df = df[df["City"] == selected_city.title()]
|
100 |
if selected_language != "All":
|
101 |
+
df = df[df["Languages"].str.contains(selected_language.lower(), na=False)]
|
102 |
|
103 |
if df.empty:
|
104 |
return "<p>No participants match your filters.</p>"
|
105 |
|
106 |
+
html = "<table style='width:100%; border-collapse: collapse;'><tr>"
|
107 |
+
headers = ["Discord", "Name", "City", "Country", "Looking", "Onlinecheck", "Languages", "Laptop", "Robot", "Skills", "Describe3", "Experience", "Idea"]
|
108 |
+
for col in headers:
|
109 |
+
html += f"<th style='border:1px solid #ccc; padding:6px;'>{col}</th>"
|
110 |
+
html += "</tr>"
|
111 |
|
|
|
|
|
|
|
112 |
for _, row in df.iterrows():
|
113 |
+
html += "<tr>"
|
114 |
+
for col in ["discord", "name", "City", "Country", "looking", "onlinecheck", "Languages", "laptop", "robot", "skills", "describe3", "experience", "idea"]:
|
115 |
+
val = row[col]
|
116 |
+
if col == "discord":
|
117 |
+
val = f"<a href='https://discord.com/users/{val}' target='_blank'>{val}</a>"
|
118 |
+
html += f"<td style='border:1px solid #eee; padding:6px;'>{val}</td>"
|
119 |
+
html += "</tr>"
|
120 |
+
|
121 |
html += "</table>"
|
122 |
return html
|
123 |
|
124 |
+
# Update city dropdown
|
125 |
+
def update_city_filter(country):
|
126 |
+
conn = sqlite3.connect(DB_FILE)
|
127 |
+
c = conn.cursor()
|
128 |
+
if country == "All":
|
129 |
+
c.execute("SELECT DISTINCT city FROM teamup")
|
130 |
+
else:
|
131 |
+
c.execute("SELECT DISTINCT city FROM teamup WHERE country = ?", (country.title(),))
|
132 |
+
cities = [r[0].title() for r in c.fetchall() if r[0]]
|
133 |
+
conn.close()
|
134 |
+
return gr.update(choices=["All"] + sorted(cities), value="All")
|
135 |
|
136 |
+
# Delete
|
137 |
def delete_by_discord(discord, code):
|
138 |
if code != ADMIN_CODE:
|
139 |
return "β Invalid admin code."
|
140 |
+
conn = sqlite3.connect(DB_FILE)
|
141 |
+
c = conn.cursor()
|
142 |
+
c.execute("DELETE FROM teamup WHERE lower(discord) = ?", (discord.lower(),))
|
143 |
+
conn.commit()
|
144 |
+
conn.close()
|
145 |
return f"ποΈ Deleted user with Discord: {discord}"
|
146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
with gr.Blocks(css=".gr-dropdown { max-height: 100px; overflow-y: auto; font-size: 12px; }") as demo:
|
148 |
gr.Markdown("# π LeRobot Worldwide Hackathon - Team-Up Dashboard")
|
149 |
|
|
|
156 |
address = gr.Text(label="Address (optional)")
|
157 |
looking = gr.Radio(["Yes", "No"], label="π Looking for a team?")
|
158 |
onlinecheck = gr.Radio(["Participate Online", "Join a Local Hackathon"], label="π I will...")
|
159 |
+
languages = gr.CheckboxGroup(choices=LANGUAGES, label="Languages Spoken *")
|
160 |
laptop = gr.Text(label="π» Laptop Setup *")
|
161 |
robot = gr.Text(label="π€ Robot Setup *")
|
162 |
skills = gr.Text(label="π§ Skills (comma separated)")
|
|
|
167 |
status = gr.Textbox(label="", interactive=False)
|
168 |
|
169 |
with gr.Column():
|
|
|
170 |
country_filter = gr.Dropdown(label="Filter by Country", choices=["All"] + ALL_COUNTRIES, value="All")
|
171 |
city_filter = gr.Dropdown(label="Filter by City", choices=["All"], value="All")
|
172 |
+
language_filter = gr.Dropdown(label="Filter by Language", choices=["All"] + LANGUAGES, value="All")
|
173 |
table_html = gr.HTML(label="Matching Participants")
|
174 |
|
|
|
|
|
|
|
175 |
country_filter.change(fn=update_city_filter, inputs=[country_filter], outputs=[city_filter])
|
176 |
+
for dropdown in [country_filter, city_filter, language_filter]:
|
177 |
+
dropdown.change(fn=filter_by_fields, inputs=[country_filter, city_filter, language_filter], outputs=[table_html])
|
|
|
178 |
|
179 |
+
submit_btn.click(fn=submit_profile, inputs=[name, discord, city, country, address, looking, onlinecheck, languages, laptop, robot, skills, describe3, experience, idea], outputs=[status])
|
180 |
+
submit_btn.click(fn=filter_by_fields, inputs=[country_filter, city_filter, language_filter], outputs=[table_html])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
|
182 |
+
gr.Markdown("---\n### π‘οΈ Admin Panel")
|
183 |
admin_discord = gr.Text(label="Discord Username")
|
184 |
admin_code = gr.Text(label="Admin Code", type="password")
|
185 |
del_btn = gr.Button("Delete Profile")
|
186 |
del_status = gr.Textbox(label="Status", interactive=False)
|
187 |
del_btn.click(delete_by_discord, inputs=[admin_discord, admin_code], outputs=[del_status])
|
188 |
|
189 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|