~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to bin/ivle-addexercise

  • Committer: William Grant
  • Date: 2010-02-17 08:57:40 UTC
  • Revision ID: grantw@unimelb.edu.au-20100217085740-3atwitguxi3ukx2l
Add a Projects breadcrumb.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
# IVLE - Informatics Virtual Learning Environment
 
3
# Copyright (C) 2007-2009 The University of Melbourne
 
4
#
 
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.
 
9
#
 
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.
 
14
#
 
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
 
18
 
 
19
# Author:  Nicholas Chadwick
 
20
 
 
21
"""Script to upload an exercise file into the database"""
 
22
 
 
23
import os, sys, traceback
 
24
import xml.dom.minidom as minidom
 
25
 
 
26
from ivle.config import Config
 
27
from ivle.database import Exercise, TestSuite, TestCase, TestSuiteVar, TestCasePart, get_store
 
28
 
 
29
class XMLMalformedError(Exception):
 
30
    """Error thrown when encountering malformed data."""
 
31
    
 
32
    def __init__(self, text):
 
33
        self.msg = text
 
34
 
 
35
def getTextData(element):
 
36
    """ Get the text and cdata inside an element
 
37
    Leading and trailing whitespace are stripped
 
38
    """
 
39
    data = ''
 
40
    for child in element.childNodes:
 
41
        if child.nodeType == child.CDATA_SECTION_NODE:
 
42
            data += child.data
 
43
        elif child.nodeType == child.TEXT_NODE:
 
44
            data += child.data
 
45
        else:
 
46
            data += child.toxml()
 
47
 
 
48
    return unicode(data.strip())
 
49
 
 
50
def add_var(store, var_type, var_name=u"", var_value=u"", arg_no=0):
 
51
    """Given an var node, parse it into a storm object."""
 
52
    new_var = TestSuiteVar()
 
53
    new_var.var_name = unicode(var_name)
 
54
    new_var.var_value = unicode(var_value)
 
55
    new_var.var_type = unicode(var_type)
 
56
    new_var.arg_no = arg_no
 
57
    store.add(new_var)
 
58
    return new_var
 
59
 
 
60
def add_test_suite(suite_node, suite_num, store):
 
61
    """Given a test suite element, get all the cases it contains."""
 
62
    cases = []
 
63
    case_num = 0
 
64
    for case_node in suite_node.getElementsByTagName('function'):
 
65
        cases.append(add_test_case(case_node, case_num, store))
 
66
        case_num += 1
 
67
 
 
68
    ## ALLOWED TAGS ##
 
69
    # stdin     - Stdin for the suite - Unique - Text inside element
 
70
    # file      - File to add to the filespace - Name - List
 
71
    # var       - Variable for the suite - Name/Value - List
 
72
    # arg       - Argument to functions - Name/Value - ORDERED List
 
73
    # exception - Allowed exception name - Name - List
 
74
    # function  - An actual test case
 
75
    suite_vars = []
 
76
    
 
77
    # Add file nodes
 
78
    file_nodes = suite_node.getElementsByTagName('file')
 
79
    for file_node in file_nodes:
 
80
        suite_vars.append(add_var(store, 'file', file_node.getAttribute('name')))
 
81
    
 
82
    # Add vars
 
83
    var_nodes = suite_node.getElementsByTagName('var')
 
84
    for var_node in var_nodes:
 
85
        var_name = var_node.getAttribute('name')
 
86
        var_value = var_node.getAttribute('value')
 
87
        suite_vars.append(add_var(store, 'var', var_name, var_value))
 
88
    
 
89
    # Args need to be numbered as they are found, as this order matters
 
90
    arg_num = 0
 
91
    for arg_node in suite_node.getElementsByTagName('arg'):
 
92
        suite_vars.append(add_var(store, 'arg', arg_node.getAttribute('name'),
 
93
                          arg_node.getAttribute('value'), arg_num))
 
94
        arg_num += 1
 
95
         
 
96
    # Add allowed exceptions
 
97
    exception_nodes = suite_node.getElementsByTagName('exception')
 
98
    for exception_node in exception_nodes:
 
99
        name = exception_node.getAttribute('name')
 
100
        suite_vars.append(add_var(store, 'exception', name))
 
101
    
 
102
    # Can only have 0-1 stdin elements
 
103
    stdin = suite_node.getElementsByTagName('stdin')
 
104
    if len(stdin) > 1:
 
105
        raise XMLMalformedError('Too many stdin tags found.')
 
106
    if stdin:
 
107
        stdin = getTextData(stdin[0])
 
108
    else:
 
109
        stdin = ""
 
110
    
 
111
    new_suite = TestSuite()
 
112
    new_suite.description = unicode(suite_node.getAttribute('name'))
 
113
    new_suite.seq_no = suite_num
 
114
    new_suite.function = unicode(suite_node.getAttribute('function'))
 
115
    new_suite.stdin = unicode(stdin)
 
116
    for testcase in cases:
 
117
        new_suite.test_cases.add(testcase)
 
118
    for var in suite_vars:
 
