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.
Files changed (50) hide show
  1. package/CLAUDE.md +115 -0
  2. package/README.md +147 -34
  3. package/dist/cjs/common-interfaces.d.ts +60 -0
  4. package/dist/cjs/common-interfaces.js +2 -0
  5. package/dist/cjs/emotions.d.ts +4 -9
  6. package/dist/cjs/emotions.js +56 -22
  7. package/dist/cjs/gonogo.d.ts +4 -9
  8. package/dist/cjs/gonogo.js +61 -14
  9. package/dist/cjs/index.d.ts +6 -5
  10. package/dist/cjs/index.js +11 -1
  11. package/dist/cjs/nback.d.ts +4 -14
  12. package/dist/cjs/nback.js +27 -13
  13. package/dist/cjs/spatial-cognition.d.ts +4 -9
  14. package/dist/cjs/spatial-cognition.js +69 -14
  15. package/dist/esm/common-interfaces.d.ts +60 -0
  16. package/dist/esm/common-interfaces.js +1 -0
  17. package/dist/esm/emotions.d.ts +4 -9
  18. package/dist/esm/emotions.js +55 -21
  19. package/dist/esm/gonogo.d.ts +4 -9
  20. package/dist/esm/gonogo.js +61 -14
  21. package/dist/esm/index.d.ts +6 -5
  22. package/dist/esm/index.js +9 -1
  23. package/dist/esm/nback.d.ts +4 -14
  24. package/dist/esm/nback.js +27 -13
  25. package/dist/esm/spatial-cognition.d.ts +4 -9
  26. package/dist/esm/spatial-cognition.js +69 -14
  27. package/example-data/emotions.json +807 -807
  28. package/example-data/gonogo.json +5392 -5392
  29. package/example-data/nback.json +892 -892
  30. package/example-data/participant.json +6 -6
  31. package/example-data/spatial-cognition.json +462 -462
  32. package/jest.config.js +13 -13
  33. package/original/emotion-recognition.R +39 -39
  34. package/original/gonogo.R +32 -32
  35. package/package.json +40 -40
  36. package/src/__tests__/integration/browserInfo.test.ts +16 -16
  37. package/src/__tests__/integration/emotions.test.ts +172 -64
  38. package/src/__tests__/integration/gonogo.test.ts +183 -80
  39. package/src/__tests__/integration/nback.test.ts +148 -55
  40. package/src/__tests__/integration/spatial-cognition.test.ts +171 -66
  41. package/src/__tests__/test-utils/testData.ts +79 -79
  42. package/src/common-interfaces.ts +79 -0
  43. package/src/emotions.ts +98 -59
  44. package/src/getters.ts +21 -21
  45. package/src/gonogo.ts +127 -76
  46. package/src/index.ts +39 -12
  47. package/src/nback.ts +61 -53
  48. package/src/spatial-cognition.ts +113 -55
  49. package/tsconfig.cjs.json +12 -12
  50. 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
- describe('spatial_extract_image_info test data', () => {
25
- test('processes test data correctly', () => {
26
- const resultsTestData = spatial_extract_image_info(ImageInfoTestData);
27
- expect(resultsTestData.length).toBe(2);
28
-
29
- expect(resultsTestData[0].image).toBe('map_left_1_90.png');
30
- expect(resultsTestData[0].direction).toBe('left');
31
- expect(resultsTestData[0].distance).toBe('1');
32
- expect(resultsTestData[0].angle).toBe('90');
33
- expect(resultsTestData[0].rotation).toBe('90');
34
- expect(resultsTestData[0].response).toBe('left');
35
- expect(resultsTestData[0].correct).toBe(true);
36
-
37
- expect(resultsTestData[1].image).toBe('map_left_3_180.png');
38
- expect(resultsTestData[1].direction).toBe('left');
39
- expect(resultsTestData[1].distance).toBe('3');
40
- expect(resultsTestData[1].angle).toBe('180');
41
- expect(resultsTestData[1].rotation).toBe('270');
42
- expect(resultsTestData[1].response).toBe('right');
43
- expect(resultsTestData[1].correct).toBe(false);
44
- });
45
-
46
- test('processes real data correctly', () => {
47
- const testData = loadTestData('spatial-cognition');
48
- const resultData = spatial_extract_image_info(testData.spatialCognition);
49
- expect(resultData).toBeDefined();
50
- });
51
- });
52
-
53
- describe('spatial_results test data', () => {
54
- test('processes test data correctly', () => {
55
- const resultsTestData = spatial_results(ImageInfoTestData);
56
- console.log(resultsTestData);
57
- expect(resultsTestData).toBeDefined();
58
- });
59
- test('processes real data correctly', () => {
60
- const testData = loadTestData('spatialCognition').spatialCognition;
61
- const resultData = spatial_calculate_results(testData);
62
- console.log(resultData);
63
- expect(resultData).toBeDefined();
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
- interface EmotionResults {
2
- n: number;
3
- avg_rt: number;
4
- avg_acc: number;
5
- performance: number;
6
- }
7
-
8
- const emotion_calculate_results = (obj: any[]): EmotionResults => {
9
- const trials = emotion_extract_trials(obj);
10
- // throw if trials is empty
11
- if (trials.length === 0) {
12
- throw new Error("No trials found");
13
- }
14
- return emotion_results(trials);
15
- };
16
-
17
- const emotion_results = (obj: any[]): EmotionResults => {
18
- // throw error if obj is empty
19
- if (obj.length === 0) {
20
- throw new Error("No trials found");
21
- }
22
- let results = {
23
- n: obj.length,
24
- avg_rt: obj.reduce((a, b) => a + b.rt, 0) / obj.length,
25
- avg_acc: obj.reduce((a, b) => a + b.correct, 0) / obj.length,
26
- performance: NaN
27
- };
28
- const best_score = 1000;
29
- const worst_score = 10000;
30
- let performance = results.avg_rt / (Math.pow(results.avg_acc, 5) + 0.001);
31
- performance = Math.min(Math.max(performance, best_score), worst_score);
32
- results.performance = (1 - ((performance - best_score) / (worst_score - best_score))) * 100;
33
- return results;
34
- }
35
-
36
- const emotion_extract_trials = (obj: any[]) => {
37
- const emotionMap = ["neutral", "sad", "happy", "surprised", "angry"];
38
- // filter trials where response and rt are defined and not null
39
- let trials = obj.filter((o: any) => 'response' in o && 'rt' in o && 'emotion' in o);
40
- trials = trials.filter((o: any) => o.response !== null && o.rt !== null);
41
- trials.forEach((o: any) => {
42
- const match = o.emotion.match(/\/([^/]+)_([0-9]+)\.png/);
43
- if (match) {
44
- o.stimulus = match[0];
45
- o.emotion = match[1];
46
- o.image_number = match[2];
47
- //this is <emotion>_<number>.png
48
- o.image = `${match[1]}_${match[2]}.png`;
49
- }
50
- })
51
- trials.forEach((o: any) => {
52
- o.emotion_response = emotionMap[o.response] || "unknown";
53
- o.correct = o.emotion === o.emotion_response;
54
- // in the older ones there were 15 trials per rotation
55
- });
56
- return trials;
57
- }
58
-
59
- export { emotion_calculate_results, emotion_extract_trials, emotion_results };
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};