#!/usr/bin/env python # -*- coding: utf8 -*- # Python program to create # password-protected bonus files # Create pdf using FPDF # Encrypt PDF using pyPDF2 # Get file name and password import PyPDF2 import random from fpdf import FPDF from pathlib import Path def shuffleOutcomes(bonusFileDict): shuffledDict = {} for key, value in bonusFileDict.items(): taskType = value[0] resolutionType = value[1] risk = value[2] x1 = risk[0][0] p1 = risk[0][1] x0 = risk[1][0] p0 = risk[1][1] if resolutionType == "shell": payoff = [x1, x0] elif resolutionType == 'cards': x1List = [x1 for i in range(int(p1 * 100))] x0List = [x0 for i in range(int(p0 * 100))] payoff = x1List + x0List random.shuffle(payoff) shuffledDict[key] = [taskType, resolutionType, payoff] return shuffledDict # Create PDFs def protectPDF(inputfile, password, outputfile): pdfFile = open(inputfile, 'rb') # Create reader and writer object pdfReader = PyPDF2.PdfFileReader(pdfFile) pdfWriter = PyPDF2.PdfFileWriter() # Add all pages to writer (accepted answer results into blank pages) for pageNum in range(pdfReader.numPages): pdfWriter.addPage(pdfReader.getPage(pageNum)) # Encrypt with your password pdfWriter.encrypt(password) # Write it to an output file. (you can delete unencrypted version now) resultPdf = open(outputfile, 'wb') pdfWriter.write(resultPdf) resultPdf.close() def verificationPDF( inputfile, bonusFileDict, shuffledDict, password_bonusFile): class PDF(FPDF): def footer(self): # Go to 1.5 cm from bottom self.set_y(-15) # Select Arial italic 8 self.set_font('DejaVu', 'I', 8) # Print centered page number self.cell(0, 10, 'Page %s' % self.page_no(), 0, 0, 'C') def titles(self, text): self.set_xy(0.0,0.0) self.set_font('DejaVu', 'B', 16) self.cell(w=210.0, h=40.0, align='C', txt=text, border=0) th = self.font_size self.ln(th) def subTitles(self, text): y_pos = self.get_y() self.set_xy(left, y_pos) self.set_font('DejaVu', 'B', 14) th = self.font_size self.multi_cell(w=pdf_w - 2 * left , h=th, align='L', txt=text, border=0) self.ln(th) def write_multicell(self, startx, text): starty = self.get_y() if starty <= top: starty = top self.set_xy(startx, starty) self.set_font('DejaVu', '', 12) th = self.font_size self.multi_cell(w=pdf_w - 2 * left , h=th * 1.2, align='J', txt=text, border=0) self.ln(th) def write_multicell_with_styles(self, startx, text_list, color = None): starty = self.get_y() self.set_font('DejaVu', '', 14) if starty <= top: starty = top self.set_xy(startx, starty) th = self.font_size #loop through differenct sections in different styles for text_part in text_list: #check and set style try: current_style = text_part['style'] self.set_font('DejaVu', current_style, 12) except KeyError: self.set_font('DejaVu', '', 12) if color: line_break = False if '\n' in text_part['text']: line_break = True # Need to use cells for writing text to achieve coloring self.set_fill_color(232, 232, 232) #loop through words and write them down space_width = self.get_string_width(' ') for word in text_part['text'].split(): #print(text_part['text']) current_pos = self.get_x() word_width = self.get_string_width(word + " ") + space_width #check for newline if (current_pos + word_width) > (pdf_w - left): self.cell(w = 0, h= th * 1.3, fill=True) self.set_y(self.get_y() + th * 1.2) self.set_x(startx) if int(round(self.get_x(),0)) != startx: self.cell(word_width, th * 1.3, " " + word, fill=True) else: self.cell(word_width, th * 1.3, word, fill=True) # Add line if last element and line break symbol if line_break and word == list(text_part['text'].split())[-1]: self.cell(w = 0, h= th * 1.3, fill=True) self.ln(th * 1.2) if text_part == text_list[-1]: ## Fill Last Line self.cell(w = 0, h= th * 1.3, fill=True) else: self.write(th * 1.2 , text_part['text']) self.ln(th) def enumeration(self, justify, number, text): self.set_font('DejaVu', '', 12) startx = self.get_x() + justify starty = self.get_y() th = self.font_size self.set_xy(startx, starty) self.cell(w=justify, h=th*1.2, align='L', txt=number, border=0) self.set_xy(startx + justify, starty) self.multi_cell(w=0, h=th * 1.2, align='L', txt=text, border=0) self.ln(th) def subEnumeration(self, justify, text): self.set_font('DejaVu', '', 12) startx = self.get_x() + 2 * justify starty = self.get_y() th = self.font_size self.set_xy(startx, starty) self.cell(w=justify, h=th * 1.2, align='L', txt="-", border=0) self.set_xy(startx + justify/2, starty) self.multi_cell(w=0, h=th*1.2, align='L', txt=text, border=0) self.ln(th) def table1(self, data): self.set_line_width(0.5) th = self.font_size col_width = (pdf_w - 2 * left) / 6 col_grid = [0.8, 0.8, 2.8, 0.8, 0.8] width_list = [col_width * space for space in col_grid] self.set_font('DejaVu', 'B', 12) self.cell(w=col_width, h=th*1.2, align='L', txt="Table 1: Content of the bonus files", border=0) self.ln(th * 1.5) startx = self.get_x() starty = self.get_y() self.line(startx, starty, startx+pdf_w - 2*left ,starty) self.cell(w=width_list[0], h=th*2, align='C', txt="Bonus file", border=0) self.cell(w=width_list[1], h=th*2, align='C', txt="Method", border=0) self.cell(w=width_list[2], h=th*2, align='C', txt="Option 1", border=0) self.cell(w=width_list[3], h=th*2, align='C', txt="Option 2", border=0) self.cell(w=width_list[4], h=th*2, align='C', txt="Details", border=0) self.line(startx, starty + th*2, startx + pdf_w - 2*left, starty + th*2) self.ln(th*2) self.set_font('DejaVu', '', 10) num = 1 page = 4 for key in data: if (num % 2) == 0: self.set_fill_color(200, 200, 200) else: self.set_fill_color(255, 255, 255) infos = data[key] resolutionMethod = infos[1].capitalize() risk = infos[2] option1= "£" + '%.2f' %risk[0][0] + " with " + '%.0f' %(risk[0][1] * 100) + "% probability and £" + '%.2f' %risk[1][0] + " otherwise." option2 = "£" + '%.2f' %infos[3] self.cell(w=width_list[0], h=th*1.2, align='C', txt=str(key), fill=True) self.cell(w=width_list[1], h=th*1.2, align='C', txt=str(resolutionMethod), fill=True) self.cell(w=width_list[2], h=th*1.2, align='C', txt=option1, fill=True) self.cell(w=width_list[3], h=th*1.2, align='C', txt=option2, fill=True) self.cell(w=width_list[4], h=th*1.2, align='C', txt="p. " + str(page), fill=True) self.ln(th*1.2) num += 1 page += 1 startx = self.get_x() starty = self.get_y() self.line(startx, starty, startx+pdf_w - 2*left ,starty) self.set_fill_color(255, 255, 255) def cardNumbersTable(self, colheaders, data, tableNumber): self.set_line_width(0.5) self.set_font('DejaVu', '', 10) th = self.font_size separationColWidth = 3 # Width of empty column in the middle of the table col_width = (pdf_w - 2 * left - separationColWidth) / (2*len(colheaders)) for key in data: self.set_font('DejaVu', 'B', 12) title = "Table " + str(tableNumber) + ": Winning numbers for the card draws in the " tableNumber += 1 #self.cell(w=col_width, h=th*1.2, align='L', txt=title, border=0) task = key + " Task" if list(key.split())[0] == "Red": self.set_fill_color(255,50,50) elif list(key.split())[0] == "Orange": self.set_fill_color(247,149,29) # write title self.write(txt=title) self.cell(w = self.get_string_width(task) + 3, txt = task, fill=True) self.ln(th * 1.5) startx = self.get_x() starty = self.get_y() self.line(startx, starty, startx+ 6 * col_width ,starty) self.line(startx + 6 * col_width + separationColWidth, starty, startx+pdf_w - 2*left ,starty) for i in range(2): self.cell(w=col_width, h=th*2, align='C', txt="", border=0) self.cell(w=col_width * (len(colheaders)-1), h=th*1.8, align='C', txt="Repetition", border=0) self.cell(w=separationColWidth, h=th*2, align='C', txt="", border=0) self.ln(th*1.5) for i in range(2): # Write Column Headers for head in colheaders: self.cell(w=col_width, h=th*1.8, align='C', txt=head, border=0) self.cell(w=separationColWidth, h=th*1.8, align='C', txt="", border=0) self.ln(th*2) starty = self.get_y() self.line(startx, starty, startx+ 6 * col_width ,starty) self.line(startx + 6 * col_width + separationColWidth, starty, startx+pdf_w - 2*left ,starty) # Write Content self.set_font('DejaVu', '', 10) num = 1 for rowheader in data[key]: if rowheader > 50: break rowcontent = data[key][rowheader] rowcontent_right = data[key][rowheader+50] header_print = str(rowheader) header_print_right = str(rowheader+50) if (num % 2) == 0: self.set_fill_color(200, 200, 200) else: self.set_fill_color(255, 255, 255) self.cell(w=col_width, h=th*1.4, align='C', txt=str(header_print), fill=True) for rowtext in rowcontent: self.cell(w=col_width, h=th*1.4, align='C', txt=str(rowtext), fill=True) self.cell(w=separationColWidth, h=th*1.4, align='C', txt="") # Empty cell, not filled #Right table column self.cell(w=col_width, h=th*1.4, align='C', txt=str(header_print_right), fill=True) for rowtext in rowcontent_right: self.cell(w=col_width, h=th*1.4, align='C', txt=str(rowtext), fill=True) self.ln(th*1.4) # Go to new line num += 1 startx = self.get_x() starty = self.get_y() self.line(startx, starty, startx+ 6 * col_width ,starty) self.line(startx + 6 * col_width + separationColWidth, starty, startx+pdf_w - 2*left ,starty) self.set_fill_color(255, 255, 255) if list(data.keys())[-1] != key: self.add_page() def shellGraph(self, img_L, img_R, payoff): graphicWidth = 120 unitWidth = graphicWidth / 7 graphicHeight = 60 startx = left + (pdf_w - 2 * left - graphicWidth)/2 starty = self.get_y() shelly = starty + (1/10) * graphicHeight shellx_left = startx + unitWidth shellx_right = startx + 4 * unitWidth self.set_x(startx) #Draw a border around self.cell(w = graphicWidth, h= graphicHeight, border=1) # Left shell self.set_xy(shellx_left, shelly ) self.image(img_L, link='', type='', w= 2 * unitWidth) # Right shell self.set_xy(shellx_right, shelly ) self.image(img_R, link='', type='', w= 2 * unitWidth) # destructure payoff payoff_1 = "£" + '%.2f' %payoff[0] payoff_2 = "£" + '%.2f' %payoff[1] self.set_font('Helvetica', '', 10) slipy = self.get_y() + (1/10) * graphicHeight # Left slip self.set_xy( shellx_left, slipy) self.cell(w= 2 * unitWidth, h= 0.6* text_height, align='C', txt=payoff_1, border=1) # Right slip self.set_xy( shellx_right, slipy) self.cell(w= 2 * unitWidth, h= 0.6* text_height, align='C', txt=payoff_2, border=1) # Move to below picture self.set_y(starty + graphicHeight) self.ln() def cardGraph(self, payoff): graphicWidth = 120 graphicHeight = 120 cardWidth = 8 cardHeight = 10 rowHeight = 11 startx = left + (pdf_w - 2 * left - graphicWidth)/2 starty = self.get_y() + 0.5 * rowHeight cardx_left = startx + cardWidth + 2 self.set_x(startx) #Draw a border around self.cell(w = graphicWidth, h= graphicHeight, border=1) # Draw cards self.set_xy( cardx_left ,starty) self.set_font('Helvetica', '', 8) uniquePay = list(set(payoff)) uniquePay.sort(reverse=True) # sorted # fill colors in RGB fillColors = [ [191, 214, 246], #blueish [255, 239, 215] #yellowish ] for i, pay in enumerate(payoff): if i > 0 and (i % 10) == 0: self.set_xy( cardx_left , self.get_y() + rowHeight) colInd = uniquePay.index(pay) coloring = fillColors[colInd] self.set_fill_color(coloring[0], coloring[1], coloring[2]) self.cell( w= cardWidth, h= cardHeight, align='C', txt="£" + '%.1f' %pay, border=1, fill=True) self.set_x( self.get_x() + 2) # Move to below picture self.set_y(starty + graphicHeight) self.ln() pdf = PDF() #our pdf object # Define width pdf_w=210 #A4 page is 240mm wide left = 10 top = 30 enumerationIndent = 10 text_height=14 option_height = 60 # Add Fonts import os cwd = os.getcwd() print(cwd) fonts = [ ('DejaVu', '', cwd + '/font/DejaVuSans.ttf'), ('DejaVu', 'B', cwd + '/font/DejaVuSans-Bold.ttf'), ('DejaVu', 'I', cwd + '/font/DejaVuSans-Oblique.ttf'), ('DejaVu', 'BI', cwd + '/font/DejaVuSans-BoldOblique.ttf'), ] for font in fonts: pdf.add_font(font[0], font[1], font[2], uni=True) pdf.set_font('DejaVu', '', 14) pdf.add_page() pdf.titles("Verification.pdf") intro_text = [{'text':'This document allows you to '}, {'style':'U','text':'personally verify'}, {'text':' that your bonus was determined fairly, exactly as described during the experiment.\n'}, {'text':'\n'}, {'text':'Specifically, you can verify the following points:\n'}] pdf.write_multicell_with_styles(left, intro_text) pdf.enumeration(enumerationIndent,"1.", "Your bonus task was selected fairly, because the set of bonus files that you chose from was not manipulated:") pdf.subEnumeration(enumerationIndent, "Had you chosen a different bonus file, a different task would have been your bonus task.") pdf.subEnumeration(enumerationIndent, "Each task was equally likely to be your bonus task, because each task was contained in exactly one bonus file.") pdf.enumeration(enumerationIndent,"2.", "The safe amounts of money in the bonus files vary fairly.") pdf.enumeration(enumerationIndent,"3.", "If your bonus was determined by the outcome of a risk, then the outcome was determined fairly, exactly as described during the experiment.") text = [{'style':'U','text':'Note:'}, {'text':' Because you downloaded this document at the beginning of the experiment, its content cannot depend on your behavior later in the experiment. That is, we―the experimenters―were committed to the content of this document before we knew what you would do. This is the reason why this document allows you to verify our truthfulness.\n'},] pdf.write_multicell_with_styles(left, text) pdf.add_page() pdf.subTitles("1. Proof that your bonus task was selected fairly, as explained during the experiment") text = [ {'text':'Early in the experiment, you chose a numbered bonus file. Now that you have the password to your bonus file (it is "'}, {'text':password_bonusFile}, {'text':'"), you can verify that this bonus file indeed contains the task that was relevant for your bonus.\n'}, {'text':'Table 1 lists the tasks contained in all bonus files that you could have chosen. You can verify that the task described in your bonus file is the same as the task described in Table 1. For example, if you chose BonusFile'}, {'text': str(list(bonusFileDict.keys())[-1])}, {'text':'.pdf, you can verify that the task described therein is indeed the one described in the last row of Table 1.\n\n' }, {'text':'Recall that you downloaded this document at the beginning of the experiment, before choosing your bonus file. At that time, we could not know which bonus file you would choose and download. If your bonus task would be different to the task described in Table 1, this manipulation would become apparent now.\n'}, {'text':'Moreover, note that each task that you completed during the experiment was contained in exactly one bonus file. Therefore, each of the three tasks was equally likely to be your bonus task.\n \n'}, ] pdf.write_multicell_with_styles(left, text) pdf.table1(bonusFileDict) pdf.ln() pdf.ln() pdf.subTitles("2. Proof that the safe amounts of money vary fairly") text = [ {'text':'The column "Option 2" in Table 1 allows you to verify that the safe amounts of money vary fairly. That is, we did not select many or especially low amounts that could have resulted in a lower bonus for you.\n'}, ] pdf.write_multicell_with_styles(left, text) pdf.add_page() pdf.subTitles("3. Proof that all bonus-relevant risks were resolved fairly according to the stated probabilities") text = [ {'text': "This section explains how you can verify that the outcome of your bonus-relevant risk was determined fairly. "}, {'text': "Remember that you downloaded this file before resolving any risk, i.e., before we knew which shell ('L' or 'R') or which card you would choose.\n \n"}, {'text': "For each task, we explain on a separate page how the risk was resolved. "}, {'text': "You can directly go to the page that is shown next to your bonus file in Table 1."}, ] pdf.write_multicell_with_styles(left, text) for key, value in bonusFileDict.items(): pdf.add_page() ## add Page in the beginning to have correct number of pages taskType = value[0] resolutionType = value[1] risk = value[2] x1 = risk[0][0] p1 = risk[0][1] x0 = risk[1][0] p0 = risk[1][1] shuffledPayoff = shuffledDict[key][2] if resolutionType == 'shell': shell_L = "_static/img/trapezoid_L.png" shell_R = "_static/img/trapezoid_R.png" title = "Risk resolution using shells – Evaluation" upper_text = [ {'text': "To resolve the risk using the shells, you had to click on either the left ('L') or right ('R') shell. "}, {'text': "Before the experiment, we assigned a monetary outcome to the left shell ('L') and another monetary outcome to the right shell ('R'). "}, {'text': "You received the outcome that was assigned to the shell you clicked. "}, {'text': "The graphic below shows the lifted shells. "}, {'text': "With this, you can verify that the same graphic was indeed shown on your screen when you resolved the risk. \n"}, ] lower_text = [ {'text': "As shell 'L' shows the outcome £" + '%.2f' %shuffledPayoff[0]}, {'text': ", and shell 'R' shows the outcome £"+ '%.2f' %shuffledPayoff[1]}, {'text': ", each outcome indeed had a probability of 50%. "}, {'text': "Because we could not know which shell you would click at the time we prepared this document, we could not manipulate the outcome.\n"}, ] #Write title pdf.subTitles(title) pdf.write_multicell_with_styles(left, upper_text) pdf.shellGraph(shell_L, shell_R, shuffledPayoff) # Get from random order! pdf.write_multicell_with_styles(left, lower_text) elif resolutionType == 'cards': if taskType == "certaintyEquivalent": title = "Risk resolution using cards – Evaluation" elif taskType == "probabilityEquivalent": title = "Risk resolution using cards – Probability evaluation" upper_text = [ {'text': "To resolve the risk using the cards, you had to click on a card from a well-shuffled card deck. "}, #{'text': "The cards were numbered from 1 to 100. "}, {'text': "Before the experiment, we assigned a monetary outcome to each card. "}, {'text': "You received the outcome that was assigned to the card you clicked. "}, {'text': "The graphic below shows you all cards face up. "}, {'text': "With this, you can verify that the same graphic was indeed shown on your screen when you resolved the risk. \n"}, ] lower_text = [ {'text': "As there are " + str(int(p1*100)) + " cards that show the outcome £" + '%.2f' %x1}, {'text': ", and " + str(int(p0*100)) + " cards that show the outcome £" + '%.2f' %x0}, {'text': ", the probability of obtaining each outcome was indeed as stated. "}, {'text': "Because we could not know which card you would click at the time we prepared this document, we could not manipulate the outcome.\n"}, ] #Write title pdf.subTitles(title) pdf.write_multicell_with_styles(left, upper_text) pdf.cardGraph(shuffledPayoff) pdf.write_multicell_with_styles(left, lower_text) pdf.output(inputfile, 'F') def createVerificationPDF( bonusFileDict, shuffledDict, password, password_bonusFile, id ): # Create Folder if not existing folder = "_static/bonusFiles/" + str(id) + "/" folder_no_pwd = "_static/bonusFiles/" + str(id) + "/no_pwd/" Path(folder).mkdir(parents=True, exist_ok=True) Path(folder_no_pwd).mkdir(parents=True, exist_ok=True) inputfile = folder_no_pwd + "Verification.pdf" outputfile = folder + "Verification.pdf" verificationPDF( inputfile, bonusFileDict, shuffledDict, password_bonusFile) protectPDF(inputfile, password, outputfile) # Testing # Create verification pdf containing this information bonusFileDict = { 307: ['certaintyEquivalent', 'shell', [[8.0, 0.5], [2.0, 0.5]], 3.87], 560: ['certaintyEquivalent', 'cards', [[26, 0.02], [4.6, 0.98]], 8.71], 596: ['probabilityEquivalent', 'cards', [[8.0, 0.13], [2.0, 0.87]], 5.0]} shuffledDict = shuffleOutcomes(bonusFileDict) password_verification = "Loewe50" password_bonusFile = "Loewe50" createVerificationPDF( bonusFileDict, shuffledDict, password_verification, password_bonusFile, 1001 )