2
# IVLE - Informatics Virtual Learning Environment
3
# Copyright (C) 2007-2009 The University of Melbourne
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
# Author: Nicholas Chadwick
21
"""Script to upload an exercise file into the database"""
24
import xml.dom.minidom as minidom
26
from ivle.database import Exercise, TestSuite, TestCase, TestSuiteVar, TestCasePart, get_store
28
class XMLMalformedError(Exception):
29
"""Error thrown when encountering malformed data."""
31
def __init__(self, text):
34
def getTextData(element):
35
""" Get the text and cdata inside an element
36
Leading and trailing whitespace are stripped
39
for child in element.childNodes:
40
if child.nodeType == child.CDATA_SECTION_NODE:
42
elif child.nodeType == child.TEXT_NODE:
47
return unicode(data.strip())
49
def add_var(store, var_type, var_name=u"", var_value=u"", arg_no=0):
50
"""Given an var node, parse it into a storm object."""
51
new_var = TestSuiteVar()
52
new_var.var_name = unicode(var_name)
53
new_var.var_value = unicode(var_value)
54
new_var.var_type = unicode(var_type)
55
new_var.arg_no = arg_no
59
def add_test_suite(suite_node, suite_num, store):
60
"""Given a test suite element, get all the cases it contains."""
63
for case_node in suite_node.getElementsByTagName('function'):
65
cases.append(add_test_case(case_node, case_num, store))
68
# stdin - Stdin for the suite - Unique - Text inside element
69
# file - File to add to the filespace - Name - List
70
# var - Variable for the suite - Name/Value - List
71
# arg - Argument to functions - Name/Value - ORDERED List
72
# exception - Allowed exception name - Name - List
73
# function - An actual test case
77
file_nodes = suite_node.getElementsByTagName('file')
78
for file_node in file_nodes:
79
suite_vars.append(add_var(store, 'file', file_node.getAttribute('name')))
82
var_nodes = suite_node.getElementsByTagName('var')
83
for var_node in var_nodes:
84
var_name = var_node.getAttribute('name')
85
var_value = var_node.getAttribute('value')
86
suite_vars.append(add_var(store, 'var', var_name, var_value))
88
# Args need to be numbered as they are found, as this order matters
90
for arg_node in suite_node.getElementsByTagName('arg'):
91
suite_vars.append(add_var(store, 'arg', arg_node.getAttribute('name'),
92
arg_node.getAttribute('value'), arg_num))
95
# Add allowed exceptions
96
exception_nodes = suite_node.getElementsByTagName('exception')
97
for exception_node in exception_nodes:
98
name = exception_node.getAttribute('name')
99
suite_vars.append(add_var(store, 'exception', name))
101
# Can only have 0-1 stdin elements
102
stdin = suite_node.getElementsByTagName('stdin')
104
raise XMLMalformedError('Too many stdin tags found.')
106
stdin = getTextData(stdin[0])
110
new_suite = TestSuite()
111
new_suite.description = unicode(suite_node.getAttribute('name'))
112
new_suite.seq_no = suite_num
113
new_suite.function = unicode(suite_node.getAttribute('function'))
114
new_suite.stdin = unicode(stdin)
115
for testcase in cases:
116
new_suite.test_cases.add(testcase)
117
for var in suite_vars:
118
new_suite.variables.add(var)
122
def add_part(store, part_type, test_type, data, filename=u""):
123
new_part = TestCasePart()
124
new_part.part_type = unicode(part_type)
125
new_part.test_type = unicode(test_type)
126
new_part.data = unicode(data)
127
new_part.filename = unicode(filename)
131
def add_test_case(case_node, case_num, store):
132
"""Given a test case node, parse it int a storm object."""
135
# A function is allowed to contain the following elements
142
allowed_parts = ['stdout', 'stderr', 'result', 'exception', 'file', 'code']
144
for child_node in case_node.childNodes:
145
if child_node.nodeType != child_node.ELEMENT_NODE:
148
if child_node.tagName == 'file':
150
test_type = child_node.getAttribute('type')
151
data = getTextData(child_node)
152
filename = child_node.getAttribute('name')
154
raise XMLMalformedException('file tag must have names')
155
parts.append(add_part(store, element_type, test_type, data,
158
elif child_node.tagName in allowed_parts:
159
part_type = child_node.tagName
160
test_type = child_node.getAttribute('type')
161
data = getTextData(child_node)
162
parts.append(add_part(store, part_type, test_type, data))
164
#Now create the object to hold the data
165
new_test_case = TestCase()
166
new_test_case.passmsg = unicode(case_node.getAttribute(u'pass'))
167
new_test_case.failmsg = unicode(case_node.getAttribute(u'fail'))
168
new_test_case.test_default = unicode(case_node.getAttribute(u'default'))
169
new_test_case.seq_no = case_num
170
store.add(new_test_case)
172
new_test_case.parts.add(part)
175
xmlfile = sys.argv[1]
177
def add_exercise(xmlfile):
179
filedom = minidom.parse(xmlfile)
181
raise Exception('ivle-addexercise: error opening file ' + xmlfile + ': ' + e[1])
183
for child in filedom.childNodes:
184
if child.nodeType == child.ELEMENT_NODE and child.tagName == 'exercise':
187
raise XMLMalformedError('ivle-addexercise: error parsing XML: root node must be "exercise"')
189
exercisename = exercise.getAttribute('name')
190
rows = exercise.getAttribute('rows')
194
partial_solution = None
197
test_suite_nodes = []
198
for child in exercise.childNodes:
199
if child.nodeType != child.ELEMENT_NODE:
201
if child.tagName == 'solution':
202
if solution is not None:
203
raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "solution" nodes')
204
solution = getTextData(child)
205
elif child.tagName == 'include':
206
if include_code is not None:
207
raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "include" nodes')
208
include_code = getTextData(child)
209
elif child.tagName == 'partial':
210
if partial_solution is not None:
211
raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "include" nodes')
212
partial_solution = getTextData(child)
213
elif child.tagName == 'case':
214
test_suite_nodes.append(child)
215
elif child.tagName == 'desc':
216
description = getTextData(child)
219
raise XMLMalformedError("ivle-addexercise: error parsing XML: No solution given")
220
if len(test_suite_nodes) == 0:
221
raise XMLMalformedError("ivle-addexercise: error parsing XML: No Tests Given!")
224
new_exercise = Exercise()
225
new_exercise.id = unicode(xmlfile)
226
new_exercise.name = exercisename
227
new_exercise.num_rows = int(rows)
228
new_exercise.partial = partial_solution
229
new_exercise.solution = solution
230
new_exercise.include = include_code
231
new_exercise.description = description
232
new_exercise.partial = partial_solution
233
store.add(new_exercise)
235
for suite in test_suite_nodes:
236
new_exercise.test_suites.add(add_test_suite(suite, suite_num, store))
239
store.add(new_exercise)
242
xmlfiles = sys.argv[1:]
243
for xmlfile in xmlfiles:
244
print "Adding exercise", xmlfile
246
add_exercise(xmlfile)
247
#except Exception, e:
248
# print "ERROR: Could not add file", xmlfile
249
# print e.stacktrace()