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) | |