FitTest-24 / app.py
CurHarsh's picture
Fix image component compatibility
602a3c9 verified
Raw History Blame Contribute Delete
3.8 kB
import io,json,os,threading,time,uuid
from pathlib import Path
from datetime import datetime,timezone
import gradio as gr
from huggingface_hub import HfApi
ROOT=Path(__file__).parent
QS=json.loads((ROOT/'questions.json').read_text())
LOCK=threading.Lock()
CRITERIA='''## Judging criteria
**At least one option fits in every question.** Two or all three may also fit. For each image, choose **Fits** or **Does not fit**.
- **Surface fit:** the object’s entire footprint must stay within the usable top surface.
- **Container fit:** the entire rigid object must fit inside the container without protruding.
'''
def new_state():return {'id':str(uuid.uuid4()),'index':0,'answers':{},'start':time.time(),'submitted':False}
def render(s):
q=QS[s['index']];a=s['answers'].get(q['id'],{})
return [s,f"### Question {s['index']+1} of 24 · {'Surface fit' if q['domain']=='area' else 'Container fit'}\n\n{q['question']}",*[str(ROOT/'images'/Path(q['images'][l]).name) for l in 'ABC'],*[gr.update(value=a.get(l),interactive=not s['submitted']) for l in 'ABC'],gr.update(interactive=s['index']>0 and not s['submitted']),gr.update(value='Submit' if s['index']==23 else 'Next',interactive=not s['submitted']), '']
def init():return render(new_state())
def move(s,a,b,c,direction):
if s is None:s=new_state()
if s['submitted']:raise gr.Error('Your responses have already been submitted.')
if direction==1 and any(v not in ['Fits','Does not fit'] for v in [a,b,c]):raise gr.Error('Please judge A, B, and C before continuing.')
s['answers'][QS[s['index']]['id']]=dict(zip('ABC',[a,b,c]))
if direction==1 and s['index']==23:
if len(s['answers'])!=24 or any(any(v not in ['Fits','Does not fit'] for v in r.values()) for r in s['answers'].values()):raise gr.Error('Please answer all options before submitting.')
token,repo=os.getenv('HF_TOKEN'),os.getenv('RESPONSES_REPO')
if not token or not repo:raise gr.Error('Response storage is not configured. Please contact the study owner.')
data={'participant_id':s['id'],'study_version':'fit-min1-24-20261009-v1','submitted_at':datetime.now(timezone.utc).isoformat(),'elapsed_seconds':int(time.time()-s['start']),'answers':{qid:{l:'Yes' if v=='Fits' else 'No' for l,v in row.items()} for qid,row in s['answers'].items()}}
try:
with LOCK:HfApi(token=token).upload_file(path_or_fileobj=io.BytesIO(json.dumps(data,indent=2).encode()),path_in_repo=f"responses/{s['id']}.json",repo_id=repo,repo_type='dataset',commit_message='Save FitTest submission')
except Exception:raise gr.Error('Your submission was not saved. Please press Submit again.')
s['submitted']=True;out=render(s);out[-1]=f"## Thank you!\nYour responses have been saved privately.\n\nReceipt: `{s['id']}`";return out
s['index']=max(0,min(23,s['index']+direction));return render(s)
def previous(s,a,b,c):return move(s,a,b,c,-1)
def next_question(s,a,b,c):return move(s,a,b,c,1)
with gr.Blocks(title='FitTest') as demo:
gr.Markdown('# FitTest')
gr.Markdown(CRITERIA)
state=gr.State()
question=gr.Markdown()
imgs=[];radios=[]
with gr.Row():
for l in 'ABC':
with gr.Column():
imgs.append(gr.Image(label='Option '+l,interactive=False))
radios.append(gr.Radio(['Fits','Does not fit'],label=l,value=None))
with gr.Row():
prev=gr.Button('Previous');nxt=gr.Button('Next',variant='primary')
status=gr.Markdown()
outputs=[state,question,*imgs,*radios,prev,nxt,status]
demo.load(init,outputs=outputs,api_name=False)
prev.click(previous,inputs=[state,*radios],outputs=outputs,api_name=False)
nxt.click(next_question,inputs=[state,*radios],outputs=outputs,api_name=False)
gr.Markdown('Responses are visible only to the study owner. Please finish in one session; refreshing the page starts a new session.')
if __name__=='__main__':demo.queue(default_concurrency_limit=8).launch()