2
# IVLE - Informatics Virtual Learning Environment
3
# Copyright (C) 2007-2008 The University of Melbourne
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.
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.
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
23
# Script to calculate the marks for all students for a particular subject.
24
# Requires root to run.
30
from xml.dom import minidom
37
print >>sys.stderr, "Must run %s as root." % os.path.basename(sys.argv[0])
40
if len(sys.argv) <= 1:
41
print >>sys.stderr, "Usage: %s subject" % os.path.basename(sys.argv[0])
44
# Regex for valid identifiers (subject/worksheet names)
45
re_ident = re.compile("[0-9A-Za-z_]+")
47
# This code copy/edited from www/apps/tutorial/__init__.py
48
def is_valid_subjname(subject):
49
m = re_ident.match(subject)
50
return m is not None and m.end() == len(subject)
54
# Subject names must be valid identifiers
55
if not is_valid_subjname(subject):
56
print >>sys.stderr, "Invalid subject name: %s." % repr(subject)
59
def get_userdata(user):
61
Given a User object, returns a list of strings for the user data which
62
will be part of the output for this user.
63
(This is not marks, it's other user data).
65
last_login = (None if user.last_login is None else
66
user.last_login.strftime("%d/%m/%y"))
67
return [user.studentid, user.login, user.fullname, last_login]
68
userdata_header = ["Student ID", "Login", "Full name", "Last login"]
70
def get_assessable_worksheets(subject):
72
Given a subject name, returns a list of strings - the worksheet names (not
73
primary key IDs) for all assessable worksheets for that subject.
74
May raise Exceptions, which are fatal.
76
# NOTE: This code is copy/edited from
77
# www/apps/tutorial/__init__.py:handle_subject_menu
78
# Should be factored out of there.
80
# Parse the subject description file
81
# The subject directory must have a file "subject.xml" in it,
82
# or it does not exist (raise exception).
84
subjectfile = open(os.path.join(ivle.conf.subjects_base, subject,
87
raise Exception("Subject %s not found." % repr(subject))
89
assessable_worksheets = []
90
# Read in data about the subject
91
subjectdom = minidom.parse(subjectfile)
93
# TEMP: All of this is for a temporary XML format, which will later
95
worksheetsdom = subjectdom.documentElement
96
worksheets = [] # List of string IDs
97
for worksheetdom in worksheetsdom.childNodes:
98
if worksheetdom.nodeType == worksheetdom.ELEMENT_NODE:
99
# (Note: assessable will default to False, unless it is explicitly
101
if worksheetdom.getAttribute("assessable") == "true":
102
assessable_worksheets.append(worksheetdom.getAttribute("id"))
104
return assessable_worksheets
106
def get_marks_header(worksheets):
108
Given a list of strings - the assessable worksheets - returns a new list
109
of strings - the column headings for the marks section of the CSV output.
111
return worksheets + ["Total %", "Mark"]
113
def get_marks_user(subject, worksheet_names, user):
115
Given a subject, a list of strings (the assessable worksheets), and a user
116
object, returns the user's percentage for each worksheet, overall, and
117
their final mark, as a list of strings, in a manner which corresponds to
118
the headings produced by get_marks_header.
120
# NOTE: This code is copy/edited from
121
# www/apps/tutorial/__init__.py:handle_subject_menu
122
# Should be factored out of there.
125
# As we go, calculate the total score for this subject
126
# (Assessable worksheets only, mandatory problems only)
130
for worksheet_name in worksheet_names:
131
worksheet = ivle.database.Worksheet.get_by_name(store,
132
subject, worksheet_name)
133
# We simply ignore optional exercises here
134
mand_done, mand_total, _, _ = (
135
ivle.worksheet.calculate_score(store, user, worksheet))
136
worksheet_pcts.append(float(mand_done) / mand_total)
137
problems_done += mand_done
138
problems_total += mand_total
139
problems_pct = float(problems_done) / problems_total
140
problems_pct_int = (100 * problems_done) / problems_total
141
# XXX Marks calculation (should be abstracted out of here!)
142
# percent / 16, rounded down, with a maximum mark of 5
144
mark = min(problems_pct_int / 16, max_mark)
145
return worksheet_pcts + [problems_pct, mark]
147
def writeuser(subject, worksheets, user, csvfile):
148
userdata = get_userdata(user)
149
marksdata = get_marks_user(subject, worksheets, user)
150
csvfile.writerow(userdata + marksdata)
153
# Get the list of assessable worksheets from the subject.xml file.
154
worksheets = get_assessable_worksheets(subject)
155
store = ivle.database.get_store()
156
except Exception, message:
157
print >>sys.stderr, "Error: " + str(message)
160
# Start writing the CSV file - header
161
csvfile = csv.writer(sys.stdout)
162
csvfile.writerow(userdata_header + get_marks_header(worksheets))
164
for user in store.find(ivle.database.User).order_by(ivle.database.User.login):
165
writeuser(subject, worksheets, user, csvfile)