Skip to content

Commit 7946115

Browse files
committed
Assign participant roles for agent participants
1 parent b6af6c0 commit 7946115

File tree

3 files changed

+104
-68
lines changed

3 files changed

+104
-68
lines changed

functions/src/agent_participant.utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import {createAgentChatMessageFromPrompt} from './chat/chat.agent';
1717
import {completeProfile} from './stages/profile.utils';
1818
import {getAgentParticipantRankingStageResponse} from './stages/ranking.agent';
19+
import {assignRolesToParticipants} from './stages/role.utils';
1920
import {getAgentParticipantSurveyStageResponse} from './stages/survey.agent';
2021
import {
2122
getExperimenterData,
@@ -100,6 +101,15 @@ export async function completeStageAsAgentParticipant(
100101
await completeStage();
101102
participantDoc.set(participant);
102103
break;
104+
case StageKind.ROLE:
105+
await assignRolesToParticipants(
106+
experimentId,
107+
participant.currentCohortId,
108+
stage.id,
109+
);
110+
await completeStage();
111+
participantDoc.set(participant);
112+
break;
103113
case StageKind.SALESPERSON:
104114
createAgentChatMessageFromPrompt(
105115
experimentId,

functions/src/stages/role.endpoints.ts

Lines changed: 2 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
getFirestoreStage,
1111
getFirestoreStagePublicDataRef,
1212
} from '../utils/firestore';
13+
import {assignRolesToParticipants} from './role.utils';
1314

1415
import * as admin from 'firebase-admin';
1516
import * as functions from 'firebase-functions';
@@ -34,72 +35,5 @@ export const setParticipantRoles = onCall(async (request) => {
3435
const cohortId = data.cohortId;
3536
const stageId = data.stageId;
3637

37-
// Define role stage config
38-
const stage = await getFirestoreStage(experimentId, stageId);
39-
if (stage.kind !== StageKind.ROLE) {
40-
return {success: false};
41-
}
42-
43-
// Define role stage public data document reference
44-
const publicDoc = getFirestoreStagePublicDataRef(
45-
experimentId,
46-
cohortId,
47-
stageId,
48-
);
49-
50-
await app.firestore().runTransaction(async (transaction) => {
51-
const publicStageData = (
52-
await publicDoc.get()
53-
).data() as RoleStagePublicData;
54-
55-
// Get relevant (active, in cohort) participants
56-
const participants = await getFirestoreActiveParticipants(
57-
experimentId,
58-
cohortId,
59-
stageId,
60-
);
61-
62-
// TODO: For each participant, check if they have been assigned a role
63-
// If not, assign role according to stage minimum/maximums
64-
const getRoleCounts = () => {
65-
const roleToFrequencyMap: Record<string, number> = {};
66-
Object.values(publicStageData.participantMap).forEach((role) => {
67-
roleToFrequencyMap[role] = (roleToFrequencyMap[role] ?? 0) + 1;
68-
});
69-
return roleToFrequencyMap;
70-
};
71-
const getNextRole = () => {
72-
const roleToFrequencyMap = getRoleCounts();
73-
// First, fill roles with minimum number of participants required
74-
for (const role of stage.roles) {
75-
const roleFrequency = roleToFrequencyMap[role.id] ?? 0;
76-
if (
77-
roleFrequency < role.minParticipants &&
78-
roleFrequency < role.maxParticipants
79-
) {
80-
return role;
81-
}
82-
}
83-
// Otherwise, randomly pick role
84-
const availableRoles = stage.roles.filter(
85-
(role) =>
86-
(roleToFrequencyMap[role.id] ?? 0) < role.maxParticipants ||
87-
role.maxParticipants === null,
88-
);
89-
return availableRoles[Math.floor(Math.random() * availableRoles.length)];
90-
91-
return null;
92-
};
93-
94-
for (const participant of participants) {
95-
if (!publicStageData.participantMap[participant.publicId]) {
96-
publicStageData.participantMap[participant.publicId] =
97-
getNextRole()?.id ?? '';
98-
}
99-
}
100-
101-
transaction.set(publicDoc, publicStageData);
102-
}); // end transaction
103-
104-
return {success: true};
38+
return await assignRolesToParticipants(experimentId, cohortId, stageId);
10539
});

functions/src/stages/role.utils.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {
2+
RoleItem,
3+
RoleStageConfig,
4+
RoleStagePublicData,
5+
StageKind,
6+
} from '@deliberation-lab/utils';
7+
8+
import {
9+
getFirestoreActiveParticipants,
10+
getFirestoreStage,
11+
getFirestoreStagePublicDataRef,
12+
} from '../utils/firestore';
13+
14+
import * as admin from 'firebase-admin';
15+
import * as functions from 'firebase-functions';
16+
17+
import {app} from '../app';
18+
/** Assign roles to participants for given stage. */
19+
export async function assignRolesToParticipants(
20+
experimentId: string,
21+
cohortId: string,
22+
stageId: string,
23+
) {
24+
// Define role stage config
25+
const stage = await getFirestoreStage(experimentId, stageId);
26+
if (stage.kind !== StageKind.ROLE) {
27+
return {success: false};
28+
}
29+
30+
// Define role stage public data document reference
31+
const publicDoc = getFirestoreStagePublicDataRef(
32+
experimentId,
33+
cohortId,
34+
stageId,
35+
);
36+
37+
await app.firestore().runTransaction(async (transaction) => {
38+
const publicStageData = (
39+
await publicDoc.get()
40+
).data() as RoleStagePublicData;
41+
42+
// Get relevant (active, in cohort) participants
43+
const participants = await getFirestoreActiveParticipants(
44+
experimentId,
45+
cohortId,
46+
stageId,
47+
);
48+
49+
// TODO: For each participant, check if they have been assigned a role
50+
// If not, assign role according to stage minimum/maximums
51+
const getRoleCounts = () => {
52+
const roleToFrequencyMap: Record<string, number> = {};
53+
Object.values(publicStageData.participantMap).forEach((role) => {
54+
roleToFrequencyMap[role] = (roleToFrequencyMap[role] ?? 0) + 1;
55+
});
56+
return roleToFrequencyMap;
57+
};
58+
const getNextRole = () => {
59+
const roleToFrequencyMap = getRoleCounts();
60+
// First, fill roles with minimum number of participants required
61+
for (const role of stage.roles) {
62+
const roleFrequency = roleToFrequencyMap[role.id] ?? 0;
63+
if (
64+
roleFrequency < role.minParticipants &&
65+
roleFrequency < role.maxParticipants
66+
) {
67+
return role;
68+
}
69+
}
70+
// Otherwise, randomly pick role
71+
const availableRoles = stage.roles.filter(
72+
(role) =>
73+
(roleToFrequencyMap[role.id] ?? 0) < role.maxParticipants ||
74+
role.maxParticipants === null,
75+
);
76+
return availableRoles[Math.floor(Math.random() * availableRoles.length)];
77+
78+
return null;
79+
};
80+
81+
for (const participant of participants) {
82+
if (!publicStageData.participantMap[participant.publicId]) {
83+
publicStageData.participantMap[participant.publicId] =
84+
getNextRole()?.id ?? '';
85+
}
86+
}
87+
88+
transaction.set(publicDoc, publicStageData);
89+
}); // end transaction
90+
91+
return {success: true};
92+
}

0 commit comments

Comments
 (0)