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

« back to all changes in this revision

Viewing changes to bin/ivle-addexercise

Fix a reference typo in ivle.database.

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
 
24
import xml.dom.minidom as minidom
 
25
 
 
26
from ivle.database import Exercise, TestSuite, TestCase, TestSuiteVar, TestCasePart, get_store
 
27
 
 
28
class XMLMalformedError(Exception):
 
29
    """Error thrown when encountering malformed data."""
 
30
    
 
31
    def __init__(self, text):
 
32
        self.msg = text
 
33
 
 
34
def getTextData(element):
 
35
    """ Get the text and cdata inside an element
 
36
    Leading and trailing whitespace are stripped
 
37
    """
 
38
    data = ''
 
39
    for child in element.childNodes:
 
40
        if child.nodeType == child.CDATA_SECTION_NODE:
 
41
            data += child.data
 
42
        elif child.nodeType == child.TEXT_NODE:
 
43
            data += child.data
 
44
        else:
 
45
            data += child.toxml()
 
46
 
 
47
    return unicode(data.strip())
 
48
 
 
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
 
56
    store.add(new_var)
 
57
    return new_var
 
58
 
 
59
def add_test_suite(suite_node, suite_num, store):
 
60
    """Given a test suite element, get all the cases it contains."""
 
61
    cases = []
 
62
    case_num = 0
 
63
    for case_node in suite_node.getElementsByTagName('function'):
 
64
        case_num += 1
 
65
        cases.append(add_test_case(case_node, case_num, store))
 
66
 
 
67
    ## ALLOWED TAGS ##
 
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
 
74
    suite_vars = []
 
75
    
 
76
    # Add file nodes
 
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')))
 
80
    
 
81
    # Add vars
 
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))
 
87
    
 
88
    # Args need to be numbered as they are found, as this order matters
 
89
    arg_num = 0
 
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))
 
93
        arg_num += 1
 
94
         
 
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))
 
100
    
 
101
    # Can only have 0-1 stdin elements
 
102
    stdin = suite_node.getElementsByTagName('stdin')
 
103
    if len(stdin) > 1:
 
104
        raise XMLMalformedError('Too many stdin tags found.')
 
105
    if stdin:
 
106
        stdin = getTextData(stdin[0])
 
107
    else:
 
108
        stdin = ""
 
109
    
 
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)
 
119
    store.add(new_suite)
 
120
    return new_suite
 
121
 
 
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)
 
128
    store.add(new_part)
 
129
    return new_part
 
130
 
 
131
def add_test_case(case_node, case_num, store):
 
132
    """Given a test case node, parse it int a storm object."""
 
133
    
 
134
    ## ALLOWED TAGS ##
 
135
    # A function is allowed to contain the following elements 
 
136
    # stdout
 
137
    # stderr
 
138
    # result
 
139
    # exception
 
140
    # file
 
141
    # code
 
142
    allowed_parts = ['stdout', 'stderr', 'result', 'exception', 'file', 'code']
 
143
    parts = []
 
144
    for child_node in case_node.childNodes:
 
145
        if child_node.nodeType != child_node.ELEMENT_NODE:
 
146
            continue
 
147
        
 
148
        if child_node.tagName == 'file':
 
149
            part_type = 'file'
 
150
            test_type = child_node.getAttribute('type')
 
151
            data = getTextData(child_node)
 
152
            filename = child_node.getAttribute('name')
 
153
            if filename == "":
 
154
                raise XMLMalformedException('file tag must have names')
 
155
            parts.append(add_part(store, element_type, test_type, data,
 
156
                                        filename))
 
157
            
 
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))
 
163
 
 
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)
 
171
    for part in parts:
 
172
        new_test_case.parts.add(part)
 
173
    return new_test_case
 
174
 
 
175
xmlfile = sys.argv[1]
 
176
 
 
177
def add_exercise(xmlfile):
 
178
    try:
 
179
        filedom = minidom.parse(xmlfile)
 
180
    except IOError, e:
 
181
        raise Exception('ivle-addexercise: error opening file ' + xmlfile + ': ' + e[1])
 
182
 
 
183
    for child in filedom.childNodes:
 
184
        if child.nodeType == child.ELEMENT_NODE and child.tagName == 'exercise':
 
185
            exercise = child
 
186
        else:
 
187
            raise XMLMalformedError('ivle-addexercise: error parsing XML: root node must be "exercise"')
 
188
 
 
189
    exercisename = exercise.getAttribute('name')
 
190
    rows = exercise.getAttribute('rows')
 
191
    if rows == '':
 
192
        rows = 4
 
193
    solution = None
 
194
    partial_solution = None
 
195
    include_code = None
 
196
    description = None
 
197
    test_suite_nodes = []
 
198
    for child in exercise.childNodes:
 
199
        if child.nodeType != child.ELEMENT_NODE:
 
200
            continue
 
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)
 
217
 
 
218
    if solution is None:
 
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!")
 
222
 
 
223
    store = get_store()
 
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
xmlfiles = sys.argv[1:]
 
243
for xmlfile in xmlfiles:
 
244
    print "Adding exercise", xmlfile
 
245
    #try:
 
246
    add_exercise(xmlfile)
 
247
    #except Exception, e:
 
248
    #    print "ERROR: Could not add file", xmlfile
 
249
    #    print e.stacktrace()