Files changed (1) hide show
  1. app.py +70 -52
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 calculate_score(self, paper):
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 # Convert time difference to hours
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=100")
37
  response.raise_for_status()
38
  data = response.json()
39
 
40
- # Sort papers by calculated score descending
41
- self.papers = sorted(
42
- data,
43
- key=lambda x: self.calculate_score(x),
44
- reverse=True
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 initialize_app():
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(initialize_app, outputs=[paper_list])
 
 
 
 
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(refresh_papers, outputs=[paper_list])
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("""