Spaces:
Runtime error
Runtime error
| from pathlib import Path | |
| import gradio as gr | |
| from datetime import datetime, timedelta | |
| from fetch_paper import fetch_papers_with_daterange | |
| from sorter import sort_by_upvotes, sort_by_comments, sort_by_date | |
| from date import Date | |
| import assets | |
| def format_author(author): | |
| """Format author information""" | |
| if not author: | |
| return "" | |
| hidden_status = " (hidden)" if author.hidden else "" | |
| if author.name: | |
| return f"<a href='https://scholar.google.com/citations?view_op=search_authors&mauthors={author.name.replace(' ', '+')}' target='_blank'>{author.name}</a>" | |
| return "Anonymous author" | |
| def format_paper_info(article): | |
| """Generate HTML content for paper display""" | |
| if not article.paper: | |
| return "Paper information missing" | |
| info = [] | |
| # Title section | |
| info.append(f"<h2 class='sf-paper-title' id='sf-flag'>{article.title or 'Untitled Paper'}</h2>") | |
| info.append(f"<p style='text-align: center'>") | |
| info.append(f"<a href='https://huggingface.co/papers/{article.paper.id}' target='_blank' class='sf-button'>Hugging Face{assets.SVG_LINK}</a>") | |
| info.append(f"<a href='https://arxiv.org/abs/{article.paper.id}' target='_blank' class='sf-button'>arXiv{assets.SVG_LINK}</a>") | |
| info.append(f"<a href='https://arxiv.org/pdf/{article.paper.id}' target='_blank' class='sf-button'>PDF{assets.SVG_LINK}</a>") | |
| info.append(f"</p>") | |
| # Thumbnail | |
| if article.thumbnail: | |
| info.append(f"<p><img src='{article.thumbnail}' style='max-width: 30em; width: 100%; margin: auto'/></p>") | |
| # Basic information | |
| info.append( | |
| f"<p><strong>Paper ID</strong>: <a href='https://huggingface.co/papers/{article.paper.id}' target='_blank'>{article.paper.id or 'Unknown'}</a></p>") | |
| info.append( | |
| f"<p><strong>Published At</strong>: {article.paper.publishedAt.strftime('%Y-%m-%d %H:%M') if article.paper.publishedAt else 'Unknown'}</p>") | |
| # Author information | |
| authors = ", ".join([format_author(a) for a in article.paper.authors]) if article.paper.authors else "Author information not available" | |
| info.append(f"<p><strong>Authors</strong>: {authors}</p>") | |
| # Summary | |
| if article.paper.summary: | |
| summary = article.paper.summary.replace('{{', '{').replace('}}', '}').replace('\n', ' ') | |
| info.append(f"<h3>Summary</h3><p>{summary}</p>") | |
| # Discussion information | |
| info.append(f"<p><strong>Upvotes</strong>: {article.paper.upvotes or 0}<span style='margin-left: .5rem'></span>") | |
| info.append(f"<strong>Comments</strong>: {article.numComments or 0}</p>") | |
| if article.paper.discussionId: | |
| info.append( | |
| f"<a href='https://huggingface.co/papers/{article.paper.id}#community' target='_blank' class='sf-button'>Join Discussion{assets.SVG_LINK}</a></p>") | |
| # Submitter information | |
| if article.submittedBy: | |
| submitter = article.submittedBy | |
| info.append(f"<hr><p><strong>Submitter</strong>: ") | |
| avatar_url = submitter.avatarUrl if submitter.avatarUrl.startswith("http") else f"https://huggingface.co{submitter.avatarUrl}" | |
| profile_url = f"https://huggingface.co/{submitter.name}" | |
| info.append( | |
| f"<span><img src='{avatar_url}' class='sf-author' /></span>{submitter.fullname}(<a href='{profile_url}' target='_blank'>@{submitter.name}</a>) ") | |
| info.append(f"Followers: {submitter.followerCount or 0}</p>") | |
| return "".join(info) | |
| def generate_table_html(papers): | |
| """Generate table HTML with clickable titles and an extra column for arXiv abs link""" | |
| html = ['<table class="paper-table"><thead><tr>' | |
| '<th>Title</th>' | |
| '<th>👍 Upvotes</th>' | |
| '<th>💬 Comments</th>' | |
| '<th>📅 Date</th>' | |
| '<th></th>' | |
| '</tr></thead><tbody>'] | |
| for article in papers: | |
| title = article.title or "Untitled" | |
| upvotes = article.paper.upvotes or 0 | |
| comments = article.numComments or 0 | |
| date = article.paper.publishedAt.strftime("%Y-%m-%d") if article.paper.publishedAt else "Unknown" | |
| paper_id = article.paper.id | |
| # 构造 arXiv abs 链接 | |
| arxiv_abs_link = f"https://huggingface.co/papers/{paper_id}" | |
| row = f""" | |
| <tr> | |
| <td><a class="paper-title" href="{arxiv_abs_link}" target="_blank">{title}{assets.SVG_LINK}</a></td> | |
| <td>{upvotes}</td> | |
| <td>{comments}</td> | |
| <td>{date}</td> | |
| <td><a href="javascript:void(0)" onclick="showDetail('{paper_id}')" class="sf-button" style="margin: 0">Details{assets.SVG_LINK}</a></td> | |
| </tr> | |
| """ | |
| html.append(row) | |
| html.append("</tbody></table>") | |
| return "".join(html) | |
| def build_html(papers): | |
| # Convert all papers to an HTML string, each paper wrapped in a div, with the div containing the paper's information, and the div's id being the paper's id | |
| html = "" | |
| for index, article in enumerate(papers): | |
| article_html = format_paper_info(article) | |
| html += f"<div id='smartflow-paper-{article.paper.id.replace('.', '-')}' style='{'' if index == 0 else 'display: none'}'>{article_html}</div>" | |
| return html | |
| def query_papers(start_date_str: str, end_date_str: str, sort_method: str): # Added sort_method parameter | |
| """Handle date range query""" | |
| try: | |
| start_date = Date(start_date_str) | |
| end_date = Date(end_date_str) | |
| papers = fetch_papers_with_daterange(start_date, end_date) | |
| # Sort papers based on the selected sorting method | |
| if sort_method == "Sort by upvotes ascending": | |
| papers = sort_by_upvotes(papers, reverse=False) | |
| elif sort_method == "Sort by upvotes descending": | |
| papers = sort_by_upvotes(papers, reverse=True) | |
| elif sort_method == "Sort by comments ascending": | |
| papers = sort_by_comments(papers, reverse=False) | |
| elif sort_method == "Sort by comments descending": | |
| papers = sort_by_comments(papers, reverse=True) | |
| elif sort_method == "Sort by date ascending": | |
| papers = sort_by_date(papers, reverse=False) | |
| elif sort_method == "Sort by date descending": | |
| papers = sort_by_date(papers, reverse=True) | |
| return generate_table_html(papers), build_html(papers) | |
| except Exception as e: | |
| print(f"Query error: {e}") | |
| return "<p>⚠️ Query failed, please check the date format (YYYY-MM-DD)</p>", "<p>⚠️ Query failed, please check the date format (YYYY-MM-DD)</p>" | |
| def show_detail(paper_id, papers): | |
| """Show paper details""" | |
| if not papers: | |
| return "Please perform a query first" | |
| return build_html(papers) | |
| # CSS 样式(可放入单独文件) | |
| custom_css = """ | |
| .detail-area { margin-top: 20px; padding: 20px; border: 1px solid #ddd; border-radius: 5px; } | |
| .sf-paper-title { text-align: center; } | |
| img.sf-author { | |
| height: 1.3rem; | |
| border: 1px solid #000; | |
| vertical-align: middle; | |
| border-radius: 50%; | |
| display: inline; | |
| margin: 0 0.1rem; | |
| } | |
| #paper-detail-area { | |
| display: none; | |
| } | |
| #paper-detail-area:has(#sf-flag) { | |
| display: block; | |
| } | |
| #query-results-html { min-height: 100px; } | |
| """ | |
| # 遍历./css文件夹下的所有文件,将文件内容作为CSS样式添加到页面中 | |
| for css_file in Path("./css").glob("*.css"): | |
| with open(css_file, "r") as f: | |
| custom_css += "\n" + f.read() + "\n" | |
| custom_js = """""" | |
| # 遍历./css文件夹下的所有文件,将文件内容作为CSS样式添加到页面中 | |
| for js_file in Path("./js").glob("*.js"): | |
| with open(js_file, "r") as f: | |
| custom_js += "\n" + f.read() + "\n" | |
| def create_interface(): | |
| """Create a new interface layout""" | |
| with gr.Blocks(title="Hugging Face Daily Paper", css=custom_css, head=f"<script>{custom_js}</script>") as app: | |
| # Main interface | |
| gr.Markdown("# 📚 Hugging Face Daily Paper") | |
| # Query control area | |
| with gr.Row(): | |
| with gr.Column(): | |
| with gr.Row(): | |
| start_date = gr.Textbox(elem_id="start_date", label="Start Date", placeholder="YYYY-MM-DD", value=str(Date() + (-7))) | |
| end_date = gr.Textbox(elem_id="end_date", label="End Date", placeholder="YYYY-MM-DD", value=str(Date())) | |
| with gr.Column(): | |
| with gr.Row(): | |
| today_btn = gr.Button("Today") | |
| last_week_btn = gr.Button("Last Week") | |
| last_month_btn = gr.Button("Last Month") | |
| query_btn = gr.Button("🔍 Query", variant="primary", elem_id="query_button") | |
| with gr.Row(): | |
| # Add sorting method selection | |
| sort_method = gr.Radio( | |
| label="Sort Method", | |
| choices=[ | |
| "Sort by upvotes descending", | |
| "Sort by comments descending", | |
| "Sort by date descending", | |
| "Sort by upvotes ascending", | |
| "Sort by comments ascending", | |
| "Sort by date ascending", | |
| ], | |
| value="Sort by upvotes descending", | |
| ) | |
| # Results display area | |
| with gr.Column(visible=True): | |
| results_html = gr.HTML(label="Query Results", elem_id="query-results-html") | |
| # Paper details area | |
| with gr.Column(visible=True, elem_classes="detail-area", elem_id="paper-detail-area"): | |
| gr.Markdown("## Paper Details") | |
| detail_html = gr.HTML(elem_id="detail-html") | |
| # Event handling | |
| query_btn.click( | |
| fn=query_papers, | |
| inputs=[start_date, end_date, sort_method], | |
| outputs=[results_html, detail_html] | |
| ) | |
| sort_method.change( | |
| fn=query_papers, | |
| inputs=[start_date, end_date, sort_method], | |
| outputs=[results_html, detail_html] | |
| ) | |
| # Add button event handling | |
| today_btn.click( | |
| fn=lambda: (str(Date()), str(Date())), | |
| outputs=[start_date, end_date] | |
| ).then( | |
| fn=query_papers, | |
| inputs=[start_date, end_date, sort_method], | |
| outputs=[results_html, detail_html] | |
| ) | |
| last_week_btn.click( | |
| fn=lambda: (str(Date() + (-7)), str(Date())), | |
| outputs=[start_date, end_date] | |
| ).then( | |
| fn=query_papers, | |
| inputs=[start_date, end_date, sort_method], | |
| outputs=[results_html, detail_html] | |
| ) | |
| last_month_btn.click( | |
| fn=lambda: (str(Date() + (-30)), str(Date())), | |
| outputs=[start_date, end_date] | |
| ).then( | |
| fn=query_papers, | |
| inputs=[start_date, end_date, sort_method], | |
| outputs=[results_html, detail_html] | |
| ) | |
| return app | |