Spaces:
Runtime error
Runtime error
Create app.py
#2
by
guy1eyal
- opened
app.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import gradio as gr
|
2 |
import requests
|
3 |
-
from datetime import datetime, timezone
|
4 |
|
5 |
API_URL = "https://huggingface.co/api/daily_papers"
|
6 |
|
@@ -10,8 +10,10 @@ class PaperManager:
|
|
10 |
self.current_page = 1
|
11 |
self.papers = []
|
12 |
self.total_pages = 1
|
|
|
|
|
13 |
|
14 |
-
def
|
15 |
"""
|
16 |
Calculate the score of a paper based on upvotes and age.
|
17 |
This mimics the "hotness" algorithm used by platforms like Hacker News.
|
@@ -21,28 +23,53 @@ class PaperManager:
|
|
21 |
try:
|
22 |
published_time = datetime.fromisoformat(published_at_str.replace('Z', '+00:00'))
|
23 |
except ValueError:
|
24 |
-
# If parsing fails, use current time to minimize the impact on sorting
|
25 |
published_time = datetime.now(timezone.utc)
|
26 |
|
27 |
time_diff = datetime.now(timezone.utc) - published_time
|
28 |
-
time_diff_hours = time_diff.total_seconds() / 3600
|
29 |
|
30 |
-
# Avoid division by zero and apply the hotness formula
|
31 |
score = upvotes / ((time_diff_hours + 2) ** 1.5)
|
32 |
return score
|
33 |
|
34 |
-
def fetch_papers(self):
|
35 |
try:
|
36 |
-
response = requests.get(f"{API_URL}?limit=
|
37 |
response.raise_for_status()
|
38 |
data = response.json()
|
39 |
|
40 |
-
|
41 |
-
self.
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
self.total_pages = max((len(self.papers) + self.papers_per_page - 1) // self.papers_per_page, 1)
|
48 |
self.current_page = 1
|
@@ -112,17 +139,12 @@ class PaperManager:
|
|
112 |
|
113 |
paper_manager = PaperManager()
|
114 |
|
115 |
-
def
|
116 |
-
if paper_manager.fetch_papers():
|
117 |
-
return paper_manager.render_papers()
|
118 |
else:
|
119 |
-
return "<div class='no-papers'>Failed to fetch papers. Please try again later.</div>"
|
120 |
|
121 |
-
def refresh_papers():
|
122 |
-
if paper_manager.fetch_papers():
|
123 |
-
return paper_manager.render_papers()
|
124 |
-
else:
|
125 |
-
return "<div class='no-papers'>Failed to refresh papers. Please try again later.</div>"
|
126 |
|
127 |
css = """
|
128 |
body {
|
@@ -131,63 +153,51 @@ body {
|
|
131 |
margin: 0;
|
132 |
padding: 0;
|
133 |
}
|
134 |
-
|
135 |
a {
|
136 |
color: #0000ff;
|
137 |
text-decoration: none;
|
138 |
}
|
139 |
-
|
140 |
a:visited {
|
141 |
color: #551A8B;
|
142 |
}
|
143 |
-
|
144 |
.container {
|
145 |
width: 85%;
|
146 |
margin: auto;
|
147 |
}
|
148 |
-
|
149 |
table {
|
150 |
width: 100%;
|
151 |
}
|
152 |
-
|
153 |
.header-table {
|
154 |
width: 100%;
|
155 |
background-color: #ff6600;
|
156 |
padding: 2px 10px;
|
157 |
}
|
158 |
-
|
159 |
.header-table a {
|
160 |
color: black;
|
161 |
font-weight: bold;
|
162 |
font-size: 14pt;
|
163 |
text-decoration: none;
|
164 |
}
|
165 |
-
|
166 |
.itemlist .athing {
|
167 |
background-color: #f6f6ef;
|
168 |
}
|
169 |
-
|
170 |
.rank {
|
171 |
font-size: 14pt;
|
172 |
color: #828282;
|
173 |
padding-right: 5px;
|
174 |
}
|
175 |
-
|
176 |
.storylink {
|
177 |
font-size: 10pt;
|
178 |
}
|
179 |
-
|
180 |
.subtext {
|
181 |
font-size: 8pt;
|
182 |
color: #828282;
|
183 |
padding-left: 40px;
|
184 |
}
|
185 |
-
|
186 |
.subtext a {
|
187 |
color: #828282;
|
188 |
text-decoration: none;
|
189 |
}
|
190 |
-
|
191 |
#refresh-button {
|
192 |
background: none;
|
193 |
border: none;
|
@@ -196,71 +206,56 @@ table {
|
|
196 |
font-size: 14pt;
|
197 |
cursor: pointer;
|
198 |
}
|
199 |
-
|
200 |
.no-papers {
|
201 |
text-align: center;
|
202 |
color: #828282;
|
203 |
padding: 1rem;
|
204 |
font-size: 14pt;
|
205 |
}
|
206 |
-
|
207 |
@media (max-width: 640px) {
|
208 |
.header-table a {
|
209 |
font-size: 12pt;
|
210 |
}
|
211 |
-
|
212 |
.storylink {
|
213 |
font-size: 9pt;
|
214 |
}
|
215 |
-
|
216 |
.subtext {
|
217 |
font-size: 7pt;
|
218 |
}
|
219 |
}
|
220 |
-
|
221 |
/* Dark mode */
|
222 |
@media (prefers-color-scheme: dark) {
|
223 |
body {
|
224 |
background-color: #121212;
|
225 |
color: #e0e0e0;
|
226 |
}
|
227 |
-
|
228 |
a {
|
229 |
color: #add8e6;
|
230 |
}
|
231 |
-
|
232 |
a:visited {
|
233 |
color: #9370db;
|
234 |
}
|
235 |
-
|
236 |
.header-table {
|
237 |
background-color: #ff6600;
|
238 |
}
|
239 |
-
|
240 |
.header-table a {
|
241 |
color: black;
|
242 |
}
|
243 |
-
|
244 |
.itemlist .athing {
|
245 |
background-color: #1e1e1e;
|
246 |
}
|
247 |
-
|
248 |
.rank {
|
249 |
color: #b0b0b0;
|
250 |
}
|
251 |
-
|
252 |
.subtext {
|
253 |
color: #b0b0b0;
|
254 |
}
|
255 |
-
|
256 |
.subtext a {
|
257 |
color: #b0b0b0;
|
258 |
}
|
259 |
-
|
260 |
#refresh-button {
|
261 |
color: #e0e0e0;
|
262 |
}
|
263 |
-
|
264 |
.no-papers {
|
265 |
color: #b0b0b0;
|
266 |
}
|
@@ -285,6 +280,7 @@ with demo:
|
|
285 |
Once your paper is submitted, it will automatically appear in this demo.
|
286 |
""")
|
287 |
# Header with Refresh Button
|
|
|
288 |
with gr.Row():
|
289 |
gr.HTML("""
|
290 |
<table border="0" cellpadding="0" cellspacing="0" class="header-table">
|
@@ -300,22 +296,44 @@ with demo:
|
|
300 |
</tr>
|
301 |
</table>
|
302 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
303 |
# Paper list
|
304 |
paper_list = gr.HTML()
|
|
|
305 |
# Navigation Buttons
|
306 |
with gr.Row():
|
307 |
prev_button = gr.Button("Prev")
|
308 |
next_button = gr.Button("Next")
|
309 |
|
310 |
-
|
311 |
# Load papers on app start
|
312 |
-
demo.load(
|
|
|
|
|
|
|
|
|
313 |
|
314 |
# Button clicks
|
315 |
prev_button.click(paper_manager.prev_page, outputs=[paper_list])
|
316 |
next_button.click(paper_manager.next_page, outputs=[paper_list])
|
317 |
refresh_button = gr.Button("Refresh", visible=False, elem_id="refresh-hidden")
|
318 |
-
refresh_button.click(
|
319 |
|
320 |
# Bind the visible Refresh button to the hidden one using JavaScript
|
321 |
gr.HTML("""
|
|
|
1 |
import gradio as gr
|
2 |
import requests
|
3 |
+
from datetime import datetime, timezone, timedelta
|
4 |
|
5 |
API_URL = "https://huggingface.co/api/daily_papers"
|
6 |
|
|
|
10 |
self.current_page = 1
|
11 |
self.papers = []
|
12 |
self.total_pages = 1
|
13 |
+
self.sort_method = "hot"
|
14 |
+
self.time_period = "day"
|
15 |
|
16 |
+
def calculate_hot_score(self, paper):
|
17 |
"""
|
18 |
Calculate the score of a paper based on upvotes and age.
|
19 |
This mimics the "hotness" algorithm used by platforms like Hacker News.
|
|
|
23 |
try:
|
24 |
published_time = datetime.fromisoformat(published_at_str.replace('Z', '+00:00'))
|
25 |
except ValueError:
|
|
|
26 |
published_time = datetime.now(timezone.utc)
|
27 |
|
28 |
time_diff = datetime.now(timezone.utc) - published_time
|
29 |
+
time_diff_hours = time_diff.total_seconds() / 3600
|
30 |
|
|
|
31 |
score = upvotes / ((time_diff_hours + 2) ** 1.5)
|
32 |
return score
|
33 |
|
34 |
+
def fetch_papers(self, sort_method, time_period=None):
|
35 |
try:
|
36 |
+
response = requests.get(f"{API_URL}?limit=1000") # Increased limit to get more papers
|
37 |
response.raise_for_status()
|
38 |
data = response.json()
|
39 |
|
40 |
+
self.sort_method = sort_method
|
41 |
+
self.time_period = time_period if sort_method == "top" else None
|
42 |
+
|
43 |
+
if sort_method == "hot":
|
44 |
+
self.papers = sorted(
|
45 |
+
data,
|
46 |
+
key=lambda x: self.calculate_hot_score(x),
|
47 |
+
reverse=True
|
48 |
+
)
|
49 |
+
elif sort_method == "top":
|
50 |
+
current_time = datetime.now(timezone.utc)
|
51 |
+
|
52 |
+
if time_period == "day":
|
53 |
+
start_time = current_time - timedelta(days=1)
|
54 |
+
elif time_period == "week":
|
55 |
+
start_time = current_time - timedelta(days=7)
|
56 |
+
elif time_period == "month":
|
57 |
+
start_time = current_time - timedelta(days=30)
|
58 |
+
elif time_period == "year":
|
59 |
+
start_time = current_time - timedelta(days=365)
|
60 |
+
else:
|
61 |
+
start_time = datetime.min.replace(tzinfo=timezone.utc)
|
62 |
+
|
63 |
+
filtered_papers = [
|
64 |
+
paper for paper in data
|
65 |
+
if datetime.fromisoformat(paper.get('publishedAt', '').replace('Z', '+00:00')) >= start_time
|
66 |
+
]
|
67 |
+
|
68 |
+
self.papers = sorted(
|
69 |
+
filtered_papers,
|
70 |
+
key=lambda x: x.get('paper', {}).get('upvotes', 0),
|
71 |
+
reverse=True
|
72 |
+
)
|
73 |
|
74 |
self.total_pages = max((len(self.papers) + self.papers_per_page - 1) // self.papers_per_page, 1)
|
75 |
self.current_page = 1
|
|
|
139 |
|
140 |
paper_manager = PaperManager()
|
141 |
|
142 |
+
def update_papers(sort_method, time_period=None):
|
143 |
+
if paper_manager.fetch_papers(sort_method, time_period):
|
144 |
+
return paper_manager.render_papers(), gr.update(visible=(sort_method == "top"))
|
145 |
else:
|
146 |
+
return "<div class='no-papers'>Failed to fetch papers. Please try again later.</div>", gr.update(visible=False)
|
147 |
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
css = """
|
150 |
body {
|
|
|
153 |
margin: 0;
|
154 |
padding: 0;
|
155 |
}
|
|
|
156 |
a {
|
157 |
color: #0000ff;
|
158 |
text-decoration: none;
|
159 |
}
|
|
|
160 |
a:visited {
|
161 |
color: #551A8B;
|
162 |
}
|
|
|
163 |
.container {
|
164 |
width: 85%;
|
165 |
margin: auto;
|
166 |
}
|
|
|
167 |
table {
|
168 |
width: 100%;
|
169 |
}
|
|
|
170 |
.header-table {
|
171 |
width: 100%;
|
172 |
background-color: #ff6600;
|
173 |
padding: 2px 10px;
|
174 |
}
|
|
|
175 |
.header-table a {
|
176 |
color: black;
|
177 |
font-weight: bold;
|
178 |
font-size: 14pt;
|
179 |
text-decoration: none;
|
180 |
}
|
|
|
181 |
.itemlist .athing {
|
182 |
background-color: #f6f6ef;
|
183 |
}
|
|
|
184 |
.rank {
|
185 |
font-size: 14pt;
|
186 |
color: #828282;
|
187 |
padding-right: 5px;
|
188 |
}
|
|
|
189 |
.storylink {
|
190 |
font-size: 10pt;
|
191 |
}
|
|
|
192 |
.subtext {
|
193 |
font-size: 8pt;
|
194 |
color: #828282;
|
195 |
padding-left: 40px;
|
196 |
}
|
|
|
197 |
.subtext a {
|
198 |
color: #828282;
|
199 |
text-decoration: none;
|
200 |
}
|
|
|
201 |
#refresh-button {
|
202 |
background: none;
|
203 |
border: none;
|
|
|
206 |
font-size: 14pt;
|
207 |
cursor: pointer;
|
208 |
}
|
|
|
209 |
.no-papers {
|
210 |
text-align: center;
|
211 |
color: #828282;
|
212 |
padding: 1rem;
|
213 |
font-size: 14pt;
|
214 |
}
|
|
|
215 |
@media (max-width: 640px) {
|
216 |
.header-table a {
|
217 |
font-size: 12pt;
|
218 |
}
|
|
|
219 |
.storylink {
|
220 |
font-size: 9pt;
|
221 |
}
|
|
|
222 |
.subtext {
|
223 |
font-size: 7pt;
|
224 |
}
|
225 |
}
|
|
|
226 |
/* Dark mode */
|
227 |
@media (prefers-color-scheme: dark) {
|
228 |
body {
|
229 |
background-color: #121212;
|
230 |
color: #e0e0e0;
|
231 |
}
|
|
|
232 |
a {
|
233 |
color: #add8e6;
|
234 |
}
|
|
|
235 |
a:visited {
|
236 |
color: #9370db;
|
237 |
}
|
|
|
238 |
.header-table {
|
239 |
background-color: #ff6600;
|
240 |
}
|
|
|
241 |
.header-table a {
|
242 |
color: black;
|
243 |
}
|
|
|
244 |
.itemlist .athing {
|
245 |
background-color: #1e1e1e;
|
246 |
}
|
|
|
247 |
.rank {
|
248 |
color: #b0b0b0;
|
249 |
}
|
|
|
250 |
.subtext {
|
251 |
color: #b0b0b0;
|
252 |
}
|
|
|
253 |
.subtext a {
|
254 |
color: #b0b0b0;
|
255 |
}
|
|
|
256 |
#refresh-button {
|
257 |
color: #e0e0e0;
|
258 |
}
|
|
|
259 |
.no-papers {
|
260 |
color: #b0b0b0;
|
261 |
}
|
|
|
280 |
Once your paper is submitted, it will automatically appear in this demo.
|
281 |
""")
|
282 |
# Header with Refresh Button
|
283 |
+
|
284 |
with gr.Row():
|
285 |
gr.HTML("""
|
286 |
<table border="0" cellpadding="0" cellspacing="0" class="header-table">
|
|
|
296 |
</tr>
|
297 |
</table>
|
298 |
""")
|
299 |
+
|
300 |
+
# Sorting method selection
|
301 |
+
with gr.Row():
|
302 |
+
sort_method = gr.Radio(
|
303 |
+
choices=["hot", "top"],
|
304 |
+
value="hot",
|
305 |
+
label="Sort by:",
|
306 |
+
interactive=True
|
307 |
+
)
|
308 |
+
|
309 |
+
# Time Period Dropdown (only visible when "top" is selected)
|
310 |
+
time_period = gr.Dropdown(
|
311 |
+
choices=["day", "week", "month", "year"],
|
312 |
+
value="day",
|
313 |
+
label="Top papers from the last:",
|
314 |
+
visible=False
|
315 |
+
)
|
316 |
+
|
317 |
# Paper list
|
318 |
paper_list = gr.HTML()
|
319 |
+
|
320 |
# Navigation Buttons
|
321 |
with gr.Row():
|
322 |
prev_button = gr.Button("Prev")
|
323 |
next_button = gr.Button("Next")
|
324 |
|
|
|
325 |
# Load papers on app start
|
326 |
+
demo.load(lambda: update_papers("hot"), outputs=[paper_list, time_period])
|
327 |
+
|
328 |
+
# Update papers when sorting method or time period changes
|
329 |
+
sort_method.change(update_papers, inputs=[sort_method, time_period], outputs=[paper_list, time_period])
|
330 |
+
time_period.change(lambda s, t: update_papers(s, t), inputs=[sort_method, time_period], outputs=[paper_list, time_period])
|
331 |
|
332 |
# Button clicks
|
333 |
prev_button.click(paper_manager.prev_page, outputs=[paper_list])
|
334 |
next_button.click(paper_manager.next_page, outputs=[paper_list])
|
335 |
refresh_button = gr.Button("Refresh", visible=False, elem_id="refresh-hidden")
|
336 |
+
refresh_button.click(update_papers, inputs=[sort_method, time_period], outputs=[paper_list, time_period])
|
337 |
|
338 |
# Bind the visible Refresh button to the hidden one using JavaScript
|
339 |
gr.HTML("""
|