Skip to content

Customize your BACE function outputs

Two key functions can be defined in /app/bace/user_convert.py to further customize your BACE application outputs.

Add components to a user profile

When the initial call to the create_profile route is activated, all inputs associated with the call will be assigned to the respondent's profile as a dictionary with profile_id, a uniquely created ID, as the key. Examples of these inputs could be the respondent's demographic characteristics. The route then returns the unique profile_id and the first optimal design.

When you modify /app/bace/add_to_profile(profile), you can add other data to each respondent's profile. These are additional characteristics added to a respondent's profile that will be held fixed throughout an experiment. For example, you can add a timestamp of when the respondent initiates the create_profile call. Another handy use is to randomize respondents into treatments. For example, a treatment group can be created based on each respondent's profile.

These additional characteristics can then be accessed in the convert_design function described below.

def add_to_profile(profile):
    # Add code to update profile
    return profile

For example, suppose you want to randomize individuals into a treatment and control group.

import random

def add_to_profile(profile):
    treatment_group = ['Control', 'Treated']
    profile['treatment_group'] = random.choice(treatment_group)
    return profile

Convert Design Output

You can define the convert_design() function, which can be used to edit raw designs (specified as a specific design_params) to be more user-friendly. For example, you can format designs into HTML tables or graphs.

The function takes as input:

  • design (dict) - the optimal design. This is a dictionary with each key from design_params.
  • profile (dict) - respondent's profile. This is a dictionary containing all information from the respondent's DynamoDB profile. This includes information from the body of the HTTP request from the initial <your-URL>/create_profile API request. It also includes information added through add_to_profile().
  • request_data (dict) - information from the body of the HTTP request from the most recent <your-URL>/update_profile API request.
# Convert design
def convert_design(design, profile, request_data):

    # Add code to edit design. Return dict that will be sent to user.

    return output_design

For example, suppose you want to present the design output (the question to ask the respondent) differently based on the respondent's treatment group (as set by the add_to_profile() function):

# Convert design
def convert_design(design, profile, request_data):

    # Add code to edit design. Return dict that will be sent to user.
    if profile['treatment_group'] == 'Treated':
        # Add code to modify the design for the treatment group
        output_design = design  # replace with actual modification code
        return output_design
    else:
        # Add code to modify the design for the control group
        output_design = design  # replace with actual modification code
        return output_design

An example

Your BACE package template comes with an example /app/bace/user_convert.py. The code is reproduced below with the associated output. This formatted output can be used directly in integration with survey platforms such as with Qualtrics integration.

def choice_message(label, price, color, pen_type):

    price = '${:,.2f}'.format(price)

    # Create the HTML table
    html_table = f"""
        <table width='300px' border='1' cellpadding='1' cellspacing='1' style='font-family: Arial, Tahoma, "Helvetica Neue", Helvetica, sans-serif; border-collapse:collapse; background-color:#eee9e7; color:black;'>
            <tbody>
                <tr>
                    <th style="text-align: center; background-color: #ded4ce;"><b>{label}</b></th>
                </tr>
                <tr>
                    <td style="text-align: center;"><em>Price:</em><br> {price}</td>
                </tr>
                <tr>
                    <td style="text-align: center;"><em>Pen Color:</em><br> {color}</td>
                </tr>
                <tr>
                    <td style="text-align: center;"><em>Pen Type:</em><br> {pen_type}</td>
                </tr>
            </tbody>
        </table>
    """
    return html_table

def convert_design(design, profile, request_data):

    # Number of questions
    Q = request_data.get('question_number') or len(profile.get('design_history'))

    output_design = {f'{key}_{Q}': value for key, value in design.items()}

    output_design[f'message_0_{Q}'] = choice_message("Pen A", design['price_a'], design['color_a'], design['type_a'])
    output_design[f'message_1_{Q}'] = choice_message("Pen B", design['price_b'], design['color_b'], design['type_b'])

    return output_design

Given the design below:

design = dict(
    price_a = 4.22,
    price_b = 4.27,
    color_a = 'blue',
    color_b = 'black',
    type_a  = 'gel',
    type_b  = 'gel'
)

The code will assign text values (HTML code) to two different options that a respondent can choose from. When these are displayed in a survey website, the result is as follows:

Output Design Example

Summary

These two functions handle many of the changes that you will want to add when running your experiment. For example, add_to_profile() can be used to specify profile-specific information that is consistently used across an experiment for an individual respondent. This information will be stored alongside the respondent's profile and can be accessed elsewhere in the application by the respondent's profile_id.

convert_design() is a very useful function for converting a raw design (JSON object) into user-friendly text. For example, you could use this function to format designs as HTML tables, incorporate images, or create figures that reflect the optimal design. (These features can be achieved using survey-side code as well, but converting designs within your application allows for extensive flexibility and can expand what a "design" is.)