Skip to content

Commit 83d997e

Browse files
committed
feat: Randomized task order.
- Updated the Video and InstructionVideo tasks to ensure that the survey always follows the video - Randomized task order after the consent form and fixation grid - Added the order of tasks to the JSON under key "TaskOrder"
1 parent 47ca09d commit 83d997e

20 files changed

+931
-893
lines changed

webcamstudy/asset-manifest.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"files": {
3-
"main.css": "https://seresl.unl.edu/webcamstudy/static/css/main.2a098134.css",
4-
"main.js": "https://seresl.unl.edu/webcamstudy/static/js/main.b893c783.js",
3+
"main.css": "https://seresl.unl.edu/webcamstudy/static/css/main.e2758196.css",
4+
"main.js": "https://seresl.unl.edu/webcamstudy/static/js/main.0d87912e.js",
55
"static/js/453.e44939a0.chunk.js": "https://seresl.unl.edu/webcamstudy/static/js/453.e44939a0.chunk.js",
66
"static/media/monkey_business.mp4": "https://seresl.unl.edu/webcamstudy/static/media/monkey_business.4c162ec3365ba53d0a3f.mp4",
77
"static/media/soccer-vid.mp4": "https://seresl.unl.edu/webcamstudy/static/media/soccer-vid.4aa09d7760e7c517c175.mp4",
@@ -214,20 +214,20 @@
214214
"static/media/AG_1314.jpg": "https://seresl.unl.edu/webcamstudy/static/media/AG_1314.b95e98cf488e5dc132b1.jpg",
215215
"static/media/AG_0424.jpg": "https://seresl.unl.edu/webcamstudy/static/media/AG_0424.001b2aa6575734faf97f.jpg",
216216
"static/media/AG_0666.jpg": "https://seresl.unl.edu/webcamstudy/static/media/AG_0666.0bb797d5847b3d3a90db.jpg",
217-
"static/media/star.mp4": "https://seresl.unl.edu/webcamstudy/static/media/star.087e6b09a89c0a6971cf.mp4",
218-
"static/media/circle.mp4": "https://seresl.unl.edu/webcamstudy/static/media/circle.814d676e3310fcb0c55d.mp4",
219-
"static/media/infinity.mp4": "https://seresl.unl.edu/webcamstudy/static/media/infinity.e3c99e977647363bba2e.mp4",
220-
"static/media/triangle.mp4": "https://seresl.unl.edu/webcamstudy/static/media/triangle.12162798fecaf045bc8b.mp4",
217+
"static/media/Star.mp4": "https://seresl.unl.edu/webcamstudy/static/media/Star.087e6b09a89c0a6971cf.mp4",
218+
"static/media/Circle.mp4": "https://seresl.unl.edu/webcamstudy/static/media/Circle.814d676e3310fcb0c55d.mp4",
219+
"static/media/Infinity.mp4": "https://seresl.unl.edu/webcamstudy/static/media/Infinity.e3c99e977647363bba2e.mp4",
220+
"static/media/Triangle.mp4": "https://seresl.unl.edu/webcamstudy/static/media/Triangle.12162798fecaf045bc8b.mp4",
221221
"static/media/left_right.mp4": "https://seresl.unl.edu/webcamstudy/static/media/left_right.85268d9ab4c8c3005ad4.mp4",
222-
"static/media/square.mp4": "https://seresl.unl.edu/webcamstudy/static/media/square.433322980c135b7cda44.mp4",
222+
"static/media/Square.mp4": "https://seresl.unl.edu/webcamstudy/static/media/Square.433322980c135b7cda44.mp4",
223223
"static/media/5point_1920x1080.png": "https://seresl.unl.edu/webcamstudy/static/media/5point_1920x1080.16294bbf8c826fc2134e.png",
224224
"index.html": "https://seresl.unl.edu/webcamstudy/index.html",
225-
"main.2a098134.css.map": "https://seresl.unl.edu/webcamstudy/static/css/main.2a098134.css.map",
226-
"main.b893c783.js.map": "https://seresl.unl.edu/webcamstudy/static/js/main.b893c783.js.map",
225+
"main.e2758196.css.map": "https://seresl.unl.edu/webcamstudy/static/css/main.e2758196.css.map",
226+
"main.0d87912e.js.map": "https://seresl.unl.edu/webcamstudy/static/js/main.0d87912e.js.map",
227227
"453.e44939a0.chunk.js.map": "https://seresl.unl.edu/webcamstudy/static/js/453.e44939a0.chunk.js.map"
228228
},
229229
"entrypoints": [
230-
"static/css/main.2a098134.css",
231-
"static/js/main.b893c783.js"
230+
"static/css/main.e2758196.css",
231+
"static/js/main.0d87912e.js"
232232
]
233233
}

