Skip to content

Commit 5530cb4

Browse files
committed
report: incorporate pdf generation and simplify arguments
And add makefile targets to generate a report from last run. Signed-off-by: Richard Alpe <[email protected]>
1 parent 6f1da9e commit 5530cb4

File tree

3 files changed

+134
-50
lines changed

3 files changed

+134
-50
lines changed

.github/workflows/main.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ jobs:
4343
python3 -m json.tool ~/.local/share/9pm/logs/last/result.json >> $GITHUB_STEP_SUMMARY
4444
echo '```' >> $GITHUB_STEP_SUMMARY
4545
46-
- name: Generate Reports from JSON
47-
run: python3 report.py ~/.local/share/9pm/logs/last/result.json all -o ~/.local/share/9pm/logs/last/
46+
- name: Generate GitHub Test Result
47+
run: make report-github
4848

49-
- name: Publish GitHub Test Result
49+
- name: Publish GitHub Report
5050
run: cat ~/.local/share/9pm/logs/last/result-gh.md >> $GITHUB_STEP_SUMMARY
5151

52-
- name: Generate PDF Report
53-
run: make report
52+
- name: Generate PDF Test Report
53+
run: make report-pdf
5454

5555
- name: Upload Logs as Artifacts
5656
uses: actions/upload-artifact@v4

Makefile

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ help:
1414
@echo "9pm Makefile - For developers of 9pm itself"
1515
@echo
1616
@echo "Available targets:"
17-
@echo " check - Run self-tests to verify 9pm functionality"
18-
@echo " test - Run unit tests with cmdl-supplied option"
19-
@echo " report - Generate all reports (markdown, asciidoc, PDF) from JSON"
20-
@echo " help - Show this help message"
17+
@echo " check - Run self-tests to verify 9pm functionality"
18+
@echo " test - Run unit tests with cmdl-supplied option"
19+
@echo " report-github - Generate GitHub markdown report from last test results"
20+
@echo " report-markdown - Generate plain markdown report from last test results"
21+
@echo " report-asciidoc - Generate AsciiDoc report from last test results"
22+
@echo " report-pdf - Generate PDF report from last test results"
23+
@echo " report - Alias for report-pdf (legacy)"
24+
@echo " help - Show this help message"
2125
@echo
2226
@echo "Variables:"
2327
@echo " TEST=$(TEST) - Specify test results to use for report (default: last)"
@@ -36,13 +40,18 @@ test:
3640
--option cmdl-supplied \
3741
unit_tests/all.yaml
3842

39-
# Generate reports from JSON results
40-
report:
41-
python3 report.py $(TESTPATH)/result.json all -o $(TESTPATH)
42-
asciidoctor-pdf --theme "$(THEME)" \
43-
-a pdf-fontsdir=report/fonts \
44-
-a logo="image:$(LOGO)" \
45-
-o $(TESTPATH)/report.pdf \
46-
$(TESTPATH)/report.adoc
43+
report-github:
44+
./report.py github
4745

48-
.PHONY: all check test report help
46+
report-markdown:
47+
./report.py markdown
48+
49+
report-asciidoc:
50+
./report.py asciidoc
51+
52+
report-pdf:
53+
./report.py pdf
54+
55+
report: report-pdf
56+
57+
.PHONY: all check test report report-github report-markdown report-asciidoc report-pdf help

report.py

Lines changed: 107 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import argparse
55
import sys
66
import os
7+
import subprocess
78
from datetime import datetime
89
from abc import ABC, abstractmethod
910

@@ -23,6 +24,32 @@ def generate(self) -> str:
2324
"""Generate the report content."""
2425
pass
2526

