Spaces:
Sleeping
Sleeping
from __future__ import annotations | |
import json | |
import logging | |
import random | |
import hashlib | |
import uuid | |
from functools import partial | |
from pathlib import Path | |
import gradio as gr | |
logger = logging.getLogger(__name__) | |
logger.setLevel(logging.DEBUG) | |
logging.basicConfig(format="%(asctime)s [%(levelname)s] - %(message)s") | |
dump_score = "scores.json" | |
dump_info = "userinfo.json" | |
# load code samples | |
# samples_num = 0 | |
# for path in Path("samples").glob("*.java"): | |
# samples_num = samples_num + 1 | |
# selected_num = int(samples_num / 2) | |
# selected_index = random.sample(range(samples_num), selected_num) | |
samples = [] | |
file_names = [] | |
for i, path in enumerate(Path("samples").glob("*.java")): | |
samples.append(path.read_text(encoding="utf-8")) | |
file_names.append(path.name) | |
file_names_with_id = [f"{i:02d} - {name}" for i, name in enumerate(file_names)] | |
logger.info(f"Loaded {len(samples)} samples") | |
# map from user name to {file name: score} | |
global_score: dict[str, dict[str, int]] = dict() | |
global_info: dict[str, list[str, str, str]] = dict() | |
if Path(dump_score).exists(): | |
with open(dump_score, encoding="utf-8") as f: | |
global_score = json.load(f) | |
if Path(dump_info).exists(): | |
with open(dump_info, encoding="utf-8") as f: | |
global_info = json.load(f) | |
score_desc = [ | |
"", | |
"根本没有注释所表达的规约", | |
"有规约,但其意义过于trivial,对于任何类似pattern的代码都有效", | |
"规约有一定意义,但对于理解程序实际所做的事没有什么帮助", | |
"规约能够充分描述程序中某些部分应满足的性质,但尚不足以完全描述整个程序所做的事", | |
"规约能够充分描述程序整体与各个部分的行为", | |
] | |
help_markdown = Path("README.md").read_text(encoding="utf-8") | |
with gr.Blocks(title="程序规约有效性评估——用户调研") as demo: | |
samples_num = len(samples) | |
selected_num = int(samples_num / 2) | |
selected_index = gr.State([]) | |
def select_index(selected_index): | |
array = [0] | |
array.extend(random.sample(range(1, samples_num), selected_num - 1)) | |
array.sort() | |
return array | |
index = gr.State(0) | |
notify_same_name = gr.State(True) | |
title = gr.HTML("<h1><center>程序规约有效性评估——请先展开README</center></h1>") | |
with gr.Accordion(label="Readme", open=False): | |
help = gr.Markdown(help_markdown) | |
with gr.Accordion(label="User Info", open=True) as info_accordion: | |
user_name = gr.State("") | |
user_gender = gr.Radio( | |
["Male", "Female", "Other", "Prefer Undisclosed"], | |
value="Unknown", | |
interactive=True, | |
label="Gender" | |
) | |
user_age = gr.Textbox(label="Age", | |
placeholder="Enter your age (a single arabic number)", | |
interactive=True, | |
autofocus=True | |
) | |
user_major = gr.Dropdown( | |
label="Major", | |
choices=["Architecture", "Astronomy", "Biology", "Chemistry", "Computer Science", "Earth Science", "Economics and Finance", "Electrical Science", "Environmental Science", "Materials Science", "Mathematics", "Physics", "Others"], | |
value="Unknown", | |
multiselect=False, | |
interactive=True | |
) | |
user_willing = gr.Textbox(label="Further Evaluation Willingness", | |
placeholder="Would you like to participate in futher evaluation? If so, please leave your contact information, e.g. your email", | |
interactive=True, | |
autofocus=True | |
) | |
with gr.Row(equal_height=False): | |
button_prev = gr.Button(value="<") | |
with gr.Column(scale=12): | |
with gr.Row(): | |
score_description = gr.Textbox(value=score_desc[1], | |
label="Description", | |
scale=3) | |
score_slider = gr.Slider( | |
minimum=1, | |
maximum=5, | |
step=1, | |
value=1, | |
label="Score", | |
) | |
score_slider.release(lambda value: gr.Text( | |
value=score_desc[value], label="Description"), | |
inputs=[score_slider], | |
outputs=[score_description]) | |
submit = gr.Button(value="Submit this question", variant="primary") | |
with gr.Row(): | |
code_title = gr.HTML( | |
f"<h2>(01/{selected_num:02d}) {file_names[index.value]}</h2>") | |
# code_select = gr.Dropdown( | |
# choices=file_names_with_id, | |
# value=file_names_with_id[index.value], | |
# interactive=True, | |
# show_label=False, | |
# multiselect=False, | |
# ) | |
code_select = gr.State(0) | |
code_block = gr.Code( | |
samples[index.value], | |
language="javascript", | |
) | |
pswd_box = gr.Textbox(value="Thanks to MuYang for his tremendous contributions to this website!", show_label=False, interactive=True) | |
with gr.Row(): | |
res_block1 = gr.Code( | |
language="javascript", | |
visible=False | |
) | |
res_block2 = gr.Code( | |
language="javascript", | |
visible=False | |
) | |
button_next = gr.Button(value=">") | |
def update_index(file_index: int, selected_index: list, username: str, delta: int): | |
progress = 0 | |
for i, index in enumerate(selected_index): | |
if index == file_index: | |
progress = (i + delta) % len(selected_index) + 1 | |
file_index = selected_index[(i + delta) % len(selected_index)] | |
break | |
# file_index = (file_index + delta + len(samples)) % len(samples) | |
file_name = file_names[file_index] | |
file_name_id = file_names_with_id[file_index] | |
scores = global_score.get(username, dict()) | |
# set score if exists | |
if file_name not in scores: | |
slider = gr.Slider( | |
minimum=1, | |
maximum=5, | |
step=1, | |
value=1, | |
label="Score", | |
) | |
desc = score_desc[1] | |
else: | |
slider = gr.Slider( | |
minimum=1, | |
maximum=5, | |
step=1, | |
value=scores[file_name], | |
label="Score", | |
) | |
desc = score_desc[scores[file_name]] | |
return (file_index, f"<h2>({progress:02d}/{len(selected_index):02d}) {file_name}</h2>", | |
samples[file_index], slider, desc, file_name_id) | |
def submit_score(file_index: int, selected_index: list, | |
username: str, usergender:str, userage:str, usermajor:str, userwilling:str, | |
value: int, | |
notify_name: bool = True): | |
# first check if user name is set | |
if not username: | |
# prompt user to set name | |
gr.Warning("Please set your name first!") | |
logging.info("User name not set.") | |
return *update_index(file_index, selected_index, username, | |
delta=0), notify_name | |
filename = file_names[file_index] | |
scores = global_score.setdefault(username, dict()) | |
userinfo = global_info.setdefault(username, ["Unknown", "Unknown", "Unknown", "Unknown"]) | |
# check if user name duplicated | |
if notify_name and filename in scores: | |
gr.Warning("Existing user name detected.") | |
logging.info(f"User name {username} duplicated.") | |
notify_name = False | |
logger.info(f"User {username} scored {filename} with {value}") | |
num_scored_prev = len(scores) | |
# update the score to global score | |
global_score[username][filename] = value | |
global_info[username] = [usergender, userage, usermajor, userwilling] | |
# check if all files scored | |
num_scored = len(global_score[username]) | |
if num_scored == selected_num and num_scored_prev < selected_num: | |
gr.Info( | |
"Congratulations! All tasks done! Thank you for your contributions!") | |
logging.info(f"User {username} scored all files.") | |
# get all scores of i | |
scores_i = [ | |
user_scores[filename] for user_scores in global_score.values() | |
if filename in user_scores | |
] | |
if len(scores_i) > 0: | |
avg = sum(scores_i) / len(scores_i) | |
plural = "s" if len(scores_i) > 1 else "" | |
file_show = f"{file_index:02d} - {file_names[file_index]}" | |
gr.Info(f"{len(scores_i)} guy{plural} scored " | |
f"[{file_show}] with {avg:.2f}/5") | |
# dump global score | |
with open(dump_score, "w", encoding="utf-8") as f: | |
json.dump(global_score, f, ensure_ascii=False, indent=4) | |
# dump global info | |
with open(dump_info, "w", encoding="utf-8") as f: | |
json.dump(global_info, f, ensure_ascii=False, indent=4) | |
# update index by 1 | |
return *update_index(file_index, selected_index, username, delta=1), notify_name | |
def select_code(filename_id: str, username: str): | |
file_index = file_names_with_id.index(filename_id) | |
return update_index(file_index, selected_index, username, delta=0) | |
def process_pswd(pswd:str): | |
m = hashlib.md5() | |
m.update(pswd.encode("utf-8")) | |
if m.hexdigest() == "1dde58fa912bb474be7e9bea10cdb6a0": | |
return (gr.Code(language="javascript",visible=True), gr.Code(language="javascript",visible=True)) | |
return (gr.Code(language="javascript",visible=False), gr.Code(language="javascript",visible=False)) | |
def load_result(): | |
res1 = json.dumps(global_score, ensure_ascii=False, indent=4) | |
res2 = json.dumps(global_info, ensure_ascii=False, indent=4) | |
return (res1, res2) | |
def gen_uuid(): | |
return uuid.uuid4().hex | |
demo.load(select_index, inputs=[selected_index], outputs=[selected_index]).then(gen_uuid, inputs=None, outputs=user_name) | |
inputs = [index, selected_index, user_name] | |
outputs = [ | |
index, code_title, code_block, score_slider, score_description, | |
code_select | |
] | |
button_prev.click(partial(update_index, delta=-1), inputs, outputs) | |
button_next.click(partial(update_index, delta=+1), inputs, outputs) | |
submit.click(submit_score, | |
inputs=[index, selected_index, | |
user_name, user_gender, user_age, user_major, user_willing, | |
score_slider, notify_same_name], | |
outputs=[ | |
index, code_title, code_block, score_slider, | |
score_description, code_select, notify_same_name | |
]) | |
# user_name.input(partial(update_index, delta=0), inputs, outputs) | |
# code_select.select(partial(select_code), | |
# inputs=[code_select, user_name], | |
# outputs=outputs) | |
pswd_box.submit(process_pswd, inputs=[pswd_box], outputs=[res_block1, res_block2]).then(load_result, inputs=None, outputs=[res_block1, res_block2]) | |
if __name__ == "__main__": | |
demo.queue().launch(debug=True, share=False) | |