akhaliq HF staff commited on
Commit
d4ba28a
·
verified ·
1 Parent(s): 63f7996

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -165
app.py CHANGED
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
5
  API_URL = "https://huggingface.co/api/daily_papers"
6
 
7
  class PaperManager:
8
- def __init__(self, papers_per_page=10):
9
  self.papers_per_page = papers_per_page
10
  self.current_page = 1
11
  self.papers = []
@@ -13,10 +13,10 @@ class PaperManager:
13
 
14
  def fetch_papers(self):
15
  try:
16
- response = requests.get(f"{API_URL}?limit=50")
17
  response.raise_for_status()
18
  data = response.json()
19
-
20
  # Sort papers primarily by 'publishedAt' descending, then by 'upvotes' descending
21
  self.papers = sorted(
22
  data,
@@ -28,7 +28,7 @@ class PaperManager:
28
  ),
29
  reverse=True # Ensures descending order
30
  )
31
-
32
  self.total_pages = (len(self.papers) + self.papers_per_page - 1) // self.papers_per_page
33
  self.current_page = 1
34
  return True
@@ -39,7 +39,7 @@ class PaperManager:
39
  print(f"Unexpected error: {e}")
40
  return False
41
 
42
- def format_paper(self, paper):
43
  title = paper.get('title', 'No title')
44
  paper_id = paper.get('paper', {}).get('id', '')
45
  url = f"https://huggingface.co/papers/{paper_id}"
@@ -50,224 +50,196 @@ class PaperManager:
50
  paper.get('publishedAt', datetime.now(timezone.utc).isoformat()).replace('Z', '+00:00')
51
  )
52
  time_ago_days = (datetime.now(timezone.utc) - published_time).days
 
53
 
 
54
  return f"""
55
- <div class="paper-item">
56
- <h3><a href="{url}" target="_blank">{title}</a></h3>
57
- <p class="paper-meta">
58
- <span class="upvotes">{upvotes} ▲</span>
59
- <span class="comments">{comments}💬</span>
60
- <span class="authors">{authors}</span>
61
- <span class="time-ago">{time_ago_days}d</span>
62
- </p>
63
- </div>
 
 
 
 
 
 
 
64
  """
65
 
66
  def render_papers(self):
67
  start = (self.current_page - 1) * self.papers_per_page
68
  end = start + self.papers_per_page
69
  current_papers = self.papers[start:end]
70
-
71
  if not current_papers:
72
  return "<div class='no-papers'>No papers available for this page.</div>"
73
-
74
- return "".join([self.format_paper(paper) for paper in current_papers])
 
 
 
 
 
75
 
76
  def next_page(self):
77
  if self.current_page < self.total_pages:
78
  self.current_page += 1
79
- return self.render_papers(), f"Page {self.current_page} of {self.total_pages}"
 
 
80
 
81
- def prev_page(self):
82
- if self.current_page > 1:
83
- self.current_page -= 1
84
- return self.render_papers(), f"Page {self.current_page} of {self.total_pages}"
 
 
 
85
 
