# -*- coding: utf-8 -*-
#
from otree.api import *
import csv
import settings
import time
#
author = 'Jan Romann (jan.romann@uni-bremen.de), ' \
'Patricia Zauchner (zauchner@uni-bremen.de)'
doc = """
Effort task: Answer as many questions as possible.
"""
class C(BaseConstants):
NAME_IN_URL = 'effort_trivial'
PLAYERS_PER_GROUP = 5 # Not really necessary in effort tasks. But so it looks better in the data
TASK_TIMER = settings.task_timer
NUM_ROUNDS = 1
POINTS_PER_CORRECT_ANSWER = 1
# Numbers for the effort task. Could also be randomly generated or derived from excel spreadsheet
with open('effort_trivial/questions.csv', encoding="utf-8") as csvfile:
reader = csv.reader(csvfile)
next(reader, None) # skip the headers
QUESTIONS = list(reader)
NUM_TASKS = len(QUESTIONS)
class Subsession(BaseSubsession):
pass
def creating_session(subsession: Subsession):
#
# Copy the group and player id structure of the first app
if "id_matrix" in subsession.session.vars:
subsession.set_group_matrix(subsession.session.vars['id_matrix'])
else:
subsession.group_randomly() # oTree function
subsession.session.vars['id_matrix'] = subsession.get_group_matrix()
print("ID Matrix created in app effort_trivial", subsession.session.vars['id_matrix'])
#
#
try:
subsession.session.vars["do_effort_task"] = subsession.session.config["do_effort_task"]
except KeyError:
subsession.session.vars["do_effort_task"] = getattr(settings, "do_effort_task", True)
# Set session vars (For counting number of effort apps in effort_intro)
subsession.session.vars["DoEffort_Trivial"] = True
#
class Group(BaseGroup):
pass
def get_question(index):
current_question = C.QUESTIONS[index]
return {
"question": current_question[0],
"false_answer_1": current_question[1],
"false_answer_2": current_question[2],
"false_answer_3": current_question[3],
"correct_answer": current_question[4],
}
def live_answer_question(player, data):
player.question_index = index = player.question_index + 1
new_question_data = get_question(index) # Get new numbers
answer = int(data.get("answer", 0))
Answer.create(player=player, question_number=index - 1, answer=answer) # Save answer in database
# Check if the answer was correct (correct value is always 4)
player.last_answer_correct = answer_correct = answer == 4
player.number_of_attempts = number_of_attempts = player.number_of_attempts + 1 # Increment number of attempts
# Retrieve old number of right guesses from the database
number_of_correct_answers = player.number_of_correct_answers
# If correct, increment number of correct guesses and, at the same time, write new value back to the database
if answer_correct:
player.number_of_correct_answers = number_of_correct_answers = number_of_correct_answers + 1
response = {
"question": new_question_data["question"],
"false_answer_1": new_question_data["false_answer_1"],
"false_answer_2": new_question_data["false_answer_2"],
"false_answer_3": new_question_data["false_answer_3"],
"correct_answer": new_question_data["correct_answer"],
"answer_correct": answer_correct,
"number_of_attempts": number_of_attempts,
"number_of_correct_answers": number_of_correct_answers,
}
return {player.id_in_group: response}
class Player(BasePlayer):
question_index = models.IntegerField(
doc="The number of the current task (for iterating through the list of values).",
initial=0)
number_of_correct_answers = models.IntegerField(
doc="Number of correct answers in total.",
initial=0)
number_of_attempts = models.IntegerField(
doc="Number of attempts in total.",
initial=0)
last_answer_correct = models.BooleanField(
doc="Did the user answer the last question correctly?",
initial=False)
class Answer(ExtraModel):
""" Creates an extra dataframe output. Is called with custom_export """
player = models.Link(Player)
question_number = models.IntegerField()
answer = models.IntegerField()
def custom_export(players):
""" Custom export xlsx/csv """
yield ['session_code', 'participant_code', 'question_number', "answer", "correct"] # Header row
for answer in Answer.filter():
player = answer.player
session = player.session
participant = player.participant
correct = 1 if 4 == answer.answer else 0
yield [session.code, participant.code, answer.question_number, answer.answer, correct]
# Pages
class EffortPage(Page):
""" Handles if other pages are displayed """
@staticmethod
def is_displayed(player):
return player.session.vars.get("do_effort_task", True)
class Intro(EffortPage):
""" Introduction to the trivial task """
@staticmethod
def before_next_page(player, timeout_happened):
# User has task_timer seconds to complete as many pages as possible
player.participant.vars['expiry_timestamp'] = time.time() + C.TASK_TIMER
class Task(EffortPage):
""" Trivial task """
live_method = 'live_answer_question'
@staticmethod
def get_timeout_seconds(player):
return player.participant.vars['expiry_timestamp'] - time.time()
timer_text = 'Verbleibende Zeit (in Sek.): '
@staticmethod
def vars_for_template(player):
index = player.question_index
question_data = get_question(index)
return {
"question": question_data["question"],
"false_answer_1": question_data["false_answer_1"],
"false_answer_2": question_data["false_answer_2"],
"false_answer_3": question_data["false_answer_3"],
"correct_answer": question_data["correct_answer"],
"debug": False
}
@staticmethod
def before_next_page(player, timeout_happened):
correct_answers = player.number_of_correct_answers
player.participant.vars["trivialpoints"] = correct_answers
player.participant.vars["trivialmistakes"] = player.number_of_attempts - correct_answers
class Results(EffortPage):
""" Results of trivial task """
pass
page_sequence = [
Intro,
Task,
Results
]