Funciones avanzadas

Estas son características avanzadas que en su mayoría no están soportadas en oTree Studio.

ExtraModel

Un ExtraModel es útil cuando necesitas almacenar decenas o cientos de puntos de datos sobre un solo jugador. Por ejemplo, una lista de ofertas o una lista de estímulos y tiempos de reacción. Se utilizan frecuentemente junto con Páginas en vivo.

Aquí hay un montón de ejemplos.

Un ExtraModel debe enlazar a otro modelo:

class Bid(ExtraModel):
    player = models.Link(Player)
    amount = models.CurrencyField()

Cada vez que el usuario hace una oferta, la almacenas en la base de datos:

Bid.create(player=player, amount=500)

Luego, puedes recuperar la lista de las ofertas de un jugador:

bids = Bid.filter(player=player)

Un ExtraModel puede tener múltiples enlaces:

class Offer(ExtraModel):
    sender = models.Link(Player)
    receiver = models.Link(Player)
    group = models.Link(Group)
    amount = models.CurrencyField()
    accepted = models.BooleanField()

Entonces puedes consultarlo de varias maneras:

this_group_offers = Offer.filter(group=group)
offers_i_accepted = Offer.filter(receiver=player, accepted=True)

Para filtros y ordenamientos más complejos, debes usar operaciones de lista:

offers_over_500 = [o for o in Offer.filter(group=group) if o.amount > 500]

Vea los ejemplos de juegos de psicología, como la tarea de Stroop, que muestran cómo generar datos de ExtraModel a partir de cada fila de una hoja de cálculo CSV.

Para exportar sus datos de ExtraModel a CSV/Excel, use Exportaciones de datos personalizadas.

Lectura de archivos CSV

Para leer un archivo CSV (que puede ser producido por Excel o cualquier otra aplicación de hojas de cálculo), puede usar read_csv(). Por ejemplo, si tiene un archivo CSV como este:

name,price,is_organic
Apple,0.99,TRUE
Mango,3.79,FALSE

read_csv() generará una lista de diccionarios, como:

[dict(name='Apple', price=0.99, is_organic=True),
 dict(name='Mango', price=3.79, is_organic=False)]

Llamas a la función así:

rows = read_csv('my_app/my_data.csv', Product)

El segundo argumento es una clase que especifica el tipo de dato de cada columna:

class Product(ExtraModel):
    name = models.StringField()
    price = models.FloatField()
    is_organic = models.BooleanField()

(Sin esta información, sería ambiguo si TRUE se supone que es un bool, o la cadena 'TRUE', etc.)

read_csv() no crea realmente ninguna instancia de esa clase. Si deseas eso, debes usar .create() adicionalmente:

rows = read_csv('my_app/my_data.csv', Product)
for row in rows:
    Product.create(
        name=row['name'],
        price=row['price'],
        is_organic=row['is_organic'],
        # any other args:
        player=player,
    )

El modelo puede ser un ExtraModel, Player, Group o Subsession. Está bien si también contiene otros campos; serán ignorados por read_csv().

Plantillas

template_name

Si la plantilla necesita tener un nombre diferente al de tu clase de página (por ejemplo, si estás compartiendo la misma plantilla para múltiples páginas), establece template_name. Ejemplo:

class Page1(Page):
    template_name = 'app_name/MyPage.html'

CSS/JS y plantillas base

Para incluir el mismo JS/CSS en todas las páginas de una aplicación, colócalo en un archivo estático o ponlo en una plantilla incluyente.

Archivos estáticos

Aquí se explica cómo incluir imágenes (o cualquier otro archivo estático como .css, .js, etc.) en tus páginas.

En la raíz de tu proyecto oTree, hay una carpeta _static/. Coloca un archivo allí, por ejemplo puppy.jpg. Luego, en tu plantilla, puedes obtener la URL de ese archivo con {{ static 'puppy.jpg' }}.

Para mostrar una imagen, utiliza la etiqueta <img>, así:

<img src="{{ static 'puppy.jpg' }}"/>

Arriba guardamos nuestra imagen en _static/puppy.jpg. Pero en realidad, es mejor crear una subcarpeta con el nombre de tu aplicación y guardarla como _static/your_app_name/puppy.jpg para mantener los archivos organizados y prevenir conflictos de nombres.

Entonces tu código HTML se convierte en:

<img src="{{ static 'your_app_name/puppy.jpg }}"/>

(Si lo prefieres, también puedes poner archivos estáticos dentro de la carpeta de tu aplicación, en una subcarpeta llamada static/your_app_name.)

Si un archivo estático no se actualiza incluso después de que lo cambiaste, es porque tu navegador ha almacenado en caché el archivo. Haz una recarga completa de la página (normalmente Ctrl+F5).

Si tienes videos o imágenes de alta resolución, es preferible almacenarlos en algún lugar en línea y referenciarlos por URL, ya que el gran tamaño del archivo puede hacer que la carga de tu archivo .otreezip sea mucho más lenta.

Páginas de espera

Plantilla de página de espera personalizada

Puedes crear una plantilla de página de espera personalizada. Por ejemplo, guarda esto en your_app_name/MyWaitPage.html:

{{ extends 'otree/WaitPage.html' }}
{{ block title }}{{ title_text }}{{ endblock }}
{{ block content }}
    {{ body_text }}
    <p>
        My custom content here.
    </p>
{{ endblock }}

Luego, indica a tu página de espera que use esta plantilla:

class MyWaitPage(WaitPage):
    template_name = 'your_app_name/MyWaitPage.html'

Entonces puedes usar vars_for_template de la manera habitual. De hecho, los atributos body_text y title_text son solo una forma abreviada de establecer vars_for_template; los siguientes 2 fragmentos de código son equivalentes:

class MyWaitPage(WaitPage):
    body_text = "foo"
class MyWaitPage(WaitPage):

    @staticmethod
    def vars_for_template(player):
        return dict(body_text="foo")

Si deseas aplicar tu plantilla de página de espera personalizada globalmente, guárdala en _templates/global/WaitPage.html. oTree la usará automáticamente en todas partes en lugar de la página de espera incorporada.

Moneda

Para personalizar el nombre «points» a algo diferente como «tokens» o «credits», establece POINTS_CUSTOM_NAME, por ejemplo, POINTS_CUSTOM_NAME = 'tokens'.

Puedes cambiar el número de decimales en las cantidades de moneda del mundo real con la configuración REAL_WORLD_CURRENCY_DECIMAL_PLACES. Si los decimales adicionales aparecen pero siempre son 0, entonces deberías restablecer la base de datos.