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

« back to all changes in this revision

Viewing changes to bin/ivle-addexercise

  • Committer: William Grant
  • Date: 2009-01-13 01:36:15 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1123
Merge setup-refactor branch. This completely breaks existing installations;
every path (both filesystem and Python) has changed. Do not upgrade without
knowing what you are doing.

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