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

1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
1
#!/usr/bin/env python
2
# IVLE - Informatics Virtual Learning Environment
1195.1.17 by Matt Giuca
ivle-marks: Updated copyright to 2009.
3
# Copyright (C) 2007-2009 The University of Melbourne
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
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
# Program: Marks
20
# Author:  Matt Giuca
21
22
# Script to calculate the marks for all students for a particular subject.
23
# Requires root to run.
24
25
import sys
26
import os
27
import csv
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
28
import datetime
1195.1.2 by Matt Giuca
Added optparse - proper options parsing.
29
import optparse
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
30
from xml.dom import minidom
31
1195.1.3 by Matt Giuca
ivle-marks: Move root-check earlier so it runs BEFORE the program crashes due
32
if os.getuid() != 0:
33
    print >>sys.stderr, "Must run %s as root." % os.path.basename(sys.argv[0])
34
    sys.exit()
35
1201 by William Grant
ivle.database.get_store() now takes a configuration object.
36
import ivle.config
1080.1.60 by Matt Giuca
ivle.worksheet: Added calculate_score. This is a nice clean Storm port of
37
import ivle.database
1099.1.220 by Nick Chadwick
Merged from trunk
38
import ivle.worksheet.utils
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
39
40
def get_userdata(user):
41
    """
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).
45
    """
46
    last_login = (None if user.last_login is None else
1080.1.71 by William Grant
bin/ivle-marks: Fix, and remove dependency on ivle.db. Use Storm to list
47
                    user.last_login.strftime("%d/%m/%y"))
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
48
    return [user.studentid, user.login, user.fullname, last_login]
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
49
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
50
userdata_header = ["Student ID", "Login", "Full name", "Last login"]
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
51
def get_header(worksheets):
52
    """
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
55
    output.
56
    """
57
    return (userdata_header + [ws.name for ws in worksheets]
58
            + ["Total %", "Mark"])
59
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
60
def get_marks_user(worksheets, user, as_of=None):
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
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.
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
64
    @param as_of: Optional datetime. If supplied, gets the marks as of as_of.
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
65
    @returns: The user's percentage for each worksheet, overall, and
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
66
    their final mark, as a list of strings, in a manner which corresponds to
67
    the headings produced by get_marks_header.
68
    """
69
    worksheet_pcts = []
70
    # As we go, calculate the total score for this subject
71
    # (Assessable worksheets only, mandatory problems only)
72
    problems_done = 0
73
    problems_total = 0
74
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
75
    for worksheet in worksheets:
1080.1.60 by Matt Giuca
ivle.worksheet: Added calculate_score. This is a nice clean Storm port of
76
        # We simply ignore optional exercises here
77
        mand_done, mand_total, _, _ = (
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
78
            ivle.worksheet.utils.calculate_score(store, user, worksheet,
79
                                                 as_of))
1195.1.15 by Matt Giuca
ivle-marks, ivle.worksheet.utils: Fixed Divide-by-zero exception if there are
80
        if mand_total > 0:
81
            worksheet_pcts.append(float(mand_done) / mand_total)
82
        else:
83
            # Avoid Div0, just give everyone 0 marks if there are none
84
            worksheet_pcts.append(0.0)
1080.1.60 by Matt Giuca
ivle.worksheet: Added calculate_score. This is a nice clean Storm port of
85
        problems_done += mand_done
86
        problems_total += mand_total
1195.1.13 by Matt Giuca
ivle.worksheet.utils: Added calculate_mark, which is from the duplicated code
87
    percent, mark, _ = (
88
        ivle.worksheet.utils.calculate_mark(problems_done, problems_total))
89
    return worksheet_pcts + [float(percent)/100, mark]
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
90
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
91
def writeuser(worksheets, user, csvfile, cutoff=None):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
92
    userdata = get_userdata(user)
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
93
    marksdata = get_marks_user(worksheets, user, cutoff)
1195.1.11 by Matt Giuca
ivle-marks: Fix up error trying to print Unicode strings with non-ASCII
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)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
98
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
99
def main(argv=None):
100
    global store
101
    if argv is None:
102
        argv = sys.argv
103
104
    usage = """usage: %prog [options] subject
