|
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 = [] |
|
|
|
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>") |
|
|
|
|
|
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>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>") |
|
|
|
|
|
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>") |
|
|
|
|
|
if article.paper.summary: |
|
summary = article.paper.summary.replace('{{', '{').replace('}}', '}').replace('\n', ' ') |
|
info.append(f"<h3>Summary</h3><p>{summary}</p>") |
|
|
|
|
|
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>") |
|
|
|
|
|
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_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): |
|
|
|
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): |
|
"""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) |
|
|
|
|
|
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>" |
|
|
|
|
|
|
|
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; } |
|
""" |
|
|
|
|
|
for css_file in Path("./css").glob("*.css"): |
|
with open(css_file, "r") as f: |
|
custom_css += "\n" + f.read() + "\n" |
|
|
|
custom_js = """""" |
|
|
|
|
|
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: |
|
|
|
|
|
gr.HTML("<div style='text-align: center'><h1>📚 Hugging Face Weekly Paper</h1></div>") |
|
gr.HTML("""<div style='text-align: center'> |
|
<span style=""> |
|
🔗 <a href='https://larkcommunity.feishu.cn/wiki/HSSTwsq7JiDur0kJrvjcnFZfnTe'>Contribute to this project</a> |
|
</span> |
|
<span style="margin-left: 1rem"> |
|
Sponsor: <a href='https://internlm.intern-ai.org.cn/api/document'>InternLM</a> |
|
</span> |
|
</div>""") |
|
|
|
|
|
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(): |
|
|
|
|
|
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", |
|
) |
|
|
|
|
|
with gr.Column(visible=True): |
|
results_html = gr.HTML(label="Query Results", elem_id="query-results-html") |
|
|
|
|
|
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") |
|
|
|
|
|
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] |
|
) |
|
|
|
|
|
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 |
|
|