Skip to content

Commit

Permalink
Added: Add bonus_points to block view and use it in Matching Pairs (#…
Browse files Browse the repository at this point in the history
…1168)

* feat: Add bonus points to experiment response

* chore: Fix linting issues

* feat: Integrate block into store

* feat: Add bonus points as default value to "total" score to MatchingPairs component

* feat: Add bonus points to Block interface and update Section interface

The Block interface now includes a `bonus_points` property of type number. Additionally, the Section interface has been added with properties `id`, `group`, and `url`.

* refactor: Remove (set)BlockLoading from store as it's unused

* refactor: Remove section group property as it's no longer used in the frontend (only used in backend now)
  • Loading branch information
drikusroor authored Jul 9, 2024
1 parent 279bd39 commit afabc59
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 84 deletions.
8 changes: 3 additions & 5 deletions backend/experiment/rules/matching_pairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def first_round(self, experiment):
playlist,
explainer
]

def next_round(self, session):
if session.rounds_passed() < 1:
trials = self.get_questionnaire(session)
Expand Down Expand Up @@ -104,7 +104,7 @@ def select_sections(self, session):
random.shuffle(pairs)
selected_pairs = pairs[:self.num_pairs]
session.save_json_data({'pairs': pairs[self.num_pairs:]})
originals = session.playlist.section_set.filter(group__in=selected_pairs, tag='Original')
originals = session.playlist.section_set.filter(group__in=selected_pairs, tag='Original')
degradations = json_data.get('degradations')
if not degradations:
degradations = ['Original', '1stDegradation', '2ndDegradation']
Expand Down Expand Up @@ -141,7 +141,7 @@ def get_matching_pairs_trial(self, session):
def calculate_score(self, result, data):
''' not used in this experiment '''
pass

def calculate_intermediate_score(self, session, result):
''' will be called every time two cards have been turned '''
result_data = json.loads(result)
Expand All @@ -168,5 +168,3 @@ def calculate_intermediate_score(self, session, result):
prepare_result('move', session, json_data=result_data,
score=score, given_response=given_response)
return score


10 changes: 9 additions & 1 deletion backend/experiment/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ def test_get_experiment(self):
file='test-image.jpg'
),
rules=Hooked.ID,
theme_config=create_theme_config()
theme_config=create_theme_config(),
rounds=3,
bonus_points=42,
)
participant = Participant.objects.create()
Session.objects.bulk_create([
Expand All @@ -261,6 +263,12 @@ def test_get_experiment(self):
self.assertEqual(
response.json()['theme']['footer']['disclaimer'], '<p>Test Disclaimer</p>'
)
self.assertEqual(
response.json()['rounds'], 3
)
self.assertEqual(
response.json()['bonus_points'], 42
)


def create_theme_config() -> ThemeConfig:
Expand Down
1 change: 1 addition & 0 deletions backend/experiment/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def get_experiment(request, slug):
'image': serialize_image(experiment.image) if experiment.image else None,
'class_name': class_name, # can be used to override style
'rounds': experiment.rounds,
'bonus_points': experiment.bonus_points,
'playlists': [
{'id': playlist.id, 'name': playlist.name}
for playlist in experiment.playlists.all()
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,15 @@ interface ScoreIntermediateResultParams {
result: unknown;
}

interface ScoreIntermediateResultResponse {
score: number;
}

export const scoreIntermediateResult = async ({
session,
participant,
result,
}: ScoreIntermediateResultParams) => {
}: ScoreIntermediateResultParams): Promise<ScoreIntermediateResultResponse | null> => {
try {
const vars = {
session_id: session.id,
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/Block/Block.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const Block = ({ match }) => {
const session = useBoundStore((state) => state.session);
const theme = useBoundStore((state) => state.theme);
const setTheme = useBoundStore((state) => state.setTheme);
const setBlock = useBoundStore((state) => state.setBlock);

const setHeadData = useBoundStore((state) => state.setHeadData);
const resetHeadData = useBoundStore((state) => state.resetHeadData);
Expand Down Expand Up @@ -122,6 +123,8 @@ const Block = ({ match }) => {
},
});

setBlock(block);

// Set theme
if (block.theme) {
setTheme(block.theme);
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/components/Block/Block.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { vi } from 'vitest';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import Block from './Block';
import * as API from '../../API';
import * as API from '../../API';

let mock = new MockAdapter(axios);

Expand Down Expand Up @@ -37,12 +37,13 @@ vi.mock('../../util/stores', () => ({
setSession: vi.fn(),
setHeadData: vi.fn(),
resetHeadData: vi.fn(),
setBlock: vi.fn(),
};

return fn(state);
},
useBoundStore: vi.fn()
}));
}));