86
  css = """
87
- /* Your existing CSS */
88
  body {
89
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
90
- background-color: #f0f2f5;
91
  margin: 0;
92
  padding: 0;
93
  }
94
- .container {
95
- max-width: 100%;
96
- margin: 0 auto;
97
- background-color: white;
98
- padding: 1rem;
99
- }
100
- .title {
101
- text-align: center;
102
- color: #1a202c;
103
- font-size: 1.5rem;
104
- margin-bottom: 1rem;
105
  }
106
- .paper-list {
107
- max-height: 60vh;
108
- overflow-y: auto;
109
- border: 1px solid #e2e8f0;
110
- border-radius: 8px;
111
- padding: 0.5rem;
112
  }
113
- .paper-item {
114
- border-bottom: 1px solid #e2e8f0;
115
- padding: 0.5rem 0;
 
116
  }
117
- .paper-item:last-child {
118
- border-bottom: none;
 
119
  }
120
- .paper-item h3 {
121
- margin: 0 0 0.25rem 0;
122
- font-size: 1rem;
 
123
  }
124
- .paper-item a {
125
- color: #2b6cb0;
126
- text-decoration: none;
127
- font-weight: 600;
 
128
  }
129
- .paper-item a:hover {
130
- text-decoration: underline;
 
131
  }
132
- .paper-meta {
133
- font-size: 0.75rem;
134
- color: #4a5568;
135
- display: flex;
136
- flex-wrap: wrap;
137
- gap: 0.5rem;
138
  }
139
- .footer {
140
- display: flex;
141
- justify-content: space-between;
142
- align-items: center;
143
- margin-top: 0.5rem;
144
- position: relative;
145
- flex-wrap: wrap;
146
- gap: 0.5rem;
147
  }
148
 
149
- /* Target Gradio button elements more specifically */
150
- #component-5, #component-7, #component-9 {
151
- background-color: #3498db !important; /* Mobile-friendly blue */
152
- color: white !important;
153
- border: none !important;
154
- padding: 0.5rem 1rem !important;
155
- border-radius: 4px !important;
156
- cursor: pointer !important;
157
- transition: background-color 0.3s !important;
158
- font-size: 0.875rem !important;
159
- line-height: 1.25rem !important;
160
- width: auto !important;
161
- max-width: 100% !important;
162
  }
163
 
164
- #component-5:hover, #component-7:hover, #component-9:hover {
165
- background-color: #2980b9 !important; /* Darker blue on hover */
166
  }
167
 
168
- /* Style for the refresh button */
169
- #component-11 {
170
- background-color: #2ecc71 !important; /* More visible green */
171
- color: white !important;
172
- border: none !important;
173
- padding: 0.5rem !important;
174
- border-radius: 4px !important;
175
- cursor: pointer !important;
176
- transition: background-color 0.3s !important;
177
- font-size: 1rem !important;
178
- width: auto !important;
179
- max-width: 100% !important;
180
- height: auto !important;
181
- line-height: 1 !important;
182
  }
183
 
184
- #component-11:hover {
185
- background-color: #27ae60 !important; /* Darker green on hover */
 
186
  }
187
 
188
- /* Make sure the page info doesn't break the layout */
189
- #component-8 {
190
- flex: 1;
191
- text-align: center;
192
- min-width: 100px;
193
  }
194
 
195
  .no-papers {
196
  text-align: center;
197
- color: #718096;
198
  padding: 1rem;
 
199
  }
200
 
201
- /* Responsive adjustments */
202
  @media (max-width: 640px) {
203
- #component-5, #component-7, #component-9, #component-11 {
204
- font-size: 0.75rem !important;
205
- padding: 0.4rem 0.8rem !important;
206
- }
207
-
208
- .footer {
209
- justify-content: center;
210
  }
211
- }
212
 
213
- @media (min-width: 640px) {
214
- .container {
215
- max-width: 640px;
216
- padding: 1.5rem;
217
- }
218
- .title {
219
- font-size: 1.75rem;
220
- }
221
- .paper-item h3 {
222
- font-size: 1.125rem;
223
  }
224
- .paper-meta {
225
- font-size: 0.875rem;
226
- }
227
- }
228
 
229
- @media (min-width: 768px) {
230
- .container {
231
- max-width: 768px;
232
- }
233
- .footer {
234
- position: sticky;
235
- bottom: 0;
236
- background-color: white;
237
  }
238
  }
239
  """
240
 
241
- paper_manager = PaperManager()
242
-
243
- def initialize_app():
244
- if paper_manager.fetch_papers():
245
- return paper_manager.render_papers(), f"Page {paper_manager.current_page} of {paper_manager.total_pages}"
246
- else:
247
- return "<div class='no-papers'>Failed to fetch papers. Please try again later.</div>", "Error"
248
-
249
- def refresh_papers():
250
- if paper_manager.fetch_papers():
251
- return paper_manager.render_papers(), f"Page {paper_manager.current_page} of {paper_manager.total_pages}"
252
- else:
253
- return "<div class='no-papers'>Failed to refresh papers. Please try again later.</div>", "Error"
254
-
255
  demo = gr.Blocks(css=css)
256
 
257
  with demo:
