Digests
This document describes all the data available in Jinja2 templates for email digests.
Table of Contents
- Main Context Variables
- Digest Information
- Equipment Data
- Qualifications Data
- Quotes Data
- Leave Data
- Individual Mode
Main Context Variables
The following top-level variables are always available in your templates:
Variable | Type | Description |
---|---|---|
digest |
object | The digest configuration and metadata |
equipment |
object | Equipment calibration data |
qualifications |
object | Staff qualifications data |
quotes |
object | Project quotes and enquiries data |
leave |
object | Leave/vacation data |
today |
date | Current date |
user |
object | Current user (only in individual mode) |
Digest Information
digest
object
Contains the digest configuration:
Field | Type | Description |
---|---|---|
id |
integer | Unique identifier |
name |
string | Name of the digest |
org |
object | Organization (see below) |
individual |
boolean | Whether individual emails are sent |
subject |
string | Email subject template |
equipment_days_before |
integer | Days before today for equipment data |
equipment_days_after |
integer | Days after today for equipment data |
qualifications_days_before |
integer | Days before today for qualifications |
qualifications_days_after |
integer | Days after today for qualifications |
leave_days_before |
integer | Days before today for leave data |
leave_days_after |
integer | Days after today for leave data |
created |
datetime | Creation timestamp |
created_by |
object | User who created (see User object) |
modified |
datetime | Last modification timestamp |
modified_by |
object | User who modified (see User object) |
digest.org
object
Field | Type | Description |
---|---|---|
id |
integer | Organization ID |
name |
string | Organization name |
Equipment Data
equipment
object
Contains two arrays of equipment items:
Field | Type | Description |
---|---|---|
expired |
array | Equipment with expired calibration |
expiring |
array | Equipment with upcoming calibration |
Equipment Item Structure
Each item in equipment.expired
or equipment.expiring
:
Field | Type | Description |
---|---|---|
id |
integer | Equipment ID |
name |
string | Equipment name |
category |
object | Equipment category (id, name) |
serial |
string | Serial number |
calibration |
date | Calibration due date |
identifier |
string | Equipment identifier |
barcode |
string | Barcode value |
Example Usage
{% if equipment.expired %} <h3>Expired Equipment</h3> <ul> {% for item in equipment.expired %} <li>{{ item.name }} - Expired on {{ item.calibration }}</li> {% endfor %} </ul> {% endif %} {% if equipment.expiring %} <h3>Equipment Due for Calibration</h3> <ul> {% for item in equipment.expiring %} <li>{{ item.name }} - Due {{ item.calibration }}</li> {% endfor %} </ul> {% endif %}
Qualifications Data
qualifications
object
Contains two arrays of qualification items:
Field | Type | Description |
---|---|---|
expired |
array | Expired qualifications |
expiring |
array | Qualifications expiring soon |
Qualification Item Structure
Each item in qualifications.expired
or qualifications.expiring
:
Field | Type | Description |
---|---|---|
id |
integer | Qualification ID |
user |
object | User with qualification (see User object) |
name |
string | Qualification name |
reference |
string | Reference number |
start |
date | Start date |
end |
date | Expiry date |
Example Usage
{% if qualifications.expiring %} <h3>Qualifications Expiring Soon</h3> <table> <tr><th>Employee</th><th>Qualification</th><th>Expires</th></tr> {% for qual in qualifications.expiring %} <tr> <td>{{ qual.user.name }}</td> <td>{{ qual.name }}</td> <td>{{ qual.end }}</td> </tr> {% endfor %} </table> {% endif %}
Quotes Data
quotes
object
Contains two arrays of project/quote items:
Field | Type | Description |
---|---|---|
created |
array | Enquiries (not yet quoted) |
open |
array | Open quotes (quoted but not decided) |
Quote Item Structure
Each item in quotes.created
or quotes.open
:
Field | Type | Description |
---|---|---|
id |
integer | Project ID |
name |
string | Project name |
reference |
string | Project reference |
client |
object | Client (id, name) |
category |
object | Project category (id, name) |
created |
datetime | Creation date |
created_by |
object | User who created (id, name) |
modified |
datetime | Last modification date |
modified_by |
object | User who modified (id, name) |
quoted |
date | Date quoted (null for enquiries) |
quoted_by |
object | User who quoted (id, name) |
price_net |
decimal | Net price |
currency |
string | Currency code |
deadline |
date | Project deadline |
quote_deadline |
date | Quote validity deadline |
quote_decision |
string | Quote decision status |
Example Usage
{% if quotes.created %} <h3>New Enquiries</h3> <ul> {% for quote in quotes.created %} <li> <strong>{{ quote.client.name }}</strong>: {{ quote.name }} (Created {{ quote.created|date:"Y-m-d" }}) </li> {% endfor %} </ul> {% endif %} {% if quotes.open %} <h3>Quotes Awaiting Decision</h3> <ul> {% for quote in quotes.open %} <li> {{ quote.client.name }}: {{ quote.name }} - Quoted {{ quote.quoted|date:"Y-m-d" }} {% if quote.quote_deadline %} (Valid until {{ quote.quote_deadline }}){% endif %} </li> {% endfor %} </ul> {% endif %}
Leave Data
leave
object
Contains two arrays of leave items:
Field | Type | Description |
---|---|---|
created |
array | Leave requests pending approval |
approved |
array | Approved leave in date range |
Leave Item Structure
Each item in leave.created
or leave.approved
:
Field | Type | Description |
---|---|---|
id |
integer | Leave ID |
user |
object | Employee (see User object) |
type |
object | Leave type (id, name) |
start |
date | Start date |
end |
date | End date |
days |
decimal | Number of days |
hours |
decimal | Number of hours |
decision |
string | Decision status (empty="Requested", "A"="Approved", "D"="Denied") |
decided |
datetime | When decision was made |
decided_by |
object | Manager who decided (see User object) |
decided_notes |
string | Decision notes |
Example Usage
{% if leave.created %} <h3>Pending Leave Requests</h3> <ul> {% for request in leave.created %} <li> {{ request.user.name }} - {{ request.type.name }} ({{ request.start }} to {{ request.end }}, {{ request.days }} days) </li> {% endfor %} </ul> {% endif %} {% if leave.approved %} <h3>Upcoming Leave</h3> <ul> {% for absence in leave.approved %} <li> {{ absence.user.name }}: {{ absence.start|date:"M d" }} - {{ absence.end|date:"M d" }} </li> {% endfor %} </ul> {% endif %}
User Object Structure
User objects appear in various places and have this structure:
Field | Type | Description |
---|---|---|
id |
integer | User ID |
name |
string | Full name |
email |
string | Email address |
first_name |
string | First name |
last_name |
string | Last name |
Individual Mode
When digest.individual
is true
, each recipient gets a personalized email with an additional user
variable:
user
object (only in individual mode)
The full user object for the recipient, containing all user profile data.
Example Usage
{% if user %} <p>Hello {{ user.first_name }},</p> <p>Here's your personal digest for {{ today|date:"F j, Y" }}:</p> {% else %} <p>Team Digest for {{ today|date:"F j, Y" }}:</p> {% endif %}
Conditional Display
Use Jinja2 conditionals to show/hide sections:
{% if equipment.expired or equipment.expiring %} <section class="equipment-section"> <!-- Equipment content --> </section> {% endif %} {% if leave.created|length > 5 %} <p class="alert">{{ leave.created|length }} leave requests pending!</p> {% endif %}
Loops and Filters
Common Jinja2 filters useful for digests:
{{ items|length }} <!-- Count items --> {{ price|floatformat:2 }} <!-- Format decimal --> {{ text|truncatewords:20 }} <!-- Limit text length --> {{ value|default:"N/A" }} <!-- Default value --> {% for item in items|slice:":5" %} <!-- First 5 items --> <!-- ... --> {% endfor %}
Complete Example Template
<!DOCTYPE html> <html> <head> <title>{{ digest.org.name }} Digest - {{ today|date:"F j, Y" }}</title> </head> <body> <h1>{{ digest.org.name }} Weekly Digest</h1> <p>Report generated on {{ today|date:"l, F j, Y" }}</p> {% if quotes.created or quotes.open %} <h2>Projects & Quotes</h2> {% if quotes.created %} <h3>New Enquiries ({{ quotes.created|length }})</h3> <ul> {% for project in quotes.created|slice:":10" %} <li>{{ project.client.name }}: {{ project.name }}</li> {% endfor %} </ul> {% endif %} {% if quotes.open %} <h3>Quotes Pending Decision ({{ quotes.open|length }})</h3> <ul> {% for project in quotes.open %} <li> {{ project.client.name }}: {{ project.name }} - {{ project.currency }} {{ project.price_net|floatformat:2 }} {% if project.quote_deadline %} (Expires: {{ project.quote_deadline|date:"M d" }}) {% endif %} </li> {% endfor %} </ul> {% endif %} {% endif %} {% if leave.created or leave.approved %} <h2>Leave Management</h2> {% if leave.created %} <h3>Pending Approvals</h3> <table> {% for request in leave.created %} <tr> <td>{{ request.user.name }}</td> <td>{{ request.start|date:"M d" }} - {{ request.end|date:"M d" }}</td> <td>{{ request.days }} days</td> </tr> {% endfor %} </table> {% endif %} {% endif %} {% if equipment.expired %} <h2>⚠️ Expired Equipment Calibrations</h2> <ul> {% for item in equipment.expired %} <li>{{ item.name }} ({{ item.category.name }}) - Expired {{ item.calibration }}</li> {% endfor %} </ul> {% endif %} {% if user %} <hr> <p><small>This digest was personalized for {{ user.name }}.</small></p> {% endif %} </body> </html>
Notes for Template Designers
- Always check for data existence before displaying sections to avoid empty headers
- Use the
|length
filter to show counts in section headers - Consider using
|slice
to limit long lists in summary views - Format dates consistently throughout the template
- Test with both individual and non-individual modes if your digest supports both
- Remember that
user
is only available in individual mode - All date/datetime fields can be null - always check before displaying
Testing Your Templates
To test your templates with sample data:
- Create a test digest in the admin panel
- Set appropriate day ranges for each data type
- Send test emails to verify formatting
- Test both with and without data in each section
- Test individual mode if applicable