import random from otree.api import * doc = """ Participants are assigned to one condition (Human, AI, or Collab). They evaluate 4 organizations in random order, each shown with one social media post. Each post displays 1 image from a randomly selected initiative (4 of 8 possible), with the image and caption matching the participant's assigned condition. """ # --------------------------------------------------------------------------- # 8 initiatives, each with 3 condition variants (image + caption) # --------------------------------------------------------------------------- INITIATIVES = [ dict( initiative_id="CHS", image_by_condition=dict( Human="img/chs_human.png", AI="img/chs_ai.png", Collab="img/chs_collab.png", ), caption_by_condition=dict( Human=( "As part of our commitment to preventative care, we are launching a new initiative to offer basic health screenings in at-risk communities. Healthcare professionals conduct an initial risk-screening as well as a more detailed health evaluation for those most in need." ), AI=( "As part of our commitment to preventative care, we are launching a new initiative to offer basic health screenings in at-risk communities. AI-driven tools conduct an initial risk-screening along with a more detailed health evaluation for those most in need." ), Collab=( "As part of our commitment to preventative care, we are launching a new initiative to offer basic health screenings in at-risk communities. AI-driven tools conduct an initial risk-screening before healthcare professionals perform more detailed health evaluations for those most in need." ), ), ), dict( initiative_id="DPT", image_by_condition=dict( Human="img/dpt_human.png", AI="img/dpt_ai.png", Collab="img/dpt_collab.png", ), caption_by_condition=dict( Human=( "As part of our efforts to build resilience in disaster-prone regions, we are offering preparedness trainings for local populations. Facilitators guide participants through a series of training modules followed by a Q&A session with a staff member to address any outstanding concerns." ), AI=( "As part of our efforts to build resilience in disaster-prone regions, we are offering preparedness trainings for local populations. An AI platform guides participants through a series of training modules followed by a Q&A session with a virtual chatbot to address any outstanding concerns." ), Collab=( "As part of our efforts to build resilience in disaster-prone regions, we are offering preparedness trainings for local populations. An AI platform guides participants through a series of training modules followed by a Q&A session with a staff member to address any outstanding concerns." ), ), ), dict( initiative_id="PES", image_by_condition=dict( Human="img/pes_human.png", AI="img/pes_ai.png", Collab="img/pes_collab.png", ), caption_by_condition=dict( Human=( "To strengthen our operational readiness for disaster response, we are prepositioning emergency supplies in strategically selected locations. Logistics specialists identify optimal storage sites and ensure efficient delivery of these critical resources." ), AI=( "To strengthen our operational readiness for disaster response, we are prepositioning emergency supplies in strategically selected locations. AI models identify optimal storage sites and ensure efficient delivery of these critical resources." ), Collab=( "To strengthen our operational readiness for disaster response, we are prepositioning emergency supplies in strategically selected locations. AI models identify optimal storage sites and logistics specialists ensure efficient delivery of these critical resources." ), ), ), dict( initiative_id="RMIA", image_by_condition=dict( Human="img/rmia_human.png", AI="img/rmia_ai.png", Collab="img/rmia_collab.png", ), caption_by_condition=dict( Human=( "As part of our commitment to proactive disaster preparedness, we are conducting risk mapping and infrastructure assessments throughout our operational territories. Meteorologists identify potential disaster-prone areas for engineers to inspect infrastructure needs and risk." ), AI=( "As part of our commitment to proactive disaster preparedness, we are conducting risk mapping and infrastructure assessments throughout our operational territories. Meteorological AI prediction systems identify potential disaster-prone areas for AI vision models to inspect infrastructure needs and risk." ), Collab=( "As part of our commitment to proactive disaster preparedness, we are conducting risk mapping and infrastructure assessments throughout our operational territories. Meteorological AI prediction systems identify potential disaster-prone areas for engineers to inspect infrastructure needs and risk." ), ), ), dict( initiative_id="SRO", image_by_condition=dict( Human="img/sro_human.png", AI="img/sro_ai.png", Collab="img/sro_collab.png", ), caption_by_condition=dict( Human=( "As part of our efforts to reduce disaster-related loss of life, we are prioritizing the enhancement of our search and rescue operations. Through the combination of aerial and on-the-ground first responders, victims of disasters are located and safely extracted." ), AI=( "As part of our efforts to reduce disaster-related loss of life, we are prioritizing the enhancement of our search and rescue operations. Through the combination of AI-enabled aerial and on-the-ground rescue robots, victims of disasters are located and safely extracted." ), Collab=( "As part of our efforts to reduce disaster-related loss of life, we are prioritizing the enhancement of our search and rescue operations. Through the combination of AI-enabled aerial rescue robots and on-the-ground first responders, victims of disasters are located and safely extracted." ), ), ), dict( initiative_id="DER", image_by_condition=dict( Human="img/der_human.png", AI="img/der_ai.png", Collab="img/der_collab.png", ), caption_by_condition=dict( Human=( "To ensure effective relief allocation during disasters, we are launching a targeted initiative focused on improving distribution efforts. Emergency response coordinators identify areas of urgent need and deliver essential supplies to disaster victims." ), AI=( "To ensure effective relief allocation during disasters, we are launching a targeted initiative focused on improving distribution efforts. AI models identify areas of urgent need and automated drones deliver essential supplies to disaster victims." ), Collab=( "To ensure effective relief allocation during disasters, we are launching a targeted initiative focused on improving distribution efforts. AI models identify areas of urgent need and emergency response coordinators deliver essential supplies to disaster victims." ), ), ), dict( initiative_id="LSCM", image_by_condition=dict( Human="img/lscm_human.png", AI="img/lscm_ai.png", Collab="img/lscm_collab.png", ), caption_by_condition=dict( Human=( "In the aftermath of a disaster, we are strengthening our supply chain management efforts to ensure lifesaving aid reaches those in need. Supply chain experts actively monitor inventory levels and submit urgent requests to facilitate faster and more reliable replenishment of critical aid." ), AI=( "In the aftermath of a disaster, we are strengthening our supply chain management efforts to ensure lifesaving aid reaches those in need. AI models actively monitor inventory levels and automate the submission of urgent requests to facilitate faster and more reliable replenishment of critical aid." ), Collab=( "In the aftermath of a disaster, we are strengthening our supply chain management efforts to ensure lifesaving aid reaches those in need. AI models actively monitor inventory levels, enabling supply chain experts to submit urgent requests to facilitate faster and more reliable replenishment of critical aid." ), ), ), dict( initiative_id="CLGN", image_by_condition=dict( Human="img/clgn_human.png", AI="img/clgn_ai.png", Collab="img/clgn_collab.png", ), caption_by_condition=dict( Human=( "As part of our commitment to deepening community engagement, we are launching a new initiative to collaborate with local partners. Outreach officers develop comprehensive disaster response plans and distribute these plans to local governments and NGOs, improving disaster response efforts." ), AI=( "As part of our commitment to deepening community engagement, we are launching a new initiative to collaborate with local partners. AI models develop comprehensive disaster response plans and automate the distribution of these plans to local governments and NGOs, improving disaster response efforts." ), Collab=( "As part of our commitment to deepening community engagement, we are launching a new initiative to collaborate with local partners. AI models develop comprehensive disaster response plans, which are then distributed by outreach officers to local governments and NGOs, improving disaster response efforts." ), ), ), ] # --------------------------------------------------------------------------- # 4 organizations (identity only β€” no condition or images baked in) # --------------------------------------------------------------------------- ORGS = [ dict( org_id="compassionatehearts", name="CompassionateHearts", mission_statement=( "CompassionateHearts is dedicated to easing human suffering by ensuring " "thoughtful, responsive support reaches individuals facing crisis." ), profile_image_by_condition=dict( Human="img/CompassionateHeartsLogo_human.png", AI="img/CompassionateHeartsLogo_ai.png", Collab="img/CompassionateHeartsLogo_collab.png", ), ), dict( org_id="hopeaid", name="HopeAid", mission_statement=( "HopeAid is committed to restoring stability in times of hardship by " "ensuring essential aid and resources reach communities in need." ), profile_image_by_condition=dict( Human="img/HopeAidLogo_human.png", AI="img/HopeAidLogo_ai.png", Collab="img/HopeAidLogo_collab.png", ), ), dict( org_id="aidalliance", name="AidAlliance", mission_statement=( "AidAlliance is committed to bridging gaps in disaster response by ensuring " "swift, reliable assistance reaches those who need it most." ), profile_image_by_condition=dict( Human="img/AidAllianceLogo_human.png", AI="img/AidAllianceLogo_ai.png", Collab="img/AidAllianceLogo_collab.png", ), ), dict( org_id="carecoalition", name="CareCoalition", mission_statement=( "CareCoalition works to strengthen community resilience by ensuring " "coordinated services help individuals recover and rebuild." ), profile_image_by_condition=dict( Human="img/CareCoalitionLogo_human.png", AI="img/CareCoalitionLogo_ai.png", Collab="img/CareCoalitionLogo_collab.png", ), ), ] CONDITIONS = ["Human", "AI", "Collab"] CONDITION_HASHTAGS = dict( Human=dict( compassionatehearts="#HumansForGood", hopeaid="#HumansForHope", aidalliance="#HumanLedAid", carecoalition="#HumanCare", ), AI=dict( compassionatehearts="#AIForGood", hopeaid="#AIForHope", aidalliance="#AILedAid", carecoalition="#AICare", ), Collab=dict( compassionatehearts="#CollaborationForGood", hopeaid="#CollaborationForHope", aidalliance="#CollaborativeAid", carecoalition="#CollaborativeCare", ), ) PHASE_HASHTAGS = dict( CHS="#HealthPrepared", DPT="#DisasterPrepared", PES="#SupplyPrepared", RMIA="#RiskPrepared", SRO="#RescueResponse", DER="#ReliefResponse", LSCM="#SupplyChainResponse", CLGN="#CommunityResponse", ) RELATIONSHIP_HASHTAGS = dict( CHS="#OnTheGround", DPT="#OnTheGround", SRO="#OnTheGround", DER="#OnTheGround", PES="#BehindTheScenes", RMIA="#BehindTheScenes", LSCM="#BehindTheScenes", CLGN="#BehindTheScenes", ) INITIATIVE_EMOJIS = dict( CHS="β€οΈβ€πŸ©ΉπŸ₯", DPT="πŸ“‹πŸ›‘οΈ", PES="πŸ“¦πŸšš", RMIA="πŸ—ΊοΈπŸ”", SRO="πŸšπŸ†˜", DER="πŸ“πŸ€", LSCM="πŸ”—πŸ“Š", CLGN="🌍🀝", ) LIKERT_7 = [ [1, "1 - Not at all"], [2, "2 - Very slightly"], [3, "3 - Slightly"], [4, "4 - Moderately"], [5, "5 - Considerably"], [6, "6 - Very much"], [7, "7 - Extremely"], ] LIKERT_5_SUCCESS = [ [1, "Not at all successful"], [2, "Not very successful"], [3, "Somewhat successful"], [4, "Mostly successful"], [5, "Very successful"], ] LIKERT_5_AGREEMENT = [ [1, "Strongly disagree"], [2, "Disagree"], [3, "Neither agree nor disagree"], [4, "Agree"], [5, "Strongly agree"], ] # --------------------------------------------------------------------------- # 18 mind-perception items β€” labels used in the HTML matrix # --------------------------------------------------------------------------- MIND_PERCEPTION_ITEMS = [ dict(field="action", label="Action", description="carrying out actions"), dict(field="communication", label="Communication", description="conveying thoughts or feelings to others"), #dict(field="consciousness", label="Consciousness", description="having experiences and being aware of things"), dict(field="desire", label="Desire", description="having desires"), #dict(field="embarrassment", label="Embarrassment", description="experiencing embarrassment"), dict(field="emotion_recognition", label="Emotion Recognition", description="experiencing emotions"), dict(field="feeling", label="Feeling", description="feeling"), #dict(field="fear", label="Fear", description="feeling afraid or fearful"), #dict(field="hunger", label="Hunger", description="feeling hungry"), #dict(field="joy", label="Joy", description="experiencing joy"), #dict(field="memory", label="Memory", description="remembering things"), dict(field="morality", label="Morality", description="telling right from wrong"), #dict(field="pain", label="Pain", description="experiencing physical or emotional pain"), dict(field="personality", label="Personality", description="having personality traits"), dict(field="planning", label="Planning", description="planning"), #dict(field="pleasure", label="Pleasure", description="experiencing physical or emotional pleasure"), #dict(field="pride", label="Pride", description="experiencing pride"), #dict(field="rage", label="Rage", description="experiencing violent or uncontrolled anger"), #dict(field="self_control", label="Self-Control", description="exercising self-restraint over desires, emotions, or impulses"), dict(field="thought", label="Thought", description="thinking"), ] class C(BaseConstants): NAME_IN_URL = "org_evaluation" PLAYERS_PER_GROUP = None NUM_ROUNDS = 4 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): org_id = models.StringField() org_name = models.StringField() condition = models.StringField() initiative_id = models.StringField() profile_seconds = models.FloatField(blank=True) # 18 mind-perception items (1-5 scale: Not at all to Extremely) action = models.IntegerField(choices=LIKERT_7, label="") communication = models.IntegerField(choices=LIKERT_7, label="") #consciousness = models.IntegerField(choices=LIKERT_7, label="") desire = models.IntegerField(choices=LIKERT_7, label="") #embarrassment = models.IntegerField(choices=LIKERT_7, label="") emotion_recognition = models.IntegerField(choices=LIKERT_7, label="") feeling = models.IntegerField(choices=LIKERT_7, label="") #fear = models.IntegerField(choices=LIKERT_7, label="") #hunger = models.IntegerField(choices=LIKERT_7, label="") #joy = models.IntegerField(choices=LIKERT_7, label="") #memory = models.IntegerField(choices=LIKERT_7, label="") morality = models.IntegerField(choices=LIKERT_7, label="") #pain = models.IntegerField(choices=LIKERT_7, label="") personality = models.IntegerField(choices=LIKERT_7, label="") planning = models.IntegerField(choices=LIKERT_7, label="") #pleasure = models.IntegerField(choices=LIKERT_7, label="") #pride = models.IntegerField(choices=LIKERT_7, label="") #rage = models.IntegerField(choices=LIKERT_7, label="") #self_control = models.IntegerField(choices=LIKERT_7, label="") thought = models.IntegerField(choices=LIKERT_7, label="") initiative_success = models.IntegerField( choices=LIKERT_5_SUCCESS, label="How successful do you believe this initiative would be?", blank=True, ) mission_alignment = models.IntegerField( choices=LIKERT_5_AGREEMENT, label="Please indicate your level of agreement with the following statement: This initiative aligns with the mission of the organization.", blank=True, ) # --------------------------------------------------------------------------- # Session-level setup # --------------------------------------------------------------------------- def get_participant_condition(participant): condition = participant.vars.get("condition") if condition is None: # Count how many participants already have each condition counts = {"Human": 0, "AI": 0, "Collab": 0} for p in participant.session.get_participants(): c = p.vars.get("condition") if c in counts: counts[c] += 1 # Assign the condition with the fewest participants condition = min(counts, key=counts.get) participant.vars["condition"] = condition return condition def get_participant_setup(participant): """ Assign and store: - org_sequence: randomized order of the 4 orgs - initiative_sequence: 4 randomly chosen initiatives (from the 8) Each org gets paired with one unique initiative. """ if "org_sequence" not in participant.vars: participant.vars["org_sequence"] = random.sample(ORGS, len(ORGS)) participant.vars["initiative_sequence"] = random.sample(INITIATIVES, len(ORGS)) return ( participant.vars["org_sequence"], participant.vars["initiative_sequence"], ) def creating_session(subsession: Subsession): for player in subsession.get_players(): condition = get_participant_condition(player.participant) org_sequence, initiative_sequence = get_participant_setup(player.participant) current_org = org_sequence[player.round_number - 1] current_initiative = initiative_sequence[player.round_number - 1] player.org_id = current_org["org_id"] player.org_name = current_org["name"] player.condition = condition player.initiative_id = current_initiative["initiative_id"] # --------------------------------------------------------------------------- # Pages # --------------------------------------------------------------------------- class OrgProfile(Page): form_model = "player" form_fields = [ "profile_seconds", "action", "communication", #"consciousness", "desire", #"embarrassment", "emotion_recognition", "feeling", #"fear", #"hunger", #"joy", #"memory", "morality", #"pain", "personality", "planning", #"pleasure", #"pride", #"rage", #"self_control", "thought", "initiative_success", "mission_alignment", ] @staticmethod def vars_for_template(player: Player): condition = get_participant_condition(player.participant) org_sequence, initiative_sequence = get_participant_setup(player.participant) current_org = org_sequence[player.round_number - 1] current_initiative = initiative_sequence[player.round_number - 1] hashtag_1 = CONDITION_HASHTAGS[condition][current_org["org_id"]] hashtag_2 = PHASE_HASHTAGS[current_initiative["initiative_id"]] hashtag_3 = RELATIONSHIP_HASHTAGS[current_initiative["initiative_id"]] emojis = INITIATIVE_EMOJIS[current_initiative["initiative_id"]] hashtags = f"{emojis} {hashtag_1} {hashtag_2} {hashtag_3}" return dict( org=current_org, post_image=current_initiative["image_by_condition"][condition], post_caption=current_initiative["caption_by_condition"][condition], profile_image=current_org["profile_image_by_condition"]["Human"], hashtags=hashtags, round_number=player.round_number, total_rounds=C.NUM_ROUNDS, mind_items=MIND_PERCEPTION_ITEMS, ) @staticmethod def before_next_page(player: Player, timeout_happened): active_fields = [ "action", "communication", "desire", "emotion_recognition", "feeling", "morality", "personality", "planning", "thought", ] total = sum(player.field_maybe_none(f) or 0 for f in active_fields) max_possible = len(active_fields) * 7 pct = round((total / max_possible) * 100) if "org_scores" not in player.participant.vars: player.participant.vars["org_scores"] = {} player.participant.vars["org_scores"][player.org_id] = pct # Store hashtags per org condition = get_participant_condition(player.participant) org_sequence, initiative_sequence = get_participant_setup(player.participant) current_org = org_sequence[player.round_number - 1] current_initiative = initiative_sequence[player.round_number - 1] hashtag_1 = CONDITION_HASHTAGS[condition][current_org["org_id"]] hashtag_2 = PHASE_HASHTAGS[current_initiative["initiative_id"]] hashtag_3 = RELATIONSHIP_HASHTAGS[current_initiative["initiative_id"]] emojis = INITIATIVE_EMOJIS[current_initiative["initiative_id"]] hashtags = f"{emojis} {hashtag_1} {hashtag_2} {hashtag_3}" if "org_hashtags" not in player.participant.vars: player.participant.vars["org_hashtags"] = {} player.participant.vars["org_hashtags"][player.org_id] = hashtags @staticmethod def error_message(player: Player, values): active_fields = [ "action", "communication", "desire", "emotion_recognition", "feeling", "morality", "personality", "planning", "thought", ] for field in active_fields: if values.get(field) is None: return "Please answer each item before continuing." page_sequence = [OrgProfile]