258
  with gr.Column(elem_classes=["container"]):
259
- gr.Markdown("# Daily Papers - Hacker News Style", elem_classes=["title"])
260
- refresh_button = gr.Button("↻ Refresh", variant="primary", elem_classes=["refresh-btn"])
261
- with gr.Row(elem_classes=["footer"]):
262
- prev_button = gr.Button("← Prev")
263
- page_info = gr.Markdown(elem_classes=["page-info"])
264
- next_button = gr.Button("Next →")
265
-
266
- paper_list = gr.HTML(elem_classes=["paper-list"])
267
-
268
- demo.load(initialize_app, outputs=[paper_list, page_info])
269
- refresh_button.click(refresh_papers, outputs=[paper_list, page_info])
270
- prev_button.click(paper_manager.prev_page, outputs=[paper_list, page_info])
271
- next_button.click(paper_manager.next_page, outputs=[paper_list, page_info])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
  demo.launch()
 
5
  API_URL = "https://huggingface.co/api/daily_papers"
6
 
7
  class PaperManager:
8
+ def __init__(self, papers_per_page=30):
9
  self.papers_per_page = papers_per_page
10
  self.current_page = 1
11
  self.papers = []
 
13
 
14
  def fetch_papers(self):
15
  try:
16
+ response = requests.get(f"{API_URL}?limit=100")
17
  response.raise_for_status()
18
  data = response.json()
19
+
20
  # Sort papers primarily by 'publishedAt' descending, then by 'upvotes' descending
21
  self.papers = sorted(
22
  data,
 
28
  ),
29
  reverse=True # Ensures descending order
30
  )
31
+
32
  self.total_pages = (len(self.papers) + self.papers_per_page - 1) // self.papers_per_page
33
  self.current_page = 1
34
  return True
 
39
  print(f"Unexpected error: {e}")
40
  return False
41
 
42
+ def format_paper(self, paper, rank):
43
  title = paper.get('title', 'No title')
44
  paper_id = paper.get('paper', {}).get('id', '')
45
  url = f"https://huggingface.co/papers/{paper_id}"
 
50
  paper.get('publishedAt', datetime.now(timezone.utc).isoformat()).replace('Z', '+00:00')
51
  )
52
  time_ago_days = (datetime.now(timezone.utc) - published_time).days
53
+ time_ago = f"{time_ago_days} days ago" if time_ago_days > 0 else "today"
54
 
55
+ # Return HTML similar to Hacker News
56
  return f"""
57
+ <tr class="athing">
58
+ <td align="right" valign="top" class="title"><span class="rank">{rank}.</span></td>
59
+ <td valign="top" class="votelinks">
60
+ <center><a href="#"><div class="votearrow"></div></a></center>
61
+ </td>
62
+ <td class="title">
63
+ <a href="{url}" class="storylink" target="_blank">{title}</a>
64
+ </td>
65
+ </tr>
66
+ <tr>
67
+ <td colspan="2"></td>
68
+ <td class="subtext">
69
+ <span class="score">{upvotes} points</span> by {authors} | {time_ago} | <a href="#">{comments} comments</a>
70
+ </td>
71
+ </tr>
72
+ <tr style="height:5px"></tr>
73
  """
74
 
75
  def render_papers(self):
76
  start = (self.current_page - 1) * self.papers_per_page
77
  end = start + self.papers_per_page
78
  current_papers = self.papers[start:end]
79
+
80
  if not current_papers:
81
  return "<div class='no-papers'>No papers available for this page.</div>"
82
+
83
+ papers_html = "".join([self.format_paper(paper, idx + start + 1) for idx, paper in enumerate(current_papers)])
84
+ return f"""
85
+ <table border="0" cellpadding="0" cellspacing="0" class="itemlist">
86
+ {papers_html}
87
+ </table>
88
+ """
89
 
90
  def next_page(self):
91
  if self.current_page < self.total_pages:
92
  self.current_page += 1
93
+ return self.render_papers()
94
+ else:
95
+ return self.render_papers() + "<center class='more'><span>No more papers.</span></center>"
96
 
