Skip to content

Displaying results

Results pages show participants their outcomes, payoffs, and other players' choices. uproot uses Jinja2 templates with full access to Python builtins, enabling calculations and logic directly in your templates.

Basic results display

Create a Results page and pass data via the context method:

class Results(Page):
    @classmethod
    def context(page, player):
        return dict(
            other=other_in_group(player),
        )

In the template, access player data and context variables:

{% extends "Base.html" %}

{% block main %}
<p>You chose to {{ "cooperate" if player.cooperate else "defect" }}.</p>
<p>Your partner chose to {{ "cooperate" if other.cooperate else "defect" }}.</p>
<p>Your payoff is <b>{{ player.payoff }}</b>.</p>
{% endblock main %}

See the prisoners_dilemma example

Accessing other players' data

Two-player groups

Use other_in_group() to get the other player:

class Results(Page):
    @classmethod
    def context(page, player):
        return dict(other=other_in_group(player))
<p>Your partner sent {{ other.amount }}.</p>

See the trust_game example · ultimatum_game example

Larger groups

Pass a list of other players:

class Results(Page):
    @classmethod
    def context(page, player):
        return dict(others=others_in_group(player))
<p>Other guesses:
{% for other in others %}
    {{ other.guess }}{% if not loop.last %}, {% endif %}
{% endfor %}
</p>

See the beauty_contest example · minimum_effort_game example

Formatting numbers

The to filter

Format decimal places with the | to(n) filter:

<p>Your score: {{ player.score | to(1) }}</p>      <!-- 7.3 -->
<p>Amount: ${{ player.amount | to(2) }}</p>        <!-- $12.50 -->
<p>Percentage: {{ player.pct | to(0) }}%</p>       <!-- 85% -->

See the big5_short example · focal_point example

The fmtnum filter

For currency and units with prefix/suffix:

{{ player.payoff | fmtnum(pre="$", places=2) }}           <!-- $10.50 -->
{{ player.payoff | fmtnum(post=" EUR", places=2) }}       <!-- 10.50 EUR -->
{{ player.change | fmtnum(pre="$", places=2) }}           <!-- −$5.00 (uses minus sign) -->

Calculations in templates

uproot passes all Python builtins to templates. Perform calculations directly:

<!-- Arithmetic -->
<p>Total: {{ player.claim + other.claim }}</p>
<p>Tripled amount: {{ sent * 3 }}</p>
<p>Share: {{ player.contribution / total * 100 | to(1) }}%</p>

<!-- Comparisons -->
{% if player.claim + other.claim <= 100 %}
<p>The sum is $100 or less.</p>
{% else %}
<p>The sum exceeds $100.</p>
{% endif %}

See the focal_point example

Using Python builtins

Call sum(), max(), min(), len(), range(), enumerate(), zip(), and other builtins:

<!-- Sum contributions -->
<p>Group total: {{ sum(p.contribution for p in others) + player.contribution }}</p>

<!-- Find extremes -->
<p>Highest bid: {{ max(p.bid for p in others) }}</p>

<!-- Enumerate items -->
{% for i, item in enumerate(items) %}
<p>{{ i + 1 }}. {{ item }}</p>
{% endfor %}

<!-- Zip lists together -->
{% for option_a, option_b in zip(options_a, options_b) %}
<tr>
    <td>{{ option_a }}</td>
    <td>{{ option_b }}</td>
</tr>
{% endfor %}

See the mpl example

Conditional display

Role-based results

Show different content based on player role:

{% if player.trustor %}
<p>You sent <b>{{ sent }}</b>, which was tripled to {{ tripled }}.</p>
<p>The other player returned <b>{{ returned }}</b> to you.</p>
{% else %}
<p>The other player sent {{ sent }}, tripled to <b>{{ tripled }}</b>.</p>
<p>You returned <b>{{ returned }}</b>.</p>
{% endif %}

See the trust_game example · dictator_game example

Outcome-based messages

{% if player.winner %}
<p><b>You won!</b></p>
{% else %}
<p>You did not win this round.</p>
{% endif %}

See the beauty_contest example

History tables

Using player.along()

For repeated games, iterate through all rounds with player.along():

<table class="table">
    <thead>
        <tr>
            <th>Round</th>
            <th>Your number</th>
        </tr>
    </thead>
    <tbody>
        {% for round, data in player.along("round") %}
        <tr>
            <td>{{ round }}</td>
            <td>{{ data.number }}</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

The along("round") method returns tuples of (round_number, player_data_for_that_round).

See the rounds example

Using player.within()

Access a specific round's data with player.within(round=n):

<table class="table">
    <thead>
        <tr>
            <th>Round</th>
            <th>You</th>
            <th>Partner</th>
        </tr>
    </thead>
    <tbody>
        {% for round in rounds_so_far %}
        <tr>
            <td>{{ round }}</td>
            <td>
                {% if player.within(round=round).cooperate %}
                Cooperated
                {% else %}
                Defected
                {% endif %}
            </td>
            <td>
                {% if other.within(round=round).cooperate %}
                Cooperated
                {% else %}
                Defected
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </tbody>
</table>

Pass rounds_so_far from context:

class Decision(Page):
    @classmethod
    def context(page, player):
        return dict(
            other=other_in_group(player),
            rounds_so_far=range(1, player.round),
        )

See the prisoners_dilemma_repeated example

Accessing constants

The C class is available in templates:

class C:
    ROUNDS = 5
    ENDOWMENT = 100
<p>Round {{ player.round }} of {{ C.ROUNDS }}</p>
<p>You started with {{ C.ENDOWMENT }} points.</p>

Available filters

Filter Purpose Example
to(n) Format to n decimal places {{ x \| to(2) }}3.14
fmtnum(pre, post, places) Format with prefix/suffix {{ x \| fmtnum(pre="$") }}
tojson Convert to JSON {{ data \| tojson }}
repr Python repr {{ x \| repr }}

Summary

Feature Purpose
context(page, player) Pass variables to template
other_in_group(player) Get partner in 2-player group
players(group) Get all group members
player.along("round") Iterate all rounds
player.within(round=n) Access specific round data
{{ expression }} Output values
{% if %}...{% endif %} Conditional display
{% for %}...{% endfor %} Loops
Python builtins sum(), max(), range(), etc.