27+
@abstractmethod
28+
def get_filename(self):
29+
"""Get the output filename for this report type."""
30+
pass
31+
32+
def _get_output_filename(self, json_file_path, output_filename):
33+
"""Get the final output filename."""
34+
if output_filename:
35+
return output_filename
36+
json_dir = os.path.dirname(os.path.abspath(json_file_path))
37+
return os.path.join(json_dir, self.get_filename())
38+
39+
def write(self, json_file_path, output_filename=None):
40+
"""Generate content and write to file."""
41+
content = self.generate()
42+
filename = self._get_output_filename(json_file_path, output_filename)
43+
44+
try:
45+
with open(filename, 'w') as f:
46+
f.write(content)
47+
print(f"Generated {filename}")
48+
return filename
49+
except IOError as e:
50+
print(f"Error writing {filename}: {e}", file=sys.stderr)
51+
sys.exit(1)
52+
2653
def get_result_counts(self):
2754
"""Get summary statistics for results."""
2855
return {
@@ -38,6 +65,10 @@ def get_result_counts(self):
3865
class AsciiDocReporter(BaseReporter):
3966
"""Generates AsciiDoc format reports."""
4067

68+
def get_filename(self):
69+
"""Get the output filename for this report type."""
70+
return 'report.adoc'
71+
4172
def generate(self) -> str:
4273
"""Generate AsciiDoc report."""
4374
content = []
@@ -220,6 +251,10 @@ def _write_output_sections(self, data, depth, is_first=True):
220251
class GitHubMarkdownReporter(BaseReporter):
221252
"""Generates GitHub-flavored Markdown with emoji icons."""
222253

254+
def get_filename(self):
255+
"""Get the output filename for this report type."""
256+
return 'result-gh.md'
257+
223258
def generate(self) -> str:
224259
"""Generate GitHub Markdown report."""
225260
content = ["# Test Result", ""]
@@ -254,6 +289,10 @@ def _write_tree(self, data, depth):
254289
class PlainMarkdownReporter(BaseReporter):
255290
"""Generates plain Markdown format."""
256291

292+
def get_filename(self):
293+
"""Get the output filename for this report type."""
294+
return 'result.md'
295+
257296
def generate(self) -> str:
258297
"""Generate plain Markdown report."""
259298
content = ["# Test Result", ""]
@@ -277,6 +316,54 @@ def _write_tree(self, data, depth):
277316
return content
278317

279318

319+
class PDFReporter(BaseReporter):
320+
"""Generates PDF reports via AsciiDoc conversion."""
321+
322+
def get_filename(self):
323+
"""Get the output filename for this report type."""
324+
return 'report.pdf'
325+
326+
def generate(self) -> str:
327+
"""Generate AsciiDoc content (same as AsciiDocReporter)."""
328+
return AsciiDocReporter(self.data).generate()
329+
330+
def write(self, json_file_path, output_filename=None):
331+
"""Generate AsciiDoc content and convert to PDF using temp file."""
332+
import tempfile
333+
334+
content = self.generate()
335+
pdf_filename = self._get_output_filename(json_file_path, output_filename)
336+
337+
with tempfile.NamedTemporaryFile(mode='w', suffix='.adoc', delete=False) as temp_adoc:
338+
temp_adoc.write(content)
339+
temp_adoc_path = temp_adoc.name
340+
341+
try:
342+
script_dir = os.path.dirname(os.path.abspath(__file__))
343+
cmd = ['asciidoctor-pdf',
344+
'--theme', os.path.join(script_dir, 'report', 'theme.yml'),
345+
'-a', f'pdf-fontsdir={os.path.join(script_dir, "report", "fonts")}',
346+
'-a', f'logo=image:{os.path.join(script_dir, "logo.png")}[top=40%, align=right, pdfwidth=8cm]',
347+
'-o', pdf_filename, temp_adoc_path]
348+
349+
result = subprocess.run(cmd, capture_output=True, text=True)
350+
if result.returncode == 0:
351+
print(f"Generated {pdf_filename}")
352+
return pdf_filename
353+
else:
354+
print(f"Error generating PDF: {result.stderr}", file=sys.stderr)
355+
sys.exit(1)
356+
357+
except FileNotFoundError:
358+
print("Error: asciidoctor-pdf not found. Install with: gem install asciidoctor-pdf", file=sys.stderr)
359+
sys.exit(1)
360+
except Exception as e:
361+
print(f"Error running asciidoctor-pdf: {e}", file=sys.stderr)
362+
sys.exit(1)
363+
finally:
364+
os.unlink(temp_adoc_path)
365+
366+
280367
def load_json_data(json_file):
281368
"""Load and parse the JSON result file."""
282369
try:
@@ -293,45 +380,33 @@ def load_json_data(json_file):
293380
def main():
294381
"""Main CLI interface."""
295382
parser = argparse.ArgumentParser(description='Generate test reports from JSON data')
296-
parser.add_argument('json_file', help='Path to the JSON result file')
297-
parser.add_argument('format', choices=['github', 'markdown', 'asciidoc', 'all'],
383+
parser.add_argument('format', choices=['github', 'markdown', 'asciidoc', 'pdf'],
298384
help='Report format to generate')
299-
parser.add_argument('-o', '--output-dir', default='.',
300-
help='Output directory for generated reports (default: current directory)')
385+
parser.add_argument('json_file', nargs='?', help='Path to the JSON result file (auto-find if omitted)')
386+
parser.add_argument('-o', '--output', help='Output filename (auto-generate if omitted)')
301387

302388
args = parser.parse_args()
303389

390+
# Auto-find JSON file if not provided
391+
if args.json_file is None:
392+
args.json_file = os.path.expanduser('~/.local/share/9pm/logs/last/result.json')
393+
304394
# Load JSON data
305395
json_data = load_json_data(args.json_file)
306396

307-
# Generate reports based on format
308-
if args.format == 'all':
309-
formats = ['github', 'markdown', 'asciidoc']
310-
else:
311-
formats = [args.format]
312-
313-
for fmt in formats:
314-
if fmt == 'github':
315-
reporter = GitHubMarkdownReporter(json_data)
316-
content = reporter.generate()
317-
filename = os.path.join(args.output_dir, 'result-gh.md')
318-
elif fmt == 'markdown':
319-
reporter = PlainMarkdownReporter(json_data)
320-
content = reporter.generate()
321-
filename = os.path.join(args.output_dir, 'result.md')
322-
elif fmt == 'asciidoc':
323-
reporter = AsciiDocReporter(json_data)
324-
content = reporter.generate()
325-
filename = os.path.join(args.output_dir, 'report.adoc')
326-
327-
# Write output file
328-
try:
329-
with open(filename, 'w') as f:
330-
f.write(content)
331-
print(f"Generated {filename}")
332-
except IOError as e:
333-
print(f"Error writing {filename}: {e}", file=sys.stderr)
334-
sys.exit(1)
397+
# Generate report based on format
398+
try:
399+
if args.format == 'github':
400+
GitHubMarkdownReporter(json_data).write(args.json_file, args.output)
401+
elif args.format == 'markdown':
402+
PlainMarkdownReporter(json_data).write(args.json_file, args.output)
403+
elif args.format == 'asciidoc':
404+
AsciiDocReporter(json_data).write(args.json_file, args.output)
405+
elif args.format == 'pdf':
406+
PDFReporter(json_data).write(args.json_file, args.output)
407+
except Exception as e:
408+
print(f"Error generating report: {e}", file=sys.stderr)
409+
sys.exit(1)
335410

336411

337412
if __name__ == '__main__':

0 commit comments

Comments
 (0)