Spaces:
Sleeping
Sleeping
Commit
·
2364a33
1
Parent(s):
41d7482
Add references in PDF report with IEEE formatter
Browse files- routes/reports.py +12 -2
- static/script.js +4 -0
- utils/service/pdf.py +77 -1
routes/reports.py
CHANGED
|
@@ -152,12 +152,22 @@ async def generate_report(
|
|
| 152 |
async def generate_report_pdf(
|
| 153 |
user_id: str = Form(...),
|
| 154 |
project_id: str = Form(...),
|
| 155 |
-
report_content: str = Form(...)
|
|
|
|
| 156 |
):
|
| 157 |
from utils.service.pdf import generate_report_pdf as generate_pdf
|
| 158 |
from fastapi.responses import Response
|
|
|
|
| 159 |
try:
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
return Response(
|
| 162 |
content=pdf_content,
|
| 163 |
media_type="application/pdf",
|
|
|
|
| 152 |
async def generate_report_pdf(
|
| 153 |
user_id: str = Form(...),
|
| 154 |
project_id: str = Form(...),
|
| 155 |
+
report_content: str = Form(...),
|
| 156 |
+
sources: str = Form("[]")
|
| 157 |
):
|
| 158 |
from utils.service.pdf import generate_report_pdf as generate_pdf
|
| 159 |
from fastapi.responses import Response
|
| 160 |
+
import json
|
| 161 |
try:
|
| 162 |
+
# Parse sources JSON
|
| 163 |
+
sources_list = []
|
| 164 |
+
if sources and sources != "[]":
|
| 165 |
+
try:
|
| 166 |
+
sources_list = json.loads(sources)
|
| 167 |
+
except json.JSONDecodeError:
|
| 168 |
+
logger.warning(f"[REPORT] Failed to parse sources JSON: {sources}")
|
| 169 |
+
|
| 170 |
+
pdf_content = await generate_pdf(report_content, user_id, project_id, sources_list)
|
| 171 |
return Response(
|
| 172 |
content=pdf_content,
|
| 173 |
media_type="application/pdf",
|
static/script.js
CHANGED
|
@@ -859,10 +859,14 @@
|
|
| 859 |
`;
|
| 860 |
|
| 861 |
try {
|
|
|
|
|
|
|
|
|
|
| 862 |
const formData = new FormData();
|
| 863 |
formData.append('user_id', user.user_id);
|
| 864 |
formData.append('project_id', currentProject.project_id);
|
| 865 |
formData.append('report_content', reportContent);
|
|
|
|
| 866 |
|
| 867 |
const response = await fetch('/report/pdf', {
|
| 868 |
method: 'POST',
|
|
|
|
| 859 |
`;
|
| 860 |
|
| 861 |
try {
|
| 862 |
+
// Find sources from the current message or recent sources
|
| 863 |
+
const sources = findCurrentSources();
|
| 864 |
+
|
| 865 |
const formData = new FormData();
|
| 866 |
formData.append('user_id', user.user_id);
|
| 867 |
formData.append('project_id', currentProject.project_id);
|
| 868 |
formData.append('report_content', reportContent);
|
| 869 |
+
formData.append('sources', JSON.stringify(sources));
|
| 870 |
|
| 871 |
const response = await fetch('/report/pdf', {
|
| 872 |
method: 'POST',
|
utils/service/pdf.py
CHANGED
|
@@ -6,6 +6,7 @@ import tempfile
|
|
| 6 |
import markdown
|
| 7 |
import re
|
| 8 |
from datetime import datetime
|
|
|
|
| 9 |
from fastapi import HTTPException
|
| 10 |
from utils.logger import get_logger
|
| 11 |
|
|
@@ -507,7 +508,70 @@ def _apply_syntax_highlight(escaped_code: str, language: str) -> str:
|
|
| 507 |
|
| 508 |
return out
|
| 509 |
|
| 510 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 511 |
"""
|
| 512 |
Generate a PDF from report content using reportlab
|
| 513 |
|
|
@@ -623,6 +687,18 @@ async def generate_report_pdf(report_content: str, user_id: str, project_id: str
|
|
| 623 |
# Enhanced markdown parser with proper formatting
|
| 624 |
story.extend(_parse_markdown_content(report_content, heading1_style, heading2_style, heading3_style, normal_style, code_style))
|
| 625 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 626 |
# Build PDF
|
| 627 |
doc.build(story)
|
| 628 |
|
|
|
|
| 6 |
import markdown
|
| 7 |
import re
|
| 8 |
from datetime import datetime
|
| 9 |
+
from typing import List, Dict
|
| 10 |
from fastapi import HTTPException
|
| 11 |
from utils.logger import get_logger
|
| 12 |
|
|
|
|
| 508 |
|
| 509 |
return out
|
| 510 |
|
| 511 |
+
|
| 512 |
+
async def _format_references_ieee(sources: List[Dict]) -> List[str]:
|
| 513 |
+
"""Format sources in IEEE citation style using NVIDIA API."""
|
| 514 |
+
try:
|
| 515 |
+
from utils.api.router import generate_answer_with_model
|
| 516 |
+
from helpers.setup import nvidia_rotator
|
| 517 |
+
|
| 518 |
+
if not sources or not nvidia_rotator:
|
| 519 |
+
return []
|
| 520 |
+
|
| 521 |
+
# Prepare source data for formatting
|
| 522 |
+
source_data = []
|
| 523 |
+
for i, source in enumerate(sources, 1):
|
| 524 |
+
source_info = {
|
| 525 |
+
"number": i,
|
| 526 |
+
"filename": source.get("filename", "Unknown"),
|
| 527 |
+
"url": source.get("url", ""),
|
| 528 |
+
"topic_name": source.get("topic_name", ""),
|
| 529 |
+
"kind": source.get("kind", "document")
|
| 530 |
+
}
|
| 531 |
+
source_data.append(source_info)
|
| 532 |
+
|
| 533 |
+
sys_prompt = """You are an expert at formatting academic references in IEEE style.
|
| 534 |
+
Format the provided sources as IEEE-style references. Each reference should be numbered and formatted according to IEEE standards.
|
| 535 |
+
|
| 536 |
+
For web sources: [1] Author/Organization, "Title," Website Name, URL, accessed: Date.
|
| 537 |
+
For documents: [1] Author, "Title," Document Type, Filename, Year.
|
| 538 |
+
|
| 539 |
+
Return only the formatted references, one per line, numbered sequentially."""
|
| 540 |
+
|
| 541 |
+
user_prompt = f"Format these sources in IEEE style:\n\n{source_data}"
|
| 542 |
+
|
| 543 |
+
selection = {"provider": "nvidia", "model": "meta/llama-3.1-8b-instruct"}
|
| 544 |
+
response = await generate_answer_with_model(selection, sys_prompt, user_prompt, None, nvidia_rotator)
|
| 545 |
+
|
| 546 |
+
# Parse the response into individual references
|
| 547 |
+
references = [line.strip() for line in response.split('\n') if line.strip() and line.strip().startswith('[')]
|
| 548 |
+
|
| 549 |
+
# If NVIDIA formatting fails, create basic IEEE format
|
| 550 |
+
if not references:
|
| 551 |
+
references = []
|
| 552 |
+
for i, source in enumerate(sources, 1):
|
| 553 |
+
if source.get("kind") == "web":
|
| 554 |
+
ref = f"[{i}] {source.get('topic_name', 'Unknown')}, \"{source.get('filename', 'Web Source')}\", {source.get('url', '')}, accessed: {datetime.now().strftime('%B %d, %Y')}."
|
| 555 |
+
else:
|
| 556 |
+
ref = f"[{i}] {source.get('topic_name', 'Unknown')}, \"{source.get('filename', 'Document')}\", Document, {datetime.now().year}."
|
| 557 |
+
references.append(ref)
|
| 558 |
+
|
| 559 |
+
return references
|
| 560 |
+
|
| 561 |
+
except Exception as e:
|
| 562 |
+
logger.warning(f"[PDF] IEEE reference formatting failed: {e}")
|
| 563 |
+
# Fallback to basic formatting
|
| 564 |
+
references = []
|
| 565 |
+
for i, source in enumerate(sources, 1):
|
| 566 |
+
if source.get("kind") == "web":
|
| 567 |
+
ref = f"[{i}] {source.get('topic_name', 'Unknown')}, \"{source.get('filename', 'Web Source')}\", {source.get('url', '')}, accessed: {datetime.now().strftime('%B %d, %Y')}."
|
| 568 |
+
else:
|
| 569 |
+
ref = f"[{i}] {source.get('topic_name', 'Unknown')}, \"{source.get('filename', 'Document')}\", Document, {datetime.now().year}."
|
| 570 |
+
references.append(ref)
|
| 571 |
+
return references
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
async def generate_report_pdf(report_content: str, user_id: str, project_id: str, sources: List[Dict] = None) -> bytes:
|
| 575 |
"""
|
| 576 |
Generate a PDF from report content using reportlab
|
| 577 |
|
|
|
|
| 687 |
# Enhanced markdown parser with proper formatting
|
| 688 |
story.extend(_parse_markdown_content(report_content, heading1_style, heading2_style, heading3_style, normal_style, code_style))
|
| 689 |
|
| 690 |
+
# Add references section if sources provided
|
| 691 |
+
if sources:
|
| 692 |
+
story.append(PageBreak())
|
| 693 |
+
story.append(Paragraph("References", heading1_style))
|
| 694 |
+
story.append(Spacer(1, 12))
|
| 695 |
+
|
| 696 |
+
# Format references in IEEE style using NVIDIA API
|
| 697 |
+
ieee_references = await _format_references_ieee(sources)
|
| 698 |
+
for ref in ieee_references:
|
| 699 |
+
story.append(Paragraph(ref, normal_style))
|
| 700 |
+
story.append(Spacer(1, 6))
|
| 701 |
+
|
| 702 |
# Build PDF
|
| 703 |
doc.build(story)
|
| 704 |
|