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}")