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

« back to all changes in this revision

Viewing changes to bin/ivle-marks

  • Committer: William Grant
  • Date: 2009-01-22 04:47:42 UTC
  • mfrom: (1080.1.93 storm)
  • Revision ID: grantw@unimelb.edu.au-20090122044742-sa8gnww0ma2bm2rv
Merge Storm branch. ivle.db is dead. Watch out for the schema change.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
# Date:    17/4/2008
 
22
 
 
23
# Script to calculate the marks for all students for a particular subject.
 
24
# Requires root to run.
 
25
 
 
26
import sys
 
27
import os
 
28
import re
 
29
import csv
 
30
from xml.dom import minidom
 
31
 
 
32
import ivle.database
 
33
import ivle.worksheet
 
34
import ivle.conf
 
35
 
 
36
if os.getuid() != 0:
 
37
    print >>sys.stderr, "Must run %s as root." % os.path.basename(sys.argv[0])
 
38
    sys.exit()
 
39
 
 
40
if len(sys.argv) <= 1:
 
41
    print >>sys.stderr, "Usage: %s subject" % os.path.basename(sys.argv[0])
 
42
    sys.exit()
 
43
 
 
44
# Regex for valid identifiers (subject/worksheet names)
 
45
re_ident = re.compile("[0-9A-Za-z_]+")
 
46
 
 
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)
 
51
 
 
52
subject = sys.argv[1]
 
53
 
 
54
# Subject names must be valid identifiers
 
55
if not is_valid_subjname(subject):
 
56
    print >>sys.stderr, "Invalid subject name: %s." % repr(subject)
 
57
    sys.exit()
 
58
 
 
59
def get_userdata(user):
 
60
    """
 
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).
 
64
    """
 
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"]
 
69
 
 
70
def get_assessable_worksheets(subject):
 
71
    """
 
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.
 
75
    """
 
76
    # NOTE: This code is copy/edited from
 
77
    # www/apps/tutorial/__init__.py:handle_subject_menu
 
78
    # Should be factored out of there.
 
79
 
 
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).
 
83
    try:
 
84
        subjectfile = open(os.path.join(ivle.conf.subjects_base, subject,
 
85
            "subject.xml"))
 
86
    except:
 
87
        raise Exception("Subject %s not found." % repr(subject))
 
88
 
 
89
    assessable_worksheets = []
 
90
    # Read in data about the subject
 
91
    subjectdom = minidom.parse(subjectfile)
 
92
    subjectfile.close()
 
93
    # TEMP: All of this is for a temporary XML format, which will later
 
94
    # change.
 
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
 
100
            # set to "true").
 
101
            if worksheetdom.getAttribute("assessable") == "true":
 
102
                assessable_worksheets.append(worksheetdom.getAttribute("id"))
 
103
 
 
104
    return assessable_worksheets
 
105
 
 
106
def get_marks_header(worksheets):
 
107
    """
 
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.
 
110
    """
 
111
    return worksheets + ["Total %", "Mark"]
 
112
 
 
113
def get_marks_user(subject, worksheet_names, user):
 
114
    """
 
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.
 
119
    """
 
120
    # NOTE: This code is copy/edited from
 
121
    # www/apps/tutorial/__init__.py:handle_subject_menu
 
122
    # Should be factored out of there.
 
123
 
 
124
    worksheet_pcts = []
 
125
    # As we go, calculate the total score for this subject
 
126
    # (Assessable worksheets only, mandatory problems only)
 
127
    problems_done = 0
 
128
    problems_total = 0
 
129
 
 
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
 
143
    max_mark = 5
 
144
    mark = min(problems_pct_int / 16, max_mark)
 
145
    return worksheet_pcts + [problems_pct, mark]
 
146
 
 
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)
 
151
 
 
152
try:
 
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)
 
158
    sys.exit(1)
 
159
 
 
160
# Start writing the CSV file - header
 
161
csvfile = csv.writer(sys.stdout)
 
162
csvfile.writerow(userdata_header + get_marks_header(worksheets))
 
163
 
 
164
for user in store.find(ivle.database.User).order_by(ivle.database.User.login):
 
165
    writeuser(subject, worksheets, user, csvfile)
 
166