webcamstudy/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="https://seresl.unl.edu/webcamstudy/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="https://seresl.unl.edu/webcamstudy/logo192.png"/><link rel="manifest" href="https://seresl.unl.edu/webcamstudy/manifest.json"/><title>Eye Tracking Webcam Study</title><script type="module">import EmbeddedPageSdk from"https://app.realeye.io/sdk/js/testRunnerEmbeddableSdk-1.7.1.js";let reSdk=null;window.addEventListener("DOMContentLoaded",()=>{reSdk=new EmbeddedPageSdk(!1,null,!1),reSdk.startNextExposure()}),window.addEventListener("endmeplease",()=>{reSdk&&reSdk.finishEyeTrackingTest()})</script><script defer="defer" src="https://seresl.unl.edu/webcamstudy/static/js/main.b893c783.js"></script><link href="https://seresl.unl.edu/webcamstudy/static/css/main.2a098134.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="https://seresl.unl.edu/webcamstudy/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="https://seresl.unl.edu/webcamstudy/logo192.png"/><link rel="manifest" href="https://seresl.unl.edu/webcamstudy/manifest.json"/><title>Eye Tracking Webcam Study</title><script type="module">import EmbeddedPageSdk from"https://app.realeye.io/sdk/js/testRunnerEmbeddableSdk-1.7.1.js";let reSdk=null;window.addEventListener("DOMContentLoaded",()=>{reSdk=new EmbeddedPageSdk(!1,null,!1),reSdk.startNextExposure()}),window.addEventListener("endmeplease",()=>{reSdk&&reSdk.finishEyeTrackingTest()})</script><script defer="defer" src="https://seresl.unl.edu/webcamstudy/static/js/main.0d87912e.js"></script><link href="https://seresl.unl.edu/webcamstudy/static/css/main.e2758196.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

webcamstudy/src/App.jsx

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,25 @@ import "./App.css";
44
import ConsentForm from "./components/ConsentForm";
55
import TextTask from "./components/Text/TextTask";
66
import SmoothPursuitVideoTask from "./components/SmoothPursuitVideoTask";
7-
import InstructionVideoTask from "./components/InstructionVideoTask";
8-
import InstructionVideoSurvey from "./components/InstructionVideoSurvey";
9-
import VideoTask from "./components/VideoTask";
10-
import VideoSurvey from "./components/VideoSurvey";
7+
import InstructionVideoTask from "./components/Instruction/InstructionVideoTask";
8+
import VideoTask from "./components/Video/VideoTask";
119
import FaceTask from "./components/FaceTask";
1210
import ValidationGrid from "./components/validation_grid/ValidationGrid";
1311

