feat(tech-check): show Technical Details section in HTML report

Adds a new "Technical Details" card to generate_comprehensive_html_report()
between the summary and the per-check detailed results. Renders only
the fields present on the technical_report dict (file size, dimensions,
DPI, page count, duration, fonts, etc. — vary by file type) and shows
a prominent filename-vs-actual match badge when filename hints were
parsed.

If technical_report is absent or kind==unknown, the section is omitted
entirely so reports for assets we can't inspect (e.g. exotic
extensions) keep the existing layout unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
nickviljoen 2026-05-14 22:00:25 +02:00
parent 2b287f3dbb
commit 377efe30e5

View file

@ -1247,6 +1247,66 @@ def generate_html_response(report_data, filename, save_to_file=False, session_id
else:
return Response(html_content, mimetype='text/html')
def _render_technical_section_html(report):
"""Render the technical pre-flight report as an HTML block. Empty string if no report."""
if not report or report.get('kind') in (None, 'unknown'):
return ''
kind = report['kind']
rows = []
size_mb = report.get('file_size_mb')
if size_mb is not None:
rows.append(f'<div class="tech-row"><strong>File size:</strong> {size_mb} MB</div>')
dims = report.get('dimensions')
if dims:
rows.append(f'<div class="tech-row"><strong>Dimensions:</strong> {dims["width"]} × {dims["height"]}</div>')
fmt = report.get('format')
if fmt:
rows.append(f'<div class="tech-row"><strong>Format:</strong> {fmt}</div>')
dpi = report.get('dpi')
if dpi:
rows.append(f'<div class="tech-row"><strong>DPI:</strong> {dpi[0]} × {dpi[1]}</div>')
mode = report.get('mode')
if mode:
rows.append(f'<div class="tech-row"><strong>Color mode:</strong> {mode}</div>')
pc = report.get('page_count')
if pc is not None:
rows.append(f'<div class="tech-row"><strong>Pages:</strong> {pc}</div>')
pdf_ver = report.get('pdf_version')
if pdf_ver:
rows.append(f'<div class="tech-row"><strong>PDF version:</strong> {pdf_ver}</div>')
duration = report.get('duration_seconds')
if duration is not None:
rows.append(f'<div class="tech-row"><strong>Duration:</strong> {duration}s</div>')
codec = report.get('video_codec')
if codec:
rows.append(f'<div class="tech-row"><strong>Video codec:</strong> {codec}</div>')
fps = report.get('fps')
if fps:
rows.append(f'<div class="tech-row"><strong>Frame rate:</strong> {fps} fps</div>')
fonts = report.get('embedded_fonts')
if fonts:
suffix = '' if len(fonts) > 8 else ''
rows.append(f'<div class="tech-row"><strong>Embedded fonts:</strong> {", ".join(fonts[:8])}{suffix}</div>')
fm = report.get('filename_match')
if fm:
if fm['match']:
badge = '<span style="background:#28a745;color:white;padding:4px 10px;border-radius:12px;font-size:0.85em;">✓ Matches filename</span>'
else:
badge = '<span style="background:#dc3545;color:white;padding:4px 10px;border-radius:12px;font-size:0.85em;">⚠ Filename mismatch</span>'
rows.append(f'<div class="tech-row" style="margin-top:8px;">{badge} <span style="color:#6c757d;font-size:0.9em;margin-left:8px;">{fm["detail"]}</span></div>')
errors = report.get('errors', [])
if errors:
rows.append(f'<div class="tech-row" style="color:#856404;font-style:italic;"><strong>Inspection notes:</strong> {"; ".join(errors)}</div>')
if not rows:
return ''
return f'''
<div class="technical">
<h2>🔧 Technical Details <small style="color:#6c757d;font-size:0.6em;font-weight:normal;">(machine-inspected, no AI)</small></h2>
<div class="technical-grid">{''.join(rows)}</div>
</div>
'''
def generate_comprehensive_html_report(analysis_result, filename, file_path=None):
"""Generate comprehensive HTML report similar to the web UI format"""
summary = analysis_result.get('summary', {})
@ -1308,7 +1368,9 @@ def generate_comprehensive_html_report(analysis_result, filename, file_path=None
avg_individual_score = overall_score / 10 # Normalize to 1-10 scale
grade_text = 'Pass' if avg_individual_score >= 6 else 'Fail'
score_color = '#28a745' if avg_individual_score >= 6 else '#dc3545'
technical_html = _render_technical_section_html(analysis_result.get('technical_report', {}))
return f'''<!DOCTYPE html>
<html lang="en">
<head>
@ -1332,6 +1394,9 @@ def generate_comprehensive_html_report(analysis_result, filename, file_path=None
.summary {{ background: linear-gradient(135deg, #FFF9E6 0%, #FFFBF0 100%); padding: 25px; border-radius: 15px; margin: 30px 0; border-left: 5px solid #FFC407; }}
.summary-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-top: 15px; }}
.summary-item {{ background: white; padding: 15px; border-radius: 10px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }}
.technical {{ background: linear-gradient(135deg, #e3f2fd 0%, #f0f7ff 100%); padding: 25px; border-radius: 15px; margin: 30px 0; border-left: 5px solid #1565c0; }}
.technical-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 8px 24px; margin-top: 12px; }}
.tech-row {{ padding: 4px 0; color: #495057; font-size: 0.95em; word-break: break-word; }}
.score-display {{ font-size: 2.5em; font-weight: bold; color: {score_color}; margin-bottom: 5px; }}
.grade {{ font-size: 1.3em; font-weight: bold; color: #495057; }}
.expandable-section {{ margin-bottom: 15px; border: 2px solid #e9ecef; border-radius: 12px; overflow: hidden; background: white; }}
@ -1392,7 +1457,9 @@ def generate_comprehensive_html_report(analysis_result, filename, file_path=None
</div>
</div>
</div>
{technical_html}
<h2>🔍 Detailed Analysis Results</h2>
<p style="color: #6c757d; margin-bottom: 20px; font-style: italic;">
Click on any section below to expand and view detailed analysis