~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
3
# Copyright (C) 2007-2008 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
# 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 re
28
import csv
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
1080.1.60 by Matt Giuca
ivle.worksheet: Added calculate_score. This is a nice clean Storm port of
36
import ivle.database
1099.1.220 by Nick Chadwick
Merged from trunk
37
import ivle.worksheet.utils
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
38
import ivle.conf
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
60
def get_marks_user(worksheets, user):
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
    @returns: The user's percentage for each worksheet, overall, and
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
65
    their final mark, as a list of strings, in a manner which corresponds to
66
    the headings produced by get_marks_header.
67
    """
68
    worksheet_pcts = []
69
    # As we go, calculate the total score for this subject
70
    # (Assessable worksheets only, mandatory problems only)
71
    problems_done = 0
72
    problems_total = 0
73
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
74
    for worksheet in worksheets:
1080.1.60 by Matt Giuca
ivle.worksheet: Added calculate_score. This is a nice clean Storm port of
75
        # We simply ignore optional exercises here
76
        mand_done, mand_total, _, _ = (
1099.1.220 by Nick Chadwick
Merged from trunk
77
            ivle.worksheet.utils.calculate_score(store, user, worksheet))
1195.1.15 by Matt Giuca
ivle-marks, ivle.worksheet.utils: Fixed Divide-by-zero exception if there are
78
        if mand_total > 0:
79
            worksheet_pcts.append(float(mand_done) / mand_total)
80
        else:
81
            # Avoid Div0, just give everyone 0 marks if there are none
82
            worksheet_pcts.append(0.0)
1080.1.60 by Matt Giuca
ivle.worksheet: Added calculate_score. This is a nice clean Storm port of
83
        problems_done += mand_done
84
        problems_total += mand_total
1195.1.13 by Matt Giuca
ivle.worksheet.utils: Added calculate_mark, which is from the duplicated code
85
    percent, mark, _ = (
86
        ivle.worksheet.utils.calculate_mark(problems_done, problems_total))
87
    return worksheet_pcts + [float(percent)/100, mark]
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
88
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
89
def writeuser(worksheets, user, csvfile):
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
90
    userdata = get_userdata(user)
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
91
    marksdata = get_marks_user(worksheets, user)
1195.1.11 by Matt Giuca
ivle-marks: Fix up error trying to print Unicode strings with non-ASCII
92
    data = userdata + marksdata
93
    # CSV writer can't handle non-ASCII characters. Encode to UTF-8.
94
    data = [unicode(x).encode('utf-8') for x in data]
95
    csvfile.writerow(data)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
96
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
97
def main(argv=None):
98
    global store
99
    if argv is None:
100
        argv = sys.argv
101
102
    usage = """usage: %prog [options] subject
103
    (requires root)
104
    Reports each student's marks for a given subject offering."""
105
106
    # Parse arguments
107
    parser = optparse.OptionParser(usage)
108
    parser.add_option("-s", "--semester",
109
        action="store", dest="semester", metavar="YEAR/SEMESTER",
110
        help="Semester of the subject's offering (eg. 2009/1). "
111
        "Defaults to the currently active semester.",
112
        default=None)
113
    (options, args) = parser.parse_args(argv[1:])
114
115
    if len(args) < 1:
116
        parser.print_help()
117
        parser.exit()
118
1195.1.5 by Matt Giuca
ivle-marks: Now gets a subject object from the db (doesn't yet use it),
119
    subject_name = unicode(args[0])
120
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
121
    if options.semester is None:
122
        year, semester = None, None
123
    else:
124
        try:
125
            year, semester = options.semester.split('/')
126
            if len(year) == 0 or len(semester) == 0:
127
                raise ValueError()
128
        except ValueError:
129
            parser.error('Invalid semester (must have form "year/semester")')
130
1195.1.5 by Matt Giuca
ivle-marks: Now gets a subject object from the db (doesn't yet use it),
131
    store = ivle.database.get_store()
132
133
    # Get the subject from the DB
134
    subject = store.find(ivle.database.Subject,
135
                     ivle.database.Subject.short_name == subject_name).one()
136
    if subject is None:
137
        print >>sys.stderr, "No subject with short name '%s'" % subject_name
138
        return 1
139
1195.1.7 by Matt Giuca
ivle-marks: Added code to get an offering from the DB (either the semester
140
    # Get the offering from the DB
141
    if semester is None:
142
        # None specified - get the current offering from the DB
143
        offerings = list(subject.active_offerings())
144
        if len(offerings) == 0:
145
            print >>sys.stderr, ("No active offering for subject '%s'"
146
                                 % subject_name)
147
            return 1
148
        elif len(offerings) > 1:
149
            print >>sys.stderr, ("Multiple active offerings for subject '%s':"
150
                                 % subject_name)
151
            print >>sys.stderr, "Please use one of:"
152
            for offering in offerings:
153
                print >>sys.stderr, ("    --semester=%s/%s"
154
                    % (offering.semester.year, offering.semester.semester))
155
            return 1
156
        else:
157
            offering = offerings[0]
158
    else:
159
        # Get the offering for the specified semester
160
        offering = subject.offering_for_semester(year, semester)
161
        if offering is None:
162
            print >>sys.stderr, (
163
                "No offering for subject '%s' in semester %s/%s"
164
                % (subject_name, year, semester))
165
            return 1
166
1195.1.8 by Matt Giuca
ivle-marks: Now gets the list of assessable worksheets from the database
167
    # 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
168
    worksheets = offering.worksheets.find(assessable=True)
1195.1.8 by Matt Giuca
ivle-marks: Now gets the list of assessable worksheets from the database
169
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
170
    # Start writing the CSV file - header
171
    csvfile = csv.writer(sys.stdout)
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
172
    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
173
1195.1.16 by Matt Giuca
ivle-marks: Now only displays users who are enrolled in the given offering.
174
    # Get all users enrolled in this offering
175
    users = store.find(ivle.database.User,
176
                   ivle.database.User.id == ivle.database.Enrolment.user_id,
177
                   offering.id == ivle.database.Enrolment.offering).order_by(
178
                        ivle.database.User.login)
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
179
    for user in users:
1195.1.9 by Matt Giuca
ivle-marks: Fixed up header and actual body calculation (now uses the database
180
        writeuser(worksheets, user, csvfile)
1195.1.4 by Matt Giuca
ivle-marks: Moved all of the various code outside any functions into a main
181
182
if __name__ == "__main__":
183
    sys.exit(main(sys.argv))