12+
// Utility to shuffle an array
13+
function shuffle(array) {
14+
const arr = [...array];
15+
for (let i = arr.length - 1; i > 0; i--) {
16+
const j = Math.floor(Math.random() * (i + 1));
17+
[arr[i], arr[j]] = [arr[j], arr[i]];
18+
}
19+
return arr;
20+
}
21+
1422
function App() {
1523
const [currentTask, setCurrentTask] = useState(0);
1624
const [taskFiles, setTaskFiles] = useState([]);
25+
const [randomizedOrder, setRandomizedOrder] = useState([]);
1726
const [studyData, setStudyData] = useState({
1827
consent: null,
1928
textTask: null,
@@ -39,18 +48,17 @@ function App() {
3948
}, []);
4049

4150
const generateTaskSequence = () => {
42-
const tasks = [
43-
"ValidationGrid",
44-
'ConsentForm',
51+
const alwaysFirst = ["ValidationGrid", "ConsentForm"];
52+
const toRandomize = [
4553
"TextTask",
4654
"SmoothPursuitVideoTask",
47-
'InstructionVideoTask',
48-
'InstructionVideoSurvey',
49-
'VideoTask',
50-
'VideoSurvey',
51-
'FaceTask',
55+
"InstructionVideoTask",
56+
"VideoTask",
57+
"FaceTask",
5258
];
53-
setTaskFiles(tasks);
59+
const randomized = shuffle(toRandomize);
60+
setRandomizedOrder(randomized);
61+
setTaskFiles([...alwaysFirst, ...randomized]);
5462
};
5563

5664
const handleTaskComplete = (taskType, data) => {
@@ -94,6 +102,7 @@ function App() {
94102
responses: [],
95103
startTime: startTime,
96104
endTime: new Date().toISOString(),
105+
TaskOrder: randomizedOrder, // Add the randomized order to output
97106
};
98107

99108
// Add text task responses (now an array of objects)
@@ -122,23 +131,20 @@ function App() {
122131
if (studyData.instructionVideoTask) {
123132
jsonData.responses.push({
124133
task: "InstructionVideo",
125-
response: {
126-
ballTransfers: studyData.instructionVideoTask.ballTransfers,
127-
curtainColor: studyData.instructionVideoTask.curtainColor,
128-
noticedGorilla: studyData.instructionVideoTask.noticedGorilla,
129-
},
134+
response: studyData.instructionVideoTask,
130135
isCorrect: true, // No right/wrong answers for this task
131136
});
132137
}
133138

134-
// Add video task response
135-
if (studyData.videoTask?.selectedAnswer) {
139+
// Add video task response (now handled in VideoTask, not VideoSurvey)
140+
if (studyData.videoTask) {
136141
jsonData.responses.push({
137142
task: "Video",
138143
response: studyData.videoTask.selectedAnswer,
139144
isCorrect: determineVideoCorrectness(
140145
studyData.videoTask.selectedAnswer
141146
),
147+
timestamp: studyData.videoTask.timestamp,
142148
});
143149
}
144150

@@ -197,7 +203,7 @@ function App() {
197203
document.body.appendChild(link);
198204
link.click();
199205
document.body.removeChild(link);
200-
}, [studyData, startTime]);
206+
}, [studyData, startTime, randomizedOrder]);
201207

202208
// Auto-download data when all tasks are completed
203209
useEffect(() => {
@@ -265,23 +271,13 @@ function App() {
265271
case "InstructionVideoTask":
266272
return (
267273
<InstructionVideoTask
268-
onVideoEnded={() => setInstructionVideoEnded(true)}
269-
/>
270-
);
271-
case "InstructionVideoSurvey":
272-
return (
273-
<InstructionVideoSurvey
274-
onSubmit={(data) =>
275-
handleTaskComplete("instructionVideoTask", data)
276-
}
274+
onComplete={(data) => handleTaskComplete("instructionVideoTask", data)}
277275
/>
278276
);
279277
case "VideoTask":
280-
return <VideoTask onVideoEnded={() => setVideoTaskEnded(true)} />;
281-
case "VideoSurvey":
282278
return (
283-
<VideoSurvey
284-
onSubmit={(data) => handleTaskComplete("videoTask", data)}
279+
<VideoTask
280+
onComplete={(data) => handleTaskComplete("videoTask", data)}
285281
/>
286282
);
287283
case "FaceTask":
@@ -319,8 +315,6 @@ function App() {
319315
((currentTaskName === "TextTask" && textTaskComplete) ||
320316
![
321317
"ConsentForm",
322-
"InstructionVideoSurvey",
323-
"VideoSurvey",
324318
"FaceTask",
325319
"TextTask",
326320
].includes(currentTaskName) &&

webcamstudy/src/components/ErrorBoundary.jsx

Whitespace-only changes.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React, { useState } from 'react';
2+
import './Instructions.css';
3+
4+
const InstructionVideoSurvey = ({ onSubmit }) => {
5+
const [ballTransfers, setBallTransfers] = useState('');
6+
const [curtainColor, setCurtainColor] = useState('');
7+
const [noticedGorilla, setNoticedGorilla] = useState('');
8+
9+
const handleSubmit = (e) => {
10+
e.preventDefault();
11+
const result = {
12+
ballTransfers: parseInt(ballTransfers) || 0,
13+
curtainColor,
14+
noticedGorilla,
15+
timestamp: new Date().toISOString()
16+
};
17+
onSubmit?.(result);
18+
};
19+
20+
const isFormValid = ballTransfers !== '' && curtainColor.trim() !== '' && noticedGorilla !== '';
21+
22+
return (
23+
<div className="instruction-survey-container">
24+
<form onSubmit={handleSubmit} className="instruction-survey-form">
25+
<h2 className="instruction-survey-title">Video Questions</h2>
26+
27+
{/* Ball Transfers Question */}
28+
<div className="instruction-survey-question-group">
29+
<h3 className="instruction-survey-question">How many times did the players wearing white pass the ball?</h3>
30+
<input
31+
type="number"
32+
value={ballTransfers}
33+
onChange={(e) => setBallTransfers(e.target.value)}
34+
placeholder="Enter number"
35+
className="instruction-survey-input"
36+
min="0"
37+
/>
38+
</div>
39+
40+
{/* Curtain Color Question */}
41+
<div className="instruction-survey-question-group">
42+
<h3 className="instruction-survey-question">What was the color of the curtain in the background at the end of the video?</h3>
43+
<input
44+
type="text"
45+
value={curtainColor}
46+
onChange={(e) => setCurtainColor(e.target.value)}
47+
placeholder="Enter color"
48+
className="instruction-survey-input"
49+
/>
50+
</div>
51+
52+
{/* Gorilla Question */}
53+
<div className="instruction-survey-question-group">
54+
<h3 className="instruction-survey-question">Did you notice the gorilla walking through the middle of the frame?</h3>
55+
<div className="instruction-survey-radio-group">
56+
{['Yes', 'No'].map(option => (
57+
<label key={option} className="instruction-survey-radio-label">
58+
<input
59+
type="radio"
60+
name="gorilla"
61+
value={option}
62+
checked={noticedGorilla === option}
63+
onChange={(e) => setNoticedGorilla(e.target.value)}
64+
className="instruction-survey-radio"
65+
/>
66+
{option}
67+
</label>
68+
))}
69+
</div>
70+
</div>
71+
72+
<button
73+
type="submit"
74+
disabled={!isFormValid}
75+
className="instruction-survey-button"
76+
style={{
77+
opacity: isFormValid ? 1 : 0.5,
78+
cursor: isFormValid ? 'pointer' : 'not-allowed'
79+
}}
80+
>
81+
Continue
82+
</button>
83+
</form>
84+
</div>
85+
);
86+
};
87+
88+
export default InstructionVideoSurvey;

0 commit comments

Comments
 (0)