105
    (requires root)
106
    Reports each student's marks for a given subject offering."""
107
108
    # Parse arguments
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). "
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
113
             "Defaults to the currently active semester.",
114
        default=None)
115
    parser.add_option("-c", "--cutoff",
116
        action="store", dest="cutoff", metavar="DATE",
117
        help="Cutoff date (calculate the marks as of this date). "
118
             "YYYY-MM-DD H:M:S.",
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
119
        default=None)
120
    (options, args) = parser.parse_args(argv[1:])
121
122
    if len(args) < 1:
123
        parser.print_help()
124
        parser.exit()
125
1195.1.5 by Matt Giuca
ivle-marks: Now gets a subject object from the db (doesn't yet use it),
126
    subject_name = unicode(args[0])
127
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
128
    if options.semester is None:
129
        year, semester = None, None
130
    else:
131
        try:
132
            year, semester = options.semester.split('/')
133
            if len(year) == 0 or len(semester) == 0:
134
                raise ValueError()
135
        except ValueError:
136
            parser.error('Invalid semester (must have form "year/semester")')
137
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
138
    if options.cutoff is not None:
139
        try:
140
            cutoff = datetime.datetime.strptime(options.cutoff,
141
                                                "%Y-%m-%d %H:%M:%S")
142
        except ValueError:
143
            parser.error("Invalid date format: '%s' "
144
                         "(must be YYYY-MM-DD H:M:S)." % options.cutoff)
145
    else:
146
        cutoff = None
147
1201 by William Grant
ivle.database.get_store() now takes a configuration object.
148
    store = ivle.database.get_store(ivle.config.Config())
1195.1.5 by Matt Giuca
ivle-marks: Now gets a subject object from the db (doesn't yet use it),
149
150
    # Get the subject from the DB
151
    subject = store.find(ivle.database.Subject,
152
                     ivle.database.Subject.short_name == subject_name).one()
153
    if subject is None:
154
        print >>sys.stderr, "No subject with short name '%s'" % subject_name
155
        return 1
156
1195.1.7 by Matt Giuca
ivle-marks: Added code to get an offering from the DB (either the semester
157
    # Get the offering from the DB
158
    if semester is None:
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'"
163
                                 % subject_name)
164
            return 1
165
        elif len(offerings) > 1:
166
            print >>sys.stderr, ("Multiple active offerings for subject '%s':"
167
                                 % subject_name)
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))
172
            return 1
173
        else:
174
            offering = offerings[0]
175
    else:
176
        # Get the offering for the specified semester
177
        offering = subject.offering_for_semester(year, semester)
178
        if offering is None:
179
            print >>sys.stderr, (
180
                "No offering for subject '%s' in semester %s/%s"
181
                % (subject_name, year, semester))
182
            return 1
183
1195.1.8 by Matt Giuca
ivle-marks: Now gets the list of assessable worksheets from the database
184
    # Get the list of assessable worksheets
1195.1.14 by Matt Giuca
ivle-marks: Do not list() the lookup for worksheets (since it doesn't need to
185
    worksheets = offering.worksheets.find(assessable=True)
1195.1.8 by Matt Giuca
ivle-marks: Now gets the list of assessable worksheets from the database
186
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
187
    # Start writing the CSV file - header
188
    csvfile = csv.writer(sys.stdout)
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
189
    csvfile.writerow(get_header(worksheets))
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
190
1195.1.16 by Matt Giuca
ivle-marks: Now only displays users who are enrolled in the given offering.
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)
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
196
    for user in users:
1195.1.18 by Matt Giuca
ivle.worksheet.utils: Can now calculate exercise and worksheet marks as of a
197
        writeuser(worksheets, user, csvfile, cutoff)
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
198
199
if __name__ == "__main__":
200
    sys.exit(main(sys.argv))