def download_file(id: int, filename: str): """ 下载题目文件 参数: 无 返回: 无 """ # print(id,filename) problem = db.session.query(Problem).filter(Problem.id == id) import flask if problem.count() == 0: flask.abort(404) problem: Problem = problem.one() # filename = secure_filename(filename) if not problem.public and not session.get("uid"): flask.abort(403) if not any((x["name"] == filename for x in problem.files)): flask.abort(404) public_file = filename in problem.downloads # if not problem.public and not permission_manager.has_any_permission(session.get("uid", None), f"problem.use.{problem.id}"): # 用户未登录 ok = False if not session.get("uid", None): # 只能下载公开题目的公开文件 if not problem.public or not public_file: flask.abort(403) else: # 用户已登录 # 区分是否有题目管理权限 # 题目创建者或者有管理权限,那么任何时候都行 if session.get( "uid", -1 ) == problem.uploader_id or permission_manager.has_any_permission( session.get("uid", None), f"problem.manage"): ok = True # 其他情况,如果是公开题或者有权限的私有题,则任何时候都允许下公开文件 else: if problem.public or permission_manager.has_any_permission( session.get("uid", -1), f"problem.use.{problem.id}"): if public_file: ok = True # if not problem.public and not permission_manager.has_any_permission(session.get("uid", None), f"problem.manage") and not public_file: # flask.abort(403) # if session.get("uid"): # user: User = db.session.query(User).filter( # User.id == session.get("uid")).one() # if not permission_manager.has_permission(user.id, f"problem.manage") and user.id != problem.uploader_id and not public_file: # flask.abort(403) # else: # if not problem.public or not public_file: # flask.abort(403) if not ok: flask.abort(403) import os file = os.path.join(basedir, f"{config.UPLOAD_DIR}/{id}/{filename}") if not os.path.exists(file): flask.abort(404) return send_file(file, as_attachment=True, conditional=True)
def rejudge(): """ 重测提交 参数: submission_id:int 提交ID { "code","message" } """ # user: User = User.by_id(session.get("uid")) if not permission_manager.has_any_permission(session.get( "uid", None), "submission.manage", "submission.rejudge"): return make_response(-1, message="你没有权限这样做") submit: Submission = Submission.by_id(request.form["submission_id"]) problem: Problem = db.session.query( Problem.problem_type).filter_by(id=submit.problem_id).one() if problem.problem_type == "submit_answer": return make_response(-1, message="提交答案题不能重测") if not submit: return make_response(-1, message="提交不存在") from api.judge import push_to_queue submit.status = "waiting" db.session.commit() push_to_queue(submit.id) return make_response(0, message="ok")
def create_problem(): """ 创建空题目 参数: 无 返回: { "code":0,//非0表示调用成功 "message":"qwq"//code非0的时候表示错误信息 "problem_id":-1//成功时表示题目ID } """ if session.get("uid") is None: return make_response(-1, message="你尚未登录!") user: User = db.session.query(User).filter( User.id == session.get("uid")).one() if not permission_manager.has_any_permission(user.id, "problem.manage", "problem.create"): return make_response(-1, message="你没有权限进行此操作") from datetime import datetime problem = Problem(uploader_id=user.id, create_time=datetime.now(), public=False) db.session.add(problem) db.session.commit() return make_response(0, problem_id=problem.id)
def import_from_syzoj(url: str, willPublic: bool): """ 从SYZOJ导入题目 参数: url:str SYZOJ题目URL willPublic:int 新题目是否公开 返回 { "code":0, "uuid":'用于websocket的uuid', "message":"" } """ import urllib import tempfile import pathlib import traceback import zipfile import shutil import os import yaml import requests from io import BytesIO from utils import decode_json if not session.get("uid"): return make_response(-1, message="请先登录") user: User = User.by_id(session.get("uid")) if not permission_manager.has_any_permission(user.id, "problem.create", "problem.manage"): return make_response(-1, message="你没有权限执行此操作") try: with requests.get(f"{url}/export") as urlf: data = decode_json(urlf.content.decode())["obj"] print("JSON data: {}".format(data)) import datetime problem = Problem(uploader_id=user.id, title=data["title"], content=data["description"], input_format=data["input_format"], output_format=data["output_format"], hint=data["limit_and_hint"], using_file_io=data["file_io"], input_file_name=data["file_io_input_name"], output_file_name=data["file_io_output_name"], create_time=datetime.datetime.now() ) if willPublic: if not permission_manager.has_any_permission(user.id, "problem.publicize", "problem.manage"): return make_response(-1, message="你没有权限公开题目") problem.public = True problem.example = [] problem.hint = "### 样例\n" + \ data["example"]+"\n\n### Hint\n"+problem.hint time_limit = int(data["time_limit"]) memory_limit = int(data["memory_limit"]) db.session.add(problem) db.session.commit() work_dir = pathlib.PurePath(tempfile.mkdtemp()) with requests.get(f"{url}/testdata/download") as urlf: pack = zipfile.ZipFile(BytesIO(urlf.content)) pack.extractall(work_dir) pack.close() problem_data_dir = pathlib.PurePath( f"{config.UPLOAD_DIR}/{problem.id}") shutil.rmtree(problem_data_dir, ignore_errors=True) shutil.copytree(work_dir, problem_data_dir) shutil.rmtree(work_dir) # 更换新的word_dir work_dir = problem_data_dir for file in filter(lambda x: x.endswith(".lock"), os.listdir(work_dir)): os.remove(work_dir/file) file_list = [] for file in filter(lambda x: not x.endswith(".lock"), os.listdir(work_dir)): with open(work_dir/(file+".lock"), "w") as f: import time last_modified_time = time.time() f.write(str(last_modified_time)) file_list.append({ "name": file, "size": os.path.getsize(work_dir/file), "last_modified_time": last_modified_time }) problem.files = file_list pure_file_list = list(map(lambda x: x["name"], file_list)) for x in pure_file_list: if x.startswith("spj_"): problem.spj_filename = x break auto_generate = True subtasks = [] if os.path.exists(work_dir/"data.yml"): # 存在data.yml with open(work_dir/"data.yml", "r", encoding="utf-8") as f: data_obj = yaml.safe_load(f) # data.yml中钦定spj if "specialJudge" in data_obj: new_spj_filename = work_dir/( "spj_"+data_obj["specialJudge"]["language"]+"."+data_obj["specialJudge"]["fileName"].split(".")[-1]) print(new_spj_filename) print(work_dir/data_obj["specialJudge"]["fileName"]) shutil.move( work_dir/data_obj["specialJudge"]["fileName"], new_spj_filename) problem.spj_filename = new_spj_filename.name if "subtasks" in data_obj: auto_generate = False def make_input(x): return data_obj["inputFile"].replace("#", str(x)) def make_output(x): return data_obj["outputFile"].replace("#", str(x)) for i, subtask in enumerate(data_obj["subtasks"]): print(subtask) subtasks.append({ "name": f"Subtask{i+1}", "score": int(subtask["score"]), "method": subtask["type"], "time_limit": time_limit, "memory_limit": memory_limit, "testcases": [] }) testcases = subtasks[-1]["testcases"] score = subtasks[-1]["score"]//len(subtask["cases"]) for testcase in subtask["cases"]: testcases.append({ "input": make_input(testcase), "output": make_output(testcase), "full_score": score }) testcases[-1]["full_score"] = subtasks[-1]["score"] - \ score*(len(testcases)-1) if auto_generate: # 不存在data.yml,直接生成子任务 input_files = list( filter(lambda x: x.endswith(".in"), pure_file_list)) output_files = list( filter(lambda x: x.endswith(".out") or x.endswith(".ans"), pure_file_list)) if len(input_files) == len(output_files): pass for i, file in enumerate(input_files): pure_file_name = file[:file.rindex(".")] subtasks.append({ "name": f"Subtask{i+1}", "score": 100//len(input_files), "method": "sum", "time_limit": time_limit, "memory_limit": memory_limit, "testcases": [{"full_score": 100//len(input_files), "input": file, "output": f"{pure_file_name}.ans" if f"{pure_file_name}.ans" in output_files else f"{pure_file_name}.out"}], "comment": "" }) diff = 100-sum(map(lambda x: x["score"], subtasks)) subtasks[-1]["score"] += diff subtasks[-1]["testcases"][0]["full_score"] += diff for file in filter(lambda x: x.endswith(".lock"), os.listdir(work_dir)): os.remove(work_dir/file) for file in filter(lambda x: not x.endswith(".lock"), os.listdir(work_dir)): with open(work_dir/(file+".lock"), "w") as f: import time last_modified_time = time.time() f.write(str(last_modified_time)) problem.files = generate_file_list(problem.id) problem.subtasks = subtasks db.session.commit() except Exception: print(traceback.format_exc()) return make_response(-1, message=traceback.format_exc()) return make_response(0, problem_id=problem.id)
def update_problem(): """ 更新题目 参数: id:int 题目ID data:dict 题目数据 返回: { "code":0,//非0表示调用成功 "message":"qwq",//code非0的时候表示错误信息 } """ problem: Problem = db.session.query(Problem).filter( Problem.id == request.form["id"]) if problem.count() == 0: return make_response(-1, message="题目ID不存在") problem = problem.one() if not session.get("uid"): return make_response(-1, message="你没有权限执行此操作") user: User = db.session.query(User).filter( User.id == session.get("uid")).one() if not permission_manager.has_permission(user.id, "problem.manage") and user.id != problem.uploader_id: return make_response(-1, message="你没有权限执行此操作") data = decode_json(request.form["data"]) for subtask in data["subtasks"]: try: subtask["score"] = int(subtask["score"]) except Exception as ex: return make_response(-1, message=f"子任务{subtask['name']}的分数非整数") for subtask in data["subtasks"]: if len(subtask["testcases"]) == 0: return make_response(-1, message=f"子任务{subtask['name']}的测试点个数为0!") if subtask["score"] < len(subtask["testcases"]): return make_response(-1, message="测试点个数不得多于分数") if subtask["method"] == "min": list(map(lambda x: x.__setitem__( "full_score", 1), subtask["testcases"])) else: score = subtask["score"]//len(subtask["testcases"]) for i in range(0, len(subtask["testcases"])-1): subtask["testcases"][i]["full_score"] = score subtask["testcases"][-1]["full_score"] = subtask["score"] - \ score*(len(subtask["testcases"])-1) if not permission_manager.has_any_permission(user.id, "problem.manage", "problem.publicize") and problem.public == False and data["public"] == True: return make_response(-1, message="你没有权限公开题目") # 更改题目ID if data["newProblemID"] != problem.id: old_id: int = int(problem.id) new_id: int = int(data["newProblemID"]) if db.session.query(Problem.id).filter(Problem.id == new_id).one_or_none(): return make_response(-1, message="题目ID已存在!") # 移动题目数据文件夹 import shutil import pathlib path = pathlib.Path(config.UPLOAD_DIR) try: shutil.move(path/str(old_id), path/str(new_id)) except Exception as ex: pass # 修改提交中涉及的题目ID db.session.query(Submission).filter( Submission.problem_id == old_id).update({Submission.problem_id: new_id}) # 至于比赛...不管了 problem.id = new_id for k, v in data.items(): if k in {"create_time", "id", "newProblemID"}: continue setattr(problem, k, v) db.session.commit() return make_response(0)
def submit(): """ 提交代码/答案 参数: problem_id:int 题目ID code:str 代码 language:str 语言ID contest_id:int 比赛ID,设置为-1表示非比赛提交,如果非-1,那么problem_id为比赛中的题目ID usedParameters:str [1,2,3] 使用到了的附加编译选项ID virtualID: 虚拟比赛ID answerData: 提交答案题答案数据 返回: { "code":0,//非0表示调用成功 "message":"qwq",//调用失败时的信息 "submission_id":-1//调用成功时的提交ID } """ if not session.get("uid"): return make_response(-1, message="请先登录") user: User = db.session.query(User).filter( User.id == session.get("uid")).one() using_contest = int(request.form["contest_id"]) != -1 virtual_id = int(request.form.get("virtualID", -1)) using_virtual = (virtual_id != -1) virtual_contest: VirtualContest = db.session.query( VirtualContest).filter_by(id=virtual_id).one_or_none() if using_contest: contest: Contest = Contest.by_id(request.form["contest_id"]) if using_virtual: if (not virtual_contest ) or virtual_contest.contest_id != contest.id: return make_response(-1, message="此虚拟比赛不对应于此实际比赛") if not virtual_contest.running(): return make_response(-1, message="虚拟比赛未在进行") problem: Problem = Problem.by_id(contest.problems[int( request.form["problem_id"])]["id"]) else: if not contest.running(): return make_response(-1, message="比赛未在进行!") if contest.private_contest and not permission_manager.has_permission( session.get("uid", -1), f"contest.use.{contest.id}"): return make_response(-1, message="你没有权限查看该比赛") problem: Problem = Problem.by_id(contest.problems[int( request.form["problem_id"])]["id"]) else: problem = db.session.query(Problem).filter( Problem.id == request.form["problem_id"]) if problem.count() == 0: return make_response(-1, message="题目ID不存在") problem: Problem = problem.one() if not problem.public: if not permission_manager.has_any_permission( user.id, f"problem.use.{problem.id}" ) and user.id != problem.uploader_id: return make_response(-1, message="你没有权限执行此操作") from typing import Set if problem.problem_type != "submit_answer": parameters: Set[int] = set(decode_json(request.form["usedParameters"])) import importlib import re for i, item in enumerate(problem.extra_parameter): if re.compile(item["lang"]).match( request.form["language"] ) and item["force"] and i not in parameters: parameters.add(i) try: importlib.import_module("langs." + request.form["language"]) except: return make_response(-1, message="不支持的语言ID") parameter_string = " ".join( (problem.extra_parameter[i]["parameter"] for i in parameters if i < len(problem.extra_parameter) and re.compile(problem.extra_parameter[i]["lang"]).match( request.form["language"]))) import datetime submit = Submission( uid=user.id, language=request.form["language"], problem_id=problem.id, submit_time=datetime.datetime.now(), public=problem.public, contest_id=request.form["contest_id"], virtual_contest_id=virtual_id if using_virtual else None, code=request.form["code"], status="waiting", extra_compile_parameter=parameter_string, selected_compile_parameters=list(parameters)) else: import base64 import datetime encoded = base64.encodebytes( request.files["answerData"].stream.read()).decode().replace( "\n", "") submit = Submission( uid=user.id, language="cpp", problem_id=problem.id, submit_time=datetime.datetime.now(), public=problem.public, contest_id=request.form["contest_id"], virtual_contest_id=virtual_id if using_virtual else None, code=encoded, status="waiting", extra_compile_parameter="", selected_compile_parameters=[]) db.session.add(submit) db.session.commit() from api.judge import push_to_queue push_to_queue(submit.id) return make_response(0, submission_id=submit.id)
def update_problem(): """ 更新题目 参数: id:int 题目ID data:dict 题目数据 返回: { "code":0,//非0表示调用成功 "message":"qwq",//code非0的时候表示错误信息 } """ problem: Problem = db.session.query(Problem).filter( Problem.id == request.form["id"]) if problem.count() == 0: return make_response(-1, message="题目ID不存在") problem = problem.one() if not session.get("uid"): return make_response(-1, message="你没有权限执行此操作") user: User = db.session.query(User).filter( User.id == session.get("uid")).one() if not permission_manager.has_permission( user.id, "problem.manage") and user.id != problem.uploader_id: return make_response(-1, message="你没有权限执行此操作") data = decode_json(request.form["data"]) for subtask in data["subtasks"]: try: subtask["score"] = int(subtask["score"]) except Exception as ex: return make_response(-1, message=f"子任务{subtask['name']}的分数非整数") for subtask in data["subtasks"]: if len(subtask["testcases"]) == 0: return make_response(-1, message=f"子任务{subtask['name']}的测试点个数为0!") if subtask["score"] < len(subtask["testcases"]): return make_response(-1, message="测试点个数不得多于分数") if subtask["method"] == "min": list( map(lambda x: x.__setitem__("full_score", 1), subtask["testcases"])) else: score = subtask["score"] // len(subtask["testcases"]) for i in range(0, len(subtask["testcases"]) - 1): subtask["testcases"][i]["full_score"] = score subtask["testcases"][-1]["full_score"] = subtask["score"] - \ score*(len(subtask["testcases"])-1) if not permission_manager.has_any_permission( user.id, "problem.manage", "problem.publicize" ) and problem.public == False and data["public"] == True: return make_response(-1, message="你没有权限公开题目") # 更改题目ID if data["newProblemID"] != problem.id: old_id: int = int(problem.id) new_id: int = int(data["newProblemID"]) if db.session.query( Problem.id).filter(Problem.id == new_id).one_or_none(): return make_response(-1, message="题目ID已存在!") # 移动题目数据文件夹 import shutil import pathlib path = pathlib.Path(config.UPLOAD_DIR) try: shutil.move(path / str(old_id), path / str(new_id)) except Exception as ex: pass # 修改提交中涉及的题目ID db.session.query(Submission).filter( Submission.problem_id == old_id).update( {Submission.problem_id: new_id}) # 至于比赛...不管了 problem.id = new_id AVAILABLE_KEYS = [ "background", "extra_parameter", "subtasks", "content", "can_see_results", "public", "input_format", "spj_filename", "output_format", "using_file_io", "hint", "input_file_name", "example", "output_file_name", "title", "downloads", "provides", "invite_code" ] submit_answer = (request.form["submitAnswer"] == "true") if problem.problem_type == "remote_judge": return make_response(-1, message="远程评测题目不得更改题目类型") problem.problem_type = "submit_answer" if submit_answer else "traditional" for key in AVAILABLE_KEYS: setattr(problem, key, data[key]) problem.submission_visible = data["submissionVisible"] db.session.commit() return make_response(0)