import gradio as gr import requests from datetime import datetime, timezone API_URL = "https://huggingface.co/api/daily_papers" class PaperManager: def __init__(self, papers_per_page=30): self.papers_per_page = papers_per_page self.current_page = 1 self.papers = [] self.total_pages = 1 def fetch_papers(self): try: response = requests.get(f"{API_URL}?limit=100") response.raise_for_status() data = response.json() # Sort papers by 'publishedAt' descending, then by 'upvotes' descending self.papers = sorted( data, key=lambda x: ( datetime.fromisoformat( x.get('publishedAt', datetime.now(timezone.utc).isoformat()).replace('Z', '+00:00') ), x.get('paper', {}).get('upvotes', 0) ), reverse=True ) self.total_pages = max((len(self.papers) + self.papers_per_page - 1) // self.papers_per_page, 1) self.current_page = 1 return True except requests.RequestException as e: print(f"Error fetching papers: {e}") return False except Exception as e: print(f"Unexpected error: {e}") return False def format_paper(self, paper, rank): title = paper.get('title', 'No title') paper_id = paper.get('paper', {}).get('id', '') url = f"https://huggingface.co/papers/{paper_id}" authors = ', '.join([author.get('name', '') for author in paper.get('paper', {}).get('authors', [])]) or 'Unknown' upvotes = paper.get('paper', {}).get('upvotes', 0) comments = paper.get('numComments', 0) published_time = datetime.fromisoformat( paper.get('publishedAt', datetime.now(timezone.utc).isoformat()).replace('Z', '+00:00') ) time_diff = datetime.now(timezone.utc) - published_time time_ago_days = time_diff.days time_ago = f"{time_ago_days} days ago" if time_ago_days > 0 else "today" return f""" <tr class="athing"> <td align="right" valign="top" class="title"><span class="rank">{rank}.</span></td> <td valign="top" class="votelinks"> <center><div class="votearrow"></div></center> </td> <td class="title"> <a href="{url}" class="storylink" target="_blank">{title}</a> </td> </tr> <tr> <td colspan="2"></td> <td class="subtext"> <span class="score">{upvotes} points</span> by {authors} | {time_ago} | <a href="#">{comments} comments</a> </td> </tr> <tr style="height:5px"></tr> """ def render_papers(self): start = (self.current_page - 1) * self.papers_per_page end = start + self.papers_per_page current_papers = self.papers[start:end] if not current_papers: return "<div class='no-papers'>No papers available for this page.</div>" papers_html = "".join([self.format_paper(paper, idx + start + 1) for idx, paper in enumerate(current_papers)]) return f""" <table border="0" cellpadding="0" cellspacing="0" class="itemlist"> {papers_html} </table> """ def next_page(self): if self.current_page < self.total_pages: self.current_page += 1 return self.render_papers() def prev_page(self): if self.current_page > 1: self.current_page -= 1 return self.render_papers() paper_manager = PaperManager() def initialize_app(): if paper_manager.fetch_papers(): return paper_manager.render_papers() else: return "<div class='no-papers'>Failed to fetch papers. Please try again later.</div>" def refresh_papers(): if paper_manager.fetch_papers(): return paper_manager.render_papers() else: return "<div class='no-papers'>Failed to refresh papers. Please try again later.</div>" css = """ body { background-color: white; font-family: Verdana, Geneva, sans-serif; margin: 0; padding: 0; } a { color: #0000ff; text-decoration: none; } a:visited { color: #551A8B; } .container { width: 85%; margin: auto; } table { width: 100%; } .title { background-color: #ff6600; padding: 2px 10px; } .title a { color: black; font-weight: bold; font-size: 14pt; } .itemlist .athing { background-color: #f6f6ef; } .rank { font-size: 14pt; color: #828282; padding-right: 5px; } .votelinks { width: 10px; } .votearrow { width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 10px solid #828282; margin: auto; } .storylink { font-size: 10pt; } .subtext { font-size: 8pt; color: #828282; padding-left: 40px; } .subtext a { color: #828282; text-decoration: none; } .more-link button { background: none; border: none; color: #0000ff; text-decoration: underline; cursor: pointer; font-size: 10pt; padding: 0; } .no-papers { text-align: center; color: #828282; padding: 1rem; font-size: 14pt; } @media (max-width: 640px) { .title a { font-size: 12pt; } .storylink { font-size: 9pt; } .subtext { font-size: 7pt; } } """ demo = gr.Blocks(css=css) with demo: with gr.Column(elem_classes=["container"]): # Header with Refresh Button gr.HTML(""" <table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="#ff6600"> <tr> <td style="padding: 8px;"> <span class="pagetop"> <b class="hnname"><a href="#" style="color: black; text-decoration: none;">Daily Papers</a></b> </span> </td> <td align="right" style="padding: 8px;"> <button id="refresh-button">Refresh</button> </td> </tr> </table> """) # Paper list paper_list = gr.HTML() # Navigation Buttons with gr.Row(): prev_button = gr.Button("Prev") next_button = gr.Button("Next") # "More" Button Styled as Link with gr.Row(): more_button = gr.Button("More", elem_classes="more-link") # Load papers on app start demo.load(initialize_app, outputs=[paper_list]) # Button clicks prev_button.click(paper_manager.prev_page, outputs=[paper_list]) next_button.click(paper_manager.next_page, outputs=[paper_list]) more_button.click(paper_manager.next_page, outputs=[paper_list]) # Bind the refresh button refresh_button = gr.Button(visible=False, elem_id="refresh-hidden") refresh_button.click(refresh_papers, outputs=[paper_list]) # JavaScript to bind the click event to the refresh button demo.load(None, _js=""" function() { document.getElementById('refresh-button').addEventListener('click', function() { document.querySelector('button#refresh-hidden').click(); }); } """) demo.launch()