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

« back to all changes in this revision

Viewing changes to ivle/webapp/tutorial/marks.py

  • Committer: William Grant
  • Date: 2010-07-28 05:06:15 UTC
  • Revision ID: grantw@unimelb.edu.au-20100728050615-uwbxn9frla3pdw8m
Encode content_type when downloading files. cjson made us write bad code.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# IVLE
 
2
# Copyright (C) 2007-2010 The University of Melbourne
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
17
 
 
18
# Author: Matt Giuca
 
19
 
 
20
"""Worksheet marks reporting functionality.
 
21
 
 
22
Displays students' worksheet marks to users with sufficient privileges.
 
23
"""
 
24
 
 
25
import datetime
 
26
import csv
 
27
import urllib
 
28
 
 
29
import ivle.database
 
30
import ivle.worksheet.utils
 
31
from ivle.webapp.base.views import BaseView
 
32
from ivle.webapp.base.xhtml import XHTMLView
 
33
from ivle.webapp.media import media_url
 
34
 
 
35
class WorksheetsMarksView(XHTMLView):
 
36
    """View for presenting all students' individual marks for worksheets."""
 
37
    permission = 'view_worksheet_marks'
 
38
    template = 'templates/worksheets_marks.html'
 
39
    tab = 'subjects'
 
40
 
 
41
    def populate(self, req, ctx):
 
42
        error = None
 
43
        offering = self.context
 
44
        ctx['req'] = req
 
45
        ctx['context'] = offering
 
46
        ctx['urllib'] = urllib
 
47
        ctx['WorksheetsMarksCSVView'] = WorksheetsMarksCSVView
 
48
 
 
49
        # User may supply a "cutoff date" to calculate marks as of that date
 
50
        # Default to current time
 
51
        cutoff = offering.worksheet_cutoff or datetime.datetime.now()
 
52
        data = dict(req.get_fieldstorage())
 
53
        if data.get('cutoff') is not None:
 
54
            try:
 
55
                cutoff = datetime.datetime.strptime(data.get('cutoff'),
 
56
                                                    "%Y-%m-%d %H:%M:%S")
 
57
            except ValueError:
 
58
                error = (
 
59
                    "Invalid date format: '%s' (must be YYYY-MM-DD H:M:S)."
 
60
                        % data.get('cutoff'))
 
61
        ctx['cutoff'] = cutoff
 
62
        ctx['error'] = error
 
63
 
 
64
        # "worksheets" is a list of (assessable, published) worksheet names
 
65
        worksheets = offering.worksheets.find(assessable=True, published=True)
 
66
        ctx['worksheets'] = [ws.name for ws in worksheets]
 
67
 
 
68
        # "students" is a list of tuples:
 
69
        # (user, worksheet_pcts, total_pct, mark)
 
70
        # user is a User object, worksheet_pcts is a list of floats (one per
 
71
        # worksheet), total_pct is a float, mark is an int
 
72
        ctx['students'] = students = []
 
73
        # Get all users enrolled in this offering
 
74
        users = req.store.find(ivle.database.User,
 
75
                       ivle.database.User.id == ivle.database.Enrolment.user_id,
 
76
                       offering.id == ivle.database.Enrolment.offering).order_by(
 
77
                            ivle.database.User.login)
 
78
        for user in users:
 
79
            worksheet_pcts, total_pct, mark = get_marks_user(req, worksheets,
 
80
                                                user, as_of=cutoff)
 
81
            students.append((user, worksheet_pcts, total_pct, mark))
 
82
 
 
83
class WorksheetsMarksCSVView(BaseView):
 
84
    """View for presenting all students' individual marks for worksheets."""
 
85
    permission = 'view_worksheet_marks'
 
86
    template = 'templates/worksheets_marks.html'
 
87
    tab = 'subjects'
 
88
 
 
89
    def render(self, req):
 
90
        offering = self.context
 
91
 
 
92
        # User may supply a "cutoff date" to calculate marks as of that date
 
93
        # Default to current time
 
94
        cutoff = offering.worksheet_cutoff or datetime.datetime.now()
 
95
        data = dict(req.get_fieldstorage())
 
96
        if data.get('cutoff') is not None:
 
97
            try:
 
98
                cutoff = datetime.datetime.strptime(data.get('cutoff'),
 
99
                                                    "%Y-%m-%d %H:%M:%S")
 
