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

« back to all changes in this revision

Viewing changes to bin/ivle-addexercise

Add support for public mode views to the new framework.

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, element_type, test_type, data, filename=u""):
 
123
    new_part = TestCasePart()
 
124
    new_part.element_type = element_type
 
125
    new_part.test_type = test_type
 
126
    new_part.data = 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
try:
 
177
    filedom = minidom.parse(xmlfile)
 
178
except IOError, e:
 
179
    sys.exit('ivle-addexercise: error opening file ' + xmlfile + ': ' + e[1])
 
180
 
 
181
 
 
182
for child in filedom.childNodes:
 
183
    if child.nodeType == child.ELEMENT_NODE and child.tagName == 'exercise':
 
184
        exercise = child
 
185
    else:
 
186
        sys.exit('ivle-addexercise: error parsing XML: root node must be "exercise"')
 
187
 
 
188
exercisename = exercise.getAttribute('name')
 
189
rows = exercise.getAttribute('rows')
 
190
solution = None
 
191
partial_solution = None
 
192
include_code = None
 
193
description = None
 
194
test_suite_nodes = []
 
195
for child in exercise.childNodes:
 
196
    if child.nodeType != child.ELEMENT_NODE:
 
197
        continue
 
198
    if child.tagName == 'solution':
 
199
        if solution is not None:
 
200
            sys.exit('ivle-addexercise: error parsing XML: multiple "solution" nodes')
 
201
        solution = getTextData(child)
 
202
    elif child.tagName == 'include':
 
203
        if include_code is not None:
 
204
            sys.exit('ivle-addexercise: error parsing XML: multiple "include" nodes')
 
205
        include_code = getTextData(child)
 
206
    elif child.tagName == 'partial':
 
207
        if partial_solution is not None:
 
208
            sys.exit('ivle-addexercise: error parsing XML: multiple "include" nodes')
 
209
        partial_solution = getTextData(child)
 
210
    elif child.tagName == 'case':
 
211
        test_suite_nodes.append(child)
 
212
    elif child.tagName == 'desc':
 
213
        description = getTextData(child)
 
214
 
 
215
if solution is None:
 
216
    sys.exit("ivle-addexercise: error parsing XML: No solution given")
 
217
if len(test_suite_nodes) == 0:
 
218
    sys.exit("ivle-addexercise: error parsing XML:")
 
219
 
 
220
store = get_store()
 
221
new_exercise = Exercise()
 
222
new_exercise.id = unicode(xmlfile)
 
223
new_exercise.name = exercisename
 
224
new_exercise.num_rows = int(rows)
 
225
new_exercise.partial = partial_solution
 
226
new_exercise.solution = solution
 
227
new_exercise.include = include_code
 
228
new_exercise.description = description
 
229
new_exercise.partial = partial_solution
 
230
store.add(new_exercise)
 
231
suite_num = 0
 
232
for suite in test_suite_nodes:
 
233
    new_exercise.test_suites.add(add_test_suite(suite, suite_num, store))
 
234
    suite_num += 1
 
235
 
 
236
store.add(new_exercise)
 
237
store.commit()