from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random from django import forms import time import datetime from otree.db.models import Model, ForeignKey author = 'Filipp Chapkovskii, UZH, chapkovski@gmail.com' doc = """ Amazon-like auction. Bidding time is extended after each bid. """ class Constants(BaseConstants): instructions_template = 'AuctionAmazon08/Instructions.html' name_in_url = 'AuctionAmazon08' players_per_group = 2 num_rounds = 5 # starting_time = 30 # extra_time = 20 timeout_seconds = 60 endowment = 100 prize = 100 bid_success_probability = 0.0 num_others = players_per_group - 1 class Subsession(BaseSubsession): def creating_session(self): # Group randomly with fixed IDs self.group_randomly(fixed_id_in_group=True) # There will be a single paying round if self.round_number == 1: paying_round = random.randint(1, Constants.num_rounds) self.session.vars['paying_round'] = paying_round # Set start time / Not necessary, only for logs for g in self.get_groups(): g.first_price = 0 g.second_price = 0 g.auctionstartdate = time.time() for p in self.get_players(): Bid.objects.create( player=p, amount = 0, at_time = time.time() - g.auctionstartdate, in_stage = 0, success = 1, in_round = self.round_number, p_value = 0 ) print('Created bid objects for round '+str(self.round_number)) for p in self.get_players(): print(p.bids()) # Add one bid to each player, otherwise error on first screen # for player in self.get_players(): # _ = player.bid_set.create() # _.save() def vars_for_admin_report(self): # Randomly select a group for each round # Then look up the players in that group, # and return their IDs, Codenames and Payoffs selected_groups = [] for r in range(0,Constants.num_rounds): selected_groups.append(random.choice(self.get_group_matrix())) selected_players = [] for round in range(0, Constants.num_rounds): players_and_payoffs = [] for player in selected_groups[round]: # selected_groups[round] is a list of players players_and_payoffs.append(player.in_round(round+1).participant.label) players_and_payoffs.append(player.in_round(round+1).preliminary_payoff) selected_players.append(players_and_payoffs) return {'selected_players': selected_players} class Group(BaseGroup): first_price = models.IntegerField(initial=0) second_price = models.IntegerField(initial=0) auctionstartdate = models.FloatField() #auctionenddate = models.FloatField() curwinner = models.IntegerField() curstage = models.IntegerField(initial=1) # def time_left(self): # now = time.time() # time_left = self.auctionenddate - now # time_left = round(time_left) if time_left > 0 else 0 # return time_left def set_payoffs(self): for p in self.get_players(): if str(self.curwinner) == str(p.id_in_group): p.preliminary_payoff = p.private_value - self.second_price else: p.preliminary_payoff = 0 if (self.round_number == self.session.vars['paying_round']): p.payoff = p.preliminary_payoff else: p.payoff = 0 def live_auction(self, id_in_group, payload): jsonmessage = payload curbidder_id = jsonmessage['id'] curbidder_id_in_group = jsonmessage['id_in_group'] cur_player_bid = int(jsonmessage['cur_player_bid']) curbidder = self.get_player_by_id(curbidder_id_in_group) message_for_group = ' ' message_for_player = ' ' stage_info = ' ' if cur_player_bid > Constants.endowment: message_for_player = 'You can not bid more than your maximum endowment.' elif cur_player_bid <= curbidder.highest_bid().amount: message_for_player = 'Your new bid must exceed your current maximum bid.' else: print('Bid is legal') Bid.objects.create( player=curbidder, amount = cur_player_bid, at_time = time.time() - self.auctionstartdate, in_stage = 1, success = 1, in_round = curbidder.subsession.round_number, p_value = curbidder.private_value ) # new_bid = curbidder.bid_set.create() # new_bid.amount = cur_player_bid # new_bid.at_time = time.time() - self.auctionstartdate # new_bid.in_stage = 1 # new_bid.success = 1 # new_bid.in_round = curbidder.subsession.round_number # new_bid.p_value = curbidder.private_value # new_bid.save() # print(new_bid) curbidder.save() if cur_player_bid <= self.second_price: message_for_player = 'Your new bid must exceed the current selling price.' else: print('Bid is relevant') # Sort players by their respective maximum bid and then the time they submitted it (in case of duplicates) sorted_players = sorted( self.get_players(), key=lambda player: (player.highest_bid().amount,-player.highest_bid().at_time), reverse = True) # Replace the prices self.first_price = sorted_players[0].highest_bid().amount self.second_price = sorted_players[1].highest_bid().amount # Set winner self.curwinner = sorted_players[0].id_in_group #print('winner', mygroup.curwinner) message_for_player = 'Your bid was submitted successfully.' self.save() # After the bid/proceed request have been handled, and all variables have # been set, send everything to the group textforgroup = { "price": self.second_price, "winner": self.curwinner, "player_maxbid": curbidder.highest_bid().amount, "message_from_id": curbidder_id_in_group, "message_all":message_for_group, "message_player":message_for_player } return {0: textforgroup} # def write_results_to_file(self): # players = '' # for p in self.get_players(): # players += str(p.participant.id_in_session) # players += 'vs' # players = players[:-2] #Cuts off last 'vs' # # with open('Amazon_1.0_started_' # + str(time.strftime('%m_%d_%H_%M_%S',time.localtime(self.auctionstartdate))) # + '_played_by_' # + players # + 'round_' # + str(self.round_number) # + '.txt','w') as f: # # First write down the winner # f.write("The participants payoffs are:" + '\n') # for p in self.get_players(): # f.write("Participant_" + str(p.participant.id_in_session) # + ': ' + str(p.preliminary_payoff) # + '\n'+ '\n'+ '\n') # # Now write down all bids # f.write("The participants bids are:" + '\n') # for p in self.get_players(): # bid_qs = Bid.objects.filter(player__exact=p) # for bid in bid_qs: # f.write(str(bid) + '\n') # f.closed class Player(BasePlayer): #max_bid = models.IntegerField(initial=0) #bid_submitted_time = models.FloatField() private_value = models.IntegerField() proceed = models.IntegerField(initial = 0) preliminary_payoff = models.IntegerField(initial = 0) second_stage_bid = models.BooleanField() second_stage_bid_amount = models.IntegerField(blank = True) second_stage_bid_success = models.BooleanField(initial = True) def bids(self): return Bid.objects.filter(player=self) def last_bid(self): sorted_bids = sorted( self.bids(), key=lambda bid: bid.at_time, reverse = True) sorted_successful_bids = [bid for bid in sorted_bids if bid.success==1] return sorted_successful_bids[0] def highest_bid(self): sorted_bids = sorted( self.bids(), key=lambda bid: bid.amount, reverse = True) sorted_successful_bids = [bid for bid in sorted_bids if bid.success==1] return sorted_successful_bids[0] # # def last_bid(self): # sorted_bids = sorted( # self.bid_set.all(), # key=lambda bid: bid.at_time, # reverse = True) # sorted_successful_bids = [bid for bid in sorted_bids if bid.success==1] # return sorted_successful_bids[0] # # def highest_bid(self): # sorted_bids = sorted( # self.bid_set.all(), # key=lambda bid: bid.amount, # reverse = True) # sorted_successful_bids = [bid for bid in sorted_bids if bid.success==1] # return sorted_successful_bids[0] class Bid(Model): mode = models.IntegerField(initial=2) amount = models.IntegerField(initial=0) at_time = models.FloatField(initial=0) in_stage = models.IntegerField(initial=0) success = models.IntegerField(initial=1) in_round = models.IntegerField(initial=0) p_value = models.IntegerField(initial=0) player = models.Link(Player) #player = ForeignKey(Player, on_delete=models.CASCADE) # creates 1:m relation -> this bid was submitted by a certain player def __str__(self): bidstr = "Round " + str(self.player.subsession.round_number) + '\n' bidstr += "Group " + str(self.player.subsession.round_number) + '\n' bidstr += "Player " + str(self.player.id_in_group) + '\n' bidstr += "Value " + str(self.player.private_value) + '\n' bidstr += "Stage " + str(self.in_stage) + '\n' bidstr += "Time " + str(self.at_time) + '\n' bidstr += "Success " + str(self.success) + '\n' bidstr += "Amount " + str(self.amount) + '\n' return bidstr def custom_export(players): for bid in Bid.objects.order_by('id'): player = bid.player participant = player.participant yield [participant.code, player.round_number, bid.in_stage, bid.at_time, bid.amount, ]