Part 1 — CSS/Contrast/Accessibility: - Raise --text-muted contrast to WCAG AA (#696969 light, #9a9a9a dark) - Add body font-size: 16px baseline - Enlarge #themeToggle to 15px / 10px 20px padding Part 2 — Start Button (user-controlled analysis): - Upload no longer auto-starts check; shows ready state with filename/size - New showReadyState() / removeFile() functions in upload.js - beginCheck() now shows progress + hides ready state on click - Add prominent "Check Another PDF" button at bottom of results Part 3 — Scoring recalibration: - Replace deduction formula with check-pass ratio + soft penalty (cap 20) - Fix run_check() to only examine issues added by the current check - Add score_breakdown (per-check table) to JSON output + results UI - Downgrade readability ERROR → WARNING (advisory, not hard failure) Part 4 — Auto-fix debugging: - Remediation failure now returns up to 2000 chars of log (was 500) - pdf_remediation.py: stderr output, sys.exit(0/1), output dir creation Part 5 — Error location: View on Page button on each issue card Part 6 — Matterhorn Protocol PDF/UA-1: - _build_matterhorn_summary() maps 19 checks → 31 checkpoints - Matterhorn card in index.html with grouped PASS/FAIL/Not-tested table - Correct M/H badges per checkpoint Part 7 — Dismiss / False Positive: - dismissed_issues table in db/init.sql + dismiss/undismiss in db_manager.py - api.php: dismiss/undismiss endpoints (file-backed), dismissed_indices injected into both handleStatus and handleResult responses - results.js: dismissIssue/undismissIssue with visual strikethrough - CSS: .dismissed, .btn-dismiss, .btn-undismiss styles Part 8 — PDF Report (WeasyPrint): - generate_pdf() in report_generator.py: PAC-style A4, Oliver branding - api.php handleExport() supports format=pdf - index.html: "PDF Report" download button in results header - requirements.txt: weasyprint>=60.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
47 lines
1.5 KiB
SQL
47 lines
1.5 KiB
SQL
-- PDF Accessibility Checker - PostgreSQL Schema
|
|
-- Run automatically on first Docker Compose startup
|
|
|
|
CREATE TABLE IF NOT EXISTS jobs (
|
|
id SERIAL PRIMARY KEY,
|
|
job_id VARCHAR(64) UNIQUE NOT NULL,
|
|
filename VARCHAR(255),
|
|
status VARCHAR(20) DEFAULT 'queued',
|
|
score INTEGER,
|
|
grade CHAR(1),
|
|
total_issues INTEGER,
|
|
critical_count INTEGER,
|
|
error_count INTEGER,
|
|
warning_count INTEGER,
|
|
result_json JSONB,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
completed_at TIMESTAMP,
|
|
processing_time FLOAT,
|
|
api_key_hash VARCHAR(64),
|
|
ip_address INET
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id SERIAL PRIMARY KEY,
|
|
job_id VARCHAR(64),
|
|
action VARCHAR(50),
|
|
details JSONB,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
ip_address INET
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
|
|
CREATE INDEX IF NOT EXISTS idx_jobs_created ON jobs(created_at);
|
|
CREATE INDEX IF NOT EXISTS idx_jobs_job_id ON jobs(job_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_job ON audit_log(job_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS dismissed_issues (
|
|
id SERIAL PRIMARY KEY,
|
|
job_id VARCHAR(64) NOT NULL,
|
|
issue_index INTEGER NOT NULL,
|
|
reason VARCHAR(255),
|
|
dismissed_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE(job_id, issue_index)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_dismissed_job ON dismissed_issues(job_id);
|