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

« back to all changes in this revision

Viewing changes to bin/ivle-addexercise

  • Committer: William Grant
  • Date: 2009-02-23 23:47:02 UTC
  • mfrom: (1099.1.211 new-dispatch)
  • Revision ID: grantw@unimelb.edu.au-20090223234702-db4b1llly46ignwo
Merge from lp:~ivle-dev/ivle/new-dispatch.

Pretty much everything changes. Reread the setup docs. Backup your databases.
Every file is now in a different installed location, the configuration system
is rewritten, the dispatch system is rewritten, URLs are different, the
database is different, worksheets and exercises are no longer on the
filesystem, we use a templating engine, jail service protocols are rewritten,
we don't repeat ourselves, we have authorization rewritten, phpBB is gone,
and probably lots of other things that I cannot remember.

This is certainly the biggest commit I have ever made, and hopefully
the largest I ever will.

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