优化pdf下载
This commit is contained in:
parent
66f4914c5a
commit
30e954d89f
@ -236,7 +236,10 @@ class ExportPDFRequest(BaseModel):
|
|||||||
async def export_chat_pdf(request: ExportPDFRequest, db: AsyncSession = Depends(get_db)):
|
async def export_chat_pdf(request: ExportPDFRequest, db: AsyncSession = Depends(get_db)):
|
||||||
"""Export selected chat sessions to PDF"""
|
"""Export selected chat sessions to PDF"""
|
||||||
from sqlalchemy import select, desc, asc
|
from sqlalchemy import select, desc, asc
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
|
# Timezone definition (Shanghai)
|
||||||
|
shanghai_tz = timezone(timedelta(hours=8))
|
||||||
|
|
||||||
if not request.session_ids:
|
if not request.session_ids:
|
||||||
raise HTTPException(status_code=400, detail="No sessions selected")
|
raise HTTPException(status_code=400, detail="No sessions selected")
|
||||||
@ -260,7 +263,13 @@ async def export_chat_pdf(request: ExportPDFRequest, db: AsyncSession = Depends(
|
|||||||
|
|
||||||
messages_html = ""
|
messages_html = ""
|
||||||
for log in logs:
|
for log in logs:
|
||||||
timestamp = log.timestamp.strftime('%Y-%m-%d %H:%M:%S') if log.timestamp else ""
|
# Convert timestamp to Shanghai time
|
||||||
|
if log.timestamp:
|
||||||
|
ts_shanghai = log.timestamp.astimezone(shanghai_tz) if log.timestamp.tzinfo else log.timestamp.replace(tzinfo=timezone.utc).astimezone(shanghai_tz)
|
||||||
|
timestamp = ts_shanghai.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
else:
|
||||||
|
timestamp = ""
|
||||||
|
|
||||||
response_html = markdown.markdown(log.response or "", extensions=['tables', 'fenced_code'])
|
response_html = markdown.markdown(log.response or "", extensions=['tables', 'fenced_code'])
|
||||||
|
|
||||||
# Add context info (Stock Code / Session) to the header since they are mixed
|
# Add context info (Stock Code / Session) to the header since they are mixed
|
||||||
@ -275,7 +284,6 @@ async def export_chat_pdf(request: ExportPDFRequest, db: AsyncSession = Depends(
|
|||||||
</div>
|
</div>
|
||||||
<div class="message-sub-meta">
|
<div class="message-sub-meta">
|
||||||
<span class="session-id">Session: {session_info}</span>
|
<span class="session-id">Session: {session_info}</span>
|
||||||
<span class="model">Model: {log.model}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="message-content">
|
<div class="message-content">
|
||||||
{response_html}
|
{response_html}
|
||||||
@ -284,6 +292,8 @@ async def export_chat_pdf(request: ExportPDFRequest, db: AsyncSession = Depends(
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
# Complete HTML
|
# Complete HTML
|
||||||
|
now_shanghai = datetime.now(shanghai_tz)
|
||||||
|
|
||||||
# Updated font-family stack to include common Linux/Windows CJK fonts
|
# Updated font-family stack to include common Linux/Windows CJK fonts
|
||||||
complete_html = f'''
|
complete_html = f'''
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@ -383,7 +393,7 @@ async def export_chat_pdf(request: ExportPDFRequest, db: AsyncSession = Depends(
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>AI 研究讨论导出</h1>
|
<h1>AI 研究讨论导出</h1>
|
||||||
<p style="text-align: center; color: #666; margin-bottom: 30px;">导出时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
<p style="text-align: center; color: #666; margin-bottom: 30px;">导出时间: {now_shanghai.strftime('%Y-%m-%d %H:%M:%S')}</p>
|
||||||
{messages_html}
|
{messages_html}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -395,7 +405,13 @@ async def export_chat_pdf(request: ExportPDFRequest, db: AsyncSession = Depends(
|
|||||||
pdf_path = tmp_file.name
|
pdf_path = tmp_file.name
|
||||||
HTML(string=complete_html).write_pdf(pdf_path)
|
HTML(string=complete_html).write_pdf(pdf_path)
|
||||||
|
|
||||||
filename = f"AI研究讨论汇总_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
|
# Determine filename prefix based on the most common stock code in logs
|
||||||
|
# or just the first one found
|
||||||
|
company_name = "AI研究讨论"
|
||||||
|
if logs and logs[0].stock_code:
|
||||||
|
company_name = logs[0].stock_code
|
||||||
|
|
||||||
|
filename = f"{company_name}_{now_shanghai.strftime('%Y%m%d')}_讨论记录.pdf"
|
||||||
filename_encoded = quote(filename)
|
filename_encoded = quote(filename)
|
||||||
|
|
||||||
return FileResponse(
|
return FileResponse(
|
||||||
|
|||||||
@ -343,6 +343,17 @@ async def download_report_pdf(report_id: int, db: AsyncSession = Depends(get_db)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Complete PDF HTML
|
# Complete PDF HTML
|
||||||
|
# Timezone conversion for display
|
||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
shanghai_tz = timezone(timedelta(hours=8))
|
||||||
|
|
||||||
|
# Use report creation time for the report date in PDF
|
||||||
|
report_time = report.created_at.astimezone(shanghai_tz) if report.created_at.tzinfo else report.created_at.replace(tzinfo=timezone.utc).astimezone(shanghai_tz)
|
||||||
|
|
||||||
|
# Current time for filename (download date)
|
||||||
|
now_shanghai = datetime.now(shanghai_tz)
|
||||||
|
download_date_str = now_shanghai.strftime('%Y%m%d')
|
||||||
|
|
||||||
complete_html = f"""
|
complete_html = f"""
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@ -515,8 +526,7 @@ async def download_report_pdf(report_id: int, db: AsyncSession = Depends(get_db)
|
|||||||
<div class="cover">
|
<div class="cover">
|
||||||
<h1>{report.company_name}</h1>
|
<h1>{report.company_name}</h1>
|
||||||
<div class="meta">{report.market} {report.symbol}</div>
|
<div class="meta">{report.market} {report.symbol}</div>
|
||||||
<div class="meta">分析日期: {report.created_at.strftime('%Y年%m月%d日')}</div>
|
<div class="meta">分析日期: {report_time.strftime('%Y年%m月%d日')}</div>
|
||||||
{f'<div class="meta">AI模型: {report.ai_model}</div>' if report.ai_model else ''}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
@ -538,7 +548,8 @@ async def download_report_pdf(report_id: int, db: AsyncSession = Depends(get_db)
|
|||||||
HTML(string=complete_html).write_pdf(pdf_path)
|
HTML(string=complete_html).write_pdf(pdf_path)
|
||||||
|
|
||||||
# Return the PDF file
|
# Return the PDF file
|
||||||
filename = f"{report.company_name}_{report.symbol}_分析报告.pdf"
|
# Filename format: Company_Symbol_Date_Report.pdf
|
||||||
|
filename = f"{report.company_name}_{report.symbol}_{download_date_str}_分析报告.pdf"
|
||||||
# Use RFC 5987 encoding for non-ASCII filenames
|
# Use RFC 5987 encoding for non-ASCII filenames
|
||||||
filename_encoded = quote(filename)
|
filename_encoded = quote(filename)
|
||||||
|
|
||||||
|
|||||||
@ -75,7 +75,14 @@ export default function AnalysisPage({ params }: { params: Promise<{ id: string
|
|||||||
const url = window.URL.createObjectURL(blob)
|
const url = window.URL.createObjectURL(blob)
|
||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = url
|
a.href = url
|
||||||
a.download = `${report.company_name}_${report.symbol}_分析报告.pdf`
|
|
||||||
|
// Format date as YYYYMMDD to match backend
|
||||||
|
const date = new Date()
|
||||||
|
const dateStr = date.getFullYear() +
|
||||||
|
String(date.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(date.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
|
a.download = `${report.company_name}_${report.symbol}_${dateStr}_分析报告.pdf`
|
||||||
document.body.appendChild(a)
|
document.body.appendChild(a)
|
||||||
a.click()
|
a.click()
|
||||||
window.URL.revokeObjectURL(url)
|
window.URL.revokeObjectURL(url)
|
||||||
|
|||||||
@ -250,11 +250,30 @@ export function HistoryView() {
|
|||||||
body: JSON.stringify({ session_ids: Array.from(selectedForExport) })
|
body: JSON.stringify({ session_ids: Array.from(selectedForExport) })
|
||||||
})
|
})
|
||||||
if (!response.ok) throw new Error("导出失败")
|
if (!response.ok) throw new Error("导出失败")
|
||||||
|
|
||||||
|
// Try to get filename from content-disposition
|
||||||
|
let filename = `讨论历史_${new Date().toISOString().slice(0, 10)}.pdf`
|
||||||
|
const disposition = response.headers.get('content-disposition')
|
||||||
|
if (disposition && disposition.indexOf('attachment') !== -1) {
|
||||||
|
const filenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:;|$)|filename="([^"]+)"(?:;|$)|filename=([^;]+)(?:;|$)/i;
|
||||||
|
const matches = filenameRegex.exec(disposition);
|
||||||
|
if (matches) {
|
||||||
|
// Try decoding the UTF-8 encoded filename
|
||||||
|
try {
|
||||||
|
if (matches[1]) filename = decodeURIComponent(matches[1]);
|
||||||
|
else if (matches[2]) filename = matches[2];
|
||||||
|
else if (matches[3]) filename = matches[3];
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error decoding filename:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const blob = await response.blob()
|
const blob = await response.blob()
|
||||||
const url = window.URL.createObjectURL(blob)
|
const url = window.URL.createObjectURL(blob)
|
||||||
const a = document.createElement("a")
|
const a = document.createElement("a")
|
||||||
a.href = url
|
a.href = url
|
||||||
a.download = `讨论历史_${new Date().toISOString().slice(0, 10)}.pdf`
|
a.download = filename
|
||||||
document.body.appendChild(a)
|
document.body.appendChild(a)
|
||||||
a.click()
|
a.click()
|
||||||
document.body.removeChild(a)
|
document.body.removeChild(a)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user