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.
31
from xml.dom import minidom
39
print >>sys.stderr, "Must run %s as root." % os.path.basename(sys.argv[0])
42
if len(sys.argv) <= 1:
43
print >>sys.stderr, "Usage: %s subject" % os.path.basename(sys.argv[0])
46
# Regex for valid identifiers (subject/worksheet names)
47
re_ident = re.compile("[0-9A-Za-z_]+")
49
# This code copy/edited from www/apps/tutorial/__init__.py
50
def is_valid_subjname(subject):
51
m = re_ident.match(subject)
52
return m is not None and m.end() == len(subject)
56
# Subject names must be valid identifiers
57
if not is_valid_subjname(subject):
58
print >>sys.stderr, "Invalid subject name: %s." % repr(subject)
61
def get_userdata(user):
63
Given a User object, returns a list of strings for the user data which
64
will be part of the output for this user.
65
(This is not marks, it's other user data).
67
last_login = (None if user.last_login is None else
68
time.strftime("%d/%m/%y", user.last_login))
69
return [user.studentid, user.login, user.fullname, last_login]
70
userdata_header = ["Student ID", "Login", "Full name", "Last login"]
72
def get_assessable_worksheets(subject):
74
Given a subject name, returns a list of strings - the worksheet names (not
75
primary key IDs) for all assessable worksheets for that subject.
76
May raise Exceptions, which are fatal.
78
# NOTE: This code is copy/edited from
79
# www/apps/tutorial/__init__.py:handle_subject_menu
80
# Should be factored out of there.
82
# Parse the subject description file
83
# The subject directory must have a file "subject.xml" in it,
84
# or it does not exist (raise exception).
86
subjectfile = open(os.path.join(ivle.conf.subjects_base, subject,
89
raise Exception("Subject %s not found." % repr(subject))
91
assessable_worksheets = []
92
# Read in data about the subject
93
subjectdom = minidom.parse(subjectfile)
95
# TEMP: All of this is for a temporary XML format, which will later
97
worksheetsdom = subjectdom.documentElement
98
worksheets = [] # List of string IDs
99
for worksheetdom in worksheetsdom.childNodes:
100
if worksheetdom.nodeType == worksheetdom.ELEMENT_NODE:
101
# (Note: assessable will default to False, unless it is explicitly
103
if worksheetdom.getAttribute("assessable") == "true":
104
assessable_worksheets.append(worksheetdom.getAttribute("id"))
106
return assessable_worksheets
108
def get_marks_header(worksheets):
110
Given a list of strings - the assessable worksheets - returns a new list
111
of strings - the column headings for the marks section of the CSV output.
113
return worksheets + ["Total %", "Mark"]
115
def get_marks_user(subject, worksheet_names, user):
117
Given a subject, a list of strings (the assessable worksheets), and a user
118
object, returns the user's percentage for each worksheet, overall, and
119
their final mark, as a list of strings, in a manner which corresponds to
120
the headings produced by get_marks_header.
122
# NOTE: This code is copy/edited from
123
# www/apps/tutorial/__init__.py:handle_subject_menu
124
# Should be factored out of there.
127
# As we go, calculate the total score for this subject
128
# (Assessable worksheets only, mandatory problems only)
132
for worksheet_name in worksheet_names:
133
worksheet = ivle.database.Worksheet.get_by_name(store,
134
subject, worksheet_name)
135
# We simply ignore optional exercises here
136
mand_done, mand_total, _, _ = (
137
ivle.worksheet.calculate_score(store, user, worksheet))
138
worksheet_pcts.append(float(mand_done) / mand_total)
139
problems_done += mand_done
140
problems_total += mand_total
141
problems_pct = float(problems_done) / problems_total
142
problems_pct_int = (100 * problems_done) / problems_total
143
# XXX Marks calculation (should be abstracted out of here!)
144
# percent / 16, rounded down, with a maximum mark of 5
146
mark = min(problems_pct_int / 16, max_mark)
147
return worksheet_pcts + [problems_pct, mark]
149
def writeuser(subject, worksheets, user, csvfile):
150
userdata = get_userdata(user)
151
marksdata = get_marks_user(subject, worksheets, user)
152
csvfile.writerow(userdata + marksdata)
155
# Get the list of assessable worksheets from the subject.xml file,
156
# and the list of all users from the DB.
157
worksheets = get_assessable_worksheets(subject)
159
store = ivle.database.get_store()
160
list = db.get_users()
161
except Exception, message:
162
print >>sys.stderr, "Error: " + str(message)
165
# Start writing the CSV file - header
166
csvfile = csv.writer(sys.stdout)
167
csvfile.writerow(userdata_header + get_marks_header(worksheets))
169
list.sort(key=lambda user: user.login)
171
writeuser(subject, worksheets, user, csvfile)