2
# IVLE - Informatics Virtual Learning Environment
3
# Copyright (C) 2007-2009 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
22
# Script to calculate the marks for all students for a particular subject.
23
# Requires root to run.
30
from xml.dom import minidom
33
print >>sys.stderr, "Must run %s as root." % os.path.basename(sys.argv[0])
37
import ivle.worksheet.utils
40
def get_userdata(user):
42
Given a User object, returns a list of strings for the user data which
43
will be part of the output for this user.
44
(This is not marks, it's other user data).
46
last_login = (None if user.last_login is None else
47
user.last_login.strftime("%d/%m/%y"))
48
return [user.studentid, user.login, user.fullname, last_login]
50
userdata_header = ["Student ID", "Login", "Full name", "Last login"]
51
def get_header(worksheets):
53
Given a list of Worksheet objects (the assessable worksheets), returns a
54
list of strings -- the column headings for the marks section of the CSV
57
return (userdata_header + [ws.name for ws in worksheets]
58
+ ["Total %", "Mark"])
60
def get_marks_user(worksheets, user, as_of=None):
61
"""Gets marks for a particular user for a particular set of worksheets.
62
@param worksheets: List of Worksheet objects to get marks for.
63
@param user: User to get marks for.
64
@param as_of: Optional datetime. If supplied, gets the marks as of as_of.
65
@returns: The user's percentage for each worksheet, overall, and
66
their final mark, as a list of strings, in a manner which corresponds to
67
the headings produced by get_marks_header.
70
# As we go, calculate the total score for this subject
71
# (Assessable worksheets only, mandatory problems only)
75
for worksheet in worksheets:
76
# We simply ignore optional exercises here
77
mand_done, mand_total, _, _ = (
78
ivle.worksheet.utils.calculate_score(store, user, worksheet,
81
worksheet_pcts.append(float(mand_done) / mand_total)
83
# Avoid Div0, just give everyone 0 marks if there are none
84
worksheet_pcts.append(0.0)
85
problems_done += mand_done
86
problems_total += mand_total
88
ivle.worksheet.utils.calculate_mark(problems_done, problems_total))
89
return worksheet_pcts + [float(percent)/100, mark]
91
def writeuser(worksheets, user, csvfile, cutoff=None):
92
userdata = get_userdata(user)
93
marksdata = get_marks_user(worksheets, user, cutoff)
94
data = userdata + marksdata
95
# CSV writer can't handle non-ASCII characters. Encode to UTF-8.
96
data = [unicode(x).encode('utf-8') for x in data]
97
csvfile.writerow(data)
104
usage = """usage: %prog [options] subject
106
Reports each student's marks for a given subject offering."""
109
parser = optparse.OptionParser(usage)
110
parser.add_option("-s", "--semester",
111
action="store", dest="semester", metavar="YEAR/SEMESTER",
112
help="Semester of the subject's offering (eg. 2009/1). "
113
"Defaults to the currently active semester.",
115
parser.add_option("-c", "--cutoff",
116
action="store", dest="cutoff", metavar="DATE",
117
help="Cutoff date (calculate the marks as of this date). "
120
(options, args) = parser.parse_args(argv[1:])
126
subject_name = unicode(args[0])
128
if options.semester is None:
129
year, semester = None, None
132
year, semester = options.semester.split('/')
133
if len(year) == 0 or len(semester) == 0:
136
parser.error('Invalid semester (must have form "year/semester")')
138
if options.cutoff is not None:
140
cutoff = datetime.datetime.strptime(options.cutoff,
143
parser.error("Invalid date format: '%s' "
144
"(must be YYYY-MM-DD H:M:S)." % options.cutoff)
148
store = ivle.database.get_store()
150
# Get the subject from the DB
151
subject = store.find(ivle.database.Subject,
152
ivle.database.Subject.short_name == subject_name).one()
154
print >>sys.stderr, "No subject with short name '%s'" % subject_name
157
# Get the offering from the DB
159
# None specified - get the current offering from the DB
160
offerings = list(subject.active_offerings())
161
if len(offerings) == 0:
162
print >>sys.stderr, ("No active offering for subject '%s'"
165
elif len(offerings) > 1:
166
print >>sys.stderr, ("Multiple active offerings for subject '%s':"
168
print >>sys.stderr, "Please use one of:"
169
for offering in offerings:
170
print >>sys.stderr, (" --semester=%s/%s"
171
% (offering.semester.year, offering.semester.semester))
174
offering = offerings[0]
176
# Get the offering for the specified semester
177
offering = subject.offering_for_semester(year, semester)
179
print >>sys.stderr, (
180
"No offering for subject '%s' in semester %s/%s"
181
% (subject_name, year, semester))
184
# Get the list of assessable worksheets
185
worksheets = offering.worksheets.find(assessable=True)
187
# Start writing the CSV file - header
188
csvfile = csv.writer(sys.stdout)
189
csvfile.writerow(get_header(worksheets))
191
# Get all users enrolled in this offering
192
users = store.find(ivle.database.User,
193
ivle.database.User.id == ivle.database.Enrolment.user_id,
194
offering.id == ivle.database.Enrolment.offering).order_by(
195
ivle.database.User.login)
197
writeuser(worksheets, user, csvfile, cutoff)
199
if __name__ == "__main__":
200
sys.exit(main(sys.argv))