Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
goelp14
GitHub Repository: goelp14/easyctf-iv-problems
Path: blob/master/checker.py
650 views
1
import imp
2
import os
3
import yaml
4
import traceback
5
import sys
6
7
if sys.version_info.major != 3:
8
import subprocess
9
try:
10
subprocess.call(['/usr/bin/env', 'python3'] + sys.argv)
11
except:
12
print >>sys.stderr, "must be run with python 3"
13
finally:
14
sys.exit(1)
15
16
problem_dir = os.path.dirname(os.path.realpath(__file__))
17
problem_names = os.listdir(problem_dir)
18
19
valid = []
20
invalid = dict()
21
22
required_filenames = ["problem.yml", "description.md", "grader.py"]
23
required_keys = ["author", "value", "title", "category"]
24
25
26
def tableprint(rows):
27
# https://stackoverflow.com/a/5910078
28
if len(rows) > 1:
29
headers = ["path", "title", "category", "author", "value", "solution", "hint"]
30
lens = dict()
31
for key in headers:
32
lens.update({
33
key: len(max([x.get(key, "") for x in rows] +
34
[key], key=lambda x: len(str(x))))
35
})
36
formats = []
37
hformats = []
38
for key in headers:
39
if isinstance(rows[0].get(key, ""), int):
40
formats.append("%%%dd" % lens[key])
41
else:
42
formats.append("%%-%ds" % lens[key])
43
hformats.append("%%-%ds" % lens[key])
44
pattern = " | ".join(formats)
45
hpattern = " | ".join(hformats)
46
separator = "-+-".join(["-" * lens[key] for key in headers])
47
print(hpattern % tuple(headers))
48
print(separator)
49
50
for line in rows:
51
print(pattern % tuple(line.get(key, "") for key in headers))
52
elif len(rows) == 1:
53
row = rows[0]
54
hwidth = len(max(row.keys(), key=lambda x: len(x)))
55
for i in range(len(row)):
56
print("%*s = %s" % (hwidth, row.keys()[i], row[i]))
57
58
59
def check_problem(name):
60
folder = os.path.join(problem_dir, name)
61
errors = []
62
missing = []
63
problem = dict(path=name)
64
grader = None
65
for name in required_filenames:
66
fullname = os.path.join(folder, name)
67
if not os.path.exists(fullname):
68
missing.append(name)
69
errors.append("could not locate '{}'".format(name))
70
metadata = None
71
if "problem.yml" not in missing:
72
metadata = yaml.load(open(os.path.join(folder, "problem.yml")))
73
missing_keys = list()
74
for key in required_keys:
75
if metadata.get(key) is None:
76
missing_keys.append(key)
77
errors.append("missing key from problem.yml: '{}'".format(key))
78
try:
79
if metadata.get("threshold") and int(metadata.get("threshold")) > 0:
80
assert metadata.get("weightmap"), "Missing weightmap"
81
except Exception as e:
82
errors.append("weightmap/threshold failure: {}".format(str(e)))
83
if not metadata.get("hint"):
84
problem.update(dict(hint="x"))
85
if not missing_keys:
86
problem.update(dict(
87
author=metadata.get("author"),
88
category=metadata.get("category"),
89
title=metadata.get("title"),
90
value=metadata.get("value"),
91
))
92
if metadata.get("autogen") == True:
93
# need generate function
94
pass
95
if "grader.py" not in missing:
96
grader = imp.new_module("grader")
97
curr = os.getcwd()
98
os.chdir(folder)
99
with open(os.path.join(folder, "grader.py")) as f:
100
data = f.read()
101
programming = metadata.get("programming")
102
if not programming:
103
try:
104
exec(data, grader.__dict__)
105
except Exception as e:
106
errors.append("grader has a syntax error: '{}'".format(e))
107
else:
108
if not hasattr(grader, "grade"):
109
errors.append("grader is missing 'grade' function")
110
os.chdir(curr)
111
112
problem.update(dict(solution="+" if os.path.exists(os.path.join(folder, "solution.txt")) else ""))
113
return problem, errors
114
115
116
if __name__ == "__main__":
117
import sys
118
sort_by = sys.argv[1] if len(sys.argv) > 1 else "value"
119
120
for name in problem_names:
121
if name.startswith("."):
122
continue
123
problem_folder = os.path.join(problem_dir, name)
124
if not os.path.isdir(problem_folder):
125
continue
126
try:
127
problem, errors = check_problem(name)
128
if not errors:
129
valid.append(problem)
130
else:
131
invalid[name] = errors
132
except:
133
invalid[name] = [
134
"error when trying to check problem", traceback.format_exc()]
135
valid.sort(key=lambda p: p.get(sort_by, 0))
136
137
# report
138
139
print("[ Report ]")
140
print()
141
print("valid problems ({}):".format(len(valid)))
142
tableprint(valid)
143
print()
144
print("invalid problems ({}):".format(len(invalid.items())))
145
for name, errors in invalid.items():
146
print(" - {}".format(name))
147
for error in errors:
148
print(" * {}".format(error))
149
150