├── .gitignore ├── .idea ├── misc.xml ├── modules.xml ├── syzoj.iml ├── vcs.xml └── workspace.xml ├── README.md ├── config.py.exmple ├── install.py ├── requirements.txt ├── run.py ├── syzoj-judge └── judge.py ├── syzoj ├── __init__.py ├── controller.py ├── models │ ├── __init__.py │ ├── article.py │ ├── contest.py │ ├── file.py │ ├── judge.py │ ├── problem.py │ └── user.py ├── static │ ├── amazeui.min.css │ ├── amazeui.min.js │ ├── auto-render.js │ ├── css │ │ ├── default.css │ │ ├── normalize.css │ │ └── search-form.css │ ├── flat-ui-icons-regular.eot │ ├── flat-ui-icons-regular.svg │ ├── flat-ui-icons-regular.ttf │ ├── flat-ui-icons-regular.woff │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── icomoon.eot │ │ ├── icomoon.svg │ │ ├── icomoon.ttf │ │ └── icomoon.woff │ ├── img │ │ ├── image.jpg │ │ └── syzoj-logo.png │ ├── jquery.ext.js │ ├── jquery.min.js │ ├── js │ │ └── jquery-1.11.0.min.js │ ├── live2d.js │ ├── marked.js │ ├── prettify.css │ ├── prettify.js │ ├── syzoj-logo.png │ ├── syzoj_32.ico │ ├── uploads │ │ ├── 3e3d3da766bde89d9ae7953a63f0b269 │ │ └── ce978e5d3f0bc87c2c693db489f6d8f0 │ ├── waifu-tips.js │ ├── waifu-tips.json │ └── waifu.css ├── templates │ ├── article.html │ ├── contest.html │ ├── contest_list.html │ ├── contest_problem.html │ ├── contest_ranklist.html │ ├── delete_article.html │ ├── discussion.html │ ├── edit_article.html │ ├── edit_contest.html │ ├── edit_notice.html │ ├── edit_problem.html │ ├── edit_user.html │ ├── error_info.html │ ├── index.html │ ├── info.html │ ├── judge_detail.html │ ├── judge_state.html │ ├── layout.html │ ├── login.html │ ├── notice.html │ ├── problem.html │ ├── problem_set.html │ ├── ranklist.html │ ├── sign_up.html │ ├── submit.html │ ├── upload_testdata.html │ ├── user.html │ └── virtual_contest.html ├── update_assistant │ └── misc.py └── views │ ├── __init__.py │ ├── common.py │ ├── contest.py │ ├── discussion.py │ ├── judge.py │ ├── problem.py │ ├── session.py │ └── user.py └── test ├── test_contest.py ├── test_controller.py └── test_problem.py /.gitignore: -------------------------------------------------------------------------------- 1 | #------------Special------------ 2 | config.pyc 3 | config.py 4 | 5 | syzoj-judge/* 6 | !syzoj-judge/judge.py 7 | 8 | #------------github/gitignore/Python.gitignore------------ 9 | # Byte-compiled / optimized / DLL files 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | wheels/ 31 | pip-wheel-metadata/ 32 | share/python-wheels/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | MANIFEST 37 | 38 | # PyInstaller 39 | # Usually these files are written by a python script from a template 40 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 41 | *.manifest 42 | *.spec 43 | 44 | # Installer logs 45 | pip-log.txt 46 | pip-delete-this-directory.txt 47 | 48 | # Unit test / coverage reports 49 | htmlcov/ 50 | .tox/ 51 | .nox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *.cover 58 | *.py,cover 59 | .hypothesis/ 60 | .pytest_cache/ 61 | 62 | # Translations 63 | *.mo 64 | *.pot 65 | 66 | # Django stuff: 67 | *.log 68 | local_settings.py 69 | db.sqlite3 70 | db.sqlite3-journal 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 103 | __pypackages__/ 104 | 105 | # Celery stuff 106 | celerybeat-schedule 107 | celerybeat.pid 108 | 109 | # SageMath parsed files 110 | *.sage.py 111 | 112 | # Environments 113 | .env 114 | .venv 115 | env/ 116 | venv/ 117 | ENV/ 118 | env.bak/ 119 | venv.bak/ 120 | 121 | # Spyder project settings 122 | .spyderproject 123 | .spyproject 124 | 125 | # Rope project settings 126 | .ropeproject 127 | 128 | # mkdocs documentation 129 | /site 130 | 131 | # mypy 132 | .mypy_cache/ 133 | .dmypy.json 134 | dmypy.json 135 | 136 | # Pyre type checker 137 | .pyre/ 138 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/syzoj.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![996.ICU](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 2 | # Hello SYOJ! 3 | > 一个开源的 OI/ACM/icpc在线测评平台,基于Flask版的开源项目[SYZOJ](https://github.com/Zhengzhou-11-Highschool/syzoj)二次开发 4 | 5 | ## 依赖项 6 | mysql 7 | *注意:本文档不介绍mysql的配置工作* 8 | 9 | python3 10 | 11 | flask 12 | 13 | flask-sqlalchemy 14 | 15 | pymysql 16 | 17 | ## 如何安装它 18 | *以下教程以ubuntu为例,其他linux请自行替换對應的包管理器* 19 | 20 | 1.安装python3以及pip 21 | ```bash 22 | sudo apt install python3 python3-pip -y 23 | ``` 24 | 2.安装flask等python模块 25 | ```bash 26 | sudo pip3 install flask flask-sqlalchemy pymysql 27 | ``` 28 | 3.下载、配置并运行syoj 29 | ```bash 30 | sudo apt install git -y && git clone https://github.com/Edify-Studio/SYOJ.git 31 | cd SYOJ && sudo python3 install.py 32 | ``` 33 | 然后根据提示安装即可,安装后会自动运行 34 | 35 | 4.直接运行syoj 36 | ```bash 37 | sudo nohup python3 run.py & 38 | ``` 39 | 40 | ## 如何运行评测机 41 | 42 | 1.安装[TJudger](https://github.com/TimHsue/TJudger) 43 | ```bash 44 | sudo python setup.py install 45 | ``` 46 | 2.给予 SYOJ/syzoj-judge/runner 运行权限 47 | ```bash 48 | chmod +x runner 49 | ``` 50 | 3.使用Python2运行judge.py 51 | ```bash 52 | sudo nohup python judge.py & 53 | ``` 54 | 55 | ## 附:MySQL中文设置: 56 | ```bash 57 | alter table XXX convert to character set utf8; 58 | ``` 59 | 60 | ## TODO LIST 61 | 62 | 0.~~后端更换使用Tornado~~ 63 | 64 | 1.一个更为简单易用的安装&管理脚本 65 | 66 | 2.网站后台界面 67 | 68 | 3.更加友好的前端交互设计 69 | 70 | 4.网页响应式 71 | 72 | 5.~~前后端分离~~ 73 | 74 | 6.评测机支持Ubuntu 16.04 75 | 76 | 7.简单的防攻击措施 77 | 78 | ## 特别鸣谢 79 | 80 | [@TimHsue](https://github.com/TimHsue)
81 | [@Kench](https://github.com/kench233)
82 | [@北海若](#)
83 | [@wanzzhehe](https://github.com/wanzzhehe)
84 | [@Chenyao233](https://github.com/Chenyao2333)
85 | [@Nishikino_KKi](https://github.com/NishikinoKKi) 86 | -------------------------------------------------------------------------------- /config.py.exmple: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | BASE_DIR = os.path.abspath(os.path.dirname(__file__)) 4 | SQLALCHEMY_DATABASE_URI = '{{ database }}' 5 | UPLOAD_FOLDER = os.path.join(BASE_DIR, "syzoj", "static", "uploads") 6 | JUDGE_TOKEN = "77783949202395150352388871624955475980489287735056" 7 | 8 | if not os.path.isdir(UPLOAD_FOLDER): 9 | os.mkdir(UPLOAD_FOLDER) 10 | -------------------------------------------------------------------------------- /install.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | if __name__ == '__main__': 5 | config = open("config.py.exmple", 'r').read() 6 | print("欢迎使用SYOJ!") 7 | domain=input("请输入mysql服务器地址(不带端口):") 8 | port=input("请输入mysql服务器端口:") 9 | user=input("请输入用户名:") 10 | passwd=input("请输入密码:") 11 | dbname=input("请输入数据库名称(请务必确保数据库存在):") 12 | open('config.py', 'w').write(config.replace("{{ database }}",'mysql+pymysql://' + user + ':' + passwd + '@' + domain +':' + port +'/' + dbname)) 13 | print("正在初始化数据库...") 14 | from syzoj import db 15 | db.drop_all() 16 | db.create_all() 17 | print("初始化完成,正在启动syoj...") 18 | os.system('nohup python3 run.py &') 19 | print("syoj已在23333端口运行,感谢使用syoj!") 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.12.3 2 | Flask-SQLAlchemy==2.3.2 3 | Jinja2==2.10 4 | PyMySQL==0.8.0 5 | requests==2.20.0 6 | SQLAlchemy==1.2.6 7 | Werkzeug==0.14.1 8 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from syzoj import oj 2 | 3 | if __name__ == '__main__': 4 | oj.debug = True 5 | oj.run(host="0.0.0.0", port=23333) 6 | -------------------------------------------------------------------------------- /syzoj-judge/judge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #!/usr/bin/python 3 | import os 4 | import time 5 | import zipfile 6 | import urllib 7 | import urllib2 8 | import json 9 | import ExTJudger 10 | import codecs 11 | import subprocess 12 | from random import randint 13 | 14 | _SYZOJ_URL = "http://localhost:8811" 15 | _DOWNLOAD_TESTDATA_URL = _SYZOJ_URL + "/static/uploads" 16 | _GET_TASK_URL = _SYZOJ_URL + "/api/waiting_judge" 17 | _UPLOAD_TASK_URL = _SYZOJ_URL + "/api/update_judge" 18 | _SESSION_ID = "77783949202395150352388871624955475980489287735056" 19 | _BASE_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__))) 20 | _TESTDATA_DIR = os.path.join(_BASE_DIR, "testdata") 21 | 22 | if not os.path.isdir(_TESTDATA_DIR): 23 | os.mkdir(_TESTDATA_DIR) 24 | 25 | 26 | def get_judge_task(): 27 | global _GET_TASK_URL, _SESSION_ID 28 | url = _GET_TASK_URL + "?" + urllib.urlencode({"session_id": _SESSION_ID}) 29 | task = urllib2.urlopen(url).read() 30 | return json.loads(task) 31 | 32 | 33 | def upload_judge_result(result, judge_id): 34 | global _UPLOAD_TASK_URL, _SESSION_ID 35 | url = _UPLOAD_TASK_URL + "/" + str(judge_id) + "?" + urllib.urlencode({"session_id": _SESSION_ID}) 36 | data = urllib.urlencode({"result": json.dumps(result)}) 37 | req = urllib2.Request(url, data) 38 | response = urllib2.urlopen(req) 39 | return response.read() 40 | 41 | 42 | def download_file(url, des): 43 | df = urllib2.urlopen(url) 44 | with open(des, "a") as f: 45 | while True: 46 | data = df.read(4096) 47 | if data: 48 | f.write(data) 49 | else: 50 | break 51 | df.close() 52 | 53 | 54 | def unzip_as_testdata(testdata, des_dir): 55 | if not os.path.isdir(des_dir): 56 | os.mkdir(des_dir) 57 | 58 | zip_file = zipfile.ZipFile(testdata) 59 | 60 | for name in zip_file.namelist(): 61 | with open(os.path.join(des_dir, name), "wb") as f: 62 | f.write(zip_file.read(name)) 63 | f.close() 64 | 65 | zip_file.close() 66 | 67 | 68 | def get_testdata_dir(testdata_name): 69 | global _TESTDATA_DIR, _DOWNLOAD_TESTDATA_URL 70 | testdata_dir = os.path.join(_TESTDATA_DIR, testdata_name) 71 | if os.path.isdir(testdata_dir): 72 | return testdata_dir 73 | 74 | tmp_zip_file = _TESTDATA_DIR + testdata_name + "_tmp.zip" 75 | download_file(_DOWNLOAD_TESTDATA_URL + "/" + testdata_name, tmp_zip_file) 76 | unzip_as_testdata(tmp_zip_file, testdata_dir) 77 | os.remove(tmp_zip_file) 78 | 79 | return testdata_dir 80 | 81 | 82 | def write_src(source, target): 83 | if os.path.isfile(target): 84 | os.remove(target) 85 | with codecs.open(target, "w", "utf-8") as f: 86 | f.write(source) 87 | 88 | 89 | def run(source_file, std_in, std_out, time_limit, memory_limit): 90 | user_out = "user_tmp.out" 91 | 92 | CFG = { 93 | 'language':'C++', 94 | 'source_name':source_file, 95 | 'in_file':std_in, 96 | 'out_file':user_out, 97 | 'ans_file':std_out, 98 | 'time_limit':time_limit, 99 | 'memory_limit':memory_limit, 100 | 'compile option':['-lm', '-DONLINE_JUDGE'] 101 | } 102 | 103 | res = ExTJudger.run(CFG) 104 | 105 | result = {} 106 | result['status'] = res['status'] 107 | 108 | if not 'use_time' in res: 109 | result['time_used'] = 0 110 | else: 111 | result["time_used"] = res["use_time"] 112 | if not 'use_memory' in res: 113 | result['memory_used'] = 0 114 | else: 115 | result["memory_used"] = res["use_memory"] 116 | 117 | result['score'] = res['score'] 118 | 119 | if 'in' in res: 120 | result["input"] = res['in'][0: min(120, len(res['in']))] 121 | if len(res['in']) > 120: 122 | result["input"] += '\n...' 123 | if 'ans' in res: 124 | result["answer"] = res['ans'][0: min(120, len(res['ans']))] 125 | if len(res['ans']) > 120: 126 | result["answer"] += '\n...' 127 | if 'out' in res: 128 | result["user_out"] = res['out'][0: min(120, len(res['out']))] 129 | if len(res['out']) > 120: 130 | result["user_out"] += '\n...' 131 | if 'compile_info' in res: 132 | result['compile_info'] = res['compile_info'] 133 | if len(res['compile_info']) > 256: 134 | result["compile_info"] += '\n...' 135 | 136 | if os.path.isfile(user_out): 137 | os.remove(user_out) 138 | return result 139 | 140 | 141 | def judge(source, time_limit, memory_limit, testdata): 142 | result = {"status": "Judging", "score": 0, "total_time": 0, "max_memory": 0, "case_num": 0} 143 | target = "tjudger_source_file.cpp" 144 | 145 | testdata_dir = get_testdata_dir(testdata) 146 | 147 | write_src(source, target) 148 | 149 | with open(os.path.join(testdata_dir, "data_rule.txt")) as f: 150 | data_rule = f.read() 151 | lines = data_rule.split('\n') 152 | for i in range(0, len(lines)): 153 | lines[i] = lines[i].replace('\r', '').replace('\n', '') 154 | dt_no = lines[0].split() 155 | std_in = lines[1] 156 | std_out = lines[2] 157 | 158 | for i, no in enumerate(dt_no): 159 | std_in_file = os.path.join(testdata_dir, std_in.replace("#", str(no))) 160 | std_out_file = os.path.join(testdata_dir, std_out.replace("#", str(no))) 161 | 162 | res = run(target, std_in_file, std_out_file, time_limit, memory_limit) 163 | 164 | if res['status'] == "Compile Error": 165 | result['compiler_output'] = res['compile_info'] 166 | result['status'] = "Compile Error" 167 | return result 168 | 169 | result[i] = res 170 | result["case_num"] += 1 171 | result["total_time"] += res["time_used"] 172 | if res["memory_used"] > result["max_memory"]: 173 | result["max_memory"] = res["memory_used"] 174 | 175 | if res["status"] == "Accepted": 176 | result["score"] += 1.0 / len(dt_no) * res['score'] 177 | elif result["status"] == "Judging": 178 | result["status"] = res["status"] 179 | 180 | result["score"] = int(result["score"] + 0.1) 181 | if result["status"] == "Judging": 182 | result["status"] = "Accepted" 183 | return result 184 | 185 | 186 | def main(): 187 | while True: 188 | time.sleep(1) 189 | task = get_judge_task() 190 | if not task["have_task"]: 191 | continue 192 | 193 | try: 194 | result = judge(task["code"], task["time_limit"], task["memory_limit"], task["testdata"]) 195 | except: 196 | result = {"status": "System Error", "score": 0, "total_time": 0, "max_memory": 0, "case_num": 0} 197 | 198 | upload_judge_result(result, task["judge_id"]) 199 | 200 | 201 | def test_connect_to_server(): 202 | task = get_judge_task() 203 | testdata_dir = get_testdata_dir(task["testdata"]) 204 | print task 205 | print testdata_dir 206 | print upload_judge_result({"status": "System Error", "score": 0, "total_time": 0, "max_memory": 0, "case_num": 0}, 207 | task["judge_id"]) 208 | 209 | 210 | if __name__ == '__main__': 211 | # test_connect_to_server() 212 | # main() 213 | while True: 214 | try: 215 | main() 216 | except: 217 | pass 218 | -------------------------------------------------------------------------------- /syzoj/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from flask import Flask 4 | from flask_sqlalchemy import SQLAlchemy 5 | 6 | oj = Flask(__name__) 7 | oj.config.from_object("config") 8 | oj.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']=True # 设置这一项是每次请求结束后都会自动提交数据库中的变动 9 | db = SQLAlchemy(oj) 10 | 11 | from .views import * 12 | from .models import * 13 | from .controller import * 14 | # import views, models, controller 15 | -------------------------------------------------------------------------------- /syzoj/controller.py: -------------------------------------------------------------------------------- 1 | from syzoj.models import User, Problem 2 | from urllib import parse 3 | import time, re 4 | 5 | 6 | class Tools(object): 7 | @staticmethod 8 | def pretty_time(t): 9 | return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t)) 10 | @staticmethod 11 | def to_str(s): 12 | return str(s) 13 | 14 | @staticmethod 15 | def url_encode(query, doseq=0): 16 | return parse.urlencode(query, doseq) 17 | 18 | @staticmethod 19 | def get_cur_user(): 20 | return User.get_cur_user() 21 | 22 | 23 | class Checker(object): 24 | @staticmethod 25 | def is_valid_username(username): 26 | if not username: 27 | return False 28 | 29 | if len(username) < 3 or len(username) > 16: 30 | return False 31 | 32 | checker = re.compile("[A-Za-z0-9_]") 33 | if not checker.match(username): 34 | return False 35 | 36 | return True 37 | 38 | @staticmethod 39 | def is_valid_email(email): 40 | if not email: 41 | return False 42 | 43 | if len(email) < 3 or len(email) > 50: 44 | return False 45 | 46 | checker = re.compile("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$") 47 | if not checker.match(email): 48 | return False 49 | 50 | return True 51 | 52 | @staticmethod 53 | def is_valid_password(password): 54 | if not password: 55 | return None 56 | 57 | # Because of the salt is "syzoj2_xxx" and the "syzoj2_xxx" 's md5 is"59cb..." 58 | # the empty password 's md5 will equal "59cb.." 59 | syzoj2_xxx_md5 = "59cb65ba6f9ad18de0dcd12d5ae11bd2" 60 | if password == syzoj2_xxx_md5: 61 | return False 62 | 63 | return True 64 | 65 | 66 | class Paginate(): 67 | query = None 68 | total_page = 0 69 | per_page = 10 70 | cur_page = 1 71 | edge_display_num = 10 72 | make_url = None 73 | other = None 74 | 75 | def __init__(self, query, make_url=None, other=None, cur_page=1, per_page=10, edge_display_num=2): 76 | if not cur_page: 77 | cur_page = 1 78 | self.cur_page = int(cur_page) 79 | self.per_page = per_page 80 | self.edge_display_num = edge_display_num 81 | self.query = query 82 | self.make_url = make_url 83 | self.other = other 84 | total = query.count() 85 | self.total_page = total / per_page 86 | if total % per_page: 87 | self.total_page += 1 88 | 89 | def have_pre(self, cur_page=None): 90 | if not cur_page: 91 | cur_page = self.cur_page 92 | # print(cur_page > 1) 93 | return cur_page > 1 94 | 95 | def have_next(self, cur_page=None): 96 | if not cur_page: 97 | cur_page = self.cur_page 98 | return cur_page < self.total_page 99 | 100 | def need_omit_left(self): 101 | return self.cur_page - self.edge_display_num > 1 102 | 103 | def need_omit_right(self): 104 | return self.cur_page + self.edge_display_num < self.total_page 105 | 106 | def range(self): 107 | start = self.cur_page - self.edge_display_num 108 | stop = self.cur_page + self.edge_display_num + 1 109 | if start < 1: 110 | start = 1 111 | if stop > self.total_page + 1: 112 | stop = self.total_page + 1 113 | return range(int(start), int(stop)) 114 | 115 | def get(self): 116 | return self.query.offset((self.cur_page - 1) * self.per_page).limit(self.per_page) 117 | 118 | def get_html(self): 119 | pre_disable = "" 120 | next_disable = "" 121 | left_omit = "" 122 | right_omit = "" 123 | pre_url = "" 124 | next_url = "" 125 | pid_list = "" 126 | 127 | if self.have_pre(): 128 | pre_url = self.make_url(self.cur_page - 1, self.other) 129 | else: 130 | pre_disable = "am-disabled" 131 | if self.have_next(): 132 | next_url = self.make_url(self.cur_page + 1, self.other) 133 | else: 134 | next_disable = "am-disabled" 135 | 136 | if self.need_omit_left(): 137 | left_omit = '''
  • ...
  • ''' 138 | if self.need_omit_right(): 139 | right_omit = '''
  • ...
  • ''' 140 | 141 | for pid in self.range(): 142 | active = "" 143 | if pid == self.cur_page: 144 | active = "am-active" 145 | pid_list += '''
  • ''' + str(pid) + '''
  • ''' 147 | 148 | html = '''
    149 | 158 |
    ''' 159 | return html 160 | 161 | 162 | def register(username, password, email): 163 | state_code = 0 164 | if not Checker.is_valid_username(username): 165 | state_code = 2002 166 | elif not Checker.is_valid_password(password): 167 | state_code = 2007 168 | elif not Checker.is_valid_password(email): 169 | state_code = 2006 170 | elif User.query.filter_by(username=username).first(): 171 | state_code = 2008 172 | else: 173 | state_code = 1 174 | user = User(username=username, password=password, email=email) 175 | user.save() 176 | return state_code 177 | 178 | 179 | def create_problem(user, title): 180 | problem = Problem(user=user, title=title) 181 | problem.save() 182 | print(problem) 183 | return problem.id 184 | 185 | 186 | def become_admin(user): 187 | user.is_admin = True 188 | user.save() 189 | 190 | 191 | def cancel_admin(user): 192 | user.is_admin = False 193 | user.save() 194 | -------------------------------------------------------------------------------- /syzoj/models/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .user import * 3 | from .problem import * 4 | from .judge import * 5 | from .contest import * 6 | from .article import * 7 | from .file import * -------------------------------------------------------------------------------- /syzoj/models/article.py: -------------------------------------------------------------------------------- 1 | from syzoj import db 2 | import time 3 | 4 | 5 | class Article(db.Model): 6 | id = db.Column(db.Integer, primary_key=True) 7 | title = db.Column(db.String(80)) 8 | content = db.Column(db.Text) 9 | 10 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 11 | user = db.relationship("User", backref=db.backref("articles", lazy='dynamic')) 12 | 13 | public_time = db.Column(db.Integer) # goodbye at 2038-1-19 14 | update_time = db.Column(db.Integer) 15 | sort_time = db.Column(db.Integer, index=True) 16 | 17 | tags = db.Column(db.Text) 18 | 19 | comments_num = db.Column(db.Integer) 20 | allow_comment = db.Column(db.Boolean) 21 | 22 | def __init__(self, title, content, user, allow_comment=True, public_time=None): 23 | if not public_time: 24 | public_time = int(time.time()) 25 | self.title = title 26 | self.content = content 27 | self.user = user 28 | self.public_time = public_time 29 | self.update_time = public_time 30 | self.sort_time = public_time 31 | self.comments_num = 0 32 | self.allow_comment = allow_comment 33 | 34 | def __repr__(self): 35 | return "
    " % self.title 36 | 37 | def save(self): 38 | db.session.add(self) 39 | db.session.commit() 40 | 41 | def delete(self): 42 | db.session.delete(self) 43 | db.session.commit() 44 | 45 | def is_allowed_edit(self, user=None): 46 | if not user: 47 | return False 48 | if user.id == self.user_id: 49 | return True 50 | if user.have_privilege(6): 51 | return True 52 | return False 53 | 54 | 55 | class Comment(db.Model): 56 | __tablename__ = 'comment' 57 | id = db.Column(db.Integer, primary_key=True) 58 | content = db.Column(db.Text) 59 | 60 | article_id = db.Column(db.Integer, db.ForeignKey("article.id"), index=True) 61 | article = db.relationship("Article", backref=db.backref("comments", lazy='dynamic')) 62 | 63 | origin_comment_id = db.Column(db.Integer, db.ForeignKey("comment.id"), index=True) 64 | 65 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 66 | user = db.relationship("User", backref=db.backref("comments", lazy='dynamic')) 67 | 68 | public_time = db.Column(db.Integer) # goodbye at 2038-1-19 69 | 70 | def __init__(self, content, article, user, public_time=None): 71 | if not public_time: 72 | public_time = int(time.time()) 73 | self.content = content 74 | self.article = article 75 | self.user = user 76 | self.public_time = public_time 77 | 78 | def __repr__(self): 79 | return "" % self.content 80 | 81 | def is_allowed_edit(self, user=None): 82 | if not user: 83 | return False 84 | if user.id == self.user_id: 85 | return True 86 | if user.have_privilege(6): 87 | return True 88 | return False 89 | 90 | def delete(self): 91 | db.session.delete(self) 92 | db.session.commit() 93 | 94 | def save(self): 95 | db.session.add(self) 96 | db.session.commit() 97 | 98 | 99 | class Notice(db.Model): 100 | id = db.Column(db.Integer, primary_key=True) 101 | title = db.Column(db.String(80)) 102 | content = db.Column(db.Text) 103 | 104 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 105 | user = db.relationship("User", backref=db.backref("notices", lazy='dynamic')) 106 | 107 | public_time = db.Column(db.Integer) # goodbye at 2038-1-19 108 | update_time = db.Column(db.Integer) 109 | sort_time = db.Column(db.Integer, index=True) 110 | 111 | tags = db.Column(db.Text) 112 | 113 | comments_num = db.Column(db.Integer) 114 | allow_comment = db.Column(db.Boolean) 115 | 116 | def __init__(self, title, content, user, allow_comment=True, public_time=None): 117 | if not public_time: 118 | public_time = int(time.time()) 119 | self.title = title 120 | self.content = content 121 | self.user = user 122 | self.public_time = public_time 123 | self.update_time = public_time 124 | self.sort_time = public_time 125 | self.comments_num = 0 126 | self.allow_comment = allow_comment 127 | 128 | def __repr__(self): 129 | return "" % self.title 130 | 131 | def save(self): 132 | db.session.add(self) 133 | db.session.commit() 134 | 135 | def delete(self): 136 | db.session.delete(self) 137 | db.session.commit() 138 | 139 | def is_allowed_edit(self, user=None): 140 | if user and user.have_privilege(4): 141 | return True 142 | if not user: 143 | return False 144 | return False 145 | -------------------------------------------------------------------------------- /syzoj/models/contest.py: -------------------------------------------------------------------------------- 1 | from syzoj import db 2 | from syzoj.models.problem import Problem 3 | import time 4 | import json 5 | 6 | 7 | class ContestRanklist(db.Model): 8 | """ 9 | save ranklist as json 10 | follow is ranklist structure 11 | { 12 | "player_num": player_num 13 | 1: the ContestPlayer's id of rank1 14 | 2: the ContestPlayer's id of rank2 15 | ... 16 | } 17 | """ 18 | id = db.Column(db.Integer, primary_key=True) 19 | ranklist = db.Column(db.Text) 20 | 21 | def __init__(self): 22 | ranklist = {"player_num": 0} 23 | self.ranklist = json.dumps(ranklist) 24 | 25 | def save(self): 26 | db.session.add(self) 27 | db.session.commit() 28 | 29 | def get_players(self): 30 | ranklist = json.loads(self.ranklist) 31 | players = [] 32 | for i in range(ranklist["player_num"]): 33 | player = ContestPlayer.query.filter_by(id=ranklist[str(i)]).first() 34 | players.append(player) 35 | return players 36 | 37 | def update(self, new_player=None): 38 | players = self.get_players() 39 | if not new_player in players: 40 | players.append(new_player) 41 | 42 | players.sort(key = lambda p: p.score, reverse = True) 43 | # players.sort(cmp=lambda x, y: x.score > y.score or (x.score == y.score and x.time_spent < y.time_spent)) 44 | 45 | ranklist = {"player_num": len(players)} 46 | for rank, player in enumerate(players): 47 | ranklist[rank] = player.id 48 | 49 | self.ranklist = json.dumps(ranklist) 50 | 51 | 52 | class ContestPlayer(db.Model): 53 | id = db.Column(db.Integer, primary_key=True) 54 | contest_id = db.Column(db.Integer, db.ForeignKey("contest.id"), index=True) 55 | contest = db.relationship("Contest", backref=db.backref("players", lazy="dynamic")) 56 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 57 | user = db.relationship("User", backref=db.backref("contests", lazy="dynamic")) 58 | 59 | score = db.Column(db.Integer) 60 | score_details = db.Column(db.Text) 61 | time_spent = db.Column(db.Integer) 62 | 63 | def __init__(self, contest_id, user_id): 64 | self.contest_id = contest_id 65 | self.user_id = user_id 66 | 67 | self.score = 0 68 | self.time_spent = 0 69 | self.score_details = "" 70 | 71 | def __repr__(self): 72 | return "" % \ 73 | (self.contest_id, self.user_id, self.score_details) 74 | 75 | def update_score(self, problem, score, judge_id): 76 | score_details = {} 77 | if self.score_details: 78 | score_details = json.loads(self.score_details) 79 | pid = str(problem.id) 80 | score_details[pid] = {} 81 | score_details[pid]["score"] = score 82 | score_details[pid]["judge_id"] = judge_id 83 | score_details["score"] = 0 84 | for key, val in score_details.items(): 85 | if isinstance(val, dict): 86 | score_details["score"] += val["score"] 87 | self.score = score_details["score"] 88 | self.time_spent = problem.submit_num 89 | self.score_details = json.dumps(score_details) 90 | 91 | def get_score_details(self): 92 | return json.loads(self.score_details) 93 | 94 | def save(self): 95 | db.session.add(self) 96 | db.session.commit() 97 | 98 | 99 | class Contest(db.Model): 100 | id = db.Column(db.Integer, primary_key=True) 101 | title = db.Column(db.String(80)) 102 | start_time = db.Column(db.Integer) # goodbye at 2038-1-19 103 | end_time = db.Column(db.Integer) 104 | 105 | holder_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 106 | holder = db.relationship("User", backref=db.backref("hold_contests", lazy='dynamic')) 107 | 108 | information = db.Column(db.Text) 109 | # save use text represent problems id,split by "|" 110 | # for example use "2|23|123" represent this contest use problems which id equal 2\23 or 123 111 | problems = db.Column(db.Text) 112 | 113 | ranklist_id = db.Column(db.Integer, db.ForeignKey("contest_ranklist.id"), index=True) 114 | ranklist = db.relationship("ContestRanklist", backref=db.backref("contests", lazy="dynamic")) 115 | 116 | def __init__(self, title, start_time, end_time, holder): 117 | self.title = title 118 | self.start_time = start_time 119 | self.end_time = end_time 120 | self.holder = holder 121 | 122 | ranklist = ContestRanklist() 123 | self.ranklist = ranklist 124 | 125 | def __repr__(self): 126 | return "" % self.title 127 | 128 | def save(self): 129 | db.session.add(self) 130 | db.session.commit() 131 | 132 | def is_allowed_edit(self, user=None): 133 | if user and user.have_privilege(4): 134 | return True 135 | if user and user.id == self.holder.id: 136 | return True 137 | return False 138 | 139 | def new_submission(self, judge): 140 | problems = self.get_problems() 141 | if judge.problem not in problems: 142 | pass 143 | return False 144 | 145 | player = self.players.filter_by(user_id=judge.user_id).first() 146 | if not player: 147 | player = ContestPlayer(self.id, judge.user_id) 148 | player.update_score(judge.problem, judge.score, judge.id) 149 | player.save() 150 | self.ranklist.update(player) 151 | self.ranklist.save() 152 | 153 | def is_running(self, now=None): 154 | if not now: 155 | now = int(time.time()) 156 | return self.start_time <= now and now <= self.end_time 157 | 158 | def get_ranklist(self): 159 | return self.ranklist.ranklist 160 | 161 | def get_problems(self): 162 | if not self.problems: 163 | return [] 164 | 165 | problems = [] 166 | for pid in self.problems.split('|'): 167 | pid = int(pid) 168 | problems.append(Problem.query.filter_by(id=int(pid)).first()) 169 | return problems 170 | 171 | def set_problems(self, problems_list): 172 | self.problems = "" 173 | for pid in problems_list: 174 | if Problem.query.filter_by(id=pid).first(): 175 | if self.problems: 176 | self.problems += '|' 177 | self.problems += str(pid) 178 | else: 179 | pass 180 | # TODO:raise error 181 | -------------------------------------------------------------------------------- /syzoj/models/file.py: -------------------------------------------------------------------------------- 1 | from syzoj import oj, db 2 | import zipfile, os 3 | import hashlib 4 | 5 | 6 | class File(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | filename = db.Column(db.String(120), index=True) 9 | md5 = db.Column(db.String(50), index=True) 10 | 11 | def __init__(self, file): 12 | self.file = file 13 | self.md5 = self.calc_md5(file) 14 | 15 | @staticmethod 16 | def calc_md5(file): 17 | file.seek(0) 18 | m = hashlib.md5() 19 | while True: 20 | data = file.read(8192) 21 | if not data: 22 | break 23 | m.update(data) 24 | 25 | md5 = m.hexdigest() 26 | return md5 27 | 28 | def save_file(self): 29 | self.file.seek(0) 30 | self.file.save(os.path.join(oj.config['UPLOAD_FOLDER'], self.md5)) 31 | 32 | def get_file_path(self): 33 | return os.path.join(oj.config["UPLOAD_FOLDER"], self.md5) 34 | 35 | def save(self): 36 | db.session.add(self) 37 | db.session.commit() 38 | 39 | 40 | class FileParser(object): 41 | @staticmethod 42 | def parse_as_testdata(file): 43 | filename = file.get_file_path() 44 | if not zipfile.is_zipfile(filename): 45 | return (False, "This file isn\'t zipfile") 46 | 47 | with zipfile.ZipFile(filename, "r") as zip_file: 48 | file_list = zip_file.namelist() 49 | 50 | if "data_rule.txt" not in file_list: 51 | return (False, "Can\'t find data_rule.txt in testdata pack.") 52 | 53 | data_rule = zip_file.read("data_rule.txt") 54 | lines = str(data_rule, encoding='utf-8').split('\n') 55 | for i in range(0, len(lines)): 56 | lines[i] = lines[i].replace('\r', '').replace('\n', '') 57 | 58 | if len(lines) < 3: 59 | print(len(lines)) 60 | return (False, "data_rule.txt should have 3 lines.") 61 | 62 | data_no = lines[0].split() 63 | input_name = lines[1] 64 | output_name = lines[2] 65 | 66 | ret = [] 67 | 68 | for i in data_no: 69 | i = int(i) 70 | input_file = input_name.replace('#', str(i)) 71 | output_file = output_name.replace('#', str(i)) 72 | if input_file not in file_list: 73 | return (False, "Can\'t find %s file." % input_file) 74 | if output_file not in file_list: 75 | return (False, "Can\'t find %s file." % output_file) 76 | ret.append((input_file, output_file)) 77 | 78 | return (True, ret) 79 | -------------------------------------------------------------------------------- /syzoj/models/judge.py: -------------------------------------------------------------------------------- 1 | from syzoj import db 2 | from syzoj.models.contest import Contest 3 | from syzoj.models.user import UserAcProblem 4 | import json 5 | import time 6 | 7 | 8 | class JudgeState(db.Model): 9 | id = db.Column(db.Integer, primary_key=True) 10 | code = db.Column(db.Text) 11 | language = db.Column(db.String(20)) 12 | 13 | status = db.Column(db.String(50), index=True) 14 | score = db.Column(db.Integer, index=True) 15 | result = db.Column(db.Text) 16 | 17 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 18 | user = db.relationship("User", backref=db.backref("submit", lazy='dynamic')) 19 | 20 | problem_id = db.Column(db.Integer, db.ForeignKey("problem.id"), index=True) 21 | problem = db.relationship("Problem", backref=db.backref("submit", lazy='dynamic')) 22 | 23 | submit_time = db.Column(db.Integer) # googbye at 2038-1-19 24 | 25 | # "type" indicate it's contest's submission(type = 1) or normal submission(type = 0) 26 | # type=2: this is a test submission 27 | # if it's contest's submission (type = 1), the type_info is contest_id 28 | # use this way represent because it's easy to expand 29 | type = db.Column(db.Integer) 30 | type_info = db.Column(db.Integer) 31 | 32 | def __init__(self, code, language, user, problem, type=0, type_info=None, submit_time=None): 33 | if not submit_time: 34 | submit_time = int(time.time()) 35 | self.code = code 36 | self.language = language 37 | self.user = user 38 | self.problem = problem 39 | self.submit_time = submit_time 40 | 41 | self.type = type 42 | self.type_info = type_info 43 | 44 | self.score = 0 45 | self.status = "Waiting" 46 | self.result = '{"status": "Waiting", "total_time": 0, "total_memory": 0, "score":0, "case_num": 0}' 47 | 48 | def __repr__(self): 49 | print("" % self.id) 50 | 51 | def save(self): 52 | db.session.add(self) 53 | db.session.commit() 54 | 55 | def is_allowed_see_result(self, user=None): 56 | if user and user.id == self.problem.user.id: 57 | return True 58 | 59 | if self.type == 0: 60 | if not self.problem.is_public: 61 | if user and (user.have_privilege(2) or user.have_privilege(3)): 62 | return True 63 | return False 64 | return True 65 | elif self.type == 1: 66 | if user and user.have_privilege(4): 67 | return True 68 | contest = Contest.query.filter_by(id=self.type_info).first() 69 | if contest.is_running(): 70 | return False 71 | else: 72 | return True 73 | elif self.type == 2: 74 | if user and (user.have_privilege(2) or user.have_privilege(3) or self.user == user.id): 75 | return True 76 | else: 77 | return False 78 | 79 | return False 80 | 81 | def is_allowed_see_code(self, user=None): 82 | if user and user.id == self.problem.user.id: 83 | return True 84 | 85 | if self.type == 0: 86 | if not self.problem.is_public: 87 | if user and (user.have_privilege(2) or user.have_privilege(3)): 88 | return True 89 | return False 90 | return True 91 | elif self.type == 1: 92 | if user and user.have_privilege(4): 93 | return True 94 | contest = Contest.query.filter_by(id=self.type_info).first() 95 | if contest.is_running(): 96 | if user and self.user == user: 97 | return True 98 | else: 99 | return False 100 | else: 101 | return True 102 | elif self.type == 2: 103 | if user and (user.have_privilege(2) or user.have_privilege(3) or self.user == user.id): 104 | return True 105 | else: 106 | return False 107 | 108 | return False 109 | 110 | def get_result(self): 111 | return json.loads(self.result) 112 | 113 | def update_result(self, result): 114 | self.score = result["score"] 115 | self.status = result["status"] 116 | self.result = json.dumps(result) 117 | 118 | def update_related_info(self): 119 | if self.type == 0: 120 | self.user.refresh_submit_info() 121 | self.user.save() 122 | 123 | self.problem.submit_num += 1 124 | if self.status == "Accepted": 125 | self.problem.ac_num += 1 126 | self.problem.save() 127 | elif self.type == 1: 128 | contest = Contest.query.filter_by(id=self.type_info).first() 129 | contest.new_submission(self) 130 | 131 | # only normal submittion is counted 132 | def update_userac_info(self): 133 | if self.type == 0: 134 | all_user_ac = UserAcProblem.query.filter_by(user_id = self.user.id).all() 135 | for ac_info in all_user_ac: 136 | if ac_info.problem_id == self.problem.id: 137 | if ac_info.is_accepted and self.status != "Accepted": 138 | return 139 | 140 | new_ac_info = UserAcProblem(user_id = ac_info.user_id, problem_id = ac_info.problem_id, judge_id = self.id) 141 | ac_info.delete() 142 | 143 | if self.status == "Accepted": 144 | new_ac_info.is_accepted = True 145 | else: 146 | new_ac_info.is_accepted = False 147 | new_ac_info.save() 148 | return 149 | 150 | new_ac_info = UserAcProblem(user_id = self.user.id, problem_id = self.problem.id, judge_id = self.id) 151 | if self.status == "Accepted": 152 | new_ac_info.is_accepted = True 153 | else: 154 | new_ac_info.is_accepted = False 155 | new_ac_info.save() 156 | return 157 | 158 | 159 | class WaitingJudge(db.Model): 160 | id = db.Column(db.Integer, primary_key=True) 161 | judge_id = db.Column(db.Integer, db.ForeignKey("judge_state.id")) 162 | judge = db.relationship("JudgeState", backref=db.backref("waiting_judge", lazy="dynamic")) 163 | 164 | def __init__(self, judge): 165 | self.judge = judge 166 | 167 | def __repr__(self): 168 | print("" % self.judge_id) 169 | 170 | def save(self): 171 | db.session.add(self) 172 | db.session.commit() 173 | 174 | def delete(self): 175 | db.session.delete(self) 176 | db.session.commit() 177 | -------------------------------------------------------------------------------- /syzoj/models/problem.py: -------------------------------------------------------------------------------- 1 | from syzoj import db 2 | from syzoj.models.file import File 3 | 4 | star_table = db.Table('star_table', 5 | db.Column('user_id', db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), primary_key=True), 6 | db.Column('problem_id', db.Integer, db.ForeignKey('problem.id', ondelete='CASCADE'), primary_key=True) 7 | ) 8 | 9 | 10 | class Problem(db.Model): 11 | id = db.Column(db.Integer, primary_key=True) 12 | 13 | title = db.Column(db.String(80), index=True) 14 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 15 | user = db.relationship("User", backref=db.backref("upload_problems", lazy='dynamic')) 16 | 17 | description = db.Column(db.Text) 18 | input_format = db.Column(db.Text) 19 | output_format = db.Column(db.Text) 20 | example = db.Column(db.Text) 21 | limit_and_hint = db.Column(db.Text) 22 | 23 | time_limit = db.Column(db.Integer) 24 | memory_limit = db.Column(db.Integer) 25 | 26 | stared = db.relationship("User", secondary=star_table, backref=db.backref('stars', lazy='dynamic')) 27 | 28 | testdata_id = db.Column(db.Integer, db.ForeignKey("file.id")) 29 | testdata = db.relationship("File", backref=db.backref('problems', lazy='dynamic')) 30 | 31 | # love = db.Column(db.Integer) 32 | tags = db.Column(db.Text) 33 | ac_num = db.Column(db.Integer) 34 | submit_num = db.Column(db.Integer) 35 | is_public = db.Column(db.Boolean) 36 | 37 | def __init__(self, title, user, 38 | description="", input_format="", output_format="", example="", limit_and_hint="", 39 | time_limit=1000, memory_limit=128, 40 | ): 41 | self.title = title 42 | self.user = user 43 | 44 | self.description = description 45 | self.input_format = input_format 46 | self.output_format = output_format 47 | self.example = example 48 | self.limit_and_hint = limit_and_hint 49 | 50 | self.time_limit = time_limit 51 | self.memory_limit = memory_limit 52 | self.ac_num = 0 53 | self.submit_num = 0 54 | self.is_public = False 55 | 56 | def __repr__(self): 57 | return "" % self.title 58 | 59 | def save(self): 60 | db.session.add(self) 61 | db.session.commit() 62 | 63 | def update(self, title=None, description=None, input_format=None, output_format=None, example=None, tags=None, 64 | limit_and_hint=None): 65 | self.title = title 66 | self.description = description 67 | self.input_format = input_format 68 | self.output_format = output_format 69 | self.example = example 70 | self.tags = tags 71 | self.limit_and_hint = limit_and_hint 72 | 73 | def update_testdata(self, file): 74 | self.testdata = File(file) 75 | self.testdata.filename = "test_data_%d.zip" % self.id 76 | self.testdata.save_file() 77 | self.testdata.save() 78 | 79 | def is_allowed_edit(self, user=None): 80 | if not user: 81 | return False 82 | if self.user_id == user.id or user.have_privilege(2): 83 | return True 84 | return False 85 | 86 | def is_allowed_use(self, user=None): 87 | if self.is_public: 88 | return True 89 | if not user: 90 | return False 91 | if self.user_id == user.id or user.have_privilege(3) or user.have_privilege(2): 92 | return True 93 | return False 94 | 95 | def is_in_stared(self, user=None): 96 | if user not in self.stared: 97 | return False 98 | return True 99 | 100 | def set_is_public(self, public): 101 | self.is_public = public 102 | self.save() 103 | 104 | -------------------------------------------------------------------------------- /syzoj/models/user.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | from syzoj import db 3 | from .problem import Problem 4 | import urllib, hashlib 5 | from random import randint 6 | import time 7 | 8 | 9 | class Session(db.Model): 10 | id = db.Column(db.String(120), primary_key=True) 11 | 12 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True) 13 | user = db.relationship("User", backref=db.backref("sessions", lazy='dynamic')) 14 | 15 | login_time = db.Column(db.Integer) # googbye at 2038-1-19 16 | expiration_time = db.Column(db.Integer) 17 | 18 | def __init__(self, user, login_time=None, valid_time=3600 * 24 * 7): 19 | if not login_time: 20 | login_time = int(time.time()) 21 | self.id = str(randint(1, int(1e50))) 22 | self.user = user 23 | self.login_time = login_time 24 | self.expiration_time = login_time + valid_time 25 | 26 | def __repr__(self): 27 | print("" % (self.username, self.password, self.email) 107 | 108 | def save(self): 109 | db.session.add(self) 110 | db.session.commit() 111 | 112 | def have_privilege(self, privilege_type): 113 | for privilege in UserPrivilege.query.filter_by(user_id = self.id).all(): 114 | if privilege.privilege_type == privilege_type or privilege.privilege_type == 1: 115 | return True 116 | return False 117 | 118 | def is_allowed_edit(self, user): 119 | if not user: 120 | return False 121 | if self.id == user.id or user.have_privilege(7): 122 | return True 123 | return False 124 | 125 | def get_submitted_problems(self): 126 | submitted_problems = dict() 127 | for ac_info in UserAcProblem.query.filter_by(user_id = self.id).all(): 128 | submitted_problems[ac_info.problem_id] = [ac_info.is_accepted, ac_info.judge_id] 129 | return submitted_problems 130 | 131 | def refresh_submit_info(self): 132 | cnt = 0 133 | for ac_info in UserAcProblem.query.filter_by(user_id = self.id).all(): 134 | if ac_info.is_accepted: 135 | cnt += 1 136 | self.ac_num = cnt 137 | 138 | @staticmethod 139 | def get_cur_user(session_id=None): 140 | if not session_id: 141 | session_id = request.cookies.get('session_id') 142 | 143 | sessions = Session.query.filter_by(id=session_id).all() 144 | for s in sessions: 145 | if s.is_valid(): 146 | return s.user 147 | 148 | return None 149 | 150 | @staticmethod 151 | def find_user(nickname=None, id=None): 152 | if id: 153 | return User.query.filter_by(id=id).first() 154 | 155 | if nickname: 156 | return User.query.filter_by(nickname=nickname).first() 157 | 158 | return None 159 | 160 | def give_privilege(self, privilege_type): 161 | for privilege in UserPrivilege.query.filter_by(user_id = self.id).all(): 162 | if privilege.privilege_type == privilege_type: 163 | return False # User already had privilege 164 | new_privilege = UserPrivilege(user_id = self.id, privilege_type = privilege_type) 165 | new_privilege.save() 166 | return True 167 | 168 | def del_privilege(self, privilege_type): 169 | for privilege in UserPrivilege.query.filter_by(user_id = self.id).all(): 170 | if privilege.privilege_type == privilege_type: 171 | privilege.delete() 172 | return True 173 | return False # User doesnt have privilege 174 | 175 | 176 | class UserAcProblem(db.Model): 177 | __tablename__ = 'UserAcProblem' 178 | id = db.Column(db.Integer, primary_key = True) 179 | user_id = db.Column(db.Integer, index = True) 180 | problem_id = db.Column(db.Integer) 181 | is_accepted = db.Column(db.BOOLEAN) 182 | judge_id = db.Column(db.Integer) 183 | 184 | def __init__(self, user_id, problem_id, judge_id): 185 | self.user_id = user_id 186 | self.problem_id = problem_id 187 | self.is_accepted = False 188 | self.judge_id = judge_id 189 | 190 | def save(self): 191 | db.session.add(self) 192 | db.session.commit() 193 | 194 | def delete(self): 195 | db.session.delete(self) 196 | db.session.commit() 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /syzoj/static/auto-render.js: -------------------------------------------------------------------------------- 1 | var findEndOfMath=function(delimiter,text,startIndex){var index=startIndex;var braceLevel=0;var delimLength=delimiter.length;while(index a { 128 | vertical-align: top; 129 | width: calc(100% - 20px); 130 | max-width: 340px; 131 | display: inline-block; 132 | text-align: center; 133 | margin: 20px 10px; 134 | padding: 25px; 135 | font-family: "Microsoft YaHei","宋体","Segoe UI", "Lucida Grande", Helvetica, Arial,sans-serif, FreeSans, Arimo; 136 | } 137 | .related a { 138 | display: inline-block; 139 | text-align: left; 140 | margin: 20px auto; 141 | padding: 10px 20px; 142 | opacity: 0.8; 143 | -webkit-transition: opacity 0.3s; 144 | transition: opacity 0.3s; 145 | -webkit-backface-visibility: hidden; 146 | } 147 | 148 | .related a:hover, 149 | .related a:active { 150 | opacity: 1; 151 | } 152 | 153 | .related a img { 154 | max-width: 100%; 155 | opacity: 0.8; 156 | border-radius: 4px; 157 | } 158 | .related a:hover img, 159 | .related a:active img { 160 | opacity: 1; 161 | } 162 | .related h3{font-family: "Microsoft YaHei", sans-serif;} 163 | .related a h3 { 164 | font-weight: 300; 165 | margin-top: 0.15em; 166 | color: #fff; 167 | } 168 | /* icomoon */ 169 | .icon-htmleaf-home-outline:before { 170 | content: "\e5000"; 171 | } 172 | 173 | .icon-htmleaf-arrow-forward-outline:before { 174 | content: "\e5001"; 175 | } -------------------------------------------------------------------------------- /syzoj/static/css/normalize.css: -------------------------------------------------------------------------------- 1 | article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { 2 | display: block; 3 | } 4 | audio, canvas, video { 5 | display: inline-block; 6 | } 7 | audio:not([controls]) { 8 | display: none; 9 | height: 0; 10 | } 11 | [hidden] { 12 | display: none; 13 | } 14 | html { 15 | font-family: sans-serif; 16 | -ms-text-size-adjust: 100%; 17 | -webkit-text-size-adjust: 100%; 18 | } 19 | body { 20 | text-align: center; 21 | margin: 0; 22 | } 23 | a:focus { 24 | color: aliceblue; 25 | outline: thin dotted; 26 | } 27 | a:active, a:hover { 28 | color: aliceblue; 29 | outline: 0; 30 | } 31 | h1 { 32 | font-size: 2em; 33 | margin: 0.67em 0; 34 | } 35 | abbr[title] { 36 | border-bottom: 1px dotted; 37 | } 38 | b, strong { 39 | font-weight: bold; 40 | } 41 | dfn { 42 | font-style: italic; 43 | } 44 | hr { 45 | -moz-box-sizing: content-box; 46 | box-sizing: content-box; 47 | height: 0; 48 | } 49 | mark { 50 | background: #ff0; 51 | color: #000; 52 | } 53 | code, kbd, pre, samp { 54 | font-family: monospace, serif; 55 | font-size: 1em; 56 | } 57 | pre { 58 | white-space: pre-wrap; 59 | } 60 | q { 61 | quotes: "\201C" "\201D" "\2018" "\2019"; 62 | } 63 | small { 64 | font-size: 80%; 65 | } 66 | sub, sup { 67 | font-size: 75%; 68 | line-height: 0; 69 | position: relative; 70 | vertical-align: baseline; 71 | } 72 | sup { 73 | top: -0.5em; 74 | } 75 | sub { 76 | bottom: -0.25em; 77 | } 78 | img { 79 | border: 0; 80 | } 81 | svg:not(:root) { 82 | overflow: hidden; 83 | } 84 | figure { 85 | margin: 0; 86 | } 87 | fieldset { 88 | border: 1px solid #c0c0c0; 89 | margin: 0 2px; 90 | padding: 0.35em 0.625em 0.75em; 91 | } 92 | legend { 93 | border: 0; 94 | padding: 0; 95 | } 96 | button, input, select, textarea { 97 | font-family: inherit; 98 | font-size: 100%; 99 | margin: 0; 100 | } 101 | button, input { 102 | line-height: normal; 103 | } 104 | button, select { 105 | text-transform: none; 106 | } 107 | button, html input[type="button"], input[type="reset"], input[type="submit"] { 108 | -webkit-appearance: button; 109 | cursor: pointer; 110 | } 111 | button[disabled], html input[disabled] { 112 | cursor: default; 113 | } 114 | input[type="checkbox"], input[type="radio"] { 115 | box-sizing: border-box; 116 | padding: 0; 117 | } 118 | input[type="search"] { 119 | -webkit-appearance: textfield; 120 | -moz-box-sizing: content-box; 121 | -webkit-box-sizing: content-box; 122 | box-sizing: content-box; 123 | } 124 | input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { 125 | -webkit-appearance:none; 126 | } 127 | button::-moz-focus-inner, input::-moz-focus-inner { 128 | border:0; 129 | padding:0; 130 | } 131 | textarea { 132 | overflow: auto; 133 | vertical-align: top; 134 | } 135 | table { 136 | border-collapse: collapse; 137 | border-spacing: 0; 138 | } 139 | * { 140 | box-sizing: border-box; 141 | } 142 | #mainlist { 143 | position: relative; 144 | list-style: none; 145 | font-weight: 400; 146 | font-size: 0; 147 | text-transform: uppercase; 148 | display: inline-block; 149 | padding: 0; 150 | margin: 100px auto auto auto; 151 | } 152 | #mainlist li { 153 | font-size: 0.8rem; 154 | display: inline-block; 155 | position: relative; 156 | padding: 15px 20px; 157 | cursor: pointer; 158 | z-index: 5; 159 | min-width: 120px; 160 | } 161 | 162 | li { 163 | text-align: center; 164 | margin: 0; 165 | } 166 | 167 | .drop { 168 | overflow: hidden; 169 | list-style: none; 170 | position: absolute; 171 | color:black; 172 | padding: 0; 173 | width: 100%; 174 | left: 0; 175 | top: 48px; 176 | } 177 | .drop div { 178 | -webkit-transform: translate(0, -100%); 179 | -moz-transform: translate(0, -100%); 180 | -ms-transform: translate(0, -100%); 181 | transform: translate(0, -100%); 182 | -webkit-transition: all 0.5s 0.1s; 183 | -moz-transition: all 0.5s 0.1s; 184 | -ms-transition: all 0.5s 0.1s; 185 | transition: all 0.5s 0.1s; 186 | position: relative; 187 | } 188 | .drop li { 189 | display: block; 190 | padding: 0; 191 | width: 100%; 192 | background: white !important; 193 | } 194 | 195 | #marker { 196 | height: 6px; 197 | background: white !important; 198 | position: absolute; 199 | bottom: 0; 200 | width: 120px; 201 | z-index: 2; 202 | -webkit-transition: all 0.35s; 203 | -moz-transition: all 0.35s; 204 | -ms-transition: all 0.35s; 205 | transition: all 0.35s; 206 | } 207 | 208 | #mainlist li:nth-child(1):hover ul div { 209 | -webkit-transform: translate(0, 0); 210 | -moz-transform: translate(0, 0); 211 | -ms-transform: translate(0, 0); 212 | transform: translate(0, 0); 213 | } 214 | #mainlist li:nth-child(1):hover ~ #marker { 215 | -webkit-transform: translate(0px, 0); 216 | -moz-transform: translate(0px, 0); 217 | -ms-transform: translate(0px, 0); 218 | transform: translate(0px, 0); 219 | } 220 | 221 | #mainlist li:nth-child(2):hover ul div { 222 | -webkit-transform: translate(0, 0); 223 | -moz-transform: translate(0, 0); 224 | -ms-transform: translate(0, 0); 225 | transform: translate(0, 0); 226 | } 227 | #mainlist li:nth-child(2):hover ~ #marker { 228 | -webkit-transform: translate(120px, 0); 229 | -moz-transform: translate(120px, 0); 230 | -ms-transform: translate(120px, 0); 231 | transform: translate(120px, 0); 232 | } 233 | 234 | #mainlist li:nth-child(3):hover ul div { 235 | -webkit-transform: translate(0, 0); 236 | -moz-transform: translate(0, 0); 237 | -ms-transform: translate(0, 0); 238 | transform: translate(0, 0); 239 | } 240 | #mainlist li:nth-child(3):hover ~ #marker { 241 | -webkit-transform: translate(240px, 0); 242 | -moz-transform: translate(240px, 0); 243 | -ms-transform: translate(240px, 0); 244 | transform: translate(240px, 0); 245 | } 246 | 247 | #mainlist li:nth-child(4):hover ul div { 248 | -webkit-transform: translate(0, 0); 249 | -moz-transform: translate(0, 0); 250 | -ms-transform: translate(0, 0); 251 | transform: translate(0, 0); 252 | } 253 | #mainlist li:nth-child(4):hover ~ #marker { 254 | -webkit-transform: translate(360px, 0); 255 | -moz-transform: translate(360px, 0); 256 | -ms-transform: translate(360px, 0); 257 | transform: translate(360px, 0); 258 | } 259 | .footer { 260 | position: absolute; 261 | top: 100%; 262 | margin-top:-30px; 263 | width: 100%; 264 | font-size: small; 265 | color: rgba(255,255,255,0.6); 266 | text-align: center; 267 | text-shadow: 0px 0px 20px rgba(0,0,0,1.0); 268 | } -------------------------------------------------------------------------------- /syzoj/static/css/search-form.css: -------------------------------------------------------------------------------- 1 | html,body {height: 100%;} 2 | body {padding: 0px; margin:0px; background:url(../img/image.jpg) ; background-position: center; background-size: cover; background-attachment: fixed; background-repeat: no-repeat;} 3 | 4 | .search-wrapper { 5 | position: absolute; 6 | -webkit-transform: translate(-50%, -50%); 7 | -moz-transform: translate(-50%, -50%); 8 | transform: translate(-50%, -50%); 9 | top:50%; 10 | left:50%; 11 | } 12 | .search-wrapper.active {} 13 | 14 | .search-wrapper .input-holder { 15 | overflow: hidden; 16 | height: 70px; 17 | background: rgba(255,255,255,0); 18 | border-radius:6px; 19 | position: relative; 20 | width:70px; 21 | -webkit-transition: all 0.3s ease-in-out; 22 | -moz-transition: all 0.3s ease-in-out; 23 | transition: all 0.3s ease-in-out; 24 | } 25 | .search-wrapper.active .input-holder { 26 | border-radius: 50px; 27 | width:450px; 28 | background: rgba(0,0,0,0.5); 29 | -webkit-transition: all .5s cubic-bezier(0.000, 0.105, 0.035, 1.570); 30 | -moz-transition: all .5s cubic-bezier(0.000, 0.105, 0.035, 1.570); 31 | transition: all .5s cubic-bezier(0.000, 0.105, 0.035, 1.570); 32 | } 33 | 34 | .search-wrapper .input-holder .search-input { 35 | width:100%; 36 | height: 50px; 37 | padding:0px 70px 0 20px; 38 | opacity: 0; 39 | position: absolute; 40 | top:0px; 41 | left:0px; 42 | background: transparent; 43 | -webkit-box-sizing: border-box; 44 | -moz-box-sizing: border-box; 45 | box-sizing: border-box; 46 | border:none; 47 | outline:none; 48 | font-family:"Open Sans", Arial, Verdana; 49 | font-size: 16px; 50 | font-weight: 400; 51 | line-height: 20px; 52 | color:#FFF; 53 | -webkit-transform: translate(0, 60px); 54 | -moz-transform: translate(0, 60px); 55 | transform: translate(0, 60px); 56 | -webkit-transition: all .3s cubic-bezier(0.000, 0.105, 0.035, 1.570); 57 | -moz-transition: all .3s cubic-bezier(0.000, 0.105, 0.035, 1.570); 58 | transition: all .3s cubic-bezier(0.000, 0.105, 0.035, 1.570); 59 | 60 | -webkit-transition-delay: 0.3s; 61 | -moz-transition-delay: 0.3s; 62 | transition-delay: 0.3s; 63 | } 64 | .search-wrapper.active .input-holder .search-input { 65 | opacity: 1; 66 | -webkit-transform: translate(0, 10px); 67 | -moz-transform: translate(0, 10px); 68 | transform: translate(0, 10px); 69 | } 70 | 71 | .search-wrapper .input-holder .search-icon { 72 | width:70px; 73 | height:70px; 74 | border:none; 75 | border-radius:6px; 76 | background: #FFF; 77 | padding:0px; 78 | outline:none; 79 | position: relative; 80 | z-index: 2; 81 | float:right; 82 | cursor: pointer; 83 | -webkit-transition: all 0.3s ease-in-out; 84 | -moz-transition: all 0.3s ease-in-out; 85 | transition: all 0.3s ease-in-out; 86 | } 87 | .search-wrapper.active .input-holder .search-icon { 88 | width: 50px; 89 | height:50px; 90 | margin: 10px; 91 | border-radius: 30px; 92 | } 93 | .search-wrapper .input-holder .search-icon span { 94 | width:22px; 95 | height:22px; 96 | display: inline-block; 97 | vertical-align: middle; 98 | position:relative; 99 | -webkit-transform: rotate(45deg); 100 | -moz-transform: rotate(45deg); 101 | transform: rotate(45deg); 102 | -webkit-transition: all .4s cubic-bezier(0.650, -0.600, 0.240, 1.650); 103 | -moz-transition: all .4s cubic-bezier(0.650, -0.600, 0.240, 1.650); 104 | transition: all .4s cubic-bezier(0.650, -0.600, 0.240, 1.650); 105 | 106 | } 107 | .search-wrapper.active .input-holder .search-icon span { 108 | -webkit-transform: rotate(-45deg); 109 | -moz-transform: rotate(-45deg); 110 | transform: rotate(-45deg); 111 | } 112 | .search-wrapper .input-holder .search-icon span::before, .search-wrapper .input-holder .search-icon span::after { 113 | position: absolute; 114 | content:''; 115 | } 116 | .search-wrapper .input-holder .search-icon span::before { 117 | width: 4px; 118 | height: 11px; 119 | left: 9px; 120 | top: 18px; 121 | border-radius: 2px; 122 | background: #974BE0; 123 | } 124 | .search-wrapper .input-holder .search-icon span::after { 125 | width: 14px; 126 | height: 14px; 127 | left: 0px; 128 | top: 0px; 129 | border-radius: 16px; 130 | border: 4px solid #974BE0; 131 | } 132 | 133 | .search-wrapper .close { 134 | position: absolute; 135 | z-index: 1; 136 | top:24px; 137 | right:20px; 138 | width:25px; 139 | height:25px; 140 | cursor: pointer; 141 | -webkit-transform: rotate(-180deg); 142 | -moz-transform: rotate(-180deg); 143 | transform: rotate(-180deg); 144 | -webkit-transition: all .3s cubic-bezier(0.285, -0.450, 0.935, 0.110); 145 | -moz-transition: all .3s cubic-bezier(0.285, -0.450, 0.935, 0.110); 146 | transition: all .3s cubic-bezier(0.285, -0.450, 0.935, 0.110); 147 | -webkit-transition-delay: 0.2s; 148 | -moz-transition-delay: 0.2s; 149 | transition-delay: 0.2s; 150 | } 151 | .search-wrapper.active .close { 152 | right:-50px; 153 | -webkit-transform: rotate(45deg); 154 | -moz-transform: rotate(45deg); 155 | transform: rotate(45deg); 156 | -webkit-transition: all .6s cubic-bezier(0.000, 0.105, 0.035, 1.570); 157 | -moz-transition: all .6s cubic-bezier(0.000, 0.105, 0.035, 1.570); 158 | transition: all .6s cubic-bezier(0.000, 0.105, 0.035, 1.570); 159 | -webkit-transition-delay: 0.5s; 160 | -moz-transition-delay: 0.5s; 161 | transition-delay: 0.5s; 162 | } 163 | .search-wrapper .close::before, .search-wrapper .close::after { 164 | position:absolute; 165 | content:''; 166 | background: #FFF; 167 | border-radius: 2px; 168 | } 169 | .search-wrapper .close::before { 170 | width: 5px; 171 | height: 25px; 172 | left: 10px; 173 | top: 0px; 174 | } 175 | .search-wrapper .close::after { 176 | width: 25px; 177 | height: 5px; 178 | left: 0px; 179 | top: 10px; 180 | } 181 | .search-wrapper .result-container { 182 | width: 100%; 183 | position: absolute; 184 | top:80px; 185 | left:0px; 186 | text-align: center; 187 | font-family: "Open Sans", Arial, Verdana; 188 | font-size: 14px; 189 | display:none; 190 | color:#B7B7B7; 191 | } 192 | 193 | 194 | @media screen and (max-width: 560px) { 195 | .search-wrapper.active .input-holder {width:200px;} 196 | } -------------------------------------------------------------------------------- /syzoj/static/flat-ui-icons-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/flat-ui-icons-regular.eot -------------------------------------------------------------------------------- /syzoj/static/flat-ui-icons-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/flat-ui-icons-regular.ttf -------------------------------------------------------------------------------- /syzoj/static/flat-ui-icons-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/flat-ui-icons-regular.woff -------------------------------------------------------------------------------- /syzoj/static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /syzoj/static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /syzoj/static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /syzoj/static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /syzoj/static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /syzoj/static/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/icomoon.eot -------------------------------------------------------------------------------- /syzoj/static/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /syzoj/static/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/icomoon.ttf -------------------------------------------------------------------------------- /syzoj/static/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/fonts/icomoon.woff -------------------------------------------------------------------------------- /syzoj/static/img/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/img/image.jpg -------------------------------------------------------------------------------- /syzoj/static/img/syzoj-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edify-Studio/SYOJ/448e8977ecf5e9b70f464547999d5c239b605fc0/syzoj/static/img/syzoj-logo.png -------------------------------------------------------------------------------- /syzoj/static/jquery.ext.js: -------------------------------------------------------------------------------- 1 | (function(e){var m=function(p,o){return(p<>>(32-o))};var a=function(s,p){var u,o,r,t,q;r=(s&2147483648);t=(p&2147483648);u=(s&1073741824);o=(p&1073741824);q=(s&1073741823)+(p&1073741823);if(u&o){return(q^2147483648^r^t)}if(u|o){if(q&1073741824){return(q^3221225472^r^t)}else{return(q^1073741824^r^t)}}else{return(q^r^t)}};var n=function(o,q,p){return(o&q)|((~o)&p)};var l=function(o,q,p){return(o&p)|(q&(~p))};var j=function(o,q,p){return(o^q^p)};var i=function(o,q,p){return(q^(o|(~p)))};var g=function(q,p,v,u,o,r,t){q=a(q,a(a(n(p,v,u),o),t));return a(m(q,r),p)};var c=function(q,p,v,u,o,r,t){q=a(q,a(a(l(p,v,u),o),t));return a(m(q,r),p)};var h=function(q,p,v,u,o,r,t){q=a(q,a(a(j(p,v,u),o),t));return a(m(q,r),p)};var d=function(q,p,v,u,o,r,t){q=a(q,a(a(i(p,v,u),o),t));return a(m(q,r),p)};var f=function(r){var v;var q=r.length;var p=q+8;var u=(p-(p%64))/64;var t=(u+1)*16;var w=Array(t-1);var o=0;var s=0;while(s>>29;return w};var b=function(r){var q="",o="",s,p;for(p=0;p<=3;p++){s=(r>>>(p*8))&255;o="0"+s.toString(16);q=q+o.substr(o.length-2,2)}return q};var k=function(p){p=p.replace(/\x0d\x0a/g,"\x0a");var o="";for(var r=0;r127)&&(q<2048)){o+=String.fromCharCode((q>>6)|192);o+=String.fromCharCode((q&63)|128)}else{o+=String.fromCharCode((q>>12)|224);o+=String.fromCharCode(((q>>6)&63)|128);o+=String.fromCharCode((q&63)|128)}}}return o};e.extend({md5:function(o){var v=Array();var G,H,p,u,F,Q,P,N,K;var D=7,B=12,z=17,w=22;var O=5,L=9,J=14,I=20;var t=4,s=11,r=16,q=23;var E=6,C=10,A=15,y=21;o=k(o);v=f(o);Q=1732584193;P=4023233417;N=2562383102;K=271733878;for(G=0;G{text} 么?"] 6 | }, 7 | { 8 | "selector": ".fui-home", 9 | "text": ["点击前往首页,想回到上一页可以使用浏览器的后退功能哦"] 10 | }, 11 | { 12 | "selector": "#tor_show", 13 | "text": ["翻页比较麻烦吗,点击可以显示这篇文章的目录呢"] 14 | }, 15 | { 16 | "selector": "#comment_go,.fui-chat", 17 | "text": ["想要去评论些什么吗?"] 18 | }, 19 | { 20 | "selector": "#night_mode", 21 | "text": ["深夜时要爱护眼睛呀"] 22 | }, 23 | { 24 | "selector": "#qrcode", 25 | "text": ["手机扫一下就能继续看,很方便呢"] 26 | }, 27 | { 28 | "selector": ".comment_reply", 29 | "text": ["要吐槽些什么呢"] 30 | }, 31 | { 32 | "selector": "#back-to-top", 33 | "text": ["回到开始的地方吧"] 34 | }, 35 | { 36 | "selector": "#author", 37 | "text": ["该怎么称呼你呢"] 38 | }, 39 | { 40 | "selector": "#mail", 41 | "text": ["留下你的邮箱,不然就是无头像人士了"] 42 | }, 43 | { 44 | "selector": "#url", 45 | "text": ["你的家在哪里呢,好让我去参观参观"] 46 | }, 47 | { 48 | "selector": "#textarea", 49 | "text": ["认真填写哦,垃圾评论是禁止事项"] 50 | }, 51 | { 52 | "selector": ".OwO-logo", 53 | "text": ["要插入一个表情吗"] 54 | }, 55 | { 56 | "selector": "#csubmit", 57 | "text": ["要[提交]^(Commit)了吗,首次评论需要审核,请耐心等待~"] 58 | }, 59 | { 60 | "selector": ".ImageBox", 61 | "text": ["点击图片可以放大呢"] 62 | }, 63 | { 64 | "selector": "input[name=s]", 65 | "text": ["找不到想看的内容?搜索看看吧"] 66 | }, 67 | { 68 | "selector": ".previous", 69 | "text": ["去上一页看看吧"] 70 | }, 71 | { 72 | "selector": ".next", 73 | "text": ["去下一页看看吧"] 74 | }, 75 | { 76 | "selector": ".dropdown-toggle", 77 | "text": ["这里是菜单"] 78 | }, 79 | { 80 | "selector": "c-player a.play-icon", 81 | "text": ["想要听点音乐吗"] 82 | }, 83 | { 84 | "selector": "c-player div.time", 85 | "text": ["在这里可以调整播放进度呢"] 86 | }, 87 | { 88 | "selector": "c-player div.volume", 89 | "text": ["在这里可以调整音量呢"] 90 | }, 91 | { 92 | "selector": "c-player div.list-button", 93 | "text": ["播放列表里都有什么呢"] 94 | }, 95 | { 96 | "selector": "c-player div.lyric-button", 97 | "text": ["有歌词的话就能跟着一起唱呢"] 98 | }, 99 | { 100 | "selector": ".waifu #live2d", 101 | "text": ["干嘛呢你,快把手拿开", "鼠…鼠标放错地方了!"] 102 | } 103 | ], 104 | "click": [ 105 | { 106 | "selector": ".waifu #live2d", 107 | "text": ["是…是不小心碰到了吧", "萝莉控是什么呀", "你看到我的小熊了吗", "再摸的话我可要报警了!⌇●﹏●⌇", "110吗,这里有个变态一直在摸我(ó﹏ò。)"] 108 | } 109 | ] 110 | } -------------------------------------------------------------------------------- /syzoj/static/waifu.css: -------------------------------------------------------------------------------- 1 | .waifu { 2 | position: fixed; 3 | bottom: 0; 4 | left: 0; 5 | z-index: 1; 6 | font-size: 0; 7 | transition: all .3s ease-in-out; 8 | -webkit-transform: translateY(3px); 9 | transform: translateY(3px); 10 | } 11 | .waifu:hover { 12 | -webkit-transform: translateY(0); 13 | transform: translateY(0); 14 | } 15 | @media (max-width: 768px) { 16 | .waifu { 17 | display: none; 18 | } 19 | } 20 | .waifu-tips { 21 | opacity: 0; 22 | width: 250px; 23 | height: 70px; 24 | margin: -20px 20px; 25 | padding: 5px 10px; 26 | border: 1px solid rgba(224, 186, 140, 0.62); 27 | border-radius: 12px; 28 | background-color: rgba(236, 217, 188, 0.5); 29 | box-shadow: 0 3px 15px 2px rgba(191, 158, 118, 0.2); 30 | font-size: 12px; 31 | text-overflow: ellipsis; 32 | overflow: hidden; 33 | position: absolute; 34 | animation-delay: 5s; 35 | animation-duration: 50s; 36 | animation-iteration-count: infinite; 37 | animation-name: shake; 38 | animation-timing-function: ease-in-out; 39 | } 40 | .waifu-tool { 41 | display: none; 42 | color: #aaa; 43 | top: 50px; 44 | right: 10px; 45 | font-size: 14px; 46 | position: absolute; 47 | } 48 | .waifu:hover .waifu-tool { 49 | display: block; 50 | } 51 | .waifu-tool span { 52 | display: block; 53 | cursor: pointer; 54 | color: #5b6c7d; 55 | line-height: 20px; 56 | transition: 0.2s; 57 | } 58 | .waifu-tool span:hover { 59 | color: #34495e; 60 | } 61 | .waifu #live2d{ 62 | position: relative; 63 | } 64 | 65 | @keyframes shake { 66 | 2% { 67 | transform: translate(0.5px, -1.5px) rotate(-0.5deg); 68 | } 69 | 70 | 4% { 71 | transform: translate(0.5px, 1.5px) rotate(1.5deg); 72 | } 73 | 74 | 6% { 75 | transform: translate(1.5px, 1.5px) rotate(1.5deg); 76 | } 77 | 78 | 8% { 79 | transform: translate(2.5px, 1.5px) rotate(0.5deg); 80 | } 81 | 82 | 10% { 83 | transform: translate(0.5px, 2.5px) rotate(0.5deg); 84 | } 85 | 86 | 12% { 87 | transform: translate(1.5px, 1.5px) rotate(0.5deg); 88 | } 89 | 90 | 14% { 91 | transform: translate(0.5px, 0.5px) rotate(0.5deg); 92 | } 93 | 94 | 16% { 95 | transform: translate(-1.5px, -0.5px) rotate(1.5deg); 96 | } 97 | 98 | 18% { 99 | transform: translate(0.5px, 0.5px) rotate(1.5deg); 100 | } 101 | 102 | 20% { 103 | transform: translate(2.5px, 2.5px) rotate(1.5deg); 104 | } 105 | 106 | 22% { 107 | transform: translate(0.5px, -1.5px) rotate(1.5deg); 108 | } 109 | 110 | 24% { 111 | transform: translate(-1.5px, 1.5px) rotate(-0.5deg); 112 | } 113 | 114 | 26% { 115 | transform: translate(1.5px, 0.5px) rotate(1.5deg); 116 | } 117 | 118 | 28% { 119 | transform: translate(-0.5px, -0.5px) rotate(-0.5deg); 120 | } 121 | 122 | 30% { 123 | transform: translate(1.5px, -0.5px) rotate(-0.5deg); 124 | } 125 | 126 | 32% { 127 | transform: translate(2.5px, -1.5px) rotate(1.5deg); 128 | } 129 | 130 | 34% { 131 | transform: translate(2.5px, 2.5px) rotate(-0.5deg); 132 | } 133 | 134 | 36% { 135 | transform: translate(0.5px, -1.5px) rotate(0.5deg); 136 | } 137 | 138 | 38% { 139 | transform: translate(2.5px, -0.5px) rotate(-0.5deg); 140 | } 141 | 142 | 40% { 143 | transform: translate(-0.5px, 2.5px) rotate(0.5deg); 144 | } 145 | 146 | 42% { 147 | transform: translate(-1.5px, 2.5px) rotate(0.5deg); 148 | } 149 | 150 | 44% { 151 | transform: translate(-1.5px, 1.5px) rotate(0.5deg); 152 | } 153 | 154 | 46% { 155 | transform: translate(1.5px, -0.5px) rotate(-0.5deg); 156 | } 157 | 158 | 48% { 159 | transform: translate(2.5px, -0.5px) rotate(0.5deg); 160 | } 161 | 162 | 50% { 163 | transform: translate(-1.5px, 1.5px) rotate(0.5deg); 164 | } 165 | 166 | 52% { 167 | transform: translate(-0.5px, 1.5px) rotate(0.5deg); 168 | } 169 | 170 | 54% { 171 | transform: translate(-1.5px, 1.5px) rotate(0.5deg); 172 | } 173 | 174 | 56% { 175 | transform: translate(0.5px, 2.5px) rotate(1.5deg); 176 | } 177 | 178 | 58% { 179 | transform: translate(2.5px, 2.5px) rotate(0.5deg); 180 | } 181 | 182 | 60% { 183 | transform: translate(2.5px, -1.5px) rotate(1.5deg); 184 | } 185 | 186 | 62% { 187 | transform: translate(-1.5px, 0.5px) rotate(1.5deg); 188 | } 189 | 190 | 64% { 191 | transform: translate(-1.5px, 1.5px) rotate(1.5deg); 192 | } 193 | 194 | 66% { 195 | transform: translate(0.5px, 2.5px) rotate(1.5deg); 196 | } 197 | 198 | 68% { 199 | transform: translate(2.5px, -1.5px) rotate(1.5deg); 200 | } 201 | 202 | 70% { 203 | transform: translate(2.5px, 2.5px) rotate(0.5deg); 204 | } 205 | 206 | 72% { 207 | transform: translate(-0.5px, -1.5px) rotate(1.5deg); 208 | } 209 | 210 | 74% { 211 | transform: translate(-1.5px, 2.5px) rotate(1.5deg); 212 | } 213 | 214 | 76% { 215 | transform: translate(-1.5px, 2.5px) rotate(1.5deg); 216 | } 217 | 218 | 78% { 219 | transform: translate(-1.5px, 2.5px) rotate(0.5deg); 220 | } 221 | 222 | 80% { 223 | transform: translate(-1.5px, 0.5px) rotate(-0.5deg); 224 | } 225 | 226 | 82% { 227 | transform: translate(-1.5px, 0.5px) rotate(-0.5deg); 228 | } 229 | 230 | 84% { 231 | transform: translate(-0.5px, 0.5px) rotate(1.5deg); 232 | } 233 | 234 | 86% { 235 | transform: translate(2.5px, 1.5px) rotate(0.5deg); 236 | } 237 | 238 | 88% { 239 | transform: translate(-1.5px, 0.5px) rotate(1.5deg); 240 | } 241 | 242 | 90% { 243 | transform: translate(-1.5px, -0.5px) rotate(-0.5deg); 244 | } 245 | 246 | 92% { 247 | transform: translate(-1.5px, -1.5px) rotate(1.5deg); 248 | } 249 | 250 | 94% { 251 | transform: translate(0.5px, 0.5px) rotate(-0.5deg); 252 | } 253 | 254 | 96% { 255 | transform: translate(2.5px, -0.5px) rotate(-0.5deg); 256 | } 257 | 258 | 98% { 259 | transform: translate(-1.5px, -1.5px) rotate(-0.5deg); 260 | } 261 | 262 | 0%, 100% { 263 | transform: translate(0, 0) rotate(0); 264 | } 265 | } 266 | @font-face { 267 | font-family: 'Flat-UI-Icons'; 268 | src: url('flat-ui-icons-regular.eot'); 269 | src: url('flat-ui-icons-regular.eot?#iefix') format('embedded-opentype'), url('flat-ui-icons-regular.woff') format('woff'), url('flat-ui-icons-regular.ttf') format('truetype'), url('flat-ui-icons-regular.svg#flat-ui-icons-regular') format('svg'); 270 | } 271 | [class^="fui-"], 272 | [class*="fui-"] { 273 | font-family: 'Flat-UI-Icons'; 274 | speak: none; 275 | font-style: normal; 276 | font-weight: normal; 277 | font-variant: normal; 278 | text-transform: none; 279 | -webkit-font-smoothing: antialiased; 280 | -moz-osx-font-smoothing: grayscale; 281 | } 282 | .fui-cross:before { 283 | content: "\e609"; 284 | } 285 | .fui-info-circle:before { 286 | content: "\e60f"; 287 | } 288 | .fui-photo:before { 289 | content: "\e62a"; 290 | } 291 | .fui-eye:before { 292 | content: "\e62c"; 293 | } 294 | .fui-chat:before { 295 | content: "\e62d"; 296 | } 297 | .fui-home:before { 298 | content: "\e62e"; 299 | } 300 | .fui-user:before { 301 | content: "\e631"; 302 | } -------------------------------------------------------------------------------- /syzoj/templates/article.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block body %} 4 |
    5 | 10 |
    11 |
    12 |

    {{article.title}}

    13 |

    14 | {{article.user.nickname}} 15 | {% if article.user.nameplate %}{{article.user.nameplate|safe}}{% endif %} 于{{tool.pretty_time(article.public_time)}}发表,{{tool.pretty_time(article.update_time)}}最后更新 16 | {% if article.is_allowed_edit(user) %} 17 | 删除文章 18 | 编辑文章 19 | {% endif %} 20 |

    21 |
    22 |
    23 |
    24 |
    {{article.content}}
    25 |
    26 |
    27 |
    28 |

    共{{ comment_num }}条回复

    29 | {% set comments = sorter.get() %} 30 | {% for comment in comments %} 31 | {% if comment.origin_comment_id == None %} 32 |
    33 |
    34 |
    35 |

    36 | {{ tool.pretty_time(comment.public_time) }} 37 |

    38 | 39 |

    {{comment.user.nickname}}

    40 |
    41 |
    {{ comment.content }}
    42 |
    43 | {% if comment.user.nickname == user.nickname %} 44 | 删除 45 | {% endif %} 46 | {% set comment_username = comment.user.nickname %} 47 |
    回复 48 |
    49 |
    50 |
    51 |
    52 |
    53 | {% if comment.origin_comment != None %} 54 | {% for oricomments in comment.origin_comment %} 55 |
    56 |
    57 |
    58 |

    59 | {{ tool.pretty_time(oricomments.public_time) }} 60 |

    61 | 62 |

    {{comment.user.nickname}}

    63 |
    64 |
    {{ oricomments.content }}
    65 |
    66 |
    67 | {% endfor %} 68 | {% endif %} 69 | {% endif %} 70 | {% endfor %} 71 | {{ sorter.get_html() | safe }} 72 |
    73 |
    74 |
    75 |
    76 |
    77 | 78 |
    79 | 80 |
    81 |
    82 |
    83 | {% endblock %} 84 | 85 | {% block script %} 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 113 | {% endblock %} 114 | -------------------------------------------------------------------------------- /syzoj/templates/contest.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block contest_tab %} am-active {% endblock %} 3 | {% block body %} 4 |
    5 |
    6 |

    {{ contest.title }}

    7 | 8 |
    9 | {% if contest.is_allowed_edit(user) or not contest.is_running() %} 10 | 排行榜 12 | {% endif %} 13 | {% if contest.is_allowed_edit(user) %} 14 | 编辑比赛 15 | {% endif %} 16 |
    17 |
    18 |
    19 |
    20 | {{ contest.information }} 21 |
    22 |
    23 |
    24 |
    25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {% set problems = contest.get_problems() %} 33 | {% set i = 0 %} 34 | {% for problem in problems %} 35 | 36 | 37 | 38 | 40 | 41 | 42 | {% set i = i+1 %} 43 | {% endfor %} 44 |
    提交状态题目
    {{ info[problem.id] | safe}}{{ problem.title 39 | }}
    45 |
    46 |
    47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /syzoj/templates/contest_list.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block contest_tab %} am-active {% endblock %} 3 | {% block body %} 4 |
    5 |
    6 |
    7 | 8 |

    比赛列表

    9 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 10 |
    11 |
    12 |
    13 | 14 | 15 |
    16 |
    17 |
    18 | 创建比赛 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% set contests = sorter.get() %} 30 | {% for contest in contests %} 31 | 32 | {% if now < contest.start_time %} 33 | {% set tag = '未开始' %} 34 | {% elif contest.is_running() %} 35 | {% set tag = '进行中' %} 36 | {% else %} 37 | {% set tag = '已结束' %} 38 | {% endif %} 39 | 40 | 41 | 42 | 43 | 44 | 45 | {% endfor %} 46 | 47 |
    比赛名称
    开始时间
    结束时间
    比赛介绍
    {{contest.title}}{{ tool.pretty_time(contest.start_time) }}{{ tag|safe }} {{ tool.pretty_time(contest.end_time) }}{{ contest.information }}
    48 |
    49 |
    50 | {{sorter.get_html()|safe}} 51 |
    52 | {% endblock %} 53 | -------------------------------------------------------------------------------- /syzoj/templates/contest_problem.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block contest_tab %}am-active{% endblock %} 3 | {% block body %} 4 |
    5 | 13 |
    14 |
    15 |

    {{ contest.title }} - {{ problem.title }}

    16 | 17 |

    18 | 内存限制: {{problem.memory_limit}} MB 19 | 时间限制: {{problem.time_limit}} ms 20 |

    21 |
    22 |
    23 |
    24 | 25 |

    题目描述

    26 |
    {{problem.description}}
    27 | 28 |

    输入格式

    29 |
    {{problem.input_format}}
    30 | 31 |

    输出格式

    32 |
    {{problem.output_format}}
    33 | 34 |

    测试样例

    35 |
    {{problem.example}}
    36 | 37 |

    数据范围与提示

    38 |
    {{problem.limit_and_hint}}
    39 |
    40 |
    41 |
    42 |

    提交代码

    43 | 44 |
    46 |
    47 | 48 | 51 | 52 |
    53 |
    54 | 55 | 56 |
    57 | 58 |
    59 |
    60 |
    61 | {% endblock %} 62 | {% block script %} 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 92 | {% endblock %} 93 | -------------------------------------------------------------------------------- /syzoj/templates/contest_ranklist.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block contest_tab %}am-active{% endblock %} 3 | {% block body %} 4 |
    5 |
    6 |

    {{ contest.title }}排名

    7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | {% set problems = contest.get_problems() %} 15 | {% set i = 0 %} 16 | {% for problem in problems %} 17 | 19 | {% set i = i + 1 %} 20 | {% endfor %} 21 | 22 | 23 | 24 | 25 | {% set players = contest.ranklist.get_players() %} 26 | {% set i = 1 %} 27 | {% for player in players%} 28 | {% set user = player.user %} 29 | {% set score_details = player.get_score_details() %} 30 | 31 | 32 | 35 | {% for problem in problems %} 36 | {% set pid = tool.to_str(problem.id) %} 37 | {% if pid in score_details %} 38 | 40 | {% else %} 41 | 42 | {% endif %} 43 | {% endfor %} 44 | 45 | 46 | {% set i = i + 1 %} 47 | {% endfor %} 48 | 49 |
    #
    用户昵称
    {{ problem.title 18 | }}
    总分
    {{ i }}{{ user.nickname }}{% if user.nameplate 33 | %}{{user.nameplate|safe}}{% endif %} 34 | {{ 39 | score_details[pid]['score'] }}{{ player.score }}
    50 |
    51 |
    52 | {%endblock%} 53 | -------------------------------------------------------------------------------- /syzoj/templates/delete_article.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |

    你真的要删除文章 {{article.title}} 吗?该操作目前不可逆,请谨慎操作

    6 | 我知道我在干什么,删除 7 |
    8 | {% endblock %} -------------------------------------------------------------------------------- /syzoj/templates/discussion.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    自由讨论

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 |
    17 | 发表文章 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% set articles = sorter.get() %} 29 | {% for article in articles %} 30 | 31 | 32 | 33 | {% set tag_list = article.tags.split(',') %} 34 | 35 | 36 | 37 | {% endfor %} 38 | 39 |
    标题
    作者
    标签
    发表时间
    {{article.title}}
    {{article.user.nickname}}
    {% for tag in tag_list[:3] %} {{tag}} {% endfor %}
    {{tool.pretty_time(article.public_time)}}
    40 |
    41 | 42 | {{sorter.get_html()|safe}} 43 |
    44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /syzoj/templates/edit_article.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    编辑文章

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 | 20 |
    21 | {% if article %} 22 | {% set article_id=article.id %} 23 | {% else %} 24 | {% set article_id=0 %} 25 | {% endif %} 26 |
    27 |
    28 | 36 |
    37 |
    38 |
    39 |
    40 | 41 | 43 |
    44 | 45 |
    46 | 47 | 48 |
    49 |
    50 | 51 | 53 |
    54 |
    55 |
    56 |
    57 |

    58 |
    59 |
    60 |
    61 |
    62 | 63 |
    64 |
    65 | {% endblock %} 66 | 67 | {% block script %} 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 86 | {% endblock %} 87 | -------------------------------------------------------------------------------- /syzoj/templates/edit_contest.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block contest_tab %}am-active{% endblock %} 3 | {% block body %} 4 |
    5 | {% if contest %} 6 |

    修改 {{ contest.title }}

    7 | {% set contest_id = contest.id %} 8 | {% else %} 9 |

    新建比赛

    10 | {% set contest_id = 0 %} 11 | {% endif %} 12 |
    13 |
    14 |
    15 | 16 | 17 |
    18 |
    19 | 20 | 22 |
    23 |
    24 | 25 | 26 |
    27 |
    28 | 29 | 30 |
    31 |
    32 | 33 | 34 |
    35 | 36 |
    37 | 38 |
    39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /syzoj/templates/edit_notice.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    编辑公告

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 |
    13 | 14 |
    15 | 20 |
    21 | {% if notice %} 22 | {% set notice_id=notice.id %} 23 | {% else %} 24 | {% set notice_id=0 %} 25 | {% endif %} 26 |
    27 |
    28 | 36 |
    37 |
    38 |
    39 |
    40 | 41 | 43 |
    44 | 45 |
    46 | 47 | 48 |
    49 |
    50 | 51 | 53 |
    54 |
    55 |
    56 |
    57 |

    58 |
    59 |
    60 |
    61 |
    62 | 63 |
    64 |
    65 | {% endblock %} 66 | 67 | {% block script %} 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 86 | {% endblock %} 87 | -------------------------------------------------------------------------------- /syzoj/templates/edit_problem.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    编辑题目

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 | 23 |
    24 | {% if problem %} 25 | {% set problem_id=problem.id %} 26 | {% else %} 27 | {% set problem_id=0 %} 28 | {% endif %} 29 |
    30 |
    31 | 39 |
    40 |
    41 |
    42 |
    43 | 44 | 46 |
    47 | 48 |
    49 | 50 | 51 |
    52 | 53 |
    54 | 55 | 56 |
    57 | 58 |
    59 | 60 | 61 |
    62 | 63 |
    64 | 65 | 66 |
    67 |
    68 | 69 | 71 |
    72 |
    73 | 74 | 75 |
    76 |
    77 |
    78 | 79 |
    80 |

    81 | 82 |

    题目描述

    83 |
    84 |

    输入格式

    85 |
    86 |

    输出格式

    87 |
    88 |

    测试样例

    89 |
    90 |

    数据范围与提示

    91 |
    92 |
    93 |
    94 |
    95 | 96 |
    97 |
    98 | {% endblock %} 99 | 100 | {% block script %} 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 123 | {% endblock %} 124 | -------------------------------------------------------------------------------- /syzoj/templates/edit_user.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 |

    修改资料

    7 |
    9 | {% if status==1 %} 10 |

    修改成功

    11 | {% elif status==3001 %} 12 |

    邮箱格式错误

    13 | {% elif status==3002 %} 14 |

    原密码错误...不要偷偷改别人密码哦

    15 | {% elif status==3003%} 16 |

    新密码不得为空

    17 | {% else %} 18 |

    未知状态

    19 | {% endif %} 20 |
    21 |
    22 |
    23 | 24 |
    25 | 26 |
    27 |
    28 |
    29 | 30 |
    31 | 32 |
    33 |
    34 |
    35 | 36 |
    37 |
    38 | 39 | 40 | 41 |
    42 |
    43 |
    44 | {% if user and not user.have_privilege(1) %} 45 |
    46 | {% endif %} 47 |
    48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
    57 | {% if user and not user.have_privilege(1) %} 58 |
    59 | {% endif %} 60 |
    61 |
    62 | 63 |
    64 |
    65 |
    66 |
    67 |
    68 | {% endblock %} 69 | 70 | {% block script %} 71 | 95 | {% endblock %} 96 | -------------------------------------------------------------------------------- /syzoj/templates/error_info.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 | 10 |
    11 |

    {{ info }}

    12 |
    13 | {% endblock %} 14 | 15 | {% block script %} 16 | 22 | {% endblock %} -------------------------------------------------------------------------------- /syzoj/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 | 15 |
    16 |
    17 | 18 |
    19 |
    20 | {% if user %} 21 | {% set submitted_problem = user.get_submitted_problems() %} 22 | {% endif %} 23 | {% for problem in problems %} 24 | {% if problem.is_allowed_use(user) %} 25 | {% if user and problem.id in submitted_problem and submitted_problem[problem.id][0] == True %} 26 | {% else %} 27 | {% set tag_list = problem.tags %} 28 | {% if tag_list != None %} 29 | {% set tag_list = problem.tags.split(',') %} 30 | {% endif %} 31 |
    32 |
    33 |

    来自标签:{% for tag in tag_list[:5] %} {{tag}} {% endfor %}

    34 |

    {{problem.title}}{% if not problem.is_public %}--[未审核]{% endif %}

    35 |

    {{problem.description[:50]}}

    36 | {% if problem.is_in_stared(user) %} 37 | 已收藏   38 | {% else %} 39 | 收藏   40 | {% endif %} 41 | FAQ 42 |
    43 |
    44 | {% endif %} 45 | {% endif %} 46 | {% endfor %} 47 |
    48 |
    49 |
    50 |
    51 |
    52 |
    53 | 54 |
    55 | 61 |
    62 |
      63 | {% for n in notice[:5] %} 64 |
    • 65 | {{ n.title }} 66 |
      {{ n.content[:20] }}...
      67 |
    • 68 | {% endfor %} 69 |
    70 |
    71 |
    72 |
    73 |
    74 |
    75 | 80 | {% endblock %} 81 | -------------------------------------------------------------------------------- /syzoj/templates/info.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |

    公告板

    9 |
    10 |
    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for n in notice[:5] %} 20 | 21 | 22 | 23 | 24 | {% endfor %} 25 | 26 |
    标题时间
    {{ n.title }}2018-09-04
    27 |
    28 |
    29 |
    30 |
    31 |

    排名

    32 |
    33 |
    34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% set rank = 1 %} 44 | {% set users = ranker.get() %} 45 | {% for user in users%} 46 | 47 | {%set rank=rank+1%} 48 | 49 | 50 | 51 | {% endfor %} 52 | 53 |
    #昵称个性签名
    {{rank}}{{user.nickname}}{% if user.nameplate %}{{user.nameplate|safe}}{% endif %}{% if user.information%}{{user.information}}{%endif%}
    54 |
    55 |
    56 |
    57 |
    58 |
    59 |
    60 |

    信息栏

    61 |
    62 |
    63 |

    请大家不要肆意复制别人代码提交!!!

    64 |
    65 |
    66 |
    67 |
    68 |

    近期比赛

    69 |
    70 |
    71 |

    还没有举行过任何比赛

    72 |
    73 |
    74 |
    75 |
    76 | {% endblock %} 77 | -------------------------------------------------------------------------------- /syzoj/templates/judge_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% set see_result=judge.is_allowed_see_result(user) %} 20 | {% set result=judge.get_result()%} 21 | 22 | 23 | 24 | {% if see_result %} 25 | 26 | 27 | 28 | {% else %} 29 | 30 | 31 | 32 | {% endif %} 33 | 34 | 35 | 36 | 37 |
    编号题目名称状态分数总时间提交者提交时间
    {{judge.id}}{{judge.problem.title}}{{result["status"]}}{{result["score"]}}{{result["total_time"]}}隐藏隐藏隐藏{{judge.user.nickname}}{{tool.pretty_time(judge.submit_time)}}
    38 |
    39 |
    40 | {% set see_code = judge.is_allowed_see_code(user) %} 41 | {% if see_code%} 42 |
    {{judge.code}}
    43 | {% else %} 44 |
    =。=现在不可以看代码!
    45 | {% endif %} 46 |
    47 | {% if see_code and see_result%} 48 | {% if result["status"] == "Compile Error" and result["compiler_output"] %} 49 |
    50 |
    {{result["compiler_output"]}}
    51 |
    52 | {% else %} 53 |
    54 |
    55 | {% for i in range(result["case_num"]) %} 56 |
    57 | {% set case = result[tool.to_str(i)]%} 58 |
    测试点:{{i + 1}} 状态:{{case["status"]}} 59 | 运行时间:{{case["time_used"]}}ms 使用内存:{{case["memory_used"]}}kb 60 |
    61 |
    62 |
    63 | 输入文件 64 |
    {{case["input"]}}
    65 | 期望输出 66 |
    {{case["answer"]}}
    67 | 你的输出 68 |
    {{case["user_out"]}}
    69 |
    70 |
    71 |
    72 | {% endfor %} 73 |
    74 |
    75 | {% endif %} 76 | {% endif %} 77 |
    78 | {% endblock %} 79 | 80 | {% block script %} 81 | 89 | {% endblock %} 90 | -------------------------------------------------------------------------------- /syzoj/templates/judge_state.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    评测记录

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 |
    13 | 14 |
    15 |
    16 |
    17 |
    18 | 19 |
    20 |
    21 | 22 |
    23 | 24 |
    25 |
    26 | 我的提交 27 |
    28 |
    29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {% for judge in judges %} 43 | {% set see_result=judge.is_allowed_see_result(user) %} 44 | {% set result=judge.get_result()%} 45 | 46 | 47 | 48 | {% if see_result %} 49 | 50 | 51 | 52 | {% if judge.is_allowed_see_code(user) %} 53 | 54 | {% else %} 55 | 56 | {% endif %} 57 | 58 | 59 | {% else %} 60 | 61 | 62 | 63 | {% endif %} 64 | 65 | 66 | 67 | {% endfor %} 68 | 69 |
    编号
    题目名称
    状态
    分数
    总时间
    提交者
    提交时间
    {{judge.id}}
    {{judge.problem.title}}
    {{judge.status}}
    {{result["score"]}}
    {{result["score"]}}
    {{result["total_time"]}}
    隐藏
    隐藏
    隐藏
    {{judge.user.nickname}}
    {{tool.pretty_time(judge.submit_time)}}
    70 |
    71 |
    72 | {{sorter.get_html()|safe}} 73 |
    74 | {% endblock %} 75 | -------------------------------------------------------------------------------- /syzoj/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | SYOJ 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 33 | 34 | 35 | 36 |
    37 |
    38 |

    39 | SYOJ 40 |

    41 | 42 |
    43 |
    44 | 50 |
    51 |
    52 |
    53 | 54 |
    55 | 56 |
    57 | {% set user=tool.get_cur_user() %} 58 | {% if user %} 59 |
    60 | 72 |
    73 | {% else %} 74 |
    75 | 78 | 81 |
    82 | {% endif %} 83 |
    84 |
    85 |
    86 | 87 | {% block body %} 88 | {% endblock %} 89 |
    90 |
    91 | 92 |
    93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
    101 |
    102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 134 | {% block script %}{% endblock %} 135 | 136 |
    137 | 140 | 143 |
    144 | 145 | 146 | -------------------------------------------------------------------------------- /syzoj/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    登录页

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 |
    17 | 20 |
    21 |
    22 |
    23 |
    24 | 25 | 26 |
    27 | 28 |
    29 |
    30 |
    31 | 32 | 33 |
    34 | 35 |
    36 |
    37 | 38 |
    39 |
    40 |

    登陆

    41 |
    42 |
    43 |
    44 |
    45 |
    46 |
    47 | 48 | {% endblock %} 49 | {% block script %} 50 | 108 | {% endblock %} 109 | -------------------------------------------------------------------------------- /syzoj/templates/notice.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block body %} 4 |
    5 | 10 |
    11 |
    12 |

    {{notice.title}}

    13 |

    14 | {{notice.user.nickname}} 15 | {% if notice.user.nameplate %}{{notice.user.nameplate|safe}}{% endif %} 于{{tool.pretty_time(notice.public_time)}}发表,{{tool.pretty_time(notice.update_time)}}最后更新 16 | {% if notice.is_allowed_edit(user) %} 17 | 删除公告 18 | 编辑公告 19 | {% endif %} 20 |

    21 |
    22 |
    23 |
    24 |
    {{notice.content}}
    25 |
    26 |
    27 |
    28 |
    29 | {% endblock %} 30 | 31 | {% block script %} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 55 | {% endblock %} 56 | -------------------------------------------------------------------------------- /syzoj/templates/problem.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 | 11 |
    12 |
    13 | 14 |

    #{{problem.id}}. {{problem.title}}{% if not problem.is_public %}--[未审核]{% endif %}

    15 |

    16 | 内存限制: {{problem.memory_limit}} MB 17 | 时间限制: {{problem.time_limit}} ms 18 |
    19 | 上传者: {{problem.user.username}} 20 |

    21 | {% set tag_list = problem.tags.split(',') %} 22 | 23 |

    标签:{% for tag in tag_list %} {{tag}} {% endfor %}

    24 |
    25 |
    26 |
    27 | 28 |
    29 |
    30 | 31 |
    32 |
    33 | 提交此题 34 | 此题提交情况 35 | {% if problem.is_allowed_edit(user) %} 36 | 编辑题面 37 | 上传测试数据 38 | {% endif %} 39 | {% if user %} 40 | {% if problem.is_in_stared(user) %} 41 | 已收藏 42 | {% else %} 43 | 收藏 44 | {% endif %} 45 | {% endif %} 46 | {% if user and user.have_privilege(2) %} 47 | {% if problem.is_public %} 48 | 49 | {%else%} 50 | 51 | {%endif %} 52 | {% endif %} 53 |
    54 |
    55 |
    56 | 57 |

    题目描述

    58 |
    {{problem.description}}
    59 | 60 |

    输入格式

    61 |
    {{problem.input_format}}
    62 | 63 |

    输出格式

    64 |
    {{problem.output_format}}
    65 | 66 |

    测试样例

    67 |
    {{problem.example}}
    68 | 69 |

    数据范围与提示

    70 |
    {{problem.limit_and_hint}}
    71 |
    72 |
    73 | 74 | {% endblock %} 75 | {% block script %} 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 134 | {% endblock %} 135 | -------------------------------------------------------------------------------- /syzoj/templates/problem_set.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    在线题库

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 |
    13 | 14 |
    15 |
    16 |
    17 |
    18 | 19 |
    20 | 21 |
    22 |
    23 | 添加题目 24 |
    25 |
    26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {% if user %} 40 | {% set submitted_problem = user.get_submitted_problems() %} 41 | {% endif %} 42 | {% for problem in problems %} 43 | {% if problem.is_allowed_use(user) %} 44 | 45 | {% if user and problem.id in submitted_problem %} 46 | {% if submitted_problem[problem.id][0] == True %} 47 | 48 | {% else %} 49 | 50 | {% endif %} 51 | {% else %} 52 | 53 | {% endif %} 54 | 55 | 56 | {% set tag_list = problem.tags.split(',') %} 57 | 58 | 59 | 60 | 61 | {% endif %} 62 | {% endfor %} 63 | 64 |
    编号
    题目名称标签
    通过
    提交
    {{problem.id}}
    {{problem.title}}{% if not problem.is_public %}--[未审核]{% endif %}{% for tag in tag_list[:3] %} {{tag}} {% endfor %}
    {{problem.ac_num}}
    {{problem.submit_num}}
    65 |
    66 | 67 | {{sorter.get_html()|safe}} 68 |
    69 | {% endblock %} 70 | -------------------------------------------------------------------------------- /syzoj/templates/ranklist.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    积分排行

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 |
    17 |
    18 | 19 |
    20 | 21 |
    22 |
    23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% set rank = sorter.per_page*(sorter.cur_page-1)+1%} 34 | {% set users = sorter.get() %} 35 | {% for user in users%} 36 | 37 | {%set rank=rank+1%} 38 | 39 | 40 | 41 | 42 | {% endfor %} 43 | 44 |
    排名
    用户昵称
    个性签名
    通过数量
    {{rank}}{{user.nickname}}{% if user.nameplate %}{{user.nameplate|safe}}{% endif %}{% if user.information%}{{user.information}}{%endif%}{{user.ac_num}}
    45 |
    46 | 47 | {{sorter.get_html()|safe}} 48 |
    49 | {%endblock%} 50 | -------------------------------------------------------------------------------- /syzoj/templates/sign_up.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | 7 |

    注册页

    8 | 纸上得来终觉浅Have fun Okay~,绝知此事要躬行 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 |
    17 | 20 |
    21 |
    22 |
    23 |
    24 |
    25 | 26 | 27 |
    28 | 29 |
    30 |
    31 |
    32 | 33 | 34 |
    35 | 36 |
    37 |
    38 |
    39 | 40 | 41 |
    42 |
    43 | 44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 |
    51 |

    注册

    52 |
    53 |
    54 |
    55 |
    56 |
    57 |
    58 |
    59 | {% endblock %} 60 | 61 | {% block script %} 62 | 137 | {% endblock %} 138 | -------------------------------------------------------------------------------- /syzoj/templates/submit.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 |

    提交{{problem.title}}的代码

    7 |
    8 |
    9 | 10 | 13 | 14 |
    15 |
    16 | 17 | 18 |
    19 | 20 |
    21 |
    22 |
    23 | {% endblock %} -------------------------------------------------------------------------------- /syzoj/templates/upload_testdata.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |
    6 | {% if problem.testdata %} 7 |

    数据包:{{problem.testdata.filename}}

    8 | {% set ok,list=parse(problem.testdata) %} 9 | {% if ok %} 10 |

    你已经上传了测试数据,并且已经解析出以下数据点

    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for case in list %} 20 | 21 | 22 | 23 | 24 | {% endfor %} 25 | 26 |
    输入文件输出文件
    {{case[0]}}{{case[1]}}
    27 | {% else %} 28 |

    你已经上传了测试数据,但是数据包出现错误{{list}}

    29 | {% endif %} 30 | {% else %} 31 |

    你还没有上传数据

    32 | {% endif %} 33 |
    34 |
    35 |
    36 |
    37 | 38 | 39 |
    40 |
    41 | 42 | 43 |
    44 |
    45 | 46 | 47 |

    数据格式请看...

    48 |
    49 | 50 |
    51 |
    52 |
    53 | {% endblock %} -------------------------------------------------------------------------------- /syzoj/templates/user.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |

    用户信息

    6 |
    7 |
    8 |
    9 |
    10 |

    头像

    11 |
    12 |
    13 | 14 |
    15 |
    16 |
    17 |
    18 |
    19 |
    20 |

    用户昵称

    21 |
    22 |
    23 |

    {{shown_user.nickname}}{%if shown_user.nameplate %}{{shown_user.nameplate|safe}}{%endif%}

    24 |
    25 | 26 |
    27 |

    用户邮箱

    28 |
    29 |
    30 |

    {{shown_user.email}}

    31 |
    32 |
    33 |
    34 | 35 |
    36 |
    37 |
    38 |

    共发表了{{ articles_num }}篇文章

    39 |
    40 |
    41 | 42 | 43 | {% for article in articles %} 44 | 45 | 46 | 47 | 48 | {% endfor %} 49 | 50 |
    {{article.title}}{{tool.pretty_time(article.public_time)}}
    51 |
    52 |
    53 |
    54 |
    55 |
    56 |
    57 |
    58 |

    个性签名

    59 |
    60 |
    61 |

    {%if shown_user.information%}{{shown_user.information}}{%else%}泥萌看什么看,{{shown_user.nickname}}什么都没有说{%endif%}

    62 |
    63 |
    64 |
    65 |
    一共通过了{{shown_user.ac_num}}道题
    66 | {% set submitted_problems = shown_user.get_submitted_problems() %} 67 |
    68 | {% for key in submitted_problems.keys() %} 69 | {% if submitted_problems[key][0] == True %} 70 | {{key}} 71 | {% endif %} 72 | {% endfor %} 73 |
    74 |
    75 |
    76 |
    Ta的收藏夹
    77 |
    78 | {% for problem in stars %} 79 | {{problem.title}} 80 | {% endfor %} 81 |
    82 |
    83 |
    84 |
    85 | {% endblock %} 86 | -------------------------------------------------------------------------------- /syzoj/templates/virtual_contest.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
    4 |
    5 |

    {{ contest.title }}

    6 |
    7 |
    8 |
    9 | {{ contest.information }} 10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /syzoj/update_assistant/misc.py: -------------------------------------------------------------------------------- 1 | from syzoj.models import JudgeState 2 | from syzoj import db 3 | 4 | db.create_all() 5 | 6 | all_judge = JudgeState.query.all() 7 | for item in all_judge: 8 | item.update_userac_info() 9 | -------------------------------------------------------------------------------- /syzoj/views/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import importlib 3 | importlib.reload(sys) 4 | import time 5 | 6 | from flask import request, render_template 7 | from urllib import parse 8 | from syzoj import oj,db 9 | from syzoj.models import User, Notice 10 | from syzoj.controller import Tools, Paginate 11 | from .session import sign_up, login 12 | from .problem import problem, problem_set 13 | from .judge import submit_code, judge_state 14 | from .user import user, edit_user 15 | from .discussion import edit_article, article, discussion 16 | from .contest import contest_list 17 | from syzoj.models import User, Problem, File, FileParser 18 | from sqlalchemy import or_ 19 | from syzoj.controller import Paginate, Tools 20 | from .common import need_login, not_have_permission, show_error 21 | from flask import jsonify, redirect, url_for, abort, request, render_template 22 | 23 | 24 | @oj.route("/") 25 | def index(): 26 | query = Problem.query.order_by(Problem.time_limit) 27 | problem_title = request.args.get("problem_title") 28 | notice = Notice.query.all() 29 | if request.args.get("problem_title"): 30 | query = query.filter( 31 | or_(Problem.title.like((u"%" + problem_title + u"%")), Problem.tags.like((u"%" + problem_title + u"%")), 32 | Problem.id.like((u"%" + problem_title + u"%")))) 33 | else: 34 | problem_title = '' 35 | 36 | def make_url(page, other): 37 | other["page"] = page 38 | return url_for("problem_set") + "?" + parse.urlencode(other) 39 | 40 | return render_template("index.html", tool=Tools, tab="home", problems=query, notice=notice) 41 | 42 | 43 | @oj.route("/info") 44 | def info(): 45 | query = User.query.order_by(db.desc(User.ac_num)) 46 | notice = Notice.query.all() 47 | ranker = Paginate(query, cur_page=1, per_page=10) 48 | return render_template("info.html", tool=Tools, tab="info", notice=notice, ranker=ranker) 49 | 50 | 51 | @oj.route("/error") 52 | def error(): 53 | info = request.args.get("info") 54 | next = request.args.get("next") 55 | # TODO:rewrite error page for beautiful 56 | return render_template("error_info.html", tool=Tools, info=info, next=next) 57 | 58 | 59 | @oj.route("/info//edit", methods=["GET", "POST"]) 60 | def edit_notice(notice_id): 61 | user = User.get_cur_user() 62 | if not user: 63 | return need_login() 64 | notice = Notice.query.filter_by(id=notice_id).first() 65 | if notice and not notice.is_allowed_edit(user): 66 | return not_have_permission() 67 | elif not (user.have_privilege(4) or user.have_privilege(5)): 68 | return not_have_permission() 69 | 70 | if request.method == "POST": 71 | if request.form.get("title") == "" or request.form.get("content") == "": 72 | return show_error("Please input title and content", 73 | url_for("edit_notice", anotice_id=notice_id)) 74 | if not notice: 75 | notice = Notice(title=request.form.get("title"), content=request.form.get("content"), user=user) 76 | 77 | notice.title = request.form.get("title") 78 | notice.content = request.form.get("content") 79 | notice.tags = request.form.get("tags") 80 | notice.update_time = time.time() 81 | notice.sort_time = time.time() 82 | notice.save() 83 | return redirect(url_for("info")) 84 | else: 85 | return render_template("edit_notice.html", tool=Tools, notice=notice, tab="notice") 86 | 87 | 88 | @oj.route("/notice/") 89 | def notice(notice_id): 90 | notice = Notice.query.filter_by(id=notice_id).first() 91 | if not notice: 92 | return show_error("找不到公告", url_for('index')) 93 | 94 | def make_url(page, other): 95 | return url_for("notice", notice_id=notice_id) + "?" + parse.urlencode({"page": page}) 96 | 97 | return render_template("notice.html", tool=Tools, notice=notice, tab="info") 98 | 99 | 100 | @oj.route("/notice//delete") 101 | def delete_notice(notice_id): 102 | user = User.get_cur_user() 103 | if not user: 104 | return need_login() 105 | notice = Notice.query.filter_by(id=notice_id).first() 106 | if notice and notice.is_allowed_edit(user) is False: 107 | return not_have_permission() 108 | notice.delete() 109 | return redirect(url_for("info")) 110 | -------------------------------------------------------------------------------- /syzoj/views/common.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from flask import redirect, url_for 4 | 5 | 6 | def show_error(error, next): 7 | return redirect(url_for("error", info=error, next=next)) 8 | 9 | 10 | def need_login(): 11 | return show_error("Please login first.", url_for("login")) 12 | 13 | 14 | def not_have_permission(): 15 | return show_error("You don't have permission", url_for("index")) -------------------------------------------------------------------------------- /syzoj/views/contest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from urllib import parse 4 | import time 5 | from flask import render_template, url_for, request, abort, redirect 6 | from syzoj import oj, db 7 | from syzoj.models import Contest, User, JudgeState 8 | from syzoj.controller import Tools, Paginate 9 | from .common import need_login, not_have_permission 10 | 11 | 12 | @oj.route("/contest") 13 | def contest_list(): 14 | query = Contest.query.order_by(db.desc(Contest.start_time)) 15 | 16 | def make_url(page, other): 17 | return url_for("contest_list") + "?" + parse.urlencode({"page": page}) 18 | 19 | sorter = Paginate(query, make_url=make_url, cur_page=request.args.get("page"), edge_display_num=3, per_page=10) 20 | return render_template("contest_list.html", tool=Tools, sorter=sorter, now=time.time()) 21 | 22 | 23 | @oj.route("/contest/") 24 | def contest(contest_id): 25 | contest = Contest.query.filter_by(id=contest_id).first() 26 | user = User.get_cur_user() 27 | 28 | now = time.time() 29 | if now < contest.start_time and not contest.is_allowed_edit(user): 30 | return not_have_permission() 31 | 32 | player = None 33 | info = {} 34 | if user: player = contest.players.filter_by(user_id = user.id).first() 35 | if player: 36 | details = player.get_score_details() 37 | for key, val in details.items(): 38 | if isinstance(val, dict): 39 | pid = int(key) 40 | jid = int(val["judge_id"]) 41 | status = JudgeState.query.filter_by(id = jid).first().status 42 | url = url_for("judge_detail", judge_id = jid) 43 | if contest.is_running(): 44 | info[pid] = u"已提交" % (url) 45 | else: 46 | info[pid] = "%s" % (url, status) 47 | 48 | return render_template("contest.html", tool=Tools, contest=contest, info = info) 49 | 50 | 51 | @oj.route("/contest//") 52 | def contest_problem(contest_id, kth_problem): 53 | contest = Contest.query.filter_by(id=contest_id).first() 54 | user = User.get_cur_user() 55 | if not contest: 56 | abort(404) 57 | 58 | now = time.time() 59 | if now < contest.start_time and not contest.is_allowed_edit(user): 60 | return not_have_permission() 61 | 62 | problem = contest.get_problems()[kth_problem] 63 | if now > contest.end_time: 64 | return redirect(url_for("problem", problem_id=problem.id)) 65 | 66 | return render_template("contest_problem.html", tool=Tools, problem=problem, contest=contest) 67 | 68 | 69 | @oj.route("/contest//ranklist") 70 | def contest_ranklist(contest_id): 71 | user = User.get_cur_user() 72 | contest = Contest.query.filter_by(id=contest_id).first() 73 | if not contest: 74 | abort(404) 75 | now = time.time() 76 | if contest.is_allowed_edit(user) or now > contest.end_time: 77 | return render_template("contest_ranklist.html", tool=Tools, contest=contest) 78 | else: 79 | return not_have_permission() 80 | 81 | 82 | @oj.route("/contest//edit", methods=["GET", "POST"]) 83 | def edit_contest(contest_id): 84 | user = User.get_cur_user() 85 | if not user: 86 | return need_login() 87 | 88 | contest = Contest.query.filter_by(id=contest_id).first() 89 | if contest and not contest.is_allowed_edit(user): 90 | return not_have_permission() 91 | elif not (user.have_privilege(4) or user.have_privilege(5)): 92 | return not_have_permission() 93 | 94 | if request.method == "POST": 95 | if not contest: 96 | contest = Contest(title=request.form.get("title"), 97 | start_time=request.form.get("start_time"), 98 | end_time=request.form.get("end_time"), 99 | holder=user) 100 | 101 | contest.title = request.form.get("title") 102 | contest.start_time = request.form.get("start_time") 103 | contest.end_time = request.form.get("end_time") 104 | contest.information = request.form.get("information") 105 | 106 | try: 107 | problems_list = [int(pid) for pid in request.form.get("problems").split(",")] 108 | except: 109 | problems_list = [] 110 | contest.set_problems(problems_list) 111 | 112 | contest.save() 113 | 114 | return redirect(url_for("contest", contest_id=contest.id)) 115 | else: 116 | return render_template("edit_contest.html", tool=Tools, contest=contest) 117 | -------------------------------------------------------------------------------- /syzoj/views/discussion.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import time 3 | from urllib import parse 4 | 5 | from flask import redirect, url_for, request, render_template, abort 6 | 7 | from syzoj import oj 8 | from syzoj.models import User, Article, Comment 9 | from syzoj.controller import Paginate, Tools 10 | from .common import need_login, not_have_permission, show_error 11 | 12 | 13 | @oj.route("/discussion") 14 | def discussion(): 15 | query = Article.query.order_by(Article.sort_time.desc()) 16 | 17 | def make_url(page, other): 18 | return url_for("discussion") + "?" + parse.urlencode({"page": page}) 19 | 20 | sorter = Paginate(query, make_url=make_url, cur_page=request.args.get("page"), edge_display_num=3, per_page=10) 21 | return render_template("discussion.html", tool=Tools, tab="discussion", sorter=sorter) 22 | 23 | 24 | @oj.route("/article/") 25 | def article(article_id): 26 | article = Article.query.filter_by(id=article_id).first() 27 | if not article: 28 | return show_error("找不到文章", url_for('index')) 29 | 30 | comments = Comment.query.filter_by(article_id=article_id) 31 | comments_num = comments.count() 32 | 33 | def make_url(page, other): 34 | return url_for("article", article_id=article_id) + "?" + parse.urlencode({"page": page}) 35 | sorter = Paginate(comments, make_url=make_url, cur_page=request.args.get("page"), edge_display_num=3, per_page=10) 36 | 37 | return render_template("article.html", tool=Tools, article=article, 38 | comment_num=comments_num, sorter=sorter, tab="discussion") 39 | 40 | 41 | @oj.route("/article//edit", methods=["GET", "POST"]) 42 | def edit_article(article_id): 43 | user = User.get_cur_user() 44 | if not user: 45 | return need_login() 46 | 47 | article = Article.query.filter_by(id=article_id).first() 48 | if article and article.is_allowed_edit(user) == False: 49 | return not_have_permission() 50 | 51 | if request.method == "POST": 52 | if request.form.get("title") == "" or request.form.get("content") == "": 53 | return show_error("Please input title and content", 54 | url_for("edit_article", article_id=article_id)) 55 | if not article: 56 | article = Article(title=request.form.get("title"), content=request.form.get("content"), user=user) 57 | 58 | article.title = request.form.get("title") 59 | article.content = request.form.get("content") 60 | article.tags = request.form.get("tags") 61 | article.update_time = time.time() 62 | article.sort_time = time.time() 63 | article.save() 64 | return redirect(url_for("article", article_id=article.id)) 65 | else: 66 | return render_template("edit_article.html", tool=Tools, article=article, tab="discussion") 67 | 68 | 69 | @oj.route("/article//delete") 70 | def delete_article(article_id): 71 | user = User.get_cur_user() 72 | article = Article.query.filter_by(id=article_id).first() 73 | 74 | if not user: 75 | return need_login() 76 | 77 | if not article: 78 | return show_error("Can't find article", url_for('index')) 79 | 80 | if article and article.is_allowed_edit(user) == False: 81 | return not_have_permission() 82 | 83 | if request.args.get("confirm") == "true": 84 | article = Article.query.filter_by(id=article_id).first() 85 | if article and article.is_allowed_edit(user) == False: 86 | return not_have_permission() 87 | 88 | if article: 89 | article.delete() 90 | return redirect(url_for("discussion")) 91 | else: 92 | return render_template("delete_article.html", tool = Tools, user=user, article=article) 93 | 94 | 95 | @oj.route("/comment//delete") 96 | def delete_comment(comment_id): 97 | user = User.get_cur_user() 98 | comment = Comment.query.filter_by(id=comment_id).first() 99 | 100 | if not user: 101 | return need_login() 102 | if not comment: 103 | return show_error("Can't find comment", url_for('index')) 104 | if comment and comment.is_allowed_edit(user) is False: 105 | return not_have_permission() 106 | if comment: 107 | comment.delete() 108 | return redirect(url_for('article', article_id=comment.article_id)) 109 | 110 | 111 | @oj.route("/article//comment", methods=["POST"]) 112 | def comment_article(article_id): 113 | user = User.get_cur_user() 114 | article = Article.query.filter_by(id=article_id).first() 115 | if not article: 116 | abort(404) 117 | if not user: 118 | return need_login() 119 | 120 | comment_str = request.form.get("comment") 121 | if not comment_str.replace(" ", ""): 122 | return show_error("请输入评论", 123 | next=url_for("article", article_id=article_id)) 124 | 125 | comment = Comment(comment_str, article, user) 126 | comment.save() 127 | 128 | return redirect(url_for("article", article_id=article.id)) 129 | 130 | 131 | @oj.route("/comment//comment", methods=["POST"]) 132 | def comment_comment(comment_id): 133 | user = User.get_cur_user() 134 | comment = Article.query.filter_by(id=comment_id).first() 135 | if not comment: 136 | abort(404) 137 | if not user: 138 | return need_login() 139 | 140 | comment_str = request.form.get("comment") 141 | if not comment_str.replace(" ", ""): 142 | return show_error("请输入评论", 143 | next=url_for("article", article_id=comment.article_id)) 144 | 145 | comment_comment = Comment(comment_str, article, user, comment_id) 146 | comment_comment.save() 147 | 148 | return redirect(url_for("article", article_id=article.id)) 149 | -------------------------------------------------------------------------------- /syzoj/views/judge.py: -------------------------------------------------------------------------------- 1 | from urllib import parse 2 | import json 3 | 4 | from flask import jsonify, redirect, url_for, abort, request, render_template 5 | 6 | from syzoj import oj, db 7 | from syzoj.controller import Tools 8 | from syzoj.models import JudgeState, WaitingJudge, Problem, User, Contest 9 | from syzoj.controller import Paginate 10 | from .common import need_login, not_have_permission, show_error 11 | 12 | 13 | @oj.route("/submit/", methods=["GET", "POST"]) 14 | def submit_code(problem_id): 15 | user = User.get_cur_user() 16 | if not user: 17 | return need_login() 18 | 19 | problem = Problem.query.filter_by(id=problem_id).first() 20 | if not problem: 21 | abort(404) 22 | 23 | if request.method == "POST": 24 | code = request.form.get("code") 25 | language = "C++" 26 | if not code or len(code) <= 0 or len(code) >= 1024 * 100: 27 | return show_error("Please check out your code length.The code should less than 100kb.", 28 | url_for("submit_code", problem_id=problem.id)) 29 | 30 | judge = JudgeState(code=code, user=user, language=language, problem=problem) 31 | 32 | contest_id = request.args.get("contest_id") 33 | if contest_id: 34 | contest = Contest.query.filter_by(id=contest_id).first() 35 | if contest and contest.is_running(): 36 | judge.type = 1 37 | judge.type_info = contest_id 38 | else: 39 | return show_error("Sorry.The contest has been ended.", next=url_for("contest", contest_id=contest.id)) 40 | else: 41 | if not problem.is_allowed_use(user): 42 | return show_error("Sorry.You don't have permission.", next=url_for("index")) 43 | if not problem.is_public: 44 | judge.type = 2 45 | 46 | judge.save() 47 | waiting = WaitingJudge(judge) 48 | waiting.save() 49 | return redirect(url_for("judge_detail", judge_id=judge.id)) 50 | else: 51 | return render_template("submit.html", tool=Tools, problem=problem, tab="judge") 52 | 53 | 54 | @oj.route("/judge_state") 55 | def judge_state(): 56 | query = JudgeState.query.order_by(db.desc(JudgeState.id)) 57 | nickname = request.args.get("submitter") 58 | problem_id = request.args.get("problem_id") 59 | if request.args.get("submitter"): 60 | submitter = User.query.filter_by(nickname=nickname).first() 61 | if submitter: 62 | submitter_id = submitter.id 63 | else: 64 | submitter_id = 0 65 | query = query.filter_by(user_id=submitter_id) 66 | if request.args.get("problem_id"): 67 | query = query.filter_by(problem_id=int(problem_id)) 68 | 69 | def make_url(page, other): 70 | other["page"] = page 71 | return url_for("judge_state") + "?" + parse.urlencode(other) 72 | 73 | if not nickname: 74 | nickname = "" 75 | if not problem_id: 76 | problem_id = "" 77 | sorter = Paginate(query, make_url=make_url, other={"submitter": nickname, "problem_id": problem_id}, 78 | cur_page=request.args.get("page"), edge_display_num=3, per_page=10) 79 | 80 | return render_template("judge_state.html", user=User.get_cur_user(), judges=sorter.get(), tab="judge", 81 | submitter=nickname, problem_id=problem_id, sorter=sorter, encode=parse.urlencode, tool=Tools) 82 | 83 | 84 | @oj.route("/api/waiting_judge", methods=["GET"]) 85 | def get_judge_info(): 86 | session_id = request.args.get('session_id') 87 | if oj.config["JUDGE_TOKEN"] != session_id: 88 | abort(404) 89 | 90 | waiting_judge = WaitingJudge.query.first() 91 | if not waiting_judge: 92 | return jsonify({"have_task": 0}) 93 | judge = waiting_judge.judge 94 | waiting_judge.delete() 95 | 96 | return jsonify({"have_task": 1, 97 | "judge_id": judge.id, 98 | "code": judge.code, 99 | "language": judge.language, 100 | "testdata": judge.problem.testdata.md5, 101 | "time_limit": judge.problem.time_limit, 102 | "memory_limit": judge.problem.memory_limit}) 103 | 104 | 105 | @oj.route("/api/update_judge/", methods=["POST"]) 106 | def update_judge_info(judge_id): 107 | token = request.args.get('session_id') 108 | if oj.config["JUDGE_TOKEN"] != token: 109 | abort(404) 110 | 111 | judge = JudgeState.query.filter_by(id=judge_id).first() 112 | if not judge: 113 | abort(404) 114 | 115 | judge.update_result(json.loads(request.form["result"])) 116 | judge.update_userac_info() 117 | judge.update_related_info() 118 | judge.save() 119 | 120 | return jsonify({"return": 0}) 121 | 122 | 123 | @oj.route("/judge_detail/") 124 | def judge_detail(judge_id): 125 | judge = JudgeState.query.filter_by(id=judge_id).first() 126 | if not judge: 127 | abort(404) 128 | 129 | return render_template("judge_detail.html", judge=judge, user=User.get_cur_user(), tab="judge", tool=Tools) 130 | -------------------------------------------------------------------------------- /syzoj/views/problem.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import importlib 3 | importlib.reload(sys) 4 | 5 | from urllib import parse 6 | 7 | from flask import jsonify, redirect, url_for, abort, request, render_template 8 | 9 | from sqlalchemy import or_ 10 | from syzoj import oj, controller 11 | from syzoj.models import User, Problem, File, FileParser, Notice 12 | from syzoj.controller import Paginate, Tools 13 | from .common import need_login, not_have_permission, show_error 14 | 15 | 16 | @oj.route("/problem") 17 | def problem_set(): 18 | query = Problem.query 19 | problem_title = request.args.get("problem_title") 20 | 21 | if request.args.get("problem_title"): 22 | query = query.filter(or_(Problem.title.like((u"%" + problem_title + u"%")), Problem.tags.like((u"%" + problem_title + u"%")), Problem.id.like((u"%" + problem_title + u"%")))) 23 | else: 24 | problem_title = '' 25 | 26 | def make_url(page, other): 27 | other["page"] = page 28 | return url_for("problem_set") + "?" + parse.urlencode(other) 29 | 30 | sorter = Paginate(query, make_url=make_url, other={"problem_title": problem_title}, 31 | cur_page=request.args.get("page"), edge_display_num=50, per_page=50) 32 | return render_template("problem_set.html", tool=Tools, tab="problem_set", sorter=sorter, problems=sorter.get(), problem_title=problem_title) 33 | 34 | 35 | @oj.route("/problem/") 36 | def problem(problem_id): 37 | user = User.get_cur_user() 38 | 39 | problem = Problem.query.filter_by(id=problem_id).first() 40 | if not problem: 41 | abort(404) 42 | 43 | if problem.is_allowed_use(user) is False: 44 | return not_have_permission() 45 | 46 | return render_template("problem.html", tool=Tools, tab="problem_set", problem=problem) 47 | 48 | 49 | @oj.route("/problem//edit", methods=["GET", "POST"]) 50 | def edit_problem(problem_id): 51 | user = User.get_cur_user() 52 | if not user: 53 | return need_login() 54 | 55 | problem = Problem.query.filter_by(id=problem_id).first() 56 | if problem and problem.is_allowed_edit(user) == False: 57 | return not_have_permission() 58 | 59 | if request.method == "POST": 60 | if not problem: 61 | problem_id = controller.create_problem(user=user, title=request.form.get("title")) 62 | problem = Problem.query.filter_by(id=problem_id).first() 63 | problem.update(title=request.form.get("title"), 64 | description=request.form.get("description"), 65 | input_format=request.form.get("input_format"), 66 | output_format=request.form.get("output_format"), 67 | example=request.form.get("example"), 68 | tags=request.form.get("tags") , 69 | limit_and_hint=request.form.get("limit_and_hint")) 70 | 71 | problem.save() 72 | 73 | return redirect(url_for("problem", problem_id=problem.id)) 74 | else: 75 | return render_template("edit_problem.html", tool=Tools, problem=problem) 76 | 77 | 78 | @oj.route("/problem//star") 79 | def star_problem(problem_id): 80 | user = User.get_cur_user() 81 | problem = Problem.query.filter_by(id=problem_id).first() 82 | if not problem: 83 | abort(404) 84 | problem.stared.append(user) 85 | problem.save() 86 | return redirect(url_for("problem", problem_id=problem_id)) 87 | 88 | 89 | @oj.route("/problem//unstar") 90 | def unstar_problem(problem_id): 91 | user = User.get_cur_user() 92 | problem = Problem.query.filter_by(id=problem_id).first() 93 | if not problem: 94 | abort(404) 95 | problem.stared.remove(user) 96 | problem.save() 97 | return redirect(url_for("problem", problem_id=problem_id)) 98 | 99 | 100 | @oj.route("/problem//upload", methods=["GET", "POST"]) 101 | def upload_testdata(problem_id): 102 | user = User.get_cur_user() 103 | if not user: 104 | return need_login() 105 | 106 | problem = Problem.query.filter_by(id=problem_id).first() 107 | if not problem: 108 | abort(404) 109 | if problem.is_allowed_edit(user) is False: 110 | return not_have_permission() 111 | if request.method == "POST": 112 | file = request.files.get("testdata") 113 | if file: 114 | problem.update_testdata(file) 115 | if request.form.get("time_limit"): 116 | problem.time_limit = int(request.form.get("time_limit")) 117 | if request.form.get("memory_limit"): 118 | problem.memory_limit = int(request.form.get("memory_limit")) 119 | problem.save() 120 | return redirect(url_for("upload_testdata", problem_id=problem_id)) 121 | else: 122 | return render_template("upload_testdata.html", tool=Tools, problem=problem, parse=FileParser.parse_as_testdata) 123 | 124 | 125 | # TODO:Maybe need add the metho of toggle is_public attr to Problem 126 | @oj.route("/api/problem//public", methods=["POST", "DELETE"]) 127 | def change_public_attr(problem_id): 128 | session_id = request.args.get('session_id') 129 | user = User.get_cur_user(session_id=session_id) 130 | problem = Problem.query.filter_by(id=problem_id).first() 131 | if problem and user and user.have_privilege(2): 132 | if request.method == "POST": 133 | problem.is_public = True 134 | elif request.method == "DELETE": 135 | problem.is_public = False 136 | problem.save() 137 | else: 138 | abort(404) 139 | return jsonify({"status": 0}) 140 | -------------------------------------------------------------------------------- /syzoj/views/session.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, render_template 2 | 3 | from syzoj import oj 4 | from syzoj.models import User, Session 5 | from syzoj.controller import Tools 6 | from syzoj import controller 7 | 8 | 9 | @oj.route("/login") 10 | def login(): 11 | return render_template("login.html", tool=Tools) 12 | 13 | 14 | @oj.route("/sign_up") 15 | def sign_up(): 16 | return render_template("sign_up.html", tool=Tools) 17 | 18 | 19 | @oj.route("/api/sign_up", methods=["POST"]) 20 | def api_sign_up(): 21 | username = request.args.get('username') 22 | password = request.args.get('password') 23 | email = request.args.get('email') 24 | error_code = controller.register(username, password, email) 25 | return jsonify({"error_code": error_code}) 26 | 27 | 28 | @oj.route("/api/login", methods=["POST"]) 29 | def api_login(): 30 | error_code = 1 31 | session_id = "???" 32 | username = request.form.get('username') 33 | password = request.form.get('password') 34 | user = User.query.filter_by(username=username).first() 35 | if not user: 36 | error_code = 1001 37 | elif user.password != password: 38 | error_code = 1002 39 | else: 40 | session = Session(user) 41 | session.save() 42 | session_id = session.id 43 | return jsonify({"error_code": error_code, "session_id": session_id}) 44 | 45 | 46 | @oj.route("/api/logout", methods=["POST"]) 47 | def api_logout(): 48 | session_id = request.args.get('session_id') 49 | user = User.get_cur_user(session_id=session_id) 50 | sessions = user.sessions.all() 51 | for s in sessions: 52 | s.delete() 53 | return jsonify({"status": "1"}) 54 | -------------------------------------------------------------------------------- /syzoj/views/user.py: -------------------------------------------------------------------------------- 1 | from flask import redirect, url_for, request, render_template 2 | 3 | from syzoj import oj, db 4 | from syzoj.models import User, Article 5 | from syzoj.controller import Paginate, Tools, Checker 6 | from .common import not_have_permission, show_error 7 | 8 | 9 | @oj.route("/user/") 10 | def user(user_id): 11 | user = User.find_user(id=user_id) 12 | if not user: 13 | return show_error("Can't find user", next=url_for("index")) 14 | 15 | user.refresh_submit_info() 16 | user.save() 17 | 18 | articles = Article.query.filter_by(user_id=user.id).order_by(Article.public_time.desc()).all() 19 | articles_num = len(articles) 20 | stars = user.stars.all() 21 | return render_template("user.html", tool=Tools, shown_user=user, articles_num=articles_num, articles=articles, stars=stars) 22 | 23 | 24 | @oj.route("/user//edit", methods=["GET", "POST"]) 25 | def edit_user(user_id): 26 | edited_user = User.find_user(id=user_id) 27 | if not edited_user: 28 | return show_error("Can't find user", next=url_for("index")) 29 | 30 | user = User.get_cur_user() 31 | if not edited_user.is_allowed_edit(user): 32 | return not_have_permission() 33 | 34 | if request.method == "POST": 35 | email = request.form.get("email") 36 | information = request.form.get("information") 37 | old_password = request.form.get("old_password") 38 | new_password = request.form.get("new_password") 39 | if user.have_privilege(1): 40 | if request.form.get("prb_mng"): 41 | edited_user.give_privilege(2) 42 | else: 43 | edited_user.del_privilege(2) 44 | if request.form.get("vwa_prb"): 45 | edited_user.give_privilege(3) 46 | else: 47 | edited_user.del_privilege(3) 48 | if request.form.get("cts_mng"): 49 | edited_user.give_privilege(4) 50 | else: 51 | edited_user.del_privilege(4) 52 | if request.form.get("cts_bld"): 53 | edited_user.give_privilege(5) 54 | else: 55 | edited_user.del_privilege(5) 56 | if request.form.get("atc_mng"): 57 | edited_user.give_privilege(6) 58 | else: 59 | edited_user.del_privilege(6) 60 | if request.form.get("usr_mng"): 61 | edited_user.give_privilege(7) 62 | else: 63 | edited_user.del_privilege(7) 64 | if request.form.get("dat_dld"): 65 | edited_user.give_privilege(8) 66 | else: 67 | edited_user.del_privilege(8) 68 | 69 | status = 1 70 | if not Checker.is_valid_email(email): 71 | status = 3001 72 | 73 | if information == "": 74 | information = None 75 | 76 | if old_password: 77 | if edited_user.password != old_password: 78 | status = 3002 79 | else: 80 | if Checker.is_valid_password(new_password): 81 | edited_user.password = new_password 82 | else: 83 | status = 3003 84 | 85 | if status == 1: 86 | edited_user.email = email 87 | edited_user.information = information 88 | edited_user.save() 89 | 90 | return render_template("edit_user.html", tool=Tools, edited_user=edited_user, status=status) 91 | else: 92 | return render_template("edit_user.html", tool=Tools, edited_user=edited_user) 93 | 94 | 95 | @oj.route("/ranklist") 96 | def ranklist(): 97 | query = User.query.order_by(db.desc(User.ac_num)) 98 | 99 | def make_url(page, other): 100 | return url_for("ranklist") + "?" + Tools.url_encode({"page": page}) 101 | 102 | sorter = Paginate(query, make_url=make_url, cur_page=request.args.get("page"), edge_display_num=3, per_page=50) 103 | 104 | return render_template("ranklist.html", tool=Tools, sorter=sorter, tab="ranklist") 105 | 106 | 107 | @oj.route("/find_user") 108 | def find_user(): 109 | nickname = request.args.get("nickname") 110 | user = User.find_user(nickname=nickname) 111 | if not user: 112 | return show_error("Can't find " + nickname, url_for("ranklist")) 113 | 114 | return redirect(url_for("user", user_id=user.id)) 115 | -------------------------------------------------------------------------------- /test/test_contest.py: -------------------------------------------------------------------------------- 1 | import syzoj 2 | from syzoj.models.contest import * 3 | from syzoj.models.user import * 4 | import unittest 5 | import time 6 | 7 | 8 | class TestContest(unittest.TestCase): 9 | def setUp(self): 10 | self.holder = User.query.filter_by(username="abc").first() 11 | self.start_time = time.time() 12 | self.contest = Contest("test_con", self.start_time + 2, self.start_time + 5, self.holder) 13 | 14 | def test_set_problems(self): 15 | prob_list = [1, 2, 3] 16 | self.contest.set_problems(prob_list) 17 | self.contest.save() 18 | 19 | probs = self.contest.get_problems() 20 | print probs 21 | self.assertTrue(probs) 22 | 23 | def test_is_running(self): 24 | before = False 25 | running = False 26 | after = False 27 | while True: 28 | ir = self.contest.is_running() 29 | if not before: 30 | if not ir: 31 | before = True 32 | elif not running: 33 | if ir: 34 | running = True 35 | elif not after: 36 | if not ir: 37 | after = True 38 | if time.time() > self.start_time + 6: 39 | break 40 | self.assertTrue(before) 41 | self.assertTrue(running) 42 | self.assertTrue(after) 43 | -------------------------------------------------------------------------------- /test/test_controller.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import syzoj 3 | import hashlib 4 | from random import randint 5 | 6 | 7 | class TestRegister(unittest.TestCase): 8 | def md5_pass(self, password): 9 | md5 = hashlib.md5() 10 | md5.update(password) 11 | return md5.hexdigest() 12 | 13 | def test_register(self): 14 | user = "tester_%d" % randint(1, int(1e9)) 15 | pw = self.md5_pass("123_%d" % randint(1, 100)) 16 | email = "84%d@qq.com" % randint(1, 10000) 17 | print(user, pw, email) 18 | self.assertEqual(syzoj.controller.register(user, pw, email), 1) 19 | 20 | self.assertNotEqual(syzoj.controller.register(user, pw, email), 1) 21 | 22 | def test_multiple_register(self): 23 | rid = randint(1, 10000) 24 | for i in range(1, 2): 25 | pw = self.md5_pass("123_%d_%d" % (rid, i)) 26 | print(i, pw) 27 | self.assertEqual(syzoj.controller.register("hello_%d_%d" % (rid, i), pw, "%d@qq.com" % i), 1) 28 | 29 | 30 | if __name__ == "__main__": 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /test/test_problem.py: -------------------------------------------------------------------------------- 1 | import syzoj 2 | from syzoj.models import Problem 3 | import unittest 4 | 5 | 6 | class TestCreateProblem(unittest.TestCase): 7 | def setUp(self): 8 | user = syzoj.models.User.query.filter_by(id=1).first() 9 | title = u"\u6D4B\u8BD5\u9898\u76EE".encode("utf-8") 10 | pid = syzoj.controller.create_problem(user, title) 11 | print pid 12 | self.problem = Problem.query.filter_by(id=pid).first() 13 | self.assertTrue(pid >= 1) 14 | 15 | def test_edit_problem(self): 16 | des = "Give you two integer A and B.Now please calculate A+B." 17 | hint = "uh...." 18 | self.problem.update(description=des) 19 | self.problem.save() 20 | self.assertEqual(self.problem.description, des) 21 | 22 | self.problem.update(limit_and_hint=hint) 23 | self.problem.save() 24 | self.assertEqual(self.problem.description, des) 25 | 26 | self.problem.is_public = True 27 | self.problem.save() 28 | --------------------------------------------------------------------------------