import random from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) from django.db import models as djmodels from django.db.models import F from otree.models import player from . import ret_functions import logging import pandas as pd logger = logging.getLogger(__name__) author = ' ' doc = """ Procurement game """ class Constants(BaseConstants): name_in_url = 'treate_E1' # this parameter defines how much time a user will stay on a RET page per round (in seconds) task_time = 180 #training_answer_All_correct = c(192) # Эта минимальная обязательная сумма выплачивается за участие в эксперименте всем участникам #participation_fee = 0 # Эта сумма должна быть выплачена победителю раунда или разделена среди n победителей # total_round_payoff = 400 players_per_group = 4 num_rounds = 5 # participation_fee = c(5) ---- not needed here - the one that is used id in the settings (!and it is already in RUB!) endowment = c(10) prize = c(10) bribe_fee = c(1) training_answer_A_correct = c(14) training_answer_B_correct = c(9) training_answer_C_correct = c(15) training_answer_D_correct = c(12) multiplier = 2 class Subsession(BaseSubsession): def creating_session(self): # we look for a corresponding Task Generator in our library (ret_functions.py) that contain all task-generating # functions. So the name of the generator in 'task_fun' parameter from settings.py should coincide with an # actual task-generating class from ret_functions. self.session.vars['task_fun'] = getattr(ret_functions, self.session.config['task']) # If a task generator gets some parameters (like a level of difficulty, or number of rows in a matrix etc.) # these parameters should be set in 'task_params' settings of an app, in a form of dictionary. For instance: # 'task_params': {'difficulty': 5} self.session.vars['task_params'] = self.session.config.get('task_params', {}) # for each player we call a function (defined in Player's model) called get_or_create_task # this is done so that when a RET page is shown to a player for the first time they would already have a task # to work on for p in self.get_players(): p.get_or_create_task() if self.round_number == 1: paying_round = random.sample(range(1, Constants.num_rounds), 1) self.session.vars['paying_round'] = paying_round if self.round_number > 1: self.group_randomly(fixed_id_in_group=True) # def set_externality(self): # for p in self.get_groups(): # if p.prize == "firm A": # !!!self.session.vars[] = Subsession.externality + p.score_A # else: # Subsession.externality = Subsession.externality + p.score_B # # need to add the externality to the payoffs later and activate the function in pages # def get_scores(self): # p1 = Group.get_player_by_id(1) # p2 = Group.get_player_by_id(2) # for p in self.get_groups(): # p.Group.score_A = p1.num_tasks_correct # p.Group.score_B = p2.num_tasks_correct class Group(BaseGroup): winner_score = models.CurrencyField() show_scores = models.BooleanField(initial=0) score_A = models.PositiveIntegerField(initial=0) score_B = models.PositiveIntegerField(initial=0) # 4 next are to record if anyone solved more than 10 matrices right (for our use only, not used in code after getting the value) score_A_unlimited = models.PositiveIntegerField(initial=0) score_B_unlimited = models.PositiveIntegerField(initial=0) prize = models.CharField( choices=['фирма A', 'фирма B'], doc="""to whom the contract is allocated""", widget=widgets.RadioSelect()) guessA = models.IntegerField( min=0, max=7, doc="""Out of the 7 officials in this session, how many do you think will choose a firm with a higher bribe instead of a firm with a higher performance, if they face such a tradeoff?""", ) guessB = models.IntegerField( min=0, max=7, doc="""Out of the 7 officials in this session, how many do you think will choose a firm with a higher bribe instead of a firm with a higher performance, if they face such a tradeoff?""", ) guessC = models.IntegerField( min=0, max=7, doc="""Out of the 7 officials in this session, how many do you think will choose a firm with a higher bribe instead of a firm with a higher performance, if they face such a tradeoff?""", ) guessD = models.IntegerField( min=0, max=7, doc="""Out of the 7 officials in this session, how many do you think will choose a firm with a higher bribe instead of a firm with a higher performance, if they face such a tradeoff?""", ) contribution_A = models.CurrencyField( min=0, max=Constants.endowment - Constants.bribe_fee, doc="""The amount contributed by the firm""", ) contribution_B = models.CurrencyField( min=0, max=Constants.endowment - Constants.bribe_fee, doc="""The amount contributed by the firm""", ) # externality = models.CurrencyField(initial=0) def set_pay(self): if self.prize == 'фирма A': for p in self.get_players(): if p.role() == "фирма A": if self.contribution_A > 0: p.pay = (Constants.endowment - Constants.bribe_fee - self.contribution_A + Constants.prize ) else: p.pay = (Constants.endowment - self.contribution_A + Constants.prize) elif p.role() == "фирма B": if self.contribution_B > 0: p.pay = (Constants.endowment - Constants.bribe_fee) else: p.pay = (Constants.endowment) elif p.role() == "Чиновник": p.pay = (Constants.endowment + self.contribution_A ) elif p.role() == "Гражданин": p.pay = (Constants.multiplier*self.winner_score ) # p.final_payoff = float(p.participant.payoff_plus_participation_fee()) # p.final_payoff = p.final_payoff.to_real_world_currency(p.session) else: for p in self.get_players(): if p.role() == "фирма A": if self.contribution_A > 0: p.pay = (Constants.endowment - Constants.bribe_fee ) else: p.pay = (Constants.endowment ) elif p.role() == "фирма B": if self.contribution_B > 0: p.pay = (Constants.endowment - Constants.bribe_fee - self.contribution_B + Constants.prize ) else: p.pay = (Constants.endowment - self.contribution_B + Constants.prize ) elif p.role() == "Чиновник": p.pay = (Constants.endowment + self.contribution_B ) elif p.role() == "Гражданин": p.pay = (Constants.multiplier*self.winner_score) # p.final_payoff = float(p.participant.payoff_plus_participation_fee()) def set_payoff(self): # this is the payoff of the paid rounds for player in self.get_players(): for rounds in self.session.vars['paying_round']: if player.round_number == rounds: player.payoff = player.pay player.final_payoff = float(player.participant.payoff_plus_participation_fee()) # player.payoff_plus_participation_fee = models.CurrencyField() # final_payoff = p.pay + Constants.participation_fee # cycle for round control(in Players??) # for round in self.player.in_all_rounds(): class Player(BasePlayer): # rank = models.IntegerField() # round_win = models.IntegerField() # pay = models.CurrencyField() final_payoff = models.FloatField() pay = models.CurrencyField() def role(self): if self.id_in_group == 1: return "фирма A" elif self.id_in_group == 2: return "фирма B" elif self.id_in_group == 3: return "Чиновник" # return "фирма C" elif self.id_in_group == 4: # return "фирма D" return "Гражданин" showscores = models.BooleanField() training_answer_A = models.CurrencyField(verbose_name='У фирмы А было бы') training_answer_B = models.CurrencyField(verbose_name='У фирмы В было бы') training_answer_C = models.CurrencyField(verbose_name='У чиновника было бы') training_answer_D = models.CurrencyField(verbose_name='У гражданина было бы') # here we store all tasks solved in this specific round - for further analysis # tasks_dump = models.LongStringField(doc='to store all tasks with answers, diff level and feedback') # training_answer_All = models.IntegerField(verbose_name='This training_answer') # this method returns number of correct tasks solved in this round @property def num_tasks_correct(self): return self.tasks.filter(correct_answer=F('answer')).count() # logger.info(f'Правильных answer: {num_tasks_correct}') # this method returns total number of tasks to which a player provided an answer @property def num_tasks_total(self): return self.tasks.filter(answer__isnull=False).count() # logger.info(f'Всего answer: {num_tasks_total}') # The following method checks if there are any unfinished (with no answer) tasks. If yes, we return the unfinished # task. If there are no uncompleted tasks we create a new one using a task-generating function from session settings def get_or_create_task(self): unfinished_tasks = self.tasks.filter(answer__isnull=True) if unfinished_tasks.exists(): return unfinished_tasks.first() else: task = Task.create(self, self.session.vars['task_fun'], **self.session.vars['task_params']) task.save() return task call = models.StringField(label='Пожалуйста, напишите ваше имя в Zoom, чтобы мы могли обратиться к вам напрямую при необходимости.' ' Эта информация будет удалена сразу же после эксперимента.') age = models.PositiveIntegerField(verbose_name='Какой ваш год рождения?', min=1930, max=2008, initial=None) gender = models.BooleanField(initial=None, choices=[[0, 'мужчина'], [1, 'женщина']], verbose_name='Какой ваш пол?', widget=widgets.RadioSelect()) residence = models.StringField(label='Ваша страна проживания? Например, "Россия".') origin = models.StringField(label='Ваше гражданство? Например, "Россия", "Украина", "Беларусь", "Казахстан".') married = models.BooleanField(initial=None, choices=[[0, 'Да'], [1, 'Нет']], verbose_name='Вы состоите в браке?', widget=widgets.RadioSelect()) children = models.IntegerField(verbose_name='Сколько у вас детей?') education = models.PositiveIntegerField( verbose_name='Какой ваш максимальный уже полученный уровень образования?', choices=[ [1, '9 классов'], [2, '11 классов'], [3, 'Диплом бакалавра'], [4, 'Диплом магистра'], [5, 'Кандидат или доктор наук'] ], widget=widgets.RadioSelect() ) religion = models.PositiveIntegerField( verbose_name='Какова ваша нынешняя религиозная принадлежность, если таковая имеется?', choices=[ [1, 'Атеист (не принадлежу к религии) '], [2, 'Буддист'], [3, 'Христианин '], [4, 'Иудей '], [5, 'Мусульманин'], [6, 'Другая '] ], widget=widgets.RadioSelect() ) religiosity = models.PositiveIntegerField( verbose_name='Как часто обычно вы посещаете религиозные службы, помимо свадеб, похорон и крестин?', choices=[ [1, 'Более одного раза в неделю'], [2, 'Раз в неделю'], [3, 'Раз в месяц'], [4, 'Только в особые праздники'], [5, 'Раз в год'], [6, 'Реже'], [7, 'Никогда / практически никогда']], widget=widgets.RadioSelect() ) trust = models.PositiveIntegerField( verbose_name='В целом, вы бы сказали, что большинству людей можно доверять или что нужно быть очень осторожным в отношениях с людьми? Отметьте вашу позицию на шкале, где 1 значит "Большинству людей можно доверять", а 10 - "Нужно быть очень осторожным в отношениях с людьми"', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], widget=widgets.RadioSelectHorizontal() ) income = models.PositiveIntegerField( verbose_name='Рассмотрим шкалу доходов, в которой «1» означает группу с самым низким доходом, а «10» - группу с самым высоким доходом в России. Мы хотели бы знать, к какой группе относится ваша семья. Укажите соответствующее число, учитывая все поступления заработной платы, окладов, пенсий и других доходов.', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], widget=widgets.RadioSelectHorizontal() ) corruption = models.PositiveIntegerField( verbose_name='Теперь мы хотели бы, чтобы вы высказали свое мнение о коррупции - когда люди дают взятку, делают подарки или делают одолжение другим людям, чтобы получить то, что им нужно, или услуги, в которых они нуждаются. Как бы вы оценили коррупцию в России по 10-балльной шкале, где «1» означает «в моей стране нет коррупции», а «10» означает «в моей стране процветает коррупция». Если ваши взгляды несколько разнятся, выберите между ними соответствующее число.', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], widget=widgets.RadioSelectHorizontal() ) risk = models.PositiveIntegerField( verbose_name='Насколько высок риск в России быть привлеченным к ответственности за дачу или получение взятки, подарка или услуги в обмен на государственную услугу? Чтобы выразить свое мнение, используйте 10-балльную шкалу, где «1» означает «полное отсутствие риска», а «10» означает «очень высокий риск».', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], widget=widgets.RadioSelectHorizontal() ) politics = models.PositiveIntegerField( verbose_name='В политических вопросах говорят о «левых» и «правых». Как бы вы в целом разместили свои взгляды на этой шкале? Чтобы указать свои взгляды, используйте 10-балльную шкалу, где «1» означает «левый», а «10» означает «правый».', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], widget=widgets.RadioSelectHorizontal() ) phone = models.StringField( label='Пожалуйста, напишите здесь свой номер телефона. Будьте аккуратны и проверьте правильность номера, так вы получите свои деньги.') phone2 = models.BooleanField(initial=None, choices=[[0, 'Да, можно положить на телефон'], [1, 'Нет, пожалуйста, положите на счет, привязанный к телефону']], verbose_name='Вы согласны получить выигрыш на свой телефон? Это предпочтительный вариант. Пожалуйста, выберите его, если можете. В противном случае мы переведем средства на ваш банковский счет, привязанный к телефону выше.', widget=widgets.RadioSelect()) name = models.StringField( label='Если вы выбрали "Нет, пожалуйста, положите на счет, привязанный к телефону" в прошлом вопросе, пожалуйста, ' 'укажите Имя и первую букву Фамилии человека, на которого зарегистрирован счет, привязанный к этому телефону. ' 'Это нужно, для проверки правильности получателя перевода.', blank=True) email = models.StringField(label='Пожалуйста, укажите ваш имейл ') # hidden_correct_input = models.IntegerField() hidden_total_answer = models.IntegerField() hidden_correct_answer = models.IntegerField() total_score = models.IntegerField(initial=0) win_per_round = models.IntegerField(initial=0) contribA = models.IntegerField(initial=0) contribB = models.IntegerField(initial=0) scoreA = models.IntegerField(initial=0) scoreB = models.IntegerField(initial=0) prize = models.StringField() # This is a custom model that contains information about individual tasks. In each round, each player can have as many # tasks as they tried to solve (we can call for the set of all tasks solved by a player by calling for instance # player.tasks.all() # Each task has a body field, html_body - actual html code shown at each page, correct answer and an answer provided by # a player. In addition there are two automatically updated/created fields that track time of creation and of an update # These fields can be used to track how long each player works on each task class Task(djmodels.Model): class Meta: ordering = ['-created_at'] player = djmodels.ForeignKey(to=Player, related_name='tasks', on_delete=djmodels.CASCADE) round_number = models.IntegerField(null=True) body = models.LongStringField() html_body = models.LongStringField() correct_answer = models.StringField() answer = models.StringField(null=True) created_at = djmodels.DateTimeField(auto_now_add=True) updated_at = djmodels.DateTimeField(auto_now=True) task_name = models.StringField() # the following method creates a new task, and requires as an input a task-generating function and (if any) some # parameters fed into task-generating function. @classmethod def create(cls, player, fun, **params): proto_task = fun(**params) task = cls(player=player, body=proto_task.body, html_body=proto_task.html_body, correct_answer=proto_task.correct_answer, task_name = proto_task.name) return task