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

« back to all changes in this revision

Viewing changes to bin/ivle-addexercise

Modified the database so that exercises are now stored in the database, rather
than in flat files.

This also necessitated adding new tables and storm classes for test suites
and test cases.

Note that this commit merely changes the database and adds a script to
upload exercises. The code for actually reading exercises has yet
to be changed.

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, 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
        if child.nodeType == child.TEXT_NODE:
 
43
            data += child.data
 
44
 
 
45
    return data.strip()
 
46
 
 
47
def add_test_suite(suite_node, suite_num, store):
 
48
    """Given a test suite element, get all the cases it contains."""
 
49
    cases = []
 
50
    case_num = 0
 
51
    for case_node in suite_node.getElementsByTagName('function'):
 
52
        case_num += 1
 
53
        cases.append(add_test_case(case_node, case_num, store))
 
54
    new_suite = TestSuite()
 
55
    new_suite.description = suite_node.getAttribute('name')
 
56
    new_suite.seq_no = suite_num
 
57
    for testcase in cases:
 
58
        new_suite.test_cases.add(testcase)
 
59
    store.add(new_suite)
 
60
    return new_suite
 
61
 
 
62
 
 
63
def add_test_case(case_node, case_num, store):
 
64
    """Given a test case, generate its data"""
 
65
    # First perform basic error checks
 
66
    if not case_node.tagName == 'function': 
 
67
        raise XMLMalformedError(case_node.tagName + ' node')
 
68
    case_data = case_node.getElementsByTagName('code')
 
69
    if not case_data:
 
70
        case_data = case_node.getElementsByTagName('stdout')
 
71
    if not case_data:
 
72
        raise XMLMalformedError()
 
73
    case_data = case_data[0]
 
74
    #Now create the object to hold the data
 
75
    new_test_case = TestCase()
 
76
    new_test_case.passmsg = case_node.getAttribute('pass')
 
77
    new_test_case.failmsg = case_node.getAttribute('fail')
 
78
    new_test_case.init = case_node.getAttribute('default')
 
79
    new_test_case.code_type = case_data.getAttribute('type')
 
80
    new_test_case.code = getTextData(case_data)
 
81
    new_test_case.testtype = case_data.tagName
 
82
    new_test_case.seq_no = case_num
 
83
    store.add(new_test_case)
 
84
    return new_test_case
 
85
 
 
86
xmlfile = sys.argv[1]
 
87
try:
 
88
    filedom = minidom.parse(xmlfile)
 
89
except IOError, e:
 
90
    sys.exit('ivle-addexercise: error opening file ' + xmlfile + ': ' + e[1])
 
91
 
 
92
 
 
93
for child in filedom.childNodes:
 
94
    if child.nodeType == child.ELEMENT_NODE and child.tagName == 'exercise':
 
95
        exercise = child
 
96
    else:
 
97
        sys.exit('ivle-addexercise: error parsing XML: root node must be "exercise"')
 
98
 
 
99
exercisename = exercise.getAttribute('name')
 
100
solution = None
 
101
partial_solution = None
 
102
include_code = None
 
103
description = None
 
104
test_suite_nodes = []
 
105
for child in exercise.childNodes:
 
106
    if child.nodeType != child.ELEMENT_NODE:
 
107
        continue
 
108
    if child.tagName == 'solution':
 
109
        if solution is not None:
 
110
            sys.exit('ivle-addexercise: error parsing XML: multiple "solution" nodes')
 
111
        solution = getTextData(child)
 
112
    elif child.tagName == 'include':
 
113
        if include_code is not None:
 
114
            sys.exit('ivle-addexercise: error parsing XML: multiple "include" nodes')
 
115
        include_code = getTextData(child)
 
116
    elif child.tagName == 'partial':
 
117
        if partial_solution is not None:
 
118
            sys.exit('ivle-addexercise: error parsing XML: multiple "include" nodes')
 
119
        partial_solution = getTextData(child)
 
120
    elif child.tagName == 'case':
 
121
        test_suite_nodes.append(child)
 
122
    elif child.tagName == 'desc':
 
123
        description = getTextData(child)
 
124
 
 
125
if solution is None:
 
126
    sys.exit("ivle-addexercise: error parsing XML: No solution given")
 
127
if len(test_suite_nodes) == 0:
 
128
    sys.exit("ivle-addexercise: error parsing XML:")
 
129
 
 
130
store = get_store()
 
131
new_exercise = Exercise()
 
132
new_exercise.id = unicode(xmlfile)
 
133
new_exercise.name = exercisename
 
134
new_exercise.partial = partial_solution
 
135
new_exercise.solution = solution
 
136
new_exercise.include = include_code
 
137
new_exercise.description = description
 
138
new_exercise.partial = partial_solution
 
139
store.add(new_exercise)
 
140
suite_num = 0
 
141
for suite in test_suite_nodes:
 
142
    new_exercise.test_suites.add(add_test_suite(suite, suite_num, store))
 
143
    suite_num += 1
 
144
 
 
145
store.add(new_exercise)
 
146
store.commit()