|
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): |
|
|
|
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) |
|
|
|
|
|
|
|
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( |
|
|
|
|
|
|
|
) |
|
|