Spaces:
Sleeping
Sleeping
import gradio as gr | |
from datetime import datetime, timedelta | |
from fetch_paper import fetch_papers, fetch_papers_with_daterange | |
from sorter import sort_by_upvotes | |
def format_author(author): | |
"""格式化作者信息""" | |
if not author: | |
return "" | |
hidden_status = "(隐藏)" if author.hidden else "" | |
if author.name: | |
return f"<a href='https://scholar.google.com/citations?view_op=search_authors&mauthors={author.name.replace(' ', '+')}'>{author.name}</a>{hidden_status}" | |
return f"匿名作者{hidden_status}" | |
def format_paper_info(article): | |
"""生成论文展示的 HTML 内容""" | |
if not article.paper: | |
return "论文信息缺失" | |
info = [] | |
# 标题部分 | |
info.append(f"<h2>{article.title or '无标题论文'}</h2>") | |
# 缩略图 | |
if article.thumbnail: | |
info.append(f"<p><img src='{article.thumbnail}' style='max-width: 30em; width: 100%; margin: auto'/></p>") | |
# 基本信息 | |
info.append(f"<p><strong>论文 ID</strong>:<a href='https://huggingface.co/papers/{article.paper.id}'>{article.paper.id or '未知'}</a></p>") | |
info.append(f"<p><strong>发布时间</strong>:{article.paper.publishedAt.strftime('%Y-%m-%d %H:%M') if article.paper.publishedAt else '未知'}</p>") | |
# 作者信息 | |
authors = "、".join([format_author(a) for a in article.paper.authors]) if article.paper.authors else "作者信息暂缺" | |
info.append(f"<p><strong>作者</strong>:{authors}</p>") | |
# 摘要 | |
if article.paper.summary: | |
summary = article.paper.summary.replace('{{', '{').replace('}}', '}').replace('\n', ' ') | |
info.append(f"<h3>摘要</h3><p>{summary}</p>") | |
# 讨论信息 | |
info.append(f"<p><strong>点赞数</strong>:{article.paper.upvotes or 0}<span style='margin-left: .5rem'></span>") | |
info.append(f"<strong>评论数</strong>:{article.numComments or 0}</p>") | |
if article.paper.discussionId: | |
info.append(f"<a href='https://huggingface.co/papers/{article.paper.id}/discussion/{article.paper.discussionId}'>进入讨论</a></p>") | |
# 提交者信息 | |
if article.submittedBy: | |
submitter = article.submittedBy | |
info.append(f"<hr><p><strong>提交者</strong>: ") | |
info.append( | |
f"<span><img src='{submitter.avatarUrl}' class='author' /></span>{submitter.fullname}(<a href='https://huggingface.co/{submitter.name}'>@{submitter.name}</a>) ") | |
info.append(f"粉丝数:{submitter.followerCount or 0}</p>") | |
return "".join(info) | |
def generate_table_html(papers): | |
"""生成带可点击标题的表格 HTML""" | |
html = ['<table class="paper-table"><tr><th>标题</th><th>👍点赞</th><th>💬评论</th><th>📅日期</th></tr>'] | |
for article in papers: | |
title = article.title or "无标题" | |
upvotes = article.paper.upvotes or 0 | |
comments = article.numComments or 0 | |
date = article.paper.publishedAt.strftime("%Y-%m-%d") if article.paper.publishedAt else "未知" | |
paper_id = article.paper.id | |
row = f""" | |
<tr> | |
<td><a class="paper-title" href="javascript:void(0)" onclick="showDetail('{paper_id}')">{title}</a></td> | |
<td>{upvotes}</td> | |
<td>{comments}</td> | |
<td>{date}</td> | |
</tr> | |
""" | |
html.append(row) | |
html.append("</table>") | |
return "".join(html) | |
def build_html(papers): | |
# 将所有的papers转换为一个html字符串,每个paper用一个div包裹,div内部包含paper的信息,div的id为paper的id | |
html = "" | |
for article in papers: | |
article_html = format_paper_info(article) | |
html += f"<div id='smartflow-paper-{article.paper.id.replace('.', '-')}' style='display: none'>{article_html}</div>" | |
return html | |
def query_papers(start_date_str, end_date_str): | |
"""处理日期查询""" | |
try: | |
start_date = datetime.strptime(start_date_str, "%Y-%m-%d") | |
end_date = datetime.strptime(end_date_str, "%Y-%m-%d") | |
papers = fetch_papers_with_daterange(start_date, end_date) | |
papers = sort_by_upvotes(papers) | |
return generate_table_html(papers), build_html(papers) | |
except Exception as e: | |
print(f"查询出错: {e}") | |
return "<p>⚠️ 查询失败,请检查日期格式(YYYY-MM-DD)</p>", "<p>⚠️ 查询失败,请检查日期格式(YYYY-MM-DD)</p>" | |
def show_detail(paper_id, papers): | |
"""显示论文详情""" | |
if not papers: | |
return "请先进行查询" | |
return build_html(papers) | |
# CSS 样式(可放入单独文件) | |
custom_css = """ | |
.paper-table { width: 100%; border-collapse: collapse; } | |
.paper-table td { padding: 12px; border-bottom: 1px solid #ddd; } | |
.paper-table th { font-weight: bold; background: #f9f9f920; } | |
.paper-table tr:hover { background: #f9f9f920; } | |
.paper-title { color: #1a73e8; cursor: pointer; text-decoration: none !important; } | |
.paper-title:hover { text-decoration: underline !important; } | |
.paper-table td:nth-child(2), .paper-table td:nth-child(3), .paper-table td:nth-child(4) { text-align: center; } | |
.paper-table th:nth-child(2), .paper-table th:nth-child(3), .paper-table th:nth-child(4) { text-align: center; } | |
.detail-area { margin-top: 20px; padding: 20px; border: 1px solid #ddd; border-radius: 5px; } | |
""" | |
custom_js = """ | |
function showDetail(paperId) { | |
// 隐藏 smartflow-paper-paperId 的所有兄弟节点 | |
var siblings = document.querySelectorAll(`div[id^='smartflow-paper-']:not(#smartflow-paper-${paperId.replace('.', '-')})`); | |
siblings.forEach(sibling => sibling.style.display = 'none'); | |
// 显示当前节点 | |
var paper = document.getElementById(`smartflow-paper-${paperId.replace('.', '-')}`); | |
if (paper) { | |
paper.style.display = 'block'; | |
} | |
} | |
""" | |
def create_interface(): | |
"""创建新的界面布局""" | |
with gr.Blocks(title="Hugging Face Daily Paper", css=custom_css, head=f"<script>{custom_js}</script>") as app: | |
# 主界面 | |
gr.Markdown("# 📚 Hugging Face Daily Paper") | |
# 查询控制区 | |
with gr.Row(): | |
start_date = gr.Textbox(label="起始日期", placeholder="YYYY-MM-DD", value=datetime.now().strftime("%Y-%m-%d")) | |
end_date = gr.Textbox(label="结束日期", placeholder="YYYY-MM-DD", value=datetime.now().strftime("%Y-%m-%d")) | |
query_btn = gr.Button("🔍 查询", variant="primary") | |
# 结果显示区 | |
with gr.Column(visible=True): | |
results_html = gr.HTML(label="查询结果") | |
# 论文详情区 | |
with gr.Column(visible=True, elem_classes="detail-area"): | |
gr.Markdown("## 论文详情") | |
detail_html = gr.HTML(elem_id="detail-html") | |
# 事件处理 | |
query_btn.click( | |
fn=query_papers, | |
inputs=[start_date, end_date], | |
outputs=[results_html, detail_html] | |
) | |
return app | |
if __name__ == "__main__": | |
gr.close_all() | |
app = create_interface() | |
app.launch( | |
# server_name="localhost", | |
# server_port=7860, | |
# share=True | |
) | |