describe('Block Component', () => {

Expand All @@ -55,7 +56,7 @@ describe('Block Component', () => {
mock.onGet().replyOnce(200, experimentObj);
render(
<MemoryRouter>
<Block match={ {params: {slug: 'test'}} }/>
<Block match={{ params: { slug: 'test' } }} />
</MemoryRouter>
);
await screen.findByText('Continue');
Expand All @@ -76,4 +77,4 @@ describe('Block Component', () => {
await waitFor(() => expect(spy).toHaveBeenCalled());
});

});
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import { vi } from 'vitest';
import { vi, describe, beforeEach, afterEach, test, expect } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import * as API from '../../API';

import MatchingPairs, { SCORE_FEEDBACK_DISPLAY } from './MatchingPairs';

let mock;
let mock: MockAdapter;

vi.mock("@/components/PlayButton/PlayCard", () => ({
default: props => <div data-testid="play-card" {...props} />
Expand All @@ -19,7 +19,8 @@ vi.mock("../../util/stores", () => ({
const state = {
participant: 1,
session: 1,
setError: vi.fn()
setError: vi.fn(),
block: { bonus_points: 42 }
};
return fn(state);
},
Expand All @@ -37,10 +38,10 @@ describe('MatchingPairs Component', () => {
})

const mockSections = [
{ id: 1, content: 'Card 1', url: '/cat-01.jpg', inactive: false, turned: false, noevents: false, seen: false, group: 1 },
{ id: 2, content: 'Card 2', url: '/cat-02.jpg', inactive: false, turned: false, noevents: false, seen: false, group: 2 },
{ id: 3, content: 'Card 1', url: '/cat-01.jpg', inactive: false, turned: false, noevents: false, seen: false, group: 1 },
{ id: 4, content: 'Card 2', url: '/cat-02.jpg', inactive: false, turned: false, noevents: false, seen: false, group: 2 },
{ id: 1, content: 'Card 1', url: '/cat-01.jpg', inactive: false, turned: false, noevents: false, seen: false },
{ id: 2, content: 'Card 2', url: '/cat-02.jpg', inactive: false, turned: false, noevents: false, seen: false },
{ id: 3, content: 'Card 1', url: '/cat-01.jpg', inactive: false, turned: false, noevents: false, seen: false },
{ id: 4, content: 'Card 2', url: '/cat-02.jpg', inactive: false, turned: false, noevents: false, seen: false },
];

const baseProps = {
Expand Down Expand Up @@ -74,7 +75,7 @@ describe('MatchingPairs Component', () => {
fireEvent.click(cards[0]);
fireEvent.click(cards[2]);

await waitFor(() => expect(getByText('Score: 110')).not.toBeNull());
await waitFor(() => expect(getByText('Score: 52')).not.toBeNull());
});

test.skip('has a blocking overlay in-between turns', async () => {
Expand All @@ -88,7 +89,7 @@ describe('MatchingPairs Component', () => {
fireEvent.click(cards[1]);

await new Promise(r => setTimeout(r, 1));
expect(screen.getByTestId('overlay').style.display).toBe('block')
expect(screen.getByTestId('overlay').style.display).toBe('block')
});

test.skip('calls scoreIntermediateResult after each turn', async () => {
Expand Down Expand Up @@ -121,7 +122,7 @@ describe('MatchingPairs Component', () => {
fireEvent.click(screen.getByTestId('overlay'));
await new Promise(r => setTimeout(r, 1));

expect(screen.getByTestId('score').textContent).toBe('Score: 110');
expect(screen.getByTestId('score').textContent).toBe('Score: 10');
expect(cards[0].classList.contains('disabled')).toBe(true);
expect(cards[2].classList.contains('disabled')).toBe(true);

Expand All @@ -130,7 +131,7 @@ describe('MatchingPairs Component', () => {
await new Promise(r => setTimeout(r, 1));
fireEvent.click(screen.getByTestId('overlay'));
await new Promise(r => setTimeout(r, 1));
expect(screen.getByTestId('score').textContent).toBe('Score: 120');
expect(screen.getByTestId('score').textContent).toBe('Score: 20');
expect(cards[1].classList.contains('disabled')).toBe(true);
expect(cards[3].classList.contains('disabled')).toBe(true);
expect(submitResult).toHaveBeenCalled();
Expand Down
Loading

0 comments on commit afabc59

Please sign in to comment.