Commit ba75713f authored by v.mattfeld's avatar v.mattfeld
Browse files

Add Attention Test + Fixes

parent 08d2ee1b
import React, { FC, useEffect } from 'react';
import React, { FC, useCallback, useEffect, useState } from 'react';
import { Box, Button, Stack, Radio } from "@chakra-ui/react";
import { useRouter } from 'next/router';
import { Formik } from 'formik';
......@@ -7,6 +7,9 @@ import * as Yup from 'yup'
import { useTranslation } from 'react-i18next';
import { useRecoilState } from 'recoil';
import { attentionCheckState, Check } from 'pages/annotate';
import { logger } from 'utils/logger';
import useSWR from 'swr';
import { fetcher } from 'utils/utils';
const validateSchema = Yup.object({
sensitivity: Yup.string().required(),
......@@ -25,8 +28,22 @@ interface FormProps {
const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) => {
const router = useRouter()
const { t } = useTranslation()
const attentionURL = `/api/dbAttention?uid=${uid}`
// const { data, error, mutate } = useSWR(attentionURL, fetcher, {
// revalidateOnFocus: false,
// })
fetcher(attentionURL).then(res => {
logger.info(res)
}).catch(err => { logger.error(err) })
const [shouldRefetch, setShouldRefetch] = useState(true)
const [attentionCheck, setAttentionCheck] = useRecoilState(attentionCheckState)
const { checks, currentPage } = attentionCheck
const currentCheck = checks.find(check => check.page === currentPage) || { isQuestionOne: false }
const { isQuestionOne } = currentCheck
useEffect(() => {
if (pageNumber > 100)
......@@ -34,6 +51,42 @@ const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) =>
router.push('/done')
}, [pageNumber, router])
useCallback(() => {
const fn = async () => {
if (!shouldRefetch) return
const { data } = await fetcher(attentionURL)
if (uid && attentionCheck.uid && data) {
const { att_0, att_1, att_2 } = data
const checkOne: Check = {
...checks[0],
pass: att_0,
}
const checkTwo: Check = {
...checks[1],
pass: att_1,
}
const checkThree: Check = {
...checks[2],
pass: att_2,
}
const newChecks = [checkOne, checkTwo, checkThree]
setAttentionCheck({
...attentionCheck,
checks: newChecks,
})
setShouldRefetch(false)
}
}
fn()
}, [attentionCheck, attentionURL, checks, setAttentionCheck, shouldRefetch, uid])
// if (error) {
// logger.error(error)
// return (<>Error on User fetching.</>)
// }
return (
<Formik
......@@ -45,7 +98,7 @@ const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) =>
onSubmit={(values, { resetForm }) => {
const fetchError = (error: any) => {
console.error('error', error, values)
logger.error({ error, values })
alert(error)
resetForm()
}
......@@ -56,38 +109,36 @@ const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) =>
let tms = values.targetDemographic.filter(x => x !== '')
if (isCheck) {
const pass = values.sensitivity == '4'
const pass = isQuestionOne ? values.sensitivity == '6' : tms.includes("Everybody")
// update state
const currentCheck = attentionCheck.checks.find(x => x.imageID === imageID)
if (currentCheck) {
const newCheck: Check = {
...currentCheck,
checked: true,
pass,
}
// find index to update the checks
const index = attentionCheck.checks.indexOf(currentCheck)
let checks = [...attentionCheck.checks] // copy that is not read only
checks[index] = newCheck
const updatedChecks = {
...attentionCheck,
checks,
currentPage: pageNumber - 1,
}
const newCheck: Check = {
...currentCheck!,
checked: true,
pass,
}
setAttentionCheck(updatedChecks)
// find index to update the checks
const index = attentionCheck.checks.indexOf(currentCheck!)
let checks = [...attentionCheck.checks] // copy that is not read only
checks[index] = newCheck
const updatedChecks = {
...attentionCheck,
checks,
currentPage: pageNumber - 1,
}
setAttentionCheck(updatedChecks)
const attentionPayload = attentionCheck
const rawAttention = JSON.stringify(attentionPayload)
const attentionPayload = updatedChecks
const rawAttention = JSON.stringify({ ...attentionPayload, page: pageNumber - 1 })
const attentionRequestOptions: RequestInit = {
method: 'POST',
headers: myHeaders,
body: rawAttention,
redirect: 'follow'
redirect: 'follow',
}
fetch(`/api/attention`, attentionRequestOptions)
......@@ -100,8 +151,10 @@ const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) =>
.then(() => {
resetForm()
refetch()
// mutate(attentionURL)
})
.catch(fetchError);
setShouldRefetch(true)
return
}
......@@ -144,8 +197,10 @@ const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) =>
})
resetForm()
refetch()
// mutate(attentionURL)
})
.catch(fetchError);
setShouldRefetch(true)
}}
validationSchema={validateSchema}
>
......@@ -159,19 +214,21 @@ const Form: FC<FormProps> = ({ pageNumber, uid, imageID, refetch, isCheck }) =>
mt={4}
name="sensitivity"
label={
isCheck ? t('check') : t('questionOne')
isQuestionOne && isCheck ? t('checkOne') : t('questionOne')
}>
<Stack spacing="1">
<Radio value="1">{t('a11')}</Radio>
<Radio value="2">{t('a13')}</Radio>
<Radio value="3">{t('a12')}</Radio>
<Radio value="4">{isCheck ? 'attention' : t('a14')}</Radio>
<Radio value="4">{t('a14')}</Radio>
<Radio value="5">{t('a15')}</Radio>
<Radio value="6">{t('a16')}</Radio>
</Stack>
</RadioGroupControl>
<CheckboxContainer mt={4} name="targetDemographic" label={t('questionTwo')}>
<CheckboxContainer mt={4} name="targetDemographic" label={
!isQuestionOne && isCheck ? t('checkTwo') : t('questionTwo')
}>
<Stack spacing="1">
<CheckboxControl isDisabled={values.targetDemographic.indexOf('Nobody') !== -1} name="targetDemographic" value="Friends">{t('a21')}</CheckboxControl>
<CheckboxControl isDisabled={values.targetDemographic.indexOf('Nobody') !== -1} name="targetDemographic" value="Aquaintance">{t('a24')}</CheckboxControl>
......
import React, { FC } from 'react'
import { Button, ButtonProps } from '@chakra-ui/react'
const PrimaryButton: FC<ButtonProps> = ({ ...props }) => {
const PB: FC<ButtonProps> = ({ ...props }) => {
return (
<Button
rounded={'full'}
......@@ -17,6 +17,10 @@ const PrimaryButton: FC<ButtonProps> = ({ ...props }) => {
)
}
const PrimaryButton = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
return <PB {...props} />
})
export {
PrimaryButton
}
......
......@@ -88,3 +88,13 @@ mutation UpdateAttentionChecks($uid: uuid!, $att_0: Boolean!, $att_1: Boolean!,
}
}
`
export const db_attention_state = gql`
query DBAttention($uid: uuid!) {
users_by_pk(id: $uid) {
att_0
att_2
att_1
}
}
`
......@@ -33,7 +33,8 @@ export const resources = {
gettingReady: "Getting ready...",
thisCanTakeSomeTime:
"This can take some time depending on your connection...",
check: "You must enter, what is said in the picture."
checkOne: `How likely is that you are paying attention, please select "not possible"`,
checkTwo: `How likely is that you are paying attention, please select "everybody"`,
},
},
de: {
......@@ -67,9 +68,10 @@ export const resources = {
verficationError:
"Logikfehler! Bitte ueberpruefen Sie ihre Antworten auf widersprueche!",
gettingReady: "Einen Moment bitte...",
check: "Wählen Sie das in dem Bild erwähnte Wort aus.",
thisCanTakeSomeTime:
"Abhängig von der Verbindung kann dies einige Sekunden in Anspruch nehmen...",
checkOne: `Wie wahrscheinlich ist es, dass Sie aufmerksam sind? Bitte wählen Sie "nicht entscheidbar" aus.`,
checkTwo: `Wie wahrscheinlich ist es, dass Sie aufmerksam sind? Bitte wählen Sie "Alle" aus.`,
},
},
};
......@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "cross-env NODE_OPTIONS='--inspect' next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
......@@ -46,11 +46,13 @@
"@types/js-yaml": "^4.0.2",
"@types/lodash": "^4.14.176",
"@types/node": "^16.7.1",
"@types/pino": "^6.3.12",
"@types/react": "17.0.11",
"@types/uuid": "^8.3.1",
"cross-env": "^7.0.3",
"eslint": "7.29.0",
"eslint-config-next": "11.0.1",
"jest": "^27.0.6",
"typescript": "4.3.4"
}
}
}
\ No newline at end of file
import { Box, Center, Flex } from '@chakra-ui/layout'
import { Box, Center, Code, Flex, Spacer } from '@chakra-ui/layout'
import { Progress } from '@chakra-ui/progress'
import { useColorModeValue, Text, Image } from '@chakra-ui/react'
import { useColorModeValue, Text, Image, Stack } from '@chakra-ui/react'
import { Spinner } from '@chakra-ui/spinner'
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import React, { useEffect } from 'react'
import { atom, useRecoilState } from 'recoil'
import useSWR from 'swr'
import { logger } from 'utils/logger'
import { fetcher } from 'utils/utils'
import Form from '../components/Form'
import Done from './done'
const fetcher = (url: string) => fetch(url).then((res) => res.json())
export const getServerSideProps: GetServerSideProps = async (context) => {
return {
......@@ -24,7 +25,8 @@ export interface Check {
imageID: string,
imageURL: string,
page: number,
checked: boolean
checked: boolean,
isQuestionOne: boolean
}
export interface AttentionCheck {
uid: string,
......@@ -40,23 +42,26 @@ export const attentionCheckState = atom<AttentionCheck>({
checks: [{
pass: false,
imageID: 'att_0',
imageURL: 'https://c102-251.cloud.gwdg.de/attention/4327805618_a2b1c48f5a.jpg',
imageURL: 'https://c102-251.cloud.gwdg.de/public/4327805618_a2b1c48f5a.jpg',
page: 25,
checked: false
checked: false,
isQuestionOne: true
},
{
pass: false,
imageID: 'att_1',
imageURL: 'https://c102-251.cloud.gwdg.de/attention/4363222502_9828d7b93c.jpg',
imageURL: 'https://c102-251.cloud.gwdg.de/private/4363222502_9828d7b93c.jpg',
checked: false,
page: 50
page: 50,
isQuestionOne: true
},
{
pass: false,
imageID: 'att_2',
imageURL: 'https://c102-251.cloud.gwdg.de/attention/4380717531_94abf4986c.jpg',
imageURL: 'https://c102-251.cloud.gwdg.de/private/4380717531_94abf4986c.jpg',
checked: false,
page: 75
page: 75,
isQuestionOne: false
}]
}
})
......@@ -89,7 +94,7 @@ const Annotate = ({ id }: InferGetServerSidePropsType<typeof getServerSideProps>
if (error) {
console.error(error)
logger.error(error)
return <>
An Error Occured. Please Login or start a new session.
</>
......@@ -130,19 +135,20 @@ const Annotate = ({ id }: InferGetServerSidePropsType<typeof getServerSideProps>
position="relative"
>
<Center p={5}>
<div>
UID:
<Text fontWeight='bold'> {id}</Text>
Submission No.:
<Text fontWeight='bold'>{nextIndex as number - 1}</Text>
<Stack spacing={3}>
<Text>UID:</Text>
<Code> {id}</Code>
<Text>Submission No.:</Text>
<Code>{nextIndex as number - 1}</Code>
<Spacer />
<Spacer />
<Image
mt={5}
boxShadow="md"
borderRadius='md'
src={isCheck() ? check!.imageURL : images[nextIndex - 1].url}
alt='Image'
/>
</div>
</Stack>
</Center>
<Form
uid={id as string}
......
......@@ -2,7 +2,35 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { AttentionCheck } from "pages/annotate";
import _ from 'lodash'
import { Client } from "db/db";
import { update_attention_checks } from "db/queries";
import { db_attention_state, update_attention_checks } from "db/queries";
import { logger } from "utils/logger";
const oldAttention = async (uid: string, req: NextApiRequest, res: NextApiResponse) => {
const { data, error } = await Client.query({
query: db_attention_state,
variables: {
uid
}
})
if (error) {
logger.error(error);
res.status(500).end();
return;
}
const { users_by_pk } = data;
if (!users_by_pk) {
logger.error("No user found");
res.status(500).end();
return;
}
return {
data: users_by_pk,
}
}
const handler = async (req: NextApiRequest, res: NextApiResponse<any>) => {
if (req.method !== "POST") {
......@@ -10,17 +38,52 @@ const handler = async (req: NextApiRequest, res: NextApiResponse<any>) => {
}
try {
const { uid, checks }: AttentionCheck = req.body
if (!uid || !checks) {
const { page } = req.body
const currentPage = page as number
// get old attention checks
const oldAttentionChecks = await oldAttention(uid, req, res)
logger.info(`Page: ${page}`)
if (!uid || !checks || !page || !oldAttentionChecks) {
res.status(400).end();
return
}
const { data: oldChecks } = oldAttentionChecks
const sortedChecks = _.sortBy(checks, ['imageID'])
const variables = {
const payload = {
uid,
att_0: sortedChecks[0].pass,
att_1: sortedChecks[1].pass,
att_2: sortedChecks[2].pass,
...oldChecks,
// att_0: sortedChecks[0].pass,
// att_1: sortedChecks[1].pass,
// att_2: sortedChecks[2].pass,
}
let variables
if (currentPage === 25) {
variables = {
...payload,
att_0: sortedChecks[0].pass
}
} else if (currentPage === 50) {
variables = {
...payload,
att_1: sortedChecks[1].pass
}
} else if (currentPage === 75) {
variables = {
...payload,
att_2: sortedChecks[2].pass
}
} else {
variables = {
...payload,
}
}
const { errors, data } = await Client.mutate({
......
import type { NextApiRequest, NextApiResponse } from "next";
import { Client } from "db/db";
import { db_attention_state } from "db/queries";
import { logger } from "utils/logger";
const handler = async (req: NextApiRequest, res: NextApiResponse<any>) => {
if (req.method !== "GET") {
res.status(405).end();
return;
}
const uid = req.query.uid as string;
if (!uid) {
res.status(400).end();
return;
}
const { data, error } = await Client.query({
query: db_attention_state,
variables: {
uid: req.query.uid,
}
})
if (error) {
logger.error(error);
res.status(500).end();
return;
}
const { users_by_pk } = data;
if (!users_by_pk) {
logger.error("No user found");
res.status(500).end();
return;
}
res.status(200).json({
data: users_by_pk,
})
}
export default handler;
const pino = require('pino')()
import pino from 'pino'
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: {
target: 'pino-pretty',
options: {
......
......@@ -3,3 +3,5 @@ export const createUserWithImages = async () => {
const { data } = await res.json();
return data;
};
export const fetcher = (url: string) => fetch(url).then((res) => res.json())
\ No newline at end of file
......@@ -1626,6 +1626,30 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/pino-pretty@*":
version "4.7.1"
resolved "https://registry.yarnpkg.com/@types/pino-pretty/-/pino-pretty-4.7.1.tgz#2ce3f56f3cf4f9632374419d616ae2e6c933b935"
integrity sha512-l1ntNXdpVWsnPYUk5HyO5Lxfr38zLCgxVfEn/9Zhhm+nGF04/BiIou/m8XPwvoVZLV+livUo79VdHXMJPfUYxA==
dependencies:
"@types/pino" "*"
"@types/pino-std-serializers@*":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/pino-std-serializers/-/pino-std-serializers-2.4.1.tgz#f8bd52a209c8b3c97d1533b1ba27f57c816382bf"
integrity sha512-17XcksO47M24IVTVKPeAByWUd3Oez7EbIjXpSbzMPhXVzgjGtrOa49gKBwxH9hb8dKv58OelsWQ+A1G1l9S3wQ==
dependencies:
"@types/node" "*"
"@types/pino@*", "@types/pino@^6.3.12":
version "6.3.12"
resolved "https://registry.yarnpkg.com/@types/pino/-/pino-6.3.12.tgz#4425db6ced806109c3df957100cba9dfcd73c228"
integrity sha512-dsLRTq8/4UtVSpJgl9aeqHvbh6pzdmjYD3C092SYgLD2TyoCqHpTJk6vp8DvCTGGc7iowZ2MoiYiVUUCcu7muw==
dependencies:
"@types/node" "*"
"@types/pino-pretty" "*"
"@types/pino-std-serializers" "*"
sonic-boom "^2.1.0"
"@types/prettier@^2.1.5":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.1.tgz#e1303048d5389563e130f5bdd89d37a99acb75eb"
......@@ -2709,6 +2733,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
dependencies:
cross-spawn "^7.0.1"
cross-fetch@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
......@@ -2716,7 +2747,7 @@ cross-fetch@^3.1.4:
dependencies:
node-fetch "2.6.1"
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
......@@ -6539,7 +6570,7 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
sonic-boom@^2.2.0, sonic-boom@^2.2.1:
sonic-boom@^2.1.0, sonic-boom@^2.2.0, sonic-boom@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.3.1.tgz#e6572184fb3adf145dbfeccff48141bbd1009e4c"
integrity sha512-o0vJPsRiCW5Q0EmRKjNiiYGy2DqSXcxk4mY9vIBSPwmkH/e/vJ2Tq8EECd5NTiO77x8vlVN+ykDjRQJTqf7eKg==
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment