Digests

This document describes all the data available in Jinja2 templates for email digests.


Table of Contents


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
tasks object Scheduled task 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
tasks_days_before integer Days before today for scheduled task data (default 0)
tasks_days_after integer Days after today for scheduled task data (default 14)
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 %}

Scheduled Tasks Data

tasks  object

Contains a single array of scheduled tasks:


Field Type Description
scheduled array Scheduled tasks within the configured date window

A task counts as scheduled when it has at least one of start /end  set and at least one assigned user. The window is controlled by tasks_days_before  and tasks_days_after , and a task is included when its date range overlaps that window. Tasks on abandoned projects are excluded, as are tasks whose closed date has already passed.


In individual mode, tasks.scheduled  is narrowed to only the tasks the recipient is assigned to. In non-individual mode it holds the full organization list.


Task Item Structure

Each item in tasks.scheduled  :


Field Type Description
id integer Task ID
name string Task name
type object Task type (id, name)
details string Task details (may be null)
days decimal Budgeted days
hours decimal Budgeted hours
start date Scheduled start date (may be null)
end date Scheduled end date (may be null)
project object Project (id, name, reference)
client object Client (id, name)
assignments array Assigned users (see below)

Assignment Structure

Each item in task.assignments  :


Field Type Description
done datetime When the assignment was marked done (null if not done)
user object Assigned user (see User object)

Done assignments are kept in the list rather than filtered out, so you can either display them or skip them by checking the done  field.


Example Usage

{% if tasks.scheduled %}
<h3>Scheduled Tasks</h3>
<ul>
{% for task in tasks.scheduled %}
  <li>
    <strong>{{ task.name }}</strong> ({{ task.project.name }})
    {% if task.start %}{{ task.start }}{% endif %}{% if task.end %} to {{ task.end }}{% endif %}
    <ul>
    {% for assignment in task.assignments %}
      <li>{{ assignment.user.name }}{% if assignment.done %} (done){% endif %}</li>
    {% endfor %}
    </ul>
  </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.


Note: in individual mode the tasks.scheduled  list is also narrowed to the tasks that recipient is assigned to.


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 tasks.scheduled %}
    <h2>Scheduled Tasks ({{ tasks.scheduled|length }})</h2>
    <ul>
    {% for task in tasks.scheduled %}
        <li>
            {{ task.name }} - {{ task.project.name }}
            {% if task.start %}({{ task.start }}{% if task.end %} to {{ task.end }}{% endif %}){% endif %}
        </li>
    {% endfor %}
    </ul>
    {% 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

  1. Always check for data existence before displaying sections to avoid empty headers
  2. Use the |length filter to show counts in section headers
  3. Consider using |slice  to limit long lists in summary views
  4. Format dates consistently throughout the template
  5. Test with both individual and non-individual modes if your digest supports both
  6. Remember that user is only available in individual mode
  7. All date/datetime fields can be null - always check before displaying

Testing Your Templates

To test your templates with sample data:


  1. Create a test digest in the admin panel
  2. Set appropriate day ranges for each data type
  3. Send test emails to verify formatting
  4. Test both with and without data in each section
  5. Test individual mode if applicable
Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us