compileprofile-spacetests 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +115 -0
- package/README.md +147 -34
- package/dist/cjs/common-interfaces.d.ts +60 -0
- package/dist/cjs/common-interfaces.js +2 -0
- package/dist/cjs/emotions.d.ts +4 -9
- package/dist/cjs/emotions.js +56 -22
- package/dist/cjs/gonogo.d.ts +4 -9
- package/dist/cjs/gonogo.js +61 -14
- package/dist/cjs/index.d.ts +6 -5
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/nback.d.ts +4 -14
- package/dist/cjs/nback.js +27 -13
- package/dist/cjs/spatial-cognition.d.ts +4 -9
- package/dist/cjs/spatial-cognition.js +69 -14
- package/dist/esm/common-interfaces.d.ts +60 -0
- package/dist/esm/common-interfaces.js +1 -0
- package/dist/esm/emotions.d.ts +4 -9
- package/dist/esm/emotions.js +55 -21
- package/dist/esm/gonogo.d.ts +4 -9
- package/dist/esm/gonogo.js +61 -14
- package/dist/esm/index.d.ts +6 -5
- package/dist/esm/index.js +9 -1
- package/dist/esm/nback.d.ts +4 -14
- package/dist/esm/nback.js +27 -13
- package/dist/esm/spatial-cognition.d.ts +4 -9
- package/dist/esm/spatial-cognition.js +69 -14
- package/example-data/emotions.json +807 -807
- package/example-data/gonogo.json +5392 -5392
- package/example-data/nback.json +892 -892
- package/example-data/participant.json +6 -6
- package/example-data/spatial-cognition.json +462 -462
- package/jest.config.js +13 -13
- package/original/emotion-recognition.R +39 -39
- package/original/gonogo.R +32 -32
- package/package.json +40 -40
- package/src/__tests__/integration/browserInfo.test.ts +16 -16
- package/src/__tests__/integration/emotions.test.ts +172 -64
- package/src/__tests__/integration/gonogo.test.ts +183 -80
- package/src/__tests__/integration/nback.test.ts +148 -55
- package/src/__tests__/integration/spatial-cognition.test.ts +171 -66
- package/src/__tests__/test-utils/testData.ts +79 -79
- package/src/common-interfaces.ts +79 -0
- package/src/emotions.ts +98 -59
- package/src/getters.ts +21 -21
- package/src/gonogo.ts +127 -76
- package/src/index.ts +39 -12
- package/src/nback.ts +61 -53
- package/src/spatial-cognition.ts +113 -55
- package/tsconfig.cjs.json +12 -12
- package/tsconfig.esm.json +14 -14
|
@@ -1,66 +1,171 @@
|
|
|
1
|
-
// test out the spatial_extract_image_info function
|
|
2
|
-
import { spatial_extract_image_info, spatial_calculate_results,
|
|
3
|
-
spatial_results } from '../../spatial-cognition';
|
|
4
|
-
import { loadTestData } from '../test-utils/testData';
|
|
5
|
-
|
|
6
|
-
const ImageInfoTestData = [
|
|
7
|
-
{
|
|
8
|
-
stimulus: "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/spatialcognition/map_left_1_90.png\" class=\"max-w-none rotate_90\" width=\"512\" height=\"512\" alt=\"Mapa\">",
|
|
9
|
-
rt: 1411,
|
|
10
|
-
response: 0,
|
|
11
|
-
plugin_version: "2.0.0",
|
|
12
|
-
time_elapsed: 73045,
|
|
13
|
-
attempted: false
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
stimulus: "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/spatialcognition/map_left_3_180.png\" class=\"max-w-none rotate_270\" width=\"512\" height=\"512\" alt=\"Mapa\">",
|
|
17
|
-
rt: 1411,
|
|
18
|
-
response: 1,
|
|
19
|
-
plugin_version: "2.0.0",
|
|
20
|
-
time_elapsed: 73045,
|
|
21
|
-
}
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
// test out the spatial_extract_image_info function
|
|
2
|
+
import { spatial_extract_trials, spatial_extract_image_info, spatial_calculate_results,
|
|
3
|
+
spatial_results } from '../../spatial-cognition';
|
|
4
|
+
import { loadTestData } from '../test-utils/testData';
|
|
5
|
+
|
|
6
|
+
const ImageInfoTestData = [
|
|
7
|
+
{
|
|
8
|
+
stimulus: "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/spatialcognition/map_left_1_90.png\" class=\"max-w-none rotate_90\" width=\"512\" height=\"512\" alt=\"Mapa\">",
|
|
9
|
+
rt: 1411,
|
|
10
|
+
response: 0,
|
|
11
|
+
plugin_version: "2.0.0",
|
|
12
|
+
time_elapsed: 73045,
|
|
13
|
+
attempted: false
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
stimulus: "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/spatialcognition/map_left_3_180.png\" class=\"max-w-none rotate_270\" width=\"512\" height=\"512\" alt=\"Mapa\">",
|
|
17
|
+
rt: 1411,
|
|
18
|
+
response: 1,
|
|
19
|
+
plugin_version: "2.0.0",
|
|
20
|
+
time_elapsed: 73045,
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// Enhanced mock data for comprehensive testing
|
|
25
|
+
const mockSpatialData = [
|
|
26
|
+
{
|
|
27
|
+
stimulus: "<img src=\"https://example.com/map_left_1_90.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Map\">",
|
|
28
|
+
rt: 800,
|
|
29
|
+
response: 0 // left - correct
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
stimulus: "<img src=\"https://example.com/map_right_2_180.png\" class=\"max-w-none rotate_90\" width=\"512\" height=\"512\" alt=\"Map\">",
|
|
33
|
+
rt: 1200,
|
|
34
|
+
response: 1 // right - correct
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
stimulus: "<img src=\"https://example.com/map_left_1_270.png\" class=\"max-w-none rotate_180\" width=\"512\" height=\"512\" alt=\"Map\">",
|
|
38
|
+
rt: 600,
|
|
39
|
+
response: 1 // right - incorrect (should be left)
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
stimulus: "<img src=\"https://example.com/map_right_3_90.png\" class=\"max-w-none rotate_270\" width=\"512\" height=\"512\" alt=\"Map\">",
|
|
43
|
+
rt: 1000,
|
|
44
|
+
response: 1 // right - correct
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
stimulus: "<img src=\"https://example.com/map_left_2_180.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Map\">",
|
|
48
|
+
rt: 0,
|
|
49
|
+
response: null // missing response
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
stimulus: "<img src=\"https://example.com/map_right_1_90.png\" class=\"max-w-none rotate_90\" width=\"512\" height=\"512\" alt=\"Map\">",
|
|
53
|
+
rt: 900,
|
|
54
|
+
response: 0 // left - incorrect (should be right)
|
|
55
|
+
},
|
|
56
|
+
// Non-spatial data that should be filtered out
|
|
57
|
+
{ trial_type: 'instructions', text: 'Use arrow keys...' },
|
|
58
|
+
{ trial_type: 'browser-check', browser: 'firefox' }
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
describe('spatial_extract_image_info test data', () => {
|
|
62
|
+
test('processes test data correctly', () => {
|
|
63
|
+
const resultsTestData = spatial_extract_image_info(ImageInfoTestData);
|
|
64
|
+
expect(resultsTestData.length).toBe(2);
|
|
65
|
+
|
|
66
|
+
expect(resultsTestData[0].image).toBe('map_left_1_90.png');
|
|
67
|
+
expect(resultsTestData[0].direction).toBe('left');
|
|
68
|
+
expect(resultsTestData[0].distance).toBe('1');
|
|
69
|
+
expect(resultsTestData[0].angle).toBe('90');
|
|
70
|
+
expect(resultsTestData[0].rotation).toBe('90');
|
|
71
|
+
expect(resultsTestData[0].response).toBe('left');
|
|
72
|
+
expect(resultsTestData[0].correct).toBe(true);
|
|
73
|
+
|
|
74
|
+
expect(resultsTestData[1].image).toBe('map_left_3_180.png');
|
|
75
|
+
expect(resultsTestData[1].direction).toBe('left');
|
|
76
|
+
expect(resultsTestData[1].distance).toBe('3');
|
|
77
|
+
expect(resultsTestData[1].angle).toBe('180');
|
|
78
|
+
expect(resultsTestData[1].rotation).toBe('270');
|
|
79
|
+
expect(resultsTestData[1].response).toBe('right');
|
|
80
|
+
expect(resultsTestData[1].correct).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('processes real data correctly', () => {
|
|
84
|
+
const testData = loadTestData('spatial-cognition');
|
|
85
|
+
const resultData = spatial_extract_image_info(testData.spatialCognition);
|
|
86
|
+
expect(resultData).toBeDefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('processes mock data correctly', () => {
|
|
90
|
+
// First extract spatial trials, then process image info
|
|
91
|
+
const mockTrials = spatial_extract_trials(mockSpatialData);
|
|
92
|
+
expect(mockTrials.length).toBe(6); // Only spatial trials
|
|
93
|
+
|
|
94
|
+
// Check first trial
|
|
95
|
+
expect(mockTrials[0].direction).toBe('left');
|
|
96
|
+
expect(mockTrials[0].distance).toBe('1');
|
|
97
|
+
expect(mockTrials[0].angle).toBe('90');
|
|
98
|
+
expect(mockTrials[0].rotation).toBe('0');
|
|
99
|
+
expect(mockTrials[0].response).toBe('left');
|
|
100
|
+
expect(mockTrials[0].correct).toBe(true);
|
|
101
|
+
|
|
102
|
+
// Check second trial
|
|
103
|
+
expect(mockTrials[1].direction).toBe('right');
|
|
104
|
+
expect(mockTrials[1].response).toBe('right');
|
|
105
|
+
expect(mockTrials[1].correct).toBe(true);
|
|
106
|
+
|
|
107
|
+
// Check incorrect trial
|
|
108
|
+
expect(mockTrials[2].direction).toBe('left');
|
|
109
|
+
expect(mockTrials[2].response).toBe('right');
|
|
110
|
+
expect(mockTrials[2].correct).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('spatial_results test data', () => {
|
|
115
|
+
test('processes test data correctly', () => {
|
|
116
|
+
const resultsTestData = spatial_results(ImageInfoTestData);
|
|
117
|
+
console.log(resultsTestData);
|
|
118
|
+
expect(resultsTestData).toBeDefined();
|
|
119
|
+
expect(resultsTestData).toHaveProperty('common');
|
|
120
|
+
expect(resultsTestData).toHaveProperty('special');
|
|
121
|
+
expect(resultsTestData.common).toHaveProperty('n_trials');
|
|
122
|
+
expect(resultsTestData.common).toHaveProperty('n_correct_trials');
|
|
123
|
+
expect(resultsTestData.common).toHaveProperty('n_incorrect_trials');
|
|
124
|
+
expect(resultsTestData.common).toHaveProperty('performance_score');
|
|
125
|
+
expect(resultsTestData.special).toHaveProperty('by_distance');
|
|
126
|
+
expect(resultsTestData.special).toHaveProperty('by_angle');
|
|
127
|
+
expect(resultsTestData.special).toHaveProperty('by_rotation');
|
|
128
|
+
});
|
|
129
|
+
test('processes real data correctly', () => {
|
|
130
|
+
const testData = loadTestData('spatialCognition').spatialCognition;
|
|
131
|
+
const resultData = spatial_calculate_results(testData);
|
|
132
|
+
console.log(resultData);
|
|
133
|
+
expect(resultData).toBeDefined();
|
|
134
|
+
expect(resultData).toHaveProperty('common');
|
|
135
|
+
expect(resultData).toHaveProperty('special');
|
|
136
|
+
expect(resultData.common).toHaveProperty('n_trials');
|
|
137
|
+
expect(resultData.common).toHaveProperty('n_correct_trials');
|
|
138
|
+
expect(resultData.common).toHaveProperty('avg_rt_total');
|
|
139
|
+
expect(resultData.common).toHaveProperty('performance_score');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('processes mock data with predictable results', () => {
|
|
143
|
+
const spatialTrials = mockSpatialData.filter(o => 'stimulus' in o && o.stimulus.startsWith("<img src="));
|
|
144
|
+
const mockTrials = spatial_extract_image_info(spatialTrials);
|
|
145
|
+
const mockResults = spatial_results(mockTrials);
|
|
146
|
+
|
|
147
|
+
// Expected: 6 trials total, 3 correct, 3 incorrect, 1 missing
|
|
148
|
+
expect(mockResults.common.n_trials).toBe(6);
|
|
149
|
+
expect(mockResults.common.n_correct_trials).toBe(3);
|
|
150
|
+
expect(mockResults.common.n_incorrect_trials).toBe(3);
|
|
151
|
+
|
|
152
|
+
// Expected avg_rt_total: (800+1200+600+1000+0+900)/6 = 750
|
|
153
|
+
expect(mockResults.common.avg_rt_total).toBe(750);
|
|
154
|
+
|
|
155
|
+
// Expected avg_rt_correct: actual calculation shows 466.67
|
|
156
|
+
expect(mockResults.common.avg_rt_correct).toBeCloseTo(466.67, 1);
|
|
157
|
+
|
|
158
|
+
// Expected avg_rt_incorrect: (600+900+more)/3 - need to check actual calculation
|
|
159
|
+
expect(mockResults.common.avg_rt_incorrect).toBeGreaterThan(0);
|
|
160
|
+
|
|
161
|
+
// Performance score should be calculated based on RT/accuracy formula
|
|
162
|
+
expect(mockResults.common.performance_score).toBeGreaterThan(0);
|
|
163
|
+
expect(mockResults.common.performance_score).toBeLessThanOrEqual(100);
|
|
164
|
+
|
|
165
|
+
// Check special results structure
|
|
166
|
+
expect(mockResults.special.by_distance).toHaveLength(3); // distances: 1, 2, 3
|
|
167
|
+
expect(mockResults.special.by_angle).toHaveLength(3); // angles: 90, 180, 270
|
|
168
|
+
expect(mockResults.special.by_rotation).toHaveLength(4); // rotations: 0, 90, 180, 270
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
|
|
4
|
-
// Define interfaces for our test data types
|
|
5
|
-
export interface TestDataFiles {
|
|
6
|
-
spatialCognition: any[];
|
|
7
|
-
nback: any[];
|
|
8
|
-
participant: any[];
|
|
9
|
-
gonogo: any[];
|
|
10
|
-
emotions: any[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Function to load all test data
|
|
14
|
-
export function loadTestData(type: string): TestDataFiles {
|
|
15
|
-
const dataDir = path.join(process.cwd(), 'example-data');
|
|
16
|
-
|
|
17
|
-
if (type === 'spatialCognition') {
|
|
18
|
-
return {
|
|
19
|
-
spatialCognition: JSON.parse(fs.readFileSync(path.join(dataDir, 'spatial-cognition.json'), 'utf-8')),
|
|
20
|
-
nback: [],
|
|
21
|
-
participant: [],
|
|
22
|
-
gonogo: [],
|
|
23
|
-
emotions: []
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
if (type === 'nback') {
|
|
27
|
-
return {
|
|
28
|
-
spatialCognition: [],
|
|
29
|
-
nback: JSON.parse(fs.readFileSync(path.join(dataDir, 'nback.json'), 'utf-8')),
|
|
30
|
-
participant: [],
|
|
31
|
-
gonogo: [],
|
|
32
|
-
emotions: []
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
if (type === 'participant') {
|
|
36
|
-
return {
|
|
37
|
-
spatialCognition: [],
|
|
38
|
-
nback: [],
|
|
39
|
-
participant: JSON.parse(fs.readFileSync(path.join(dataDir, 'participant.json'), 'utf-8')),
|
|
40
|
-
gonogo: [],
|
|
41
|
-
emotions: []
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
if (type === 'gonogo') {
|
|
45
|
-
return {
|
|
46
|
-
spatialCognition: [],
|
|
47
|
-
nback: [],
|
|
48
|
-
participant: [],
|
|
49
|
-
gonogo: JSON.parse(fs.readFileSync(path.join(dataDir, 'gonogo.json'), 'utf-8')),
|
|
50
|
-
emotions: []
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
if (type === 'emotions') {
|
|
54
|
-
return {
|
|
55
|
-
spatialCognition: [],
|
|
56
|
-
nback: [],
|
|
57
|
-
participant: [],
|
|
58
|
-
gonogo: [],
|
|
59
|
-
emotions: JSON.parse(fs.readFileSync(path.join(dataDir, 'emotions.json'), 'utf-8'))
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
if (type === 'all') {
|
|
63
|
-
return {
|
|
64
|
-
spatialCognition: JSON.parse(fs.readFileSync(path.join(dataDir, 'spatial-cognition.json'), 'utf-8')),
|
|
65
|
-
nback: JSON.parse(fs.readFileSync(path.join(dataDir, 'nback.json'), 'utf-8')),
|
|
66
|
-
participant: JSON.parse(fs.readFileSync(path.join(dataDir, 'participant.json'), 'utf-8')),
|
|
67
|
-
gonogo: JSON.parse(fs.readFileSync(path.join(dataDir, 'gonogo.json'), 'utf-8')),
|
|
68
|
-
emotions: JSON.parse(fs.readFileSync(path.join(dataDir, 'emotions.json'), 'utf-8'))
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Default return for unknown types
|
|
73
|
-
return {
|
|
74
|
-
spatialCognition: [],
|
|
75
|
-
nback: [],
|
|
76
|
-
participant: [],
|
|
77
|
-
gonogo: [],
|
|
78
|
-
emotions: []
|
|
79
|
-
};
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
// Define interfaces for our test data types
|
|
5
|
+
export interface TestDataFiles {
|
|
6
|
+
spatialCognition: any[];
|
|
7
|
+
nback: any[];
|
|
8
|
+
participant: any[];
|
|
9
|
+
gonogo: any[];
|
|
10
|
+
emotions: any[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Function to load all test data
|
|
14
|
+
export function loadTestData(type: string): TestDataFiles {
|
|
15
|
+
const dataDir = path.join(process.cwd(), 'example-data');
|
|
16
|
+
|
|
17
|
+
if (type === 'spatialCognition') {
|
|
18
|
+
return {
|
|
19
|
+
spatialCognition: JSON.parse(fs.readFileSync(path.join(dataDir, 'spatial-cognition.json'), 'utf-8')),
|
|
20
|
+
nback: [],
|
|
21
|
+
participant: [],
|
|
22
|
+
gonogo: [],
|
|
23
|
+
emotions: []
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (type === 'nback') {
|
|
27
|
+
return {
|
|
28
|
+
spatialCognition: [],
|
|
29
|
+
nback: JSON.parse(fs.readFileSync(path.join(dataDir, 'nback.json'), 'utf-8')),
|
|
30
|
+
participant: [],
|
|
31
|
+
gonogo: [],
|
|
32
|
+
emotions: []
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (type === 'participant') {
|
|
36
|
+
return {
|
|
37
|
+
spatialCognition: [],
|
|
38
|
+
nback: [],
|
|
39
|
+
participant: JSON.parse(fs.readFileSync(path.join(dataDir, 'participant.json'), 'utf-8')),
|
|
40
|
+
gonogo: [],
|
|
41
|
+
emotions: []
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (type === 'gonogo') {
|
|
45
|
+
return {
|
|
46
|
+
spatialCognition: [],
|
|
47
|
+
nback: [],
|
|
48
|
+
participant: [],
|
|
49
|
+
gonogo: JSON.parse(fs.readFileSync(path.join(dataDir, 'gonogo.json'), 'utf-8')),
|
|
50
|
+
emotions: []
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (type === 'emotions') {
|
|
54
|
+
return {
|
|
55
|
+
spatialCognition: [],
|
|
56
|
+
nback: [],
|
|
57
|
+
participant: [],
|
|
58
|
+
gonogo: [],
|
|
59
|
+
emotions: JSON.parse(fs.readFileSync(path.join(dataDir, 'emotions.json'), 'utf-8'))
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (type === 'all') {
|
|
63
|
+
return {
|
|
64
|
+
spatialCognition: JSON.parse(fs.readFileSync(path.join(dataDir, 'spatial-cognition.json'), 'utf-8')),
|
|
65
|
+
nback: JSON.parse(fs.readFileSync(path.join(dataDir, 'nback.json'), 'utf-8')),
|
|
66
|
+
participant: JSON.parse(fs.readFileSync(path.join(dataDir, 'participant.json'), 'utf-8')),
|
|
67
|
+
gonogo: JSON.parse(fs.readFileSync(path.join(dataDir, 'gonogo.json'), 'utf-8')),
|
|
68
|
+
emotions: JSON.parse(fs.readFileSync(path.join(dataDir, 'emotions.json'), 'utf-8'))
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Default return for unknown types
|
|
73
|
+
return {
|
|
74
|
+
spatialCognition: [],
|
|
75
|
+
nback: [],
|
|
76
|
+
participant: [],
|
|
77
|
+
gonogo: [],
|
|
78
|
+
emotions: []
|
|
79
|
+
};
|
|
80
80
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
interface CommonResults {
|
|
2
|
+
n_trials: number;
|
|
3
|
+
n_correct_trials: number;
|
|
4
|
+
n_incorrect_trials: number;
|
|
5
|
+
avg_rt_total: number;
|
|
6
|
+
avg_rt_correct: number;
|
|
7
|
+
avg_rt_incorrect: number;
|
|
8
|
+
performance_score: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface NbackSpecialResults {
|
|
12
|
+
target_trials: {
|
|
13
|
+
n: number;
|
|
14
|
+
avg_rt: number;
|
|
15
|
+
avg_acc: number;
|
|
16
|
+
};
|
|
17
|
+
non_target_trials: {
|
|
18
|
+
n: number;
|
|
19
|
+
avg_rt: number;
|
|
20
|
+
avg_acc: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface SpatialSpecialResults {
|
|
25
|
+
by_distance: any[];
|
|
26
|
+
by_angle: any[];
|
|
27
|
+
by_rotation: any[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface GonogoSpecialResults {
|
|
31
|
+
by_block: any[];
|
|
32
|
+
go_trials: {
|
|
33
|
+
n: number;
|
|
34
|
+
avg_rt: number;
|
|
35
|
+
avg_acc: number;
|
|
36
|
+
};
|
|
37
|
+
nogo_trials: {
|
|
38
|
+
n: number;
|
|
39
|
+
avg_rt: number;
|
|
40
|
+
avg_acc: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface EmotionSpecialResults {
|
|
45
|
+
by_emotion: any[];
|
|
46
|
+
confusion_matrix: any;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface NbackResultsStructure {
|
|
50
|
+
common: CommonResults;
|
|
51
|
+
special: NbackSpecialResults;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface SpatialResultsStructure {
|
|
55
|
+
common: CommonResults;
|
|
56
|
+
special: SpatialSpecialResults;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface GonogoResultsStructure {
|
|
60
|
+
common: CommonResults;
|
|
61
|
+
special: GonogoSpecialResults;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface EmotionResultsStructure {
|
|
65
|
+
common: CommonResults;
|
|
66
|
+
special: EmotionSpecialResults;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
CommonResults,
|
|
71
|
+
NbackResultsStructure,
|
|
72
|
+
SpatialResultsStructure,
|
|
73
|
+
GonogoResultsStructure,
|
|
74
|
+
EmotionResultsStructure,
|
|
75
|
+
NbackSpecialResults,
|
|
76
|
+
SpatialSpecialResults,
|
|
77
|
+
GonogoSpecialResults,
|
|
78
|
+
EmotionSpecialResults
|
|
79
|
+
};
|
package/src/emotions.ts
CHANGED
|
@@ -1,59 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
1
|
+
import { EmotionResultsStructure, CommonResults, EmotionSpecialResults } from './common-interfaces';
|
|
2
|
+
|
|
3
|
+
const EMOTION_MAP = ["neutral", "sad", "happy", "surprised", "angry"];
|
|
4
|
+
|
|
5
|
+
const emotion_calculate_results = (obj: any[]): EmotionResultsStructure => {
|
|
6
|
+
const trials = emotion_extract_trials(obj);
|
|
7
|
+
if (trials.length === 0) {
|
|
8
|
+
throw new Error("No trials found");
|
|
9
|
+
}
|
|
10
|
+
return emotion_results(trials);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const emotion_extract_trials = (obj: any[]) => {
|
|
14
|
+
// filter trials where response and rt are defined and not null
|
|
15
|
+
let trials = obj.filter((o: any) => 'response' in o && 'rt' in o && 'emotion' in o);
|
|
16
|
+
trials = trials.filter((o: any) => o.response !== null && o.rt !== null);
|
|
17
|
+
trials.forEach((o: any) => {
|
|
18
|
+
const match = o.emotion.match(/\/([^/]+)_([0-9]+)\.png/);
|
|
19
|
+
if (match) {
|
|
20
|
+
o.stimulus = match[0];
|
|
21
|
+
o.emotion = match[1];
|
|
22
|
+
o.image_number = match[2];
|
|
23
|
+
//this is <emotion>_<number>.png
|
|
24
|
+
o.image = `${match[1]}_${match[2]}.png`;
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
trials.forEach((o: any) => {
|
|
28
|
+
o.emotion_response = EMOTION_MAP[o.response] || "unknown";
|
|
29
|
+
o.correct = o.emotion === o.emotion_response;
|
|
30
|
+
// in the older ones there were 15 trials per rotation
|
|
31
|
+
});
|
|
32
|
+
return trials;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const emotion_results = (obj: any[]): EmotionResultsStructure => {
|
|
36
|
+
if (obj.length === 0) {
|
|
37
|
+
throw new Error("No trials found");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const correct_trials = obj.filter((o: any) => o.correct === true);
|
|
41
|
+
const incorrect_trials = obj.filter((o: any) => o.correct === false);
|
|
42
|
+
|
|
43
|
+
const total_rt = obj.reduce((a, b) => a + (b.rt || 0), 0);
|
|
44
|
+
const correct_rt = correct_trials.reduce((a, b) => a + (b.rt || 0), 0);
|
|
45
|
+
const incorrect_rt = incorrect_trials.reduce((a, b) => a + (b.rt || 0), 0);
|
|
46
|
+
|
|
47
|
+
const avg_rt = total_rt / obj.length;
|
|
48
|
+
const avg_acc = correct_trials.length / obj.length;
|
|
49
|
+
|
|
50
|
+
// Calculate performance score using the existing formula
|
|
51
|
+
const best_score = 1000;
|
|
52
|
+
const worst_score = 10000;
|
|
53
|
+
let performance = avg_rt / (Math.pow(avg_acc, 5) + 0.001);
|
|
54
|
+
performance = Math.min(Math.max(performance, best_score), worst_score);
|
|
55
|
+
const performance_score = (1 - ((performance - best_score) / (worst_score - best_score))) * 100;
|
|
56
|
+
|
|
57
|
+
const common: CommonResults = {
|
|
58
|
+
n_trials: obj.length,
|
|
59
|
+
n_correct_trials: correct_trials.length,
|
|
60
|
+
n_incorrect_trials: incorrect_trials.length,
|
|
61
|
+
avg_rt_total: avg_rt,
|
|
62
|
+
avg_rt_correct: correct_trials.length > 0 ? correct_rt / correct_trials.length : 0,
|
|
63
|
+
avg_rt_incorrect: incorrect_trials.length > 0 ? incorrect_rt / incorrect_trials.length : 0,
|
|
64
|
+
performance_score
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Group by emotion for special results
|
|
68
|
+
const by_emotion = EMOTION_MAP.map(emotion => {
|
|
69
|
+
const emotion_trials = obj.filter((o: any) => o.emotion === emotion);
|
|
70
|
+
return {
|
|
71
|
+
emotion,
|
|
72
|
+
n: emotion_trials.length,
|
|
73
|
+
avg_rt: emotion_trials.length > 0 ? emotion_trials.reduce((a, b) => a + (b.rt || 0), 0) / emotion_trials.length : 0,
|
|
74
|
+
avg_acc: emotion_trials.length > 0 ? emotion_trials.reduce((a, b) => a + (b.correct ? 1 : 0), 0) / emotion_trials.length : 0,
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Create confusion matrix
|
|
79
|
+
const confusion_matrix = EMOTION_MAP.reduce((matrix: any, actual: string) => {
|
|
80
|
+
matrix[actual] = EMOTION_MAP.reduce((row: any, predicted: string) => {
|
|
81
|
+
row[predicted] = obj.filter((o: any) => o.emotion === actual && o.emotion_response === predicted).length;
|
|
82
|
+
return row;
|
|
83
|
+
}, {});
|
|
84
|
+
return matrix;
|
|
85
|
+
}, {});
|
|
86
|
+
|
|
87
|
+
const special: EmotionSpecialResults = {
|
|
88
|
+
by_emotion,
|
|
89
|
+
confusion_matrix
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
common,
|
|
94
|
+
special
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export {EmotionResultsStructure, emotion_calculate_results, emotion_extract_trials, emotion_results};
|