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=10): 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=50") response.raise_for_status() data = response.json() self.papers = sorted(data, key=lambda x: x.get('paper', {}).get('upvotes', 0), reverse=True) self.total_pages = (len(self.papers) + self.papers_per_page - 1) // self.papers_per_page self.current_page = 1 return True except requests.RequestException as e: print(f"Error fetching papers: {e}") return False def format_paper(self, paper): title = paper.get('title', 'No title') url = f"https://huggingface.co/papers/{paper['paper'].get('id', '')}" authors = ', '.join([author.get('name', '') for author in paper['paper'].get('authors', [])]) 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_ago = (datetime.now(timezone.utc) - published_time).days return f"""

{title}

{upvotes} ▲ {authors} {time_ago}d {comments}💬

""" 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 "
No papers available for this page.
" return "".join([self.format_paper(paper) for paper in current_papers]) def search_papers(self, query): if not query: self.current_page = 1 return self.render_papers() filtered_papers = [paper for paper in self.papers if query.lower() in paper.get('title', '').lower()] return "".join([self.format_paper(paper) for paper in filtered_papers[:self.papers_per_page]]) def next_page(self): if self.current_page < self.total_pages: self.current_page += 1 return self.render_papers(), f"Page {self.current_page} of {self.total_pages}" def prev_page(self): if self.current_page > 1: self.current_page -= 1 return self.render_papers(), f"Page {self.current_page} of {self.total_pages}" css = """ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; background-color: #f0f2f5; margin: 0; padding: 0; } .container { max-width: 100%; margin: 0 auto; background-color: white; padding: 1rem; } .title { text-align: center; color: #1a202c; font-size: 1.5rem; margin-bottom: 1rem; } .search-row { display: flex; gap: 0.5rem; margin-bottom: 1rem; align-items: flex-end; } .search-row > div:first-child { flex-grow: 1; } .paper-list { max-height: 70vh; overflow-y: auto; border: 1px solid #e2e8f0; border-radius: 8px; padding: 0.5rem; } .paper-item { border-bottom: 1px solid #e2e8f0; padding: 0.5rem 0; } .paper-item:last-child { border-bottom: none; } .paper-item h3 { margin: 0 0 0.25rem 0; font-size: 1rem; } .paper-item a { color: #2b6cb0; text-decoration: none; font-weight: 600; } .paper-item a:hover { text-decoration: underline; } .paper-meta { font-size: 0.75rem; color: #4a5568; display: flex; flex-wrap: wrap; gap: 0.5rem; } .footer { display: flex; justify-content: space-between; align-items: center; margin-top: 0.5rem; } button, .button { background-color: #4299e1; color: white; border: none; padding: 0.25rem 0.5rem; border-radius: 4px; cursor: pointer; transition: background-color 0.3s; font-size: 0.875rem; line-height: 1.25rem; } button:hover, .button:hover { background-color: #3182ce; } .no-papers { text-align: center; color: #718096; padding: 1rem; } #component-0 > div:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; } #component-1 { height: 45px; border-top-left-radius: 0; border-bottom-left-radius: 0; } @media (min-width: 640px) { .container { max-width: 640px; padding: 1.5rem; } .title { font-size: 1.75rem; } .paper-item h3 { font-size: 1.125rem; } .paper-meta { font-size: 0.875rem; } } @media (min-width: 768px) { .container { max-width: 768px; } } """ paper_manager = PaperManager() def initialize_app(): if paper_manager.fetch_papers(): prev_disabled = paper_manager.current_page == 1 next_disabled = paper_manager.current_page >= paper_manager.total_pages return ( paper_manager.render_papers(), f"Page {paper_manager.current_page} of {paper_manager.total_pages}", prev_disabled, next_disabled ) else: return "
Failed to fetch papers. Please try again later.
", "Error", True, True def refresh_papers(): if paper_manager.fetch_papers(): prev_disabled = paper_manager.current_page == 1 next_disabled = paper_manager.current_page >= paper_manager.total_pages return ( paper_manager.render_papers(), f"Page {paper_manager.current_page} of {paper_manager.total_pages}", prev_disabled, next_disabled ) else: return "
Failed to refresh papers. Please try again later.
", "Error", True, True demo = gr.Blocks(css=css) with demo: with gr.Column(elem_classes=["container"]): gr.Markdown("# Daily Papers", elem_classes=["title"]) with gr.Row(elem_classes=["search-row"]): search_input = gr.Textbox(label="", placeholder="Search papers...") refresh_button = gr.Button("↻", variant="primary") paper_list = gr.HTML(elem_classes=["paper-list"]) with gr.Row(elem_classes=["footer"]): prev_button = gr.Button("← Prev", interactive=True) page_info = gr.Markdown() next_button = gr.Button("Next →", interactive=True) demo.load(initialize_app, outputs=[paper_list, page_info, prev_button, next_button]) search_input.change(paper_manager.search_papers, inputs=[search_input], outputs=[paper_list]) refresh_button.click(refresh_papers, outputs=[paper_list, page_info, prev_button, next_button]) prev_button.click(paper_manager.prev_page, outputs=[paper_list, page_info, prev_button, next_button]) next_button.click(paper_manager.next_page, outputs=[paper_list, page_info, prev_button, next_button]) demo.launch()