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

« back to all changes in this revision

Viewing changes to bin/ivle-addexercise

Dispatch now generates an index for each plugin type, allowing plugins to
be written which are aware of other plugins, and other plugin types.

All view plugins now subclass from ivle.webapp.base.plugins.ViewPlugin,
as opposed to subclassing BasePlugin directly. This will allow us to
easily re-write console as an OverlayPlugin, and allow future new
plugins types to be created.

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.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, part_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
 
    store = get_store()
179
 
 
180
 
    # Skip existing ones.
181
 
    if store.find(Exercise, id=unicode(xmlfile)).count():
182
 
        return
183
 
 
184
 
    print "Adding exercise", xmlfile
185
 
    try:
186
 
        filedom = minidom.parse(xmlfile)
187
 
    except IOError, e:
188
 
        raise Exception('ivle-addexercise: error opening file ' + xmlfile + ': ' + e[1])
189
 
 
190
 
    for child in filedom.childNodes:
191
 
        if child.nodeType == child.ELEMENT_NODE and child.tagName == 'exercise':
192
 
            exercise = child
193
 
        else:
194
 
            raise XMLMalformedError('ivle-addexercise: error parsing XML: root node must be "exercise"')
195
 
 
196
 
    exercisename = exercise.getAttribute('name')
197
 
    rows = exercise.getAttribute('rows')
198
 
    if rows == '':
199
 
        rows = 4
200
 
    solution = None
201
 
    partial_solution = None
202
 
    include_code = None
203
 
    description = None
204
 
    test_suite_nodes = []
205
 
    for child in exercise.childNodes:
206
 
        if child.nodeType != child.ELEMENT_NODE:
207
 
            continue
208
 
        if child.tagName == 'solution':
209
 
            if solution is not None:
210
 
                raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "solution" nodes')
211
 
            solution = getTextData(child)
212
 
        elif child.tagName == 'include':
213
 
            if include_code is not None:
214
 
                raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "include" nodes')
215
 
            include_code = getTextData(child)
216
 
        elif child.tagName == 'partial':
217
 
            if partial_solution is not None:
218
 
                raise XMLMalformedError('ivle-addexercise: error parsing XML: multiple "include" nodes')
219
 
            partial_solution = getTextData(child)
220
 
        elif child.tagName == 'case':
221
 
            test_suite_nodes.append(child)
222
 
        elif child.tagName == 'desc':
223
 
            description = getTextData(child)
224
 
 
225
 
    new_exercise = Exercise()
226
 
    new_exercise.id = unicode(xmlfile)
227
 
    new_exercise.name = exercisename
228
 
    new_exercise.num_rows = int(rows)
229
 
    new_exercise.partial = partial_solution
230
 
    new_exercise.solution = solution
231
 
    new_exercise.include = include_code
232
 
    new_exercise.description = description
233
 
    new_exercise.partial = partial_solution
234
 
    store.add(new_exercise)
235
 
    suite_num = 0
236
 
    for suite in test_suite_nodes:
237
 
        new_exercise.test_suites.add(add_test_suite(suite, suite_num, store))
238
 
        suite_num += 1
239
 
 
240
 
    store.add(new_exercise)
241
 
    store.commit()
242
 
    
243
 
xmlfiles = sys.argv[1:]
244
 
for xmlfile in xmlfiles:
245
 
    try:
246
 
        add_exercise(xmlfile)
247
 
    except Exception, e:
248
 
        print "ERROR: Could not add file", xmlfile