216 lines
6.3 KiB
Python
216 lines
6.3 KiB
Python
import hashlib
|
|
import imp
|
|
import logger
|
|
import os
|
|
import shutil
|
|
import utils
|
|
|
|
from flask import Blueprint, jsonify, session, request
|
|
from flask import current_app as app
|
|
from werkzeug import secure_filename
|
|
|
|
from models import db, Files, Problems, Solves, Teams
|
|
from decorators import admins_only, api_wrapper, login_required, InternalException, WebException
|
|
|
|
blueprint = Blueprint("problem", __name__)
|
|
|
|
@blueprint.route("/add", methods=["POST"])
|
|
@admins_only
|
|
@api_wrapper
|
|
def problem_add():
|
|
title = request.form["title"]
|
|
category = request.form["category"]
|
|
description = request.form["description"]
|
|
hint = request.form["hint"]
|
|
value = request.form["value"]
|
|
grader_contents = request.form["grader_contents"]
|
|
bonus = request.form["bonus"]
|
|
pid = utils.generate_string()
|
|
while Problems.query.filter_by(pid=pid).first():
|
|
pid = utils.generate_string()
|
|
|
|
title_exist = Problems.query.filter_by(title=title).first()
|
|
if title_exist:
|
|
raise WebException("Problem name already taken.")
|
|
|
|
try:
|
|
exec(grader_contents)
|
|
except Exception, e:
|
|
raise WebException("There is a syntax error in the grader: %s" % e)
|
|
|
|
problem = Problems(pid, title, category, description, value, hint=hint, bonus=bonus)
|
|
db.session.add(problem)
|
|
db.session.commit()
|
|
|
|
files = request.files.getlist("files[]")
|
|
for _file in files:
|
|
filename = secure_filename(_file.filename)
|
|
|
|
if len(filename) == 0:
|
|
continue
|
|
|
|
file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)
|
|
|
|
_file.save(file_path)
|
|
db_file = Files(problem.pid, "/".join(file_path.split("/")[2:]))
|
|
db.session.add(db_file)
|
|
|
|
grader_folder = os.path.join(app.config["GRADER_FOLDER"], pid)
|
|
if not os.path.exists(grader_folder):
|
|
os.makedirs(grader_folder)
|
|
grader_path = os.path.join(grader_folder, "grader.py")
|
|
grader_file = open(grader_path, "w")
|
|
grader_file.write(grader_contents)
|
|
grader_file.close()
|
|
problem.grader = grader_path
|
|
db.session.commit()
|
|
|
|
return { "success": 1, "message": "Success!" }
|
|
|
|
@blueprint.route("/delete", methods=["POST"])
|
|
@admins_only
|
|
@api_wrapper
|
|
def problem_delete():
|
|
pid = request.form["pid"]
|
|
problem = Problems.query.filter_by(pid=pid).first()
|
|
if problem:
|
|
Solves.query.filter_by(pid=pid).delete()
|
|
Problems.query.filter_by(pid=pid).delete()
|
|
grader_folder = "/".join(problem.grader.split("/")[:-1])
|
|
shutil.rmtree(grader_folder)
|
|
db.session.commit()
|
|
return { "success": 1, "message": "Success!" }
|
|
raise WebException("Problem does not exist!")
|
|
|
|
@blueprint.route("/update", methods=["POST"])
|
|
@admins_only
|
|
@api_wrapper
|
|
def problem_update():
|
|
pid = request.form["pid"]
|
|
title = request.form["title"]
|
|
category = request.form["category"]
|
|
description = request.form["description"]
|
|
hint = request.form["hint"]
|
|
value = request.form["value"]
|
|
grader_contents = request.form["grader_contents"]
|
|
bonus = request.form["bonus"]
|
|
try:
|
|
exec(grader_contents)
|
|
except Exception, e:
|
|
raise WebException("There is a syntax error in the grader: %s" % e)
|
|
|
|
problem = Problems.query.filter_by(pid=pid).first()
|
|
if problem:
|
|
problem.title = title
|
|
problem.category = category
|
|
problem.description = description
|
|
problem.hint = hint
|
|
problem.value = value
|
|
problem.bonus = bonus
|
|
|
|
grader = open(problem.grader, "w")
|
|
grader.write(grader_contents)
|
|
grader.close()
|
|
|
|
db.session.add(problem)
|
|
db.session.commit()
|
|
|
|
return { "success": 1, "message": "Success!" }
|
|
raise WebException("Problem does not exist!")
|
|
|
|
@blueprint.route("/submit", methods=["POST"])
|
|
@api_wrapper
|
|
@login_required
|
|
def problem_submit():
|
|
pid = request.form["pid"]
|
|
flag = request.form["flag"]
|
|
tid = session["tid"]
|
|
|
|
problem = Problems.query.filter_by(pid=pid).first()
|
|
team = Teams.query.filter_by(tid=tid).first()
|
|
solved = Solves.query.filter_by(pid=pid, tid=tid, correct=1).first()
|
|
if solved:
|
|
raise WebException("You already solved this problem.")
|
|
|
|
if problem:
|
|
grader = imp.load_source("grader", problem.grader)
|
|
correct, response = grader.grade(flag)
|
|
|
|
solve = Solves(pid, tid, flag, correct)
|
|
db.session.add(solve)
|
|
db.session.commit()
|
|
|
|
if correct:
|
|
# Wait until after the solve has been added to the database before adding bonus
|
|
solves = Solves.query.filter_by(pid=pid, correct=1).count()
|
|
if solves < 4:
|
|
bonus = solves
|
|
else:
|
|
bonus = -1
|
|
solve.bonus = bonus
|
|
db.session.add(solve)
|
|
db.session.commit()
|
|
|
|
logger.log(__name__, "%s has solved %s by submitting %s" % (team.teamname, problem.title, flag), level=logger.WARNING)
|
|
return { "success": 1, "message": response }
|
|
|
|
else:
|
|
logger.log(__name__, "%s has incorrectly submitted %s to %s" % (team.teamname, flag, problem.title), level=logger.WARNING)
|
|
raise WebException(response)
|
|
|
|
else:
|
|
raise WebException("Problem does not exist!")
|
|
|
|
@blueprint.route("/data", methods=["GET"])
|
|
@login_required
|
|
@api_wrapper
|
|
def problem_data():
|
|
problems = Problems.query.order_by(Problems.value).all()
|
|
problems_return = []
|
|
|
|
for problem in problems:
|
|
solves = Solves.query.filter_by(pid=problem.pid, correct=1).count()
|
|
solved = Solves.query.filter_by(pid=problem.pid, tid=session.get("tid", None), correct=1)
|
|
solved = ["Solved", "Unsolved"][solved is None]
|
|
problems_return.append({
|
|
"pid": problem.pid,
|
|
"title": problem.title,
|
|
"category": problem.category,
|
|
"description": problem.description,
|
|
"hint": problem.hint,
|
|
"value": problem.value,
|
|
"solves": solves,
|
|
"solved": solved
|
|
})
|
|
return { "success": 1, "problems": problems_return }
|
|
|
|
def insert_problem(data, force=False):
|
|
with app.app_context():
|
|
if len(list(get_problem(pid=data["pid"]).all())) > 0:
|
|
if force == True:
|
|
_problem = Problems.query.filter_by(pid=data["pid"]).first()
|
|
db.session.delete(_problem)
|
|
db.session.commit()
|
|
else:
|
|
raise InternalException("Problem already exists.")
|
|
|
|
insert = Problems(data["pid"], data["title"], data["category"], data["description"], data["value"])
|
|
if "hint" in data: insert.hint = data["hint"]
|
|
if "autogen" in data: insert.autogen = data["autogen"]
|
|
if "bonus" in data: insert.bonus = data["bonus"]
|
|
if "threshold" in data: insert.threshold = data["threshold"]
|
|
if "weightmap" in data: insert.weightmap = data["weightmap"]
|
|
db.session.add(insert)
|
|
db.session.commit()
|
|
|
|
return True
|
|
|
|
def get_problem(title=None, pid=None):
|
|
match = {}
|
|
if title != None:
|
|
match.update({ "title": title })
|
|
elif pid != None:
|
|
match.update({ "pid": pid })
|
|
with app.app_context():
|
|
result = Problems.query.filter_by(**match)
|
|
return result
|