97
+ paper_manager = PaperManager()
98
+
99
+ def initialize_app():
100
+ if paper_manager.fetch_papers():
101
+ return paper_manager.render_papers()
102
+ else:
103
+ return "<div class='no-papers'>Failed to fetch papers. Please try again later.</div>"
104
 
105
  css = """
 
106
  body {
107
+ background-color: white;
108
+ font-family: Verdana, Geneva, sans-serif;
109
  margin: 0;
110
  padding: 0;
111
  }
112
+
113
+ a {
114
+ color: #0000ff;
115
+ text-decoration: none;
 
 
 
 
 
 
 
116
  }
117
+
118
+ a:visited {
119
+ color: #551A8B;
 
 
 
120
  }
121
+
122
+ .container {
123
+ width: 85%;
124
+ margin: auto;
125
  }
126
+
127
+ table {
128
+ width: 100%;
129
  }
130
+
131
+ .title {
132
+ background-color: #ff6600;
133
+ padding: 2px 10px;
134
  }
135
+
136
+ .title a {
137
+ color: black;
138
+ font-weight: bold;
139
+ font-size: 14pt;
140
  }
141
+
142
+ .itemlist .athing {
143
+ background-color: #f6f6ef;
144
  }
145
+
146
+ .rank {
147
+ font-size: 14pt;
148
+ color: #828282;
149
+ padding-right: 5px;
 
150
  }
151
+
152
+ .votelinks {
153
+ width: 10px;
 
 
 
 
 
154
  }
155
 
156
+ .votearrow {
157
+ width: 0;
158
+ height: 0;
159
+ border-left: 5px solid transparent;
160
+ border-right: 5px solid transparent;
161
+ border-bottom: 10px solid #828282;
162
+ margin: auto;
 
 
 
 
 
 
163
  }
164
 
165
+ .storylink {
166
+ font-size: 10pt;
167
  }
168
 
169
+ .subtext {
170
+ font-size: 8pt;
171
+ color: #828282;
172
+ padding-left: 40px;
 
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
+ .subtext a {
176
+ color: #828282;
177
+ text-decoration: none;
178
  }
179
 
180
+ .more {
181
+ padding: 10px;
 
 
 
182
  }
183
 
184
  .no-papers {
185
  text-align: center;
186
+ color: #828282;
187
  padding: 1rem;
188
+ font-size: 14pt;
189
  }
190
 
 
191
  @media (max-width: 640px) {
192
+ .title a {
193
+ font-size: 12pt;
 
 
 
 
 
194
  }
 
195
 
196
+ .storylink {
197
+ font-size: 9pt;
 
 
 
 
 
 
 
 
198
  }
 
 
 
 
199
 
200
+ .subtext {
201
+ font-size: 7pt;
 
 
 
 
 
 
202
  }
203
  }
204
  """
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  demo = gr.Blocks(css=css)
207
 
208
  with demo:
209
  with gr.Column(elem_classes=["container"]):
210
+ # Header
211
+ gr.HTML("""
212
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="#ff6600">
213
+ <tr>
214
+ <td style="padding: 8px;">
215
+ <span class="pagetop">
216
+ <b class="hnname"><a href="#" style="color: black; text-decoration: none;">Daily Papers</a></b>
217
+ </span>
218
+ </td>
219
+ </tr>
220
+ </table>
221
+ """)
222
+ # Paper list
223
+ paper_list = gr.HTML()
224
+ # Footer
225
+ more_button = gr.HTML("""
226
+ <center class='more'>
227
+ <a href="#" id="more-button">More</a>
228
+ </center>
229
+ """)
230
+
231
+ demo.load(initialize_app, outputs=[paper_list])
232
+
233
+ # JavaScript to handle the "More" link click
234
+ demo.add_script("""
235
+ document.getElementById('more-button').addEventListener('click', function(e) {
236
+ e.preventDefault();
237
+ gradioApp().querySelector('#component-2 button').click();
238
+ });
239
+ """)
240
+
241
+ # Hidden button to trigger next page
242
+ hidden_next_button = gr.Button(visible=False)
243
+ hidden_next_button.click(paper_manager.next_page, outputs=[paper_list])
244
 
245
  demo.launch()