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

1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
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
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
26
from ivle.database import Exercise, TestSuite, TestCase, TestSuiteVar, TestCasePart, get_store
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
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
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
42
        elif child.nodeType == child.TEXT_NODE:
43
            data += child.data
44
        else:
45
            data += child.toxml()
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
46
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
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
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
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))
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
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
    
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
110
    new_suite = TestSuite()
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
111
    new_suite.description = unicode(suite_node.getAttribute('name'))
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
112
    new_suite.seq_no = suite_num
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
113
    new_suite.function = unicode(suite_node.getAttribute('function'))
114
    new_suite.stdin = unicode(stdin)
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
115
    for testcase in cases:
116
        new_suite.test_cases.add(testcase)
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
117
    for var in suite_vars:
118
        new_suite.variables.add(var)
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
119
    store.add(new_suite)
120
    return new_suite
121
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
122
def add_part(store, part_type, test_type, data, filename=u""):
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
123
    new_part = TestCasePart()
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
124
    new_part.part_type = unicode(part_type)
125
    new_part.test_type = unicode(test_type)
126
    new_part.data = unicode(data)
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
127
    new_part.filename = unicode(filename)
128
    store.add(new_part)
129
    return new_part
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
130
131
def add_test_case(case_node, case_num, store):
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
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')
1099.1.186 by Nick Chadwick
fixed a slight bug on line 155, which referenced a non-existant variable
155
            parts.append(add_part(store, part_type, test_type, data,
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
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
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
164
    #Now create the object to hold the data
165
    new_test_case = TestCase()
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
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'))
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
169
    new_test_case.seq_no = case_num
170
    store.add(new_test_case)
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
171
    for part in parts:
172
        new_test_case.parts.add(part)
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
173
    return new_test_case
174
175
xmlfile = sys.argv[1]
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
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
1099.1.187 by Nick Chadwick
Modified ivle-addexercise. It will now (once more) attempt to add every
245
    try:
246
        add_exercise(xmlfile)
247
    except Exception, e:
248
        print "ERROR: Could not add file", xmlfile
249
        print e.stacktrace()