GopalKrushnaMahapatra commited on
Commit
84b414d
·
verified ·
1 Parent(s): c9dc165

Create pdf_reports.py

Browse files
Files changed (1) hide show
  1. pdf_reports.py +186 -0
pdf_reports.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # pdf_reports.py
2
+ import os
3
+ import uuid
4
+ from datetime import datetime
5
+ from reportlab.lib.pagesizes import A4
6
+ from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
7
+ from reportlab.lib.enums import TA_CENTER, TA_RIGHT, TA_JUSTIFY, TA_LEFT
8
+ from reportlab.lib import colors
9
+ from reportlab.platypus import (
10
+ BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle
11
+ )
12
+ from reportlab.pdfbase.ttfonts import TTFont
13
+ from reportlab.pdfbase import pdfmetrics
14
+
15
+ # Try to load a nicer serif; fallback to Times
16
+ try:
17
+ pdfmetrics.registerFont(TTFont('DejaVuSerif', '/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf'))
18
+ BODY_FONT = 'DejaVuSerif'
19
+ except Exception:
20
+ BODY_FONT = 'Times-Roman'
21
+
22
+
23
+ def _build_doc(filepath, title_text, tiles, counts, sections, matched_sources, footer_text):
24
+ PAGE_WIDTH, PAGE_HEIGHT = A4
25
+ MARGIN = 36
26
+ usable_width = PAGE_WIDTH - 2 * MARGIN
27
+
28
+ styles = getSampleStyleSheet()
29
+ styles.add(ParagraphStyle(name='ReportTitle', fontName=BODY_FONT, fontSize=18, alignment=TA_CENTER, leading=22))
30
+ styles.add(ParagraphStyle(name='SmallRight', fontName=BODY_FONT, fontSize=9, alignment=TA_RIGHT, textColor=colors.HexColor("#555555")))
31
+ styles.add(ParagraphStyle(name='TileBig', fontName=BODY_FONT, fontSize=30, alignment=TA_CENTER, leading=32))
32
+ styles.add(ParagraphStyle(name='TileLabel', fontName=BODY_FONT, fontSize=10, alignment=TA_CENTER, textColor=colors.HexColor("#666666")))
33
+ styles.add(ParagraphStyle(name='SectionHeading', fontName=BODY_FONT, fontSize=13, spaceBefore=8, spaceAfter=4, leading=15))
34
+ styles.add(ParagraphStyle(name='Body', fontName=BODY_FONT, fontSize=11, leading=15, alignment=TA_JUSTIFY))
35
+ styles.add(ParagraphStyle(name='HighlightYellow', fontName=BODY_FONT, fontSize=11, leading=15, backColor=colors.HexColor("#fff3b0"), alignment=TA_JUSTIFY))
36
+ styles.add(ParagraphStyle(name='HighlightRed', fontName=BODY_FONT, fontSize=11, leading=15, backColor=colors.HexColor("#ffd6d6"), alignment=TA_JUSTIFY))
37
+ styles.add(ParagraphStyle(name='Footer', fontName=BODY_FONT, fontSize=9, alignment=TA_RIGHT, textColor=colors.HexColor("#666666")))
38
+ styles.add(ParagraphStyle(name='MatchedHeader', fontName=BODY_FONT, fontSize=12, leading=14, alignment=TA_LEFT, spaceBefore=6, spaceAfter=6))
39
+
40
+ def header_footer(canvas, doc):
41
+ canvas.saveState()
42
+ date_str = datetime.now().strftime("%d %B %Y, %H:%M")
43
+ canvas.setFont(BODY_FONT, 9)
44
+ canvas.setFillColor(colors.HexColor("#555555"))
45
+ canvas.drawString(MARGIN, PAGE_HEIGHT - MARGIN + 8, f"Date: {date_str}")
46
+ canvas.setFont(BODY_FONT, 16)
47
+ canvas.setFillColor(colors.black)
48
+ canvas.drawCentredString(PAGE_WIDTH / 2.0, PAGE_HEIGHT - MARGIN + 4, title_text)
49
+ canvas.setFont(BODY_FONT, 9)
50
+ canvas.setFillColor(colors.HexColor("#666666"))
51
+ canvas.drawRightString(PAGE_WIDTH - MARGIN, MARGIN - 10, f"Page {doc.page}")
52
+ canvas.restoreState()
53
+
54
+ doc = BaseDocTemplate(filepath, pagesize=A4,
55
+ leftMargin=MARGIN, rightMargin=MARGIN,
56
+ topMargin=MARGIN, bottomMargin=MARGIN)
57
+ frame = Frame(MARGIN, MARGIN, usable_width, PAGE_HEIGHT - 2 * MARGIN, id='normal')
58
+ template = PageTemplate(id='report', frames=[frame], onPage=header_footer)
59
+ doc.addPageTemplates([template])
60
+
61
+ story = []
62
+
63
+ # Tiles (4 small summary tiles)
64
+ tile_values = tiles # list of 4 dicts: {'value': '12%', 'label': 'Plagiarism'}
65
+ tiles_data = [
66
+ [Paragraph(f"<b>{tile_values[i]['value']}</b>", styles['TileBig']) for i in range(4)],
67
+ [Paragraph(tile_values[i]['label'], styles['TileLabel']) for i in range(4)]
68
+ ]
69
+ tiles_table = Table(tiles_data, colWidths=[usable_width / 4.0] * 4, rowHeights=[46, 18])
70
+ tiles_table.setStyle(TableStyle([
71
+ ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#f7f7f9")),
72
+ ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
73
+ ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
74
+ ('BOX', (0, 0), (-1, -1), 0.6, colors.HexColor("#dddddd")),
75
+ ]))
76
+ story.append(tiles_table)
77
+ story.append(Spacer(1, 12))
78
+
79
+ # Counts row
80
+ if counts:
81
+ counts_table = Table([list(counts.keys()), list(counts.values())],
82
+ colWidths=[usable_width / len(counts)] * len(counts))
83
+ counts_table.setStyle(TableStyle([
84
+ ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#f4f6f7")),
85
+ ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
86
+ ('BOX', (0, 0), (-1, -1), 0.5, colors.HexColor("#e6e6e6")),
87
+ ]))
88
+ story.append(counts_table)
89
+ story.append(Spacer(1, 12))
90
+
91
+ # Sections + highlighting: sections is list of dicts: {'heading': 'Abstract', 'paragraphs': [p1, p2...]}
92
+ for sec in sections or []:
93
+ if sec.get('heading'):
94
+ story.append(Paragraph(sec['heading'], styles['SectionHeading']))
95
+ for para in sec.get('paragraphs', []):
96
+ # para may be dict {'text': '...', 'highlight':'yellow'/'red'/None}
97
+ if isinstance(para, dict):
98
+ text = para.get('text', '')
99
+ hl = para.get('highlight')
100
+ if hl == 'yellow':
101
+ story.append(Paragraph(text, styles['HighlightYellow']))
102
+ elif hl == 'red':
103
+ story.append(Paragraph(text, styles['HighlightRed']))
104
+ else:
105
+ story.append(Paragraph(text, styles['Body']))
106
+ else:
107
+ story.append(Paragraph(para, styles['Body']))
108
+ story.append(Spacer(1, 6))
109
+
110
+ story.append(Spacer(1, 10))
111
+
112
+ # Matched Sources table (if any)
113
+ if matched_sources:
114
+ story.append(Paragraph("Matched Sources", styles['MatchedHeader']))
115
+ ms_table_data = [["#", "Source Title", "URL", "Similarity"]]
116
+ for i, ms in enumerate(matched_sources, start=1):
117
+ title_par = Paragraph(ms.get('title', ''), styles['Body'])
118
+ url_par = Paragraph(f'<link href="{ms.get("url", "")}">{ms.get("url", "")}</link>', styles['Body'])
119
+ ms_table_data.append([str(i), title_par, url_par, ms.get('similarity', '')])
120
+ ms_table = Table(ms_table_data, colWidths=[30, usable_width * 0.35, usable_width * 0.45, usable_width * 0.15])
121
+ ms_table.setStyle(TableStyle([
122
+ ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#f2f4f5")),
123
+ ('TEXTCOLOR', (0, 0), (-1, 0), colors.HexColor("#333333")),
124
+ ('ALIGN', (0, 0), (-1, 0), 'CENTER'),
125
+ ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
126
+ ('BOX', (0, 0), (-1, -1), 0.6, colors.HexColor("#e0e0e0")),
127
+ ('INNERGRID', (0, 0), (-1, -1), 0.4, colors.HexColor("#efefef")),
128
+ ('LEFTPADDING', (1, 1), (1, -1), 6),
129
+ ('LEFTPADDING', (2, 1), (2, -1), 6),
130
+ ]))
131
+ story.append(ms_table)
132
+ story.append(Spacer(1, 14))
133
+
134
+ # Matched Source Overview block (footer_text)
135
+ if footer_text:
136
+ matched_table = Table(
137
+ [[Paragraph("<b>Matched Source Overview</b>", styles['Body'])],
138
+ [Paragraph(footer_text, styles['Body'])]],
139
+ colWidths=[usable_width]
140
+ )
141
+ matched_table.setStyle(TableStyle([
142
+ ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#f7fafb")),
143
+ ('BOX', (0, 0), (-1, -1), 0.5, colors.HexColor("#e6e6e6")),
144
+ ('LEFTPADDING', (0, 0), (-1, -1), 8),
145
+ ('RIGHTPADDING', (0, 0), (-1, -1), 8),
146
+ ('TOPPADDING', (0, 0), (-1, -1), 6),
147
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
148
+ ]))
149
+ story.append(matched_table)
150
+ story.append(Spacer(1, 24))
151
+
152
+ story.append(Paragraph("Generated by TrueWrite Scan • https://gopalkrushnamahapatra-truewrite-scan.static.hf.space", styles['Footer']))
153
+
154
+ doc.build(story)
155
+
156
+
157
+ def generate_report(report_type: str, out_dir: str = "/tmp", **kwargs) -> str:
158
+ """
159
+ report_type: "ai" | "grammar" | "plagiarism"
160
+ kwargs expected:
161
+ - title_text: str
162
+ - tiles: list of 4 dicts [{'value': '12%', 'label': 'Plagiarism'}, ...]
163
+ - counts: dict {'Words': 950, ...}
164
+ - sections: list [{'heading':'','paragraphs':[...]}]
165
+ - matched_sources: list [{'title','url','similarity'}]
166
+ - footer_text: str
167
+ Returns: path to generated PDF
168
+ """
169
+ os.makedirs(out_dir, exist_ok=True)
170
+ filename = f"{report_type}_report_{uuid.uuid4().hex[:8]}.pdf"
171
+ filepath = os.path.join(out_dir, filename)
172
+
173
+ title_text = kwargs.get('title_text', "Report")
174
+ tiles = kwargs.get('tiles') or [
175
+ {'value': '0%', 'label': 'Plagiarism'},
176
+ {'value': '0%', 'label': 'Exact Match'},
177
+ {'value': '0%', 'label': 'Partial Match'},
178
+ {'value': '100%', 'label': 'Unique'},
179
+ ]
180
+ counts = kwargs.get('counts') or {}
181
+ sections = kwargs.get('sections') or []
182
+ matched_sources = kwargs.get('matched_sources') or []
183
+ footer_text = kwargs.get('footer_text') or ''
184
+
185
+ _build_doc(filepath, title_text, tiles, counts, sections, matched_sources, footer_text)
186
+ return filepath