119
        new_suite.variables.add(var)
 
120
    store.add(new_suite)
 
121
    return new_suite
 
122
 
 
123
def add_part(store, part_type, test_type, data, filename=u""):
 
124
    new_part = TestCasePart()
 
125
    new_part.part_type = unicode(part_type)
 
126
    new_part.test_type = unicode(test_type)
 
127
    new_part.data = unicode(data)
 
128
    new_part.filename = unicode(filename)
 
129
    store.add(new_part)
 
130
    return new_part
 
131
 
 
132
def add_test_case(case_node, case_num, store):
 
133
    """Given a test case node, parse it int a storm object."""
 
134
    
 
135
    ## ALLOWED TAGS ##
 
136
    # A function is allowed to contain the following elements 
 
137
    # stdout
 
138
    # stderr
 
139
    # result
 
140
    # exception
 
141
    # file
 
142
    # code
 
143
    allowed_parts = ['stdout', 'stderr', 'result', 'exception', 'file', 'code']
 
144
    parts = []
 
145
    for child_node in case_node.childNodes:
 
146
        if child_node.nodeType != child_node.ELEMENT_NODE:
 
147
            continue
 
148
        
 
149
        if child_node.tagName == 'file':
 
150
            part_type = 'file'
 
151
            test_type = child_node.getAttribute('type')
 
152
            data = getTextData(child_node)
 
153
            filename = child_node.getAttribute('name')
 
154
            if filename == "":
 
155
                raise XMLMalformedException('file tag must have names')
 
156
            parts.append(add_part(store, part_type, test_type, data,
 
157
                                        filename))
 
158
            
 
159
        elif child_node.tagName in allowed_parts:
 
160
            part_type = child_node.tagName
 
161
            test_type = child_node.getAttribute('type')
 
162
            data = getTextData(child_node)    
 
163
            parts.append(add_part(store, part_type, test_type, data))
 
164
 
 
165
    #Now create the object to hold the data
 
166
    new_test_case = TestCase()
 
167
    new_test_case.passmsg = unicode(case_node.getAttribute(u'pass'))
 
168
    new_test_case.failmsg = unicode(case_node.getAttribute(u'fail'))
 
169
    new_test_case.test_default = unicode(case_node.getAttribute(u'default'))
 
170
    new_test_case.seq_no = case_num
 
171
    store.add(new_test_case)
 
172
    for part in parts:
 
173
        new_test_case.parts.add(part)
 
174
    return new_test_case
 
175
 
 
176
xmlfile = sys.argv[1]
 
177
 
 
178
def add_exercise(xmlfile):
 
179
    # Skip existing ones.
 
180
    if store.find(Exercise, id=unicode(xmlfile)).count():
 
181
        return
 
182
 
 
183
    print "Adding exercise", xmlfile
 
184
    try:
 
185
        filedom = minidom.parse(xmlfile)
 
186
    except IOError, e:
 
187
        raise Exception('ivle-addexercise: error opening file ' + xmlfile + ': ' + e[1])
 
188
 
 
189
    for child in filedom.childNodes:
 
190
        if child.nodeType == child.ELEMENT_NODE and child.tagName == 'exercise':
 
191
            exercise = child
 
192
        else:
 
193
            raise XMLMalformedError('ivle-addexercise: error parsing XML: root node must be "exercise"')
 
194
 
 
195
    exercisename = exercise.getAttribute('name')
 
196
    rows = exercise.getAttribute('rows')
 
197
    if rows == '':
 
198
        rows = 4
 
199
    solution = None
 
200
    partial_solution = None
 
201
    include_code = None
 
202
    description = None
 
203
    test_suite_nodes = []
 
204
    for child in exercise.childNodes:
 
205
        if child.nodeType != child.ELEMENT_NODE:
 
206
            continue
 
207
        if child.tagName == 'solution':
 
208
            if solution is not None:
 
209
                raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "solution" nodes')
 
210
            solution = getTextData(child)
 
211
        elif child.tagName == 'include':
 
212
            if include_code is not None:
 
213
                raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "include" nodes')
 
214
            include_code = getTextData(child)
 
215
        elif child.tagName == 'partial':
 
216
            if partial_solution is not None:
 
217
                raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "include" nodes')
 
218
            partial_solution = getTextData(child)
 
219
        elif child.tagName == 'case':
 
220
            test_suite_nodes.append(child)
 
221
        elif child.tagName == 'desc':
 
222
            description = getTextData(child)
 
223
 
 
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)
 
234
    suite_num = 0
 
235
    for suite in test_suite_nodes:
 
236
        new_exercise.test_suites.add(add_test_suite(suite, suite_num, store))
 
237
        suite_num += 1
 
238
 
 
239
    store.add(new_exercise)
 
240
    store.commit()
 
241
 
 
242
store = get_store(Config())
 
243
 
 
244
xmlfiles = sys.argv[1:]
 
245
for xmlfile in xmlfiles:
 
246
    try:
 
247
        add_exercise(xmlfile)
 
248
    except Exception, e:
 
249
        print "ERROR: Could not add file", xmlfile