Bots: características avanzadas
Estas son características avanzadas que en su mayoría no están soportadas en oTree Studio.
Bots de línea de comandos
Una alternativa a ejecutar bots en tu navegador web es ejecutarlos en la línea de comandos. Los bots de línea de comandos se ejecutan más rápido y requieren menos configuración.
Ejecuta esto:
otree test mysession
Para probar con un número específico de participantes (de lo contrario, se utilizará por defecto num_demo_participants):
otree test mysession 6
Para ejecutar pruebas para todas las configuraciones de sesión:
otree test
Exportando datos
Utiliza el --export para exportar los resultados a un archivo CSV:
otree test mysession --export
Para especificar la carpeta donde se guardan los datos, haz lo siguiente:
otree test mysession --export=myfolder
Bots de navegador de línea de comandos
Puedes lanzar bots de navegador desde la línea de comandos, utilizando otree browser_bots.
Asegúrate de que Google Chrome esté instalado, o configura
BROWSER_COMMANDensettings.py(más información a continuación).Configura la variable de entorno
OTREE_REST_KEYcomo se describe en REST.Ejecuta tu servidor.
Cierra todas las ventanas de Chrome.
Ejecuta esto:
otree browser_bots mysession
Esto abrirá varias pestañas de Chrome y ejecutará los bots. Cuando termine, las pestañas se cerrarán y verás un informe en tu ventana de terminal.
Si Chrome no cierra las ventanas correctamente, asegúrate de haber cerrado todas las ventanas de Chrome antes de lanzar el comando.
Bots de navegador en línea de comandos en un servidor remoto (por ejemplo, Heroku)
Si el servidor se está ejecutando en un host/puerto diferente al habitual http://localhost:8000, necesitas pasar --server-url. Por ejemplo, si está en Heroku, lo harías así:
otree browser_bots mysession --server-url=https://YOUR-SITE.herokuapp.com
Eligiendo configuraciones y tamaños de sesión
Puedes especificar el número de participantes:
otree browser_bots mysession 6
Para probar todas las configuraciones de sesión, simplemente ejecuta esto:
otree browser_bots
Bots de navegador: notas varias
Puedes usar un navegador diferente a Chrome configurando BROWSER_COMMAND en settings.py. Luego, oTree abrirá el navegador ejecutando algo como subprocess.Popen(settings.BROWSER_COMMAND).
Casos de prueba
Puedes definir un atributo cases en tu clase PlayerBot que liste diferentes casos de prueba. Por ejemplo, en un juego de bienes públicos, puede que desees probar 3 escenarios:
Todos los jugadores contribuyen con la mitad de su dotación.
Todos los jugadores no contribuyen en absoluto.
Todos los jugadores contribuyen con su dotación completa (100 puntos).
Podemos llamar a estos 3 casos de prueba «basic», «min» y «max», respectivamente, y ponerlos en cases. Luego, oTree ejecutará el bot 3 veces, una para cada caso de prueba. Cada vez, un valor diferente de cases se asignará a self.case en el bot.
Por ejemplo:
class PlayerBot(Bot):
cases = ['basic', 'min', 'max']
def play_round(self):
yield (pages.Introduction)
if self.case == 'basic':
assert self.player.payoff == None
if self.case == 'basic':
if self.player.id_in_group == 1:
for invalid_contribution in [-1, 101]:
yield SubmissionMustFail(pages.Contribute, {'contribution': invalid_contribution})
contribution = {
'min': 0,
'max': 100,
'basic': 50,
}[self.case]
yield (pages.Contribute, {"contribution": contribution})
yield (pages.Results)
if self.player.id_in_group == 1:
if self.case == 'min':
expected_payoff = 110
elif self.case == 'max':
expected_payoff = 190
else:
expected_payoff = 150
assert self.player.payoff == expected_payoff
Nota
Si utilizas casos, es mejor usar Bots de línea de comandos ya que los bots de navegador solo ejecutarán un solo caso.
cases debe ser una lista, pero puede contener cualquier tipo de dato, como cadenas, enteros o incluso diccionarios. Aquí hay un bot de juego de confianza que utiliza diccionarios como casos.
class PlayerBot(Bot):
cases = [
{'offer': 0, 'return': 0, 'p1_payoff': 10, 'p2_payoff': 0},
{'offer': 5, 'return': 10, 'p1_payoff': 15, 'p2_payoff': 5},
{'offer': 10, 'return': 30, 'p1_payoff': 30, 'p2_payoff': 0}
]
def play_round(self):
case = self.case
if self.player.id_in_group == 1:
yield (pages.Send, {"sent_amount": case['offer']})
else:
for invalid_return in [-1, case['offer'] * C.MULTIPLICATION_FACTOR + 1]:
yield SubmissionMustFail(pages.SendBack, {'sent_back_amount': invalid_return})
yield (pages.SendBack, {'sent_back_amount': case['return']})
yield (pages.Results)
if self.player.id_in_group == 1:
expected_payoff = case['p1_payoff']
else:
expected_payoff = case['p2_payoff']
assert self.player.payoff == expected_payoff
error_fields
Al usar SubmissionMustFail en formularios con múltiples campos, puedes usar error_fields para mayor exhaustividad.
Por ejemplo, supongamos que enviamos un age válido, pero un weight y height inválidos:
yield SubmissionMustFail(
pages.Survey,
dict(
age=20,
weight=-1,
height=-1,
)
)
Lo que falta es que el sistema de bots no nos dice exactamente por qué falla la presentación. ¿Es un weight, height inválido, o ambos? error_fields puede resolver la ambigüedad:
yield SubmissionMustFail(
pages.Survey,
dict(
age=20,
weight=-1,
height=-1,
),
error_fields=['weight', 'height']
)
Esto verificará que weight y height contenían errores, pero age no.
Si error_message devuelve un error, entonces error_fields será ['__all__'].
Nota miscelánea
En los bots, es arriesgado asignar player = self.player (o participant = self.participant, etc.), aunque ese tipo de código se fomente en otros lugares.
Porque si hay un yield en medio, los datos pueden estar desactualizados:
player = self.player
expect(player.money_left, cu(10))
yield pages.Contribute, dict(contribution=cu(1))
# don't do this!
# "player" variable still has the data from BEFORE pages.Contribute was submitted.
expect(player.money_left, cu(9))
Es más seguro usar self.player.money_left directamente, porque hacer self.player obtiene los datos más recientes de la base de datos.
Páginas en vivo
Para probar métodos en vivo con bots, define call_live_method como una función de nivel superior en tests.py. (No disponible en oTree Studio.) Esta función debe simular la secuencia de llamadas a tu live_method. El argumento method simula el método en vivo en tu modelo de Player. Por ejemplo, method(3, 'hello') llama al método en vivo en el Jugador 3 con data establecido en 'hello'. Por ejemplo:
def call_live_method(method, **kwargs):
method(1, {"offer": 50})
method(2, {"accepted": False})
method(1, {"offer": 60})
retval = method(2, {"accepted": True})
# you can do asserts on retval
kwargs contiene al menos los siguientes parámetros.
casecomo se describe en Casos de prueba.page_class: la clase de página actual, por ejemplo,pages.MyPage.round_number
call_live_method se ejecutará automáticamente cuando el bot más rápido del grupo llegue a una página con live_method. (Otros bots pueden estar en páginas anteriores en ese momento, a menos que restrinjas esto con un WaitPage.)