100
            except ValueError:
 
101
                req.write(
 
102
                    "Invalid date format: '%s' (must be YYYY-MM-DD H:M:S)."
 
103
                        % data.get('cutoff'))
 
104
                return
 
105
 
 
106
        req.content_type = "text/csv"
 
107
        req.headers_out.add('Content-Disposition',
 
108
            "attachment; filename=marks-%s-%ss%s.csv" %
 
109
            (offering.subject.short_name, offering.semester.year,
 
110
             offering.semester.url_name))
 
111
 
 
112
        # "worksheets" is a list of (assessable, published) worksheet names
 
113
        worksheets = offering.worksheets.find(assessable=True, published=True)
 
114
 
 
115
        # Start writing the CSV file - header
 
116
        csvfile = csv.writer(req)
 
117
        csvfile.writerow(csv_get_header(worksheets))
 
118
 
 
119
        # Get all users enrolled in this offering
 
120
        users = req.store.find(ivle.database.User,
 
121
                   ivle.database.User.id == ivle.database.Enrolment.user_id,
 
122
                   offering.id == ivle.database.Enrolment.offering).order_by(
 
123
                        ivle.database.User.login)
 
124
        for user in users:
 
125
            csv_writeuser(req, worksheets, user, csvfile, cutoff)
 
126
 
 
127
def get_marks_user(req, worksheets, user, as_of=None):
 
128
    """Gets marks for a particular user for a particular set of worksheets.
 
129
    @param worksheets: List of Worksheet objects to get marks for.
 
130
    @param user: User to get marks for.
 
131
    @param as_of: Optional datetime. If supplied, gets the marks as of as_of.
 
132
    @returns: (worksheet_pcts, total_pct, mark)
 
133
    """
 
134
    worksheet_pcts = []
 
135
    # As we go, calculate the total score for this subject
 
136
    # (Assessable worksheets only, mandatory problems only)
 
137
    problems_done = 0
 
138
    problems_total = 0
 
139
 
 
140
    for worksheet in worksheets:
 
141
        # We simply ignore optional exercises here
 
142
        mand_done, mand_total, _, _ = (
 
143
            ivle.worksheet.utils.calculate_score(req.store, user, worksheet,
 
144
                                                 as_of))
 
145
        if mand_total > 0:
 
146
            worksheet_pcts.append(float(mand_done) / mand_total)
 
147
        else:
 
148
            # Avoid Div0, just give everyone 0 marks if there are none
 
149
            worksheet_pcts.append(0.0)
 
150
        problems_done += mand_done
 
151
        problems_total += mand_total
 
152
    percent, mark, _ = (
 
153
        ivle.worksheet.utils.calculate_mark(problems_done, problems_total))
 
154
    return (worksheet_pcts, float(percent)/100, mark)
 
155
 
 
156
def csv_get_userdata(user):
 
157
    """
 
158
    Given a User object, returns a list of strings for the user data which
 
159
    will be part of the output for this user.
 
160
    (This is not marks, it's other user data).
 
161
    """
 
162
    last_login = ("" if user.last_login is None else
 
163
                    user.last_login.strftime("%Y-%m-%d"))
 
164
    return [user.studentid or "", user.login, user.fullname, last_login]
 
165
 
 
166
csv_userdata_header = ["Student ID", "Login", "Full name", "Last login"]
 
167
def csv_get_header(worksheets):
 
168
    """
 
169
    Given a list of Worksheet objects (the assessable worksheets), returns a
 
170
    list of strings -- the column headings for the marks section of the CSV
 
171
    output.
 
172
    """
 
173
    return (csv_userdata_header + [ws.name for ws in worksheets]
 
174
            + ["Total %", "Mark"])
 
175
 
 
176
def csv_writeuser(req, worksheets, user, csvfile, cutoff=None):
 
177
    userdata = csv_get_userdata(user)
 
178
    worksheet_pcts, total_pct, mark = get_marks_user(req, worksheets, user,
 
179
                                                     cutoff)
 
180
    data = userdata + worksheet_pcts + [total_pct, mark]
 
181
    # CSV writer can't handle non-ASCII characters. Encode to UTF-8.
 
182
    data = [unicode(x).encode('utf-8') for x in data]
 
183
    csvfile.writerow(data)