Skip to content

Commit

Permalink
fix: add relations between study participant and game player(s)
Browse files Browse the repository at this point in the history
this is a more reliable way to keep track of the games that a
participant played, using the User relation involves some guesswork
  • Loading branch information
sgfost committed Oct 14, 2024
1 parent 9907194 commit 69c0b15
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 2 deletions.
15 changes: 15 additions & 0 deletions server/src/entity/ProlificStudyParticipant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToOne, PrimaryGeneratedColumn
import { ProlificStudy } from "./ProlificStudy";
import { User } from "./User";
import { SoloGameTreatment } from "./SoloGameTreatment";
import { SoloPlayer } from "./SoloPlayer";

@Entity()
export class ProlificStudyParticipant {
Expand Down Expand Up @@ -35,4 +36,18 @@ export class ProlificStudyParticipant {

@Column()
prolificVariableTreatmentId!: number;

@OneToOne(type => SoloPlayer, { nullable: true })
@JoinColumn()
prolificBaselinePlayer!: SoloPlayer;

@Column({ nullable: true })
prolificBaselinePlayerId!: number;

@OneToOne(type => SoloPlayer, { nullable: true })
@JoinColumn()
prolificVariablePlayer!: SoloPlayer;

@Column({ nullable: true })
prolificVariablePlayerId!: number;
}
24 changes: 24 additions & 0 deletions server/src/migration/1727891252710-AddProlificParticipantPlayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class AddProlificParticipantPlayer1727891252710 implements MigrationInterface {
name = 'AddProlificParticipantPlayer1727891252710'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "prolific_study_participant" ADD "prolificBaselinePlayerId" integer`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" ADD CONSTRAINT "UQ_da2b1f49f29eef7941ab06eca11" UNIQUE ("prolificBaselinePlayerId")`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" ADD "prolificVariablePlayerId" integer`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" ADD CONSTRAINT "UQ_b5a15c60ff91e4b754305c64137" UNIQUE ("prolificVariablePlayerId")`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" ADD CONSTRAINT "FK_da2b1f49f29eef7941ab06eca11" FOREIGN KEY ("prolificBaselinePlayerId") REFERENCES "solo_player"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" ADD CONSTRAINT "FK_b5a15c60ff91e4b754305c64137" FOREIGN KEY ("prolificVariablePlayerId") REFERENCES "solo_player"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "prolific_study_participant" DROP CONSTRAINT "FK_b5a15c60ff91e4b754305c64137"`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" DROP CONSTRAINT "FK_da2b1f49f29eef7941ab06eca11"`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" DROP CONSTRAINT "UQ_b5a15c60ff91e4b754305c64137"`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" DROP COLUMN "prolificVariablePlayerId"`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" DROP CONSTRAINT "UQ_da2b1f49f29eef7941ab06eca11"`);
await queryRunner.query(`ALTER TABLE "prolific_study_participant" DROP COLUMN "prolificBaselinePlayerId"`);
}

}
7 changes: 5 additions & 2 deletions server/src/rooms/sologame/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ export class SetGameParamsCmd extends CmdWithoutPayload {

export class PersistGameCmd extends CmdWithoutPayload {
async execute() {
const { sologame: service } = getServices();
const game = await service.createGame(this.state);
const { sologame, study } = getServices();
const game = await sologame.createGame(this.state);
if (this.state.type === "prolificVariable" || this.state.type === "prolificBaseline") {
await study.setProlificParticipantPlayer(this.state.type, game.player);
}
this.state.gameId = game.id;
// keep track of deck card db ids after persisting the deck
this.state.eventCardDeck.forEach((card, index) => {
Expand Down
1 change: 1 addition & 0 deletions server/src/services/sologame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export class SoloGameService extends BaseService {
return gameRepo.findOneOrFail({
where: { id: game.id },
relations: {
player: true,
deck: {
cards: true,
},
Expand Down
66 changes: 66 additions & 0 deletions server/src/services/study.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,72 @@ export class StudyService extends BaseService {
return participant;
}

async setProlificParticipantPlayer(
gameType: Extract<SoloGameType, "prolificBaseline" | "prolificVariable">,
player: SoloPlayer
): Promise<ProlificStudyParticipant> {
const participant = await this.getParticipantRepository().findOneOrFail({
where: { userId: player.userId },
relations: [`${gameType}Player`],
});
if (gameType === "prolificBaseline") {
participant.prolificBaselinePlayer = player;
} else {
participant.prolificVariablePlayer = player;
}
return this.getParticipantRepository().save(participant);
}

async getAllParticipantPoints(
studyId: string
): Promise<Array<{ prolificId: string; points: number }>> {
const study = await this.getProlificStudy(studyId);
if (!study) {
throw new ServerError({
code: 404,
message: `Invalid study ID: ${studyId}`,
displayMessage: `Invalid study ID: ${studyId}`,
});
}
const participants = await this.getParticipantRepository().find({
where: { studyId: study.id },
relations: {
prolificBaselinePlayer: {
game: true,
},
prolificVariablePlayer: {
game: true,
},
},
});
return (
participants
// filter out participants who haven't fully completed both games
.filter(
p =>
p.prolificBaselinePlayer?.game &&
p.prolificVariablePlayer?.game &&
p.prolificBaselinePlayer.points &&
p.prolificVariablePlayer.points
)
// return an array of prolificId and total points earned, if they lost they get 0 points
.map(p => {
const prolificBaselinePoints =
p.prolificBaselinePlayer.game.status === "victory"
? p.prolificBaselinePlayer.points!
: 0;
const prolificVariablePoints =
p.prolificVariablePlayer.game.status === "victory"
? p.prolificVariablePlayer.points!
: 0;
return {
prolificId: p.prolificId,
points: prolificBaselinePoints + prolificVariablePoints,
};
})
);
}

async getRandomTreatmentFor(
gameType: Extract<SoloGameType, "prolificBaseline" | "prolificVariable">
): Promise<SoloGameTreatment> {
Expand Down

0 comments on commit 69c0b15

Please sign in to comment.