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"""
23
import os, sys, traceback
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, part_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)
248
print "ERROR: Could not add file", xmlfile