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

1689.1.2 by Matt Giuca
Added new view WorksheetsMarksView (offering/+worksheets/+edit) which shows the student marks for a particular worksheet. No links yet.
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
1689.1.5 by Matt Giuca
tutorial.marks: Added 'cutoff' query string argument, allowing a cutoff time to be set. The cutoff time (whether explicit or not) will now be displayed on the page.
25
import datetime
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
26
import csv
1689.1.10 by Matt Giuca
tutorial.marks: Provide a download link at the bottom of the page to download as a CSV file.
27
import urllib
1689.1.5 by Matt Giuca
tutorial.marks: Added 'cutoff' query string argument, allowing a cutoff time to be set. The cutoff time (whether explicit or not) will now be displayed on the page.
28
1689.1.7 by Matt Giuca
tutorial.marks: Added the body of the table, showing all of the fields including worksheet marks.
29
import ivle.database
30
import ivle.worksheet.utils
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
31
from ivle.webapp.base.views import BaseView
1689.1.2 by Matt Giuca
Added new view WorksheetsMarksView (offering/+worksheets/+edit) which shows the student marks for a particular worksheet. No links yet.
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."""
1689.1.4 by Matt Giuca
WorksheetMarksView: Fixed permission check. Now checks for view_worksheet_marks permissions.
37
    permission = 'view_worksheet_marks'
1689.1.2 by Matt Giuca
Added new view WorksheetsMarksView (offering/+worksheets/+edit) which shows the student marks for a particular worksheet. No links yet.
38
    template = 'templates/worksheets_marks.html'
39
    tab = 'subjects'
40
41
    def populate(self, req, ctx):
1689.1.5 by Matt Giuca
tutorial.marks: Added 'cutoff' query string argument, allowing a cutoff time to be set. The cutoff time (whether explicit or not) will now be displayed on the page.
42
        error = None
1689.1.7 by Matt Giuca
tutorial.marks: Added the body of the table, showing all of the fields including worksheet marks.
43
        offering = self.context
1689.1.10 by Matt Giuca
tutorial.marks: Provide a download link at the bottom of the page to download as a CSV file.
44
        ctx['req'] = req
1689.1.7 by Matt Giuca
tutorial.marks: Added the body of the table, showing all of the fields including worksheet marks.
45
        ctx['context'] = offering
1689.1.10 by Matt Giuca
tutorial.marks: Provide a download link at the bottom of the page to download as a CSV file.
46
        ctx['urllib'] = urllib
47
        ctx['WorksheetsMarksCSVView'] = WorksheetsMarksCSVView
1689.1.5 by Matt Giuca
tutorial.marks: Added 'cutoff' query string argument, allowing a cutoff time to be set. The cutoff time (whether explicit or not) will now be displayed on the page.
48
49
        # User may supply a "cutoff date" to calculate marks as of that date
50
        # Default to current time
1740 by Matt Giuca
Worksheet marks page (and CSV download) now respects the worksheet_cutoff, if supplied, by default. Still allows the cutoff to be manually overridden. Fixes Launchpad bug #526976.
51
        cutoff = offering.worksheet_cutoff or datetime.datetime.now()
1689.1.5 by Matt Giuca
tutorial.marks: Added 'cutoff' query string argument, allowing a cutoff time to be set. The cutoff time (whether explicit or not) will now be displayed on the page.
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'))
1689.1.6 by Matt Giuca
tutorial.marks: Moved strftime for cutoff into html template, since it's part of the display.
61
        ctx['cutoff'] = cutoff
1689.1.5 by Matt Giuca
tutorial.marks: Added 'cutoff' query string argument, allowing a cutoff time to be set. The cutoff time (whether explicit or not) will now be displayed on the page.
62
        ctx['error'] = error
1689.1.7 by Matt Giuca
tutorial.marks: Added the body of the table, showing all of the fields including worksheet marks.
63
1689.1.19 by Matt Giuca
tutorial.marks: Do not include worksheets in marks calculation if they are unpublished.
64
        # "worksheets" is a list of (assessable, published) worksheet names
65
        worksheets = offering.worksheets.find(assessable=True, published=True)
1689.1.7 by Matt Giuca
tutorial.marks: Added the body of the table, showing all of the fields including worksheet marks.
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
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
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
1740 by Matt Giuca
Worksheet marks page (and CSV download) now respects the worksheet_cutoff, if supplied, by default. Still allows the cutoff to be manually overridden. Fixes Launchpad bug #526976.
94
        cutoff = offering.worksheet_cutoff or datetime.datetime.now()
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
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.semester))
111
1689.1.19 by Matt Giuca
tutorial.marks: Do not include worksheets in marks calculation if they are unpublished.
112
        # "worksheets" is a list of (assessable, published) worksheet names
113
        worksheets = offering.worksheets.find(assessable=True, published=True)
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
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
1689.1.7 by Matt Giuca
tutorial.marks: Added the body of the table, showing all of the fields including worksheet marks.
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)
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
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
    """
1703 by Matt Giuca
tutorial.marks: CSV output no longer writes 'N/A' in certain fields. Just outputs the empty string.
162
    last_login = ("" if user.last_login is None else
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
163
                    user.last_login.strftime("%Y-%m-%d"))
1703 by Matt Giuca
tutorial.marks: CSV output no longer writes 'N/A' in certain fields. Just outputs the empty string.
164
    return [user.studentid or "", user.login, user.fullname, last_login]
1689.1.9 by Matt Giuca
Added worksheet marks CSV view, which presents the same table as the worksheet marks table, but as a downloadable CSV file.
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)