├── public ├── affect.png └── favicon.ico ├── src ├── pages │ ├── _app.js │ ├── admin │ │ ├── experiment │ │ │ └── [exId].tsx │ │ └── index.tsx │ ├── _document.js │ ├── api │ │ ├── login.ts │ │ ├── experiment │ │ │ ├── index.ts │ │ │ └── [exId] │ │ │ │ ├── data.ts │ │ │ │ └── index.ts │ │ └── migrate.ts │ ├── experiment │ │ └── [exId].tsx │ └── login.tsx ├── lib │ ├── bcrypt-cli.js │ ├── useShuffled.ts │ ├── hashCode.ts │ ├── setupdb-cli.ts │ ├── downloadData.ts │ ├── ironSession.ts │ ├── config │ │ ├── configUISchema.ts │ │ ├── sampleConfig.ts │ │ └── configSchema.ts │ ├── useAxios.ts │ ├── errors.ts │ ├── types.ts │ └── db.ts ├── components │ ├── videoTask │ │ ├── taskTypes.ts │ │ ├── ContinuousVideoTask.tsx │ │ ├── TimestampVideoTask.tsx │ │ ├── SelfVideoTask.tsx │ │ ├── ConsensusVideoTask.tsx │ │ └── VideoTaskView.tsx │ ├── questions │ │ ├── OpenQuestion.tsx │ │ ├── GridQuestion.tsx │ │ ├── MultipleChoiceQuestion.tsx │ │ ├── ScaleQuestion.tsx │ │ ├── Grid1D.tsx │ │ ├── ContinuousGridQuestions.tsx │ │ ├── VideoQuestions.tsx │ │ └── Grid.tsx │ ├── GatedButton.tsx │ ├── TrialBlockWrapper.tsx │ ├── Instructions.js │ ├── TestTrialBlock.tsx │ ├── TrialBlock.tsx │ ├── ExperimentRunner.tsx │ └── admin │ │ └── ExperimentConfigurator.tsx └── App.css ├── next-env.d.ts ├── now.json ├── tsconfig.standalone.json ├── env.d.ts ├── .env.development ├── .gitignore ├── tsconfig.json ├── README.md ├── package.json └── LICENSE /public/affect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandboxnu/empathic-accuracy/master/public/affect.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandboxnu/empathic-accuracy/master/public/favicon.ico -------------------------------------------------------------------------------- /src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import "bootstrap/dist/css/bootstrap.min.css"; 2 | import "../App.css"; 3 | import App from "next/app"; 4 | 5 | export default App; 6 | -------------------------------------------------------------------------------- /src/lib/bcrypt-cli.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcrypt"); 2 | 3 | if (require.main === module) { 4 | console.log(bcrypt.hashSync(process.argv[2], 10)); 5 | } 6 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "routes": [ 4 | { 5 | "src": "/", 6 | "status": 404, 7 | "dest": "/404.html" 8 | } 9 | ], 10 | "regions": ["iad1"], 11 | "alias": "empathic.sandboxneu.com" 12 | } -------------------------------------------------------------------------------- /src/lib/useShuffled.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { shuffle } from "lodash"; 3 | 4 | export function useShuffled(data: T[], shouldShuffle: boolean): T[] { 5 | return useState(shouldShuffle ? shuffle(data) : data)[0]; 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.standalone.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "dist", 6 | "target": "es2017", 7 | "isolatedModules": false, 8 | "noEmit": false 9 | } 10 | } -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | export interface ProcessEnv { 3 | ACCESS_KEY_ID?: string; 4 | SECRET_KEY?: string; 5 | ROLLBAR_TOKEN?: string; 6 | ADMIN_KEY: string; 7 | ADMIN_PASS_HASH: string; 8 | DYNAMO_TABLE?: string; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/hashCode.ts: -------------------------------------------------------------------------------- 1 | export function hashCode(str: string) { 2 | let hash = 0; 3 | for (let i = 0; i < str.length; i++) { 4 | const char = str.charCodeAt(i); 5 | hash = (hash << 5) - hash + char; 6 | hash = hash & hash; // Convert to 32bit integer 7 | } 8 | return hash; 9 | } 10 | -------------------------------------------------------------------------------- /src/components/videoTask/taskTypes.ts: -------------------------------------------------------------------------------- 1 | import { AnswerSetWithMetadata } from "lib/types"; 2 | 3 | type SaveData = ( 4 | a: AnswerSetWithMetadata[], 5 | d: { videoWidth: number; videoHeight: number } 6 | ) => void; 7 | 8 | export type OnDone = SaveData; 9 | export type SavePartialData = SaveData; 10 | -------------------------------------------------------------------------------- /src/lib/setupdb-cli.ts: -------------------------------------------------------------------------------- 1 | import { setupTable, createExperiment } from "./db"; 2 | 3 | if (require.main === module) { 4 | (async () => { 5 | await setupTable(); 6 | console.log("created table"); 7 | await createExperiment(); 8 | console.log("setup experiment 1 with sample data"); 9 | })().catch(console.error); 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/downloadData.ts: -------------------------------------------------------------------------------- 1 | import Axios from "axios"; 2 | import fileDownload from "js-file-download"; 3 | 4 | export function downloadExperimentData(exId: string) { 5 | Axios.get(`/api/experiment/${exId}/data`, { responseType: "arraybuffer" }) 6 | .then(({ data }) => fileDownload(data, `experiment_${exId}_data.json`)) 7 | .catch((error) => console.log(error)); 8 | } 9 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # Fake data for dev. Password is "hello" 2 | ROLLBAR_KEY=jiosjoijiaosd 3 | ADMIN_KEY=WEIJFIOWJEFIAOJDFIAFJAIWOEFJFJIOWAJIFOWJIOAFJIWOEJFIAOWJFIOWEAJI 4 | ADMIN_PASS_HASH=\$2b\$10\$Lx9EsR9mah.J0VPCwPTA/OgWQoa0KC0eSbTDVmu16PFTFINKFWx/u 5 | 6 | # You need to provide this for prod 7 | ACCESS_KEY_ID=FAKE 8 | SECRET_KEY=HAHAHAHA 9 | DYNAMO_ENDPOINT=http://localhost:8000 10 | DYNAMO_TABLE=empathic-accuracy -------------------------------------------------------------------------------- /src/lib/ironSession.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | dotenv.config(); 3 | import { IronSessionOptions } from "iron-session"; 4 | 5 | if (process.env.ADMIN_KEY === undefined) { 6 | console.error("ENV: ADMIN_KEY is not defined"); 7 | } 8 | export const IRON_SESSION_CONFIG: IronSessionOptions = { 9 | password: process.env.ADMIN_KEY!, 10 | cookieName: "sandbox/challenge-admin", 11 | cookieOptions: { 12 | secure: process.env.NODE_ENV === "production", 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | .vercel -------------------------------------------------------------------------------- /src/pages/admin/experiment/[exId].tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useRouter } from "next/router"; 3 | import ExperimentConfigurator from "components/admin/ExperimentConfigurator"; 4 | 5 | export default function ExperimentPage() { 6 | const router = useRouter(); 7 | const experimentId = router.query.exId as string | undefined; 8 | 9 | return ( 10 | <> 11 | {experimentId !== undefined && ( 12 | 13 | )} 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/config/configUISchema.ts: -------------------------------------------------------------------------------- 1 | import { UiSchema } from "react-jsonschema-form-bs4"; 2 | 3 | const configUISchema: UiSchema = { 4 | "ui:order": ["paradigm", "*"], 5 | trialBlocks: { 6 | instructions: { 7 | instructionScreens: { 8 | items: { 9 | "ui:widget": "textarea", 10 | }, 11 | }, 12 | instructionsOverlay: { 13 | "ui:widget": "textarea", 14 | }, 15 | pauseInstructions: { 16 | "ui:widget": "textarea", 17 | }, 18 | }, 19 | }, 20 | }; 21 | 22 | export default configUISchema; 23 | -------------------------------------------------------------------------------- /src/components/questions/OpenQuestion.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TextArea } from "informed"; 3 | import { OpenQuestion as OpenQuestionProps, QuestionBaseProp } from "lib/types"; 4 | 5 | export default function OpenQuestion({ 6 | id, 7 | label, 8 | }: OpenQuestionProps & QuestionBaseProp) { 9 | return ( 10 |
11 | 14 |
15 |