-
Notifications
You must be signed in to change notification settings - Fork 2
SDK Report
Cairn generates PDF, DOCX, PPTX and XLSX deliverables : the Statement of Applicability, the audit report, the risk register, management review minutes. They are the artefacts that leave the platform and land in an auditor's inbox, which sets the bar for what "done" means here.
| Format | Library | Used for |
|---|---|---|
| WeasyPrint | Anything laid out as a document : the SoA, the audit report | |
| XLSX | openpyxl |
Tabular exports : the risk register |
| DOCX | python-docx |
Editable deliverables |
| PPTX | python-pptx |
Steering decks |
WeasyPrint renders HTML and CSS, which is the reason PDF generation here is a template exercise rather than a drawing exercise. The report template is a Django template like any other, and the print stylesheet is CSS.
reports/generators.py, one function per deliverable, taking the domain objects
and the requesting user:
def generate_supplier_attestation_pdf(supplier, user):
"""Render the supplier's attestation summary as a PDF."""
html = render_to_string("reports/supplier_attestations.html", {
"supplier": supplier,
"attestations": reportable(supplier.attestations.all()),
"generated_by": user,
"generated_at": timezone.now(),
"company": CompanySettings.objects.first(),
})
return HTML(string=html, base_url=settings.STATIC_ROOT).write_pdf()Separate the data from the rendering when the data is non-trivial. The
SoA does this : build_soa_frameworks_data(frameworks) returns a structure, and
generate_soa_pdf(frameworks, user) renders it. That split is what makes the
data testable without parsing a PDF.
Only reportable records. Use reportable(), never a hardcoded status. A
report that counts draft rows is a report that disagrees with the dashboard, and
whichever one the auditor read first is the one you will be defending.
The caller's perimeter. A report is a read like any other. Filter by the user's scopes; a PDF is an excellent way to exfiltrate a perimeter someone was never granted.
Provenance on the page. Who generated it, when, from which version, for which scope. A deliverable that does not say what it is a snapshot of cannot be reconciled with anything later.
Both languages. Report templates are translated like every other template. A French user generating an English deliverable is a defect.
ReportType in reports/constants.py, then wire the generator into
reports/views.py and the API. Add the matching
MCP tool so an assistant can produce the deliverable too.
Do not assert on PDF bytes. Test the data function directly, and for the rendered output assert that generation succeeds and that the response carries the right content type and filename.
def test_soa_data_excludes_draft_frameworks():
data = build_soa_frameworks_data(Framework.objects.all())
assert draft_framework.name not in [f["name"] for f in data]
def test_report_respects_scope(client, user_in_one_scope):
response = client.get(reverse("reports:soa-pdf"))
assert response["Content-Type"] == "application/pdf"The scope test is the one that earns its keep.
- Generator in
reports/generators.py, data split from rendering if non-trivial - Only
reportable()records included - Filtered by the caller's scopes
- Provenance rendered on the document
- Template translated, both languages checked
-
ReportTypeentry added, view and API wired - MCP tool added
- Tests cover the data function and the scope filtering
Built from docs/ at v0.36.0. Edits made here are overwritten by the next release : open a pull request against the source instead.
- Administration
- Ask Cairn
- Assets and suppliers
- Compliance
- The dashboard
- Finding your way
- Getting started
- Incidents
- How records move
- Organisational context
- Reports and management review
- Risks
- Trust Center
- Architecture
- Configuration
- Contributing
- The documentation system
- Installation
- Internationalisation
- Operations
- Release process
- Security
- Testing
- Adding an assistant provider
- Adding a dashboard widget
- Adding a domain entity
- Declaring a lifecycle
- Adding an MCP tool
- Adding a REST endpoint
- Adding a report
- Interface conventions
- Dashboard widgets
- Lifecycles
- MCP tools
- MCP tool parameters : Assets
- MCP tool parameters : Compliance
- MCP tool parameters : Governance and context
- MCP tool parameters : General
- MCP tool parameters : Incidents
- MCP tool parameters : Reports and management review
- MCP tool parameters : Risks
- MCP tool parameters : System and administration
- MCP tool parameters : Trust Center
- Management commands
- Models
- Permissions
- REST endpoints
- Environment variables
- MCP server
- REST API
- Assistant module (Ask Cairn)
- Module 0: User Management and Access Control
- Module 1: Context and Organization
- Module 2: Asset Management
- Module 3: Compliance
- Module 4: Risk Management
- Module 4 bis - EBIOS Risk Manager
- Module 5 : Trust Center
- Module 6 : Security Incident Management
- Management review : ISO 27001:2022 compliance (clause 9.3)