from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch, cm
from reportlab.platypus import (
SimpleDocTemplate,
Paragraph,
Spacer,
Table,
TableStyle,
PageBreak,
HRFlowable,
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.pdfgen import canvas
from datetime import datetime
import os
class AnalysisReport:
def __init__(self, output_path):
self.output_path = output_path
self.doc = SimpleDocTemplate(
output_path,
pagesize=A4,
rightMargin=2 * cm,
leftMargin=2 * cm,
topMargin=2 * cm,
bottomMargin=2 * cm,
)
self.styles = getSampleStyleSheet()
self.story = []
self._setup_styles()
def _setup_styles(self):
self.title_style = ParagraphStyle(
"CustomTitle",
parent=self.styles["Title"],
fontSize=24,
textColor=colors.HexColor("#0B0F1A"),
spaceAfter=30,
alignment=TA_CENTER,
)
self.heading1_style = ParagraphStyle(
"CustomHeading1",
parent=self.styles["Heading1"],
fontSize=16,
textColor=colors.HexColor("#1E3A5F"),
spaceBefore=20,
spaceAfter=12,
borderPadding=5,
)
self.heading2_style = ParagraphStyle(
"CustomHeading2",
parent=self.styles["Heading2"],
fontSize=13,
textColor=colors.HexColor("#2563EB"),
spaceBefore=15,
spaceAfter=8,
)
self.body_style = ParagraphStyle(
"CustomBody",
parent=self.styles["Normal"],
fontSize=10,
leading=14,
alignment=TA_JUSTIFY,
spaceAfter=8,
)
self.bullet_style = ParagraphStyle(
"BulletStyle",
parent=self.styles["Normal"],
fontSize=10,
leading=14,
leftIndent=20,
spaceAfter=4,
)
def add_header(self):
self.story.append(Paragraph("CitationEdge", self.title_style))
self.story.append(
Paragraph(
"Agentic Architecture Analysis Report",
ParagraphStyle(
"Subtitle",
parent=self.styles["Normal"],
fontSize=14,
textColor=colors.HexColor("#64748B"),
alignment=TA_CENTER,
spaceAfter=5,
),
)
)
self.story.append(
Paragraph(
f"Generated: {datetime.now().strftime('%B %d, %Y')}",
ParagraphStyle(
"Date",
parent=self.styles["Normal"],
fontSize=10,
textColor=colors.HexColor("#94A3B8"),
alignment=TA_CENTER,
spaceAfter=30,
),
)
)
self.story.append(
HRFlowable(
width="100%",
thickness=2,
color=colors.HexColor("#2563EB"),
spaceAfter=30,
)
)
def add_summary_table(self):
self.story.append(Paragraph("1. Executive Summary", self.heading1_style))
data = [
["Metric", "Value"],
["Total Papers Analyzed", "13"],
["Success Rate", "100% (all completed)"],
["Avg Processing Time", "~40 seconds"],
["Papers with Complete Data", "10/13 (77%)"],
["Papers with Partial Data", "3/13 (23%)"],
]
table = Table(data, colWidths=[5 * cm, 10 * cm])
table.setStyle(
TableStyle(
[
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1E3A5F")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 11),
("BOTTOMPADDING", (0, 0), (-1, 0), 12),
("TOPPADDING", (0, 0), (-1, 0), 12),
("BACKGROUND", (0, 1), (-1, -1), colors.HexColor("#F8FAFC")),
("GRID", (0, 0), (-1, -1), 1, colors.HexColor("#E2E8F0")),
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 10),
("BOTTOMPADDING", (0, 1), (-1, -1), 8),
("TOPPADDING", (0, 1), (-1, -1), 8),
]
)
)
self.story.append(table)
self.story.append(Spacer(1, 20))
def add_strengths(self):
self.story.append(Paragraph("2. Strengths", self.heading1_style))
strengths = [
"Fast Processing: 30-140s per paper is excellent for multi-agent analysis",
"Keyword Extraction: Captures domain-specific terms accurately (LSTM, Self-Attention, reinforcement learning)",
"Citation Detection: Identifies real citations and cross-references well",
"Claim Classification: Distinguishes between method/result/finding claims effectively",
"Multi-Agent Pipeline: Good separation of concerns between agents (parser, keyword, claim, citation, evidence, argumentation, scoring)",
]
for s in strengths:
self.story.append(Paragraph(f"• {s}", self.bullet_style))
self.story.append(Spacer(1, 15))
def add_weaknesses(self):
self.story.append(Paragraph("3. Weaknesses & Gaps", self.heading1_style))
self.story.append(
Paragraph("3.1 Low Citation Gap Detection", self.heading2_style)
)
self.story.append(
Paragraph(
"Most reports show only 0-2 citation gaps, which appears significantly under-detected. "
"Academic papers typically cite 15-40+ references. For a thorough analysis, expected citation "
"gaps should be in the range of 5-15 per paper, depending on the paper length and field.",
self.body_style,
)
)
self.story.append(Paragraph("3.2 Weak Evidence Grounding", self.heading2_style))
self.story.append(
Paragraph(
"Only 1-5% of claims are grounded with citations. For example, Paper 4 with 50 claims "
"shows only 3 grounded claims (0.06%). A well-structured research paper should have 30-50% "
"of claims properly grounded with citations to source materials.",
self.body_style,
)
)
self.story.append(
Paragraph("3.3 Incomplete Claim Detection", self.heading2_style)
)
self.story.append(
Paragraph(
"Average 20-50 claims per paper seems low for dense ML papers. Papers 1, 6, and 9 show "
"only 15-18 claims, which likely indicates under-detection. High-impact ML papers often "
"contain 80-150+ distinct claims that should be identified.",
self.body_style,
)
)
self.story.append(Paragraph("3.4 Citation Format Issues", self.heading2_style))
self.story.append(
Paragraph(
"Some citations show 'Unknown Title' or missing metadata. Author names are occasionally "
"wrong or truncated. The citation extraction should leverage reference section parsing "
"more aggressively.",
self.body_style,
)
)
self.story.append(Paragraph("3.5 No Limitation Analysis", self.heading2_style))
self.story.append(
Paragraph(
"Papers mention 'Limitations' in text but no agent captures this systematically. "
"A dedicated Limitations agent should identify: (a) scope constraints, (b) generalizability "
"issues, (c) comparison baselines, (d) reproducibility concerns.",
self.body_style,
)
)
self.story.append(
Paragraph("3.6 Inconsistent Argumentation Mapping", self.heading2_style)
)
self.story.append(
Paragraph(
"While 'Related To' chains exist in reports, they are sparse. Papers with 50+ claims "
"show minimal argument mapping. The argumentation agent may be failing silently or not "
"finding sufficient claim relationships to map.",
self.body_style,
)
)
def add_improvements(self):
self.story.append(PageBreak())
self.story.append(Paragraph("4. Priority Improvements", self.heading1_style))
improvements = [
(
"HIGH",
"Evidence Grounding Agent",
"Increase grounding from 1-5% to 30-50%. Add sentence-level citation matching. "
"Use coreference resolution to link pronouns to entities.",
),
(
"HIGH",
"Citation Gap Detection",
"Improve to 5-15 gaps per paper. Implement cross-reference analysis (when A cites B, "
"but B's claims aren't addressed). Add methodology comparison gaps.",
),
(
"HIGH",
"Claim Detection",
"Target 80-150 claims per high-impact paper. Add hedging language detection "
"(may, might, suggests). Separate major from minor claims.",
),
(
"MEDIUM",
"Citation Extraction",
"Parse reference section more aggressively. Extract full author lists, venue, year. "
"Handle multiple citation formats (APA, MLA, IEEE, Nature).",
),
(
"MEDIUM",
"Limitation Analysis Agent",
"Add new agent or extend claim agent to identify limitations. Classify: scope, "
"generalizability, baseline, reproducibility. Rate severity.",
),
(
"MEDIUM",
"Argumentation Mapping",
"Ensure all claims with relationships are mapped. Connect claims across sections "
"(introduction → method → result → conclusion).",
),
(
"LOW",
"Multi-modal Support",
"Add support for tables, figures, equations. Extract claims from figure captions. "
"Link table data to supporting claims.",
),
(
"LOW",
"Real-time Streaming",
"Provide incremental agent results as jobs run. Better UX for long papers.",
),
]
for priority, title, desc in improvements:
color = (
colors.HexColor("#DC2626")
if priority == "HIGH"
else (
colors.HexColor("#CA8A04")
if priority == "MEDIUM"
else colors.HexColor("#64748B")
)
)
data = [
[
Paragraph(
f"[{priority}]",
ParagraphStyle("P", textColor=color, fontSize=9),
),
Paragraph(f"{title}
{desc}", self.body_style),
]
]
table = Table(data, colWidths=[2 * cm, 13 * cm])
table.setStyle(
TableStyle(
[
("VALIGN", (0, 0), (-1, -1), "TOP"),
("BACKGROUND", (0, 0), (-1, -1), colors.HexColor("#F8FAFC")),
("BOX", (0, 0), (-1, -1), 1, colors.HexColor("#E2E8F0")),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
("TOPPADDING", (0, 0), (-1, -1), 8),
("BOTTOMPADDING", (0, 0), (-1, -1), 8),
]
)
)
self.story.append(table)
self.story.append(Spacer(1, 8))
def add_agent_breakdown(self):
self.story.append(Spacer(1, 20))
self.story.append(Paragraph("5. Agent Pipeline Analysis", self.heading1_style))
agents = [
["Agent", "Purpose", "Current Status", "Rating"],
["Parser Agent", "Extract text & metadata", "Working well", "Good"],
["Keyword Agent", "Key concept extraction", "Working well", "Good"],
["Claim Agent", "Research claim detection", "Under-detecting", "Fair"],
["Citation Gap Agent", "Gap identification", "Too few gaps", "Poor"],
[
"Evidence Grounding Agent",
"Grounding in source",
"Very low grounding",
"Poor",
],
["Argumentation Agent", "Structure mapping", "Sparse mapping", "Fair"],
["Scoring Agent", "Quality assessment", "Working", "Good"],
]
table = Table(agents, colWidths=[4 * cm, 4 * cm, 4 * cm, 2 * cm])
table.setStyle(
TableStyle(
[
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1E3A5F")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("BOTTOMPADDING", (0, 0), (-1, -1), 8),
("TOPPADDING", (0, 0), (-1, -1), 8),
("BACKGROUND", (0, 1), (-1, 1), colors.HexColor("#DCFCE7")),
("BACKGROUND", (0, 2), (-1, 2), colors.HexColor("#DCFCE7")),
("BACKGROUND", (0, 3), (-1, 3), colors.HexColor("#FEF3C7")),
("BACKGROUND", (0, 4), (-1, 4), colors.HexColor("#FEE2E2")),
("BACKGROUND", (0, 5), (-1, 5), colors.HexColor("#FEE2E2")),
("BACKGROUND", (0, 6), (-1, 6), colors.HexColor("#FEF3C7")),
("BACKGROUND", (0, 7), (-1, 7), colors.HexColor("#DCFCE7")),
("GRID", (0, 0), (-1, -1), 1, colors.HexColor("#E2E8F0")),
]
)
)
self.story.append(table)
def add_recommendations(self):
self.story.append(PageBreak())
self.story.append(Paragraph("6. Recommendations", self.heading1_style))
short_term = [
"Improve evidence grounding to target 30%+ coverage",
"Enhance citation gap detection with cross-reference analysis",
"Add hedging language detection for claim classification",
"Parse reference sections more aggressively",
]
medium_term = [
"Add dedicated Limitations analysis agent",
"Implement full argumentation chain mapping",
"Add multi-modal support for tables and figures",
"Improve real-time progress streaming",
]
long_term = [
"Fine-tune models on research paper corpus",
"Add comparison with arXiv/PubMed knowledge base",
"Implement claim verification against source claims",
"Add novelty/innovation scoring",
]
self.story.append(Paragraph("Short-term (1-2 weeks)", self.heading2_style))
for item in short_term:
self.story.append(Paragraph(f"• {item}", self.bullet_style))
self.story.append(Paragraph("Medium-term (1-2 months)", self.heading2_style))
for item in medium_term:
self.story.append(Paragraph(f"• {item}", self.bullet_style))
self.story.append(Paragraph("Long-term (3-6 months)", self.heading2_style))
for item in long_term:
self.story.append(Paragraph(f"• {item}", self.bullet_style))
def add_footer(self):
self.story.append(Spacer(1, 40))
self.story.append(
HRFlowable(
width="100%",
thickness=1,
color=colors.HexColor("#E2E8F0"),
spaceAfter=20,
)
)
self.story.append(
Paragraph(
"Report generated by CitationEdge Analysis Platform | IIT Patna Research Project",
ParagraphStyle(
"Footer",
fontSize=8,
textColor=colors.HexColor("#94A3B8"),
alignment=TA_CENTER,
),
)
)
def build(self):
self.add_header()
self.add_summary_table()
self.add_strengths()
self.add_weaknesses()
self.add_improvements()
self.add_agent_breakdown()
self.add_recommendations()
self.add_footer()
self.doc.build(self.story)
return self.output_path
if __name__ == "__main__":
output_path = os.path.join(
os.path.dirname(__file__), "..", "reports", "architecture_analysis_report.pdf"
)
report = AnalysisReport(output_path)
report.build()
print(f"Report saved: {output_path}")