- Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathProblemFolder.py
More file actions
Latest commit
213 lines (167 loc) · 7.79 KB
/
Copy pathProblemFolder.py
File metadata and controls
213 lines (167 loc) · 7.79 KB
1
2
3
4
5
6
7
8
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
fromProblemimport*
fromTopCoderParserimport*
importos
importjson
importshutil
## naming formats ##
FOLDER_NAME_FORMAT="%d_%s"
JSON_FILE_FORMAT="%s.json"
HTML_FILE_FORMAT="%s.html"
PYTHON_FILE_FORMAT="%s.py"
INIT_FILE_FORMAT="__init__.py"
classProblemFolder(object):
"""The class for TopCoder problem directories, which contain TopCoder
problems.
Has methods for adding and removing Problem objects from the directory.
self.problems is a list of tuples, (path_to_dir, number, name), for each problem.
"""
## init ##
def__init__(self, directory):
"""Creates a new Problem Folder in the given directory.
If problems already exist in that directory, adds them to the object."""
self.loc=directory
# get existing problems
self.problems, broken_problems=load_existing_problems(self.loc)
## public methods ##
defget_problem_numbers(self):
"""Returns a list of all problem numbers in this directory."""
return [x[1] forxinself.problems]
defscrape_and_add_problem(self, n, force=False, opener=None):
"""Given a problem number, scrapes the problem and adds it to the
directory.
If provided with an opener, uses that instead of re-connecting.
If force is True, overwrites any existing Python files for this problem."""
# get problem
problem=scrape_problem(n, opener)
ifproblem:
# save problem
print"Saving files...",
error_msg=self.add_problem(problem, force)
iferror_msg==None:
print"OK"
else:
raiseException(error_msg)
deffind_problem(self, name=None, number=None, path=None):
"""Returns a list of problem tuples (path, number, problem) that match the search query."""
# how many search terms were specified?
rule_num=len([xforxin [name, number, path] ifx!=None])
matches= []
forprobleminself.problems:
score=0
ifpath!=None:
ifproblem[0] ==path:
score+=1
ifnumber!=None:
ifproblem[1] ==number:
score+=1
ifname!=None:
ifproblem[2] ==name:
score+=1
ifscore==rule_num:
matches.append(problem)
returnmatches
defload_problem(self, problem):
"""Takes a tuple (rel_path, number, name) and returns the full problem object."""
returnProblem(problem[0] +os.sep+ (JSON_FILE_FORMAT%problem[2]))
defadd_problem(self, problem, force=False):
"""Adds a new problem to this folder.
If this problem already exists, overwrites the description files (but
not any Python scripts, unless force is True).
"""
# decide where to save this problem
problem_foldername=FOLDER_NAME_FORMAT% (problem[P_PROBLEM_NUMBER], problem[P_PROBLEM_NAME])
target_dir=self.loc+os.sep+problem_foldername
# decide on filenames
json_path=target_dir+os.sep+ (JSON_FILE_FORMAT%problem[P_PROBLEM_NAME])
html_path=target_dir+os.sep+ (HTML_FILE_FORMAT%problem[P_PROBLEM_NAME])
python_path=target_dir+os.sep+ (PYTHON_FILE_FORMAT%problem[P_PROBLEM_DEFINITION]['class'])
init_path=target_dir+os.sep+ (INIT_FILE_FORMAT)
# does this problem directory already exist?
ifnotos.path.isdir(target_dir):
# create folder
os.mkdir(target_dir)
# does this problem already exist?
ifnotself.find_problem(path=target_dir):
# save problem tuple
self.problems.append((target_dir, problem[P_PROBLEM_NUMBER], problem[P_PROBLEM_NAME]))
# save JSON and HTML files, regardless
problem.to_json_file(json_path)
problem.to_html_file(html_path)
# check if python file exists: if it doesn't, create it
ifforceornotos.access(python_path, os.F_OK):
problem.to_python_file(python_path)
# create init file, if it doesn't exist
ifforceornotos.access(init_path, os.F_OK):
open(init_path, 'w').close()
defdel_problem(self, problem):
"""Deletes all problems with the same name and number as the given problem.
Removes the problem directory from the file system, including any files
in it.
Returns the number of problems deleted."""
problems_to_delete=self.find_problem(name=problem[P_PROBLEM_NAME], number=problem[P_PROBLEM_NUMBER])
forpinproblems_to_delete:
# delete entire folder
shutil.rmtree(p[0])
# delete from list
self.problems.remove(p)
returnlen(problems_to_delete)
deftest_problems(self, problems):
"""Given a list of problem tuples (rel_path, number, name), runs the tests
for each problem.
Uses the method provided in the Python file in the problem directory."""
forpinproblems:
# load problem
problem=self.load_problem(p)
print" * Running tests for problem %d: %s *"% (problem[P_PROBLEM_NUMBER], problem[P_PROBLEM_NAME])
# execute python file text in namespace ns
ns= {}
python_filename=p[0] +os.sep+ (PYTHON_FILE_FORMAT%problem[P_PROBLEM_DEFINITION]['class'])
python_file=open(python_filename, 'rU')
execpython_fileinns
python_file.close()
# get method
method=ns[problem[P_PROBLEM_DEFINITION]['method']]
# run tests
problem.test_method(method)
defclean(self):
"""Refreshes and re-scans the directory for problems, as well as re-creating missing files.
The difference between this and __init__ is that this removes all directories that contain
invalid JSON files, as well as recreating HTML files."""
working_problems, broken_problems=load_existing_problems(self.loc)
self.problems= []
# remove broken problems
forpathinbroken_problems:
shutil.rmtree(path)
# recreate missing files
forpath, number, nameinworking_problems:
self.add_problem(Problem(path+os.sep+ (JSON_FILE_FORMAT%name)))
## helper functions ##
defload_existing_problems(directory):
"""Given a directory, finds all problem folders.
Returns two lists:
- the first is a list of all valid problem files in the directory, as a tuple (dir_path, num, problem_name)
- the second is a list of all problem folders that contain an invalid JSON file
e.g. [('./47_WordFilter/problem', 47, 'WordFilter'], ['./48_Name', './49_BadJSON']
A problem directory is valid if there is a single JSON file inside the
directory, in the correct format.
"""
problems= []
broken_problems= []
forroot, dirs, filesinos.walk(directory):
# is there only 1 JSON file?
json_files= [xforxinfilesifos.path.splitext(x)[1].lower() =='.json']
iflen(json_files) ==1:
# load json file
json_path=root+os.sep+json_files[0]
try:
json_file=open(json_path, 'rU')
data=json.load(json_file)
# add to list
problems.append((root, data[P_PROBLEM_NUMBER], data[P_PROBLEM_NAME]))
# close json file
json_file.close()
deldata
exceptValueError, e:
# json file could not be decoded
broken_problems.append(root)
return (problems, broken_problems)