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

621 by mattgiuca
Added 2 new apps: home and subjects. Both fairly incomplete, just a basic
1
# IVLE
2
# Copyright (C) 2007-2008 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
# App: subjects
19
# Author: Matt Giuca
20
# Date: 29/2/2008
21
22
# This is an IVLE application.
23
# A sample / testing application for IVLE.
24
627 by mattgiuca
subjects: Now presents a top-level subjects menu (same as tutorials).
25
import os
1165.3.61 by William Grant
Provide a Subversion command to grab each submission.
26
import os.path
627 by mattgiuca
subjects: Now presents a top-level subjects menu (same as tutorials).
27
import urllib
1165.3.61 by William Grant
Provide a Subversion command to grab each submission.
28
import urlparse
627 by mattgiuca
subjects: Now presents a top-level subjects menu (same as tutorials).
29
import cgi
30
1294.2.52 by William Grant
Port subject-related views to object traversal.
31
from storm.locals import Desc, Store
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
32
import genshi
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
33
from genshi.filters import HTMLFormFiller
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
34
from genshi.template import Context, TemplateLoader
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
35
import formencode
1125 by William Grant
Rework ivle.webapp.admin.subjects#SubjectsView to split offerings nicely by
36
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
37
from ivle.webapp.base.xhtml import XHTMLView
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
38
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
1294.2.52 by William Grant
Port subject-related views to object traversal.
39
from ivle.webapp import ApplicationRoot
1165.3.9 by Nick Chadwick
merge from trunk
40
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
41
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
1165.3.8 by Nick Chadwick
Added a total submissions and total assesseds to the project view
42
                          ProjectSet, Project, ProjectSubmission
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
43
from ivle import util
1165.3.14 by William Grant
Improve ProjectView's template substantially.
44
import ivle.date
621 by mattgiuca
Added 2 new apps: home and subjects. Both fairly incomplete, just a basic
45
1375.1.2 by William Grant
Remove ProjectRESTView, which did nothing and was unused.
46
from ivle.webapp.admin.projectservice import ProjectSetRESTView
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
47
from ivle.webapp.admin.offeringservice import OfferingRESTView
1294.3.2 by William Grant
Router->Publisher
48
from ivle.webapp.admin.publishing import (root_to_subject,
1294.2.70 by William Grant
Split out ivle.webapp.admin's routes into annotated functions in ivle.webapp.traversal.
49
            subject_to_offering, offering_to_projectset, offering_to_project,
50
            subject_url, offering_url, projectset_url, project_url)
1294.2.96 by William Grant
Add a UserBreadcrumb.
51
from ivle.webapp.admin.breadcrumbs import (SubjectBreadcrumb,
1294.2.98 by William Grant
Add a ProjectBreadcrumb.
52
            OfferingBreadcrumb, UserBreadcrumb, ProjectBreadcrumb)
1358 by William Grant
Use the publishing framework to generate URLs to projectsets.
53
from ivle.webapp.groups import GroupsView
1442.1.31 by William Grant
Show the worksheet listing with marks and schtuff on the offering index.
54
from ivle.webapp.tutorial import Plugin as TutorialPlugin
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
55
1099.1.20 by William Grant
ivle.webapp.admin.subject: Port www/apps/subjects to new framework.
56
class SubjectsView(XHTMLView):
57
    '''The view of the list of subjects.'''
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
58
    template = 'templates/subjects.html'
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
59
    tab = 'subjects'
1099.1.20 by William Grant
ivle.webapp.admin.subject: Port www/apps/subjects to new framework.
60
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
61
    def authorize(self, req):
1138 by William Grant
SubjectsView now tells users if they have no enrolments.
62
        return req.user is not None
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
63
1099.1.20 by William Grant
ivle.webapp.admin.subject: Port www/apps/subjects to new framework.
64
    def populate(self, req, ctx):
1139 by William Grant
Show group administration links on SubjectsView where privileges allow it.
65
        ctx['user'] = req.user
1125 by William Grant
Rework ivle.webapp.admin.subjects#SubjectsView to split offerings nicely by
66
        ctx['semesters'] = []
67
        for semester in req.store.find(Semester).order_by(Desc(Semester.year),
68
                                                     Desc(Semester.semester)):
1372 by Matt Giuca
admin/subject: Admin users now see all offerings in the system, not just the ones they are enrolled in.
69
            if req.user.admin:
70
                # For admins, show all subjects in the system
71
                offerings = list(semester.offerings.find())
72
            else:
73
                offerings = [enrolment.offering for enrolment in
1371 by Matt Giuca
admin/subject: Now sends a list of offerings the user is enrolled in to the Genshi template, rather than a list of enrolment objects (of which only the offering is observed). This allows us to send non-enrolment offerings to the template.
74
                                    semester.enrolments.find(user=req.user)]
75
            if len(offerings):
76
                ctx['semesters'].append((semester, offerings))
1099.1.20 by William Grant
ivle.webapp.admin.subject: Port www/apps/subjects to new framework.
77
1442.1.2 by William Grant
Add basic (ie. pretty much empty) offering index.
78
class OfferingView(XHTMLView):
79
    """The home page of an offering."""
80
    template = 'templates/offering.html'
81
    tab = 'subjects'
82
    permission = 'view'
83
84
    def populate(self, req, ctx):
1442.1.31 by William Grant
Show the worksheet listing with marks and schtuff on the offering index.
85
        # Need the worksheet result styles.
86
        self.plugin_styles[TutorialPlugin] = ['tutorial.css']
1442.1.2 by William Grant
Add basic (ie. pretty much empty) offering index.
87
        ctx['context'] = self.context
88
        ctx['req'] = req
1442.1.5 by William Grant
Improve worksheet management experience from the offering index.
89
        ctx['permissions'] = self.context.get_permissions(req.user)
1515 by Matt Giuca
Submit view: The projects list is now identical (except for radio buttons) to the view on the subjects page. It is much clearer and contains more info. The code is somewhat different, because it's a table, not a list, so I didn't abstract it. Moved a function out of subject.py to ivle.util, as it is shared by both views.
90
        ctx['format_submission_principal'] = util.format_submission_principal
1442.1.10 by William Grant
Add a nice padded list of projects.
91
        ctx['format_datetime'] = ivle.date.make_date_nice
92
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
1451.1.7 by William Grant
Add a 'Change details' link on the offering index, pointing to +edit.
93
        ctx['OfferingEdit'] = OfferingEdit
1442.1.2 by William Grant
Add basic (ie. pretty much empty) offering index.
94
1442.1.31 by William Grant
Show the worksheet listing with marks and schtuff on the offering index.
95
        # As we go, calculate the total score for this subject
96
        # (Assessable worksheets only, mandatory problems only)
97
98
        ctx['worksheets'], problems_total, problems_done = (
99
            ivle.worksheet.utils.create_list_of_fake_worksheets_and_stats(
100
                req.store, req.user, self.context))
101
102
        ctx['exercises_total'] = problems_total
103
        ctx['exercises_done'] = problems_done
104
        if problems_total > 0:
105
            if problems_done >= problems_total:
106
                ctx['worksheets_complete_class'] = "complete"
107
            elif problems_done > 0:
108
                ctx['worksheets_complete_class'] = "semicomplete"
109
            else:
110
                ctx['worksheets_complete_class'] = "incomplete"
111
            # Calculate the final percentage and mark for the subject
112
            (ctx['exercises_pct'], ctx['worksheet_mark'],
113
             ctx['worksheet_max_mark']) = (
114
                ivle.worksheet.utils.calculate_mark(
115
                    problems_done, problems_total))
116
1442.1.2 by William Grant
Add basic (ie. pretty much empty) offering index.
117
1451.1.5 by William Grant
Add an OfferingEdit view, for setting the description and URL.
118
class OfferingSchema(formencode.Schema):
1451.1.8 by William Grant
Allow unsetting of the URL or description.
119
    description = formencode.validators.UnicodeString(
120
        if_missing=None, not_empty=False)
121
    url = formencode.validators.URL(if_missing=None, not_empty=False)
1451.1.5 by William Grant
Add an OfferingEdit view, for setting the description and URL.
122
123
124
class OfferingEdit(XHTMLView):
125
    """A form to edit an offering's details."""
126
    template = 'templates/offering-edit.html'
1523 by William Grant
Declare appropriate tabs on the rest of the views.
127
    tab = 'subjects'
1451.1.5 by William Grant
Add an OfferingEdit view, for setting the description and URL.
128
    permission = 'edit'
129
130
    def filter(self, stream, ctx):
131
        return stream | HTMLFormFiller(data=ctx['data'])
132
133
    def populate(self, req, ctx):
134
        if req.method == 'POST':
135
            data = dict(req.get_fieldstorage())
136
            try:
137
                validator = OfferingSchema()
138
                data = validator.to_python(data, state=req)
139
1451.1.8 by William Grant
Allow unsetting of the URL or description.
140
                self.context.url = unicode(data['url']) if data['url'] else None
1451.1.5 by William Grant
Add an OfferingEdit view, for setting the description and URL.
141
                self.context.description = data['description']
142
                req.store.commit()
143
                req.throw_redirect(req.publisher.generate(self.context))
144
            except formencode.Invalid, e:
145
                errors = e.unpack_errors()
146
        else:
147
            data = {
148
                'url': self.context.url,
149
                'description': self.context.description,
150
            }
151
            errors = {}
152
153
        ctx['data'] = data or {}
154
        ctx['context'] = self.context
155
        ctx['errors'] = errors
156
157
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
158
class UserValidator(formencode.FancyValidator):
159
    """A FormEncode validator that turns a username into a user.
160
161
    The state must have a 'store' attribute, which is the Storm store
162
    to use."""
163
    def _to_python(self, value, state):
164
        user = User.get_by_login(state.store, value)
165
        if user:
166
            return user
167
        else:
1150 by William Grant
Refuse a +enrol if the user is already enrolled. This stops overwriting
168
            raise formencode.Invalid('User does not exist', value, state)
169
170
171
class NoEnrolmentValidator(formencode.FancyValidator):
172
    """A FormEncode validator that ensures absence of an enrolment.
173
174
    The state must have an 'offering' attribute.
175
    """
176
    def _to_python(self, value, state):
177
        if state.offering.get_enrolment(value):
178
            raise formencode.Invalid('User already enrolled', value, state)
179
        return value
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
180
181
1377 by Matt Giuca
database: Added finer-grained enrol permissions on offerings.
182
class RoleEnrolmentValidator(formencode.FancyValidator):
183
    """A FormEncode validator that checks permission to enrol users with a
184
    particular role.
185
186
    The state must have an 'offering' attribute.
187
    """
188
    def _to_python(self, value, state):
189
        if ("enrol_" + value) not in state.offering.get_permissions(state.user):
190
            raise formencode.Invalid('Not allowed to assign users that role',
191
                                     value, state)
192
        return value
193
194
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
195
class EnrolSchema(formencode.Schema):
1150 by William Grant
Refuse a +enrol if the user is already enrolled. This stops overwriting
196
    user = formencode.All(NoEnrolmentValidator(), UserValidator())
1377 by Matt Giuca
database: Added finer-grained enrol permissions on offerings.
197
    role = formencode.All(formencode.validators.OneOf(
198
                                ["lecturer", "tutor", "student"]),
199
                          RoleEnrolmentValidator(),
200
                          formencode.validators.UnicodeString())
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
201
202
1365 by Matt Giuca
Added a new view under Offering/+enrolments to display all staff and students in an offering.
203
class EnrolmentsView(XHTMLView):
204
    """A page which displays all users enrolled in an offering."""
205
    template = 'templates/enrolments.html'
1523 by William Grant
Declare appropriate tabs on the rest of the views.
206
    tab = 'subjects'
1365 by Matt Giuca
Added a new view under Offering/+enrolments to display all staff and students in an offering.
207
    permission = 'edit'
208
209
    def populate(self, req, ctx):
210
        ctx['offering'] = self.context
211
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
212
class EnrolView(XHTMLView):
213
    """A form to enrol a user in an offering."""
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
214
    template = 'templates/enrol.html'
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
215
    tab = 'subjects'
1376 by Matt Giuca
database: More granular permissions on offerings: Added 'enrol' permission.
216
    permission = 'enrol'
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
217
218
    def filter(self, stream, ctx):
219
        return stream | HTMLFormFiller(data=ctx['data'])
220
221
    def populate(self, req, ctx):
222
        if req.method == 'POST':
223
            data = dict(req.get_fieldstorage())
224
            try:
225
                validator = EnrolSchema()
1150 by William Grant
Refuse a +enrol if the user is already enrolled. This stops overwriting
226
                req.offering = self.context # XXX: Getting into state.
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
227
                data = validator.to_python(data, state=req)
1377 by Matt Giuca
database: Added finer-grained enrol permissions on offerings.
228
                self.context.enrol(data['user'], data['role'])
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
229
                req.store.commit()
230
                req.throw_redirect(req.uri)
231
            except formencode.Invalid, e:
232
                errors = e.unpack_errors()
233
        else:
234
            data = {}
235
            errors = {}
236
237
        ctx['data'] = data or {}
238
        ctx['offering'] = self.context
1379 by Matt Giuca
Enrolments page: Don't show choices in the list if they are unauthorized. (Note that this is a UI tweak -- roles are already authz-checked.)
239
        ctx['roles_auth'] = self.context.get_permissions(req.user)
1149 by William Grant
Allow tutors and lecturers to enrol people in their offerings.
240
        ctx['errors'] = errors
241
1165.3.19 by William Grant
Rename SubjectProjectSetView to OfferingProjectsView.
242
class OfferingProjectsView(XHTMLView):
243
    """View the projects for an offering."""
244
    template = 'templates/offering_projects.html'
1165.2.3 by Nick Chadwick
Added a new Admin view, which allows for the administration of projects
245
    permission = 'edit'
1165.3.18 by William Grant
Put the project listing and view in the Subjects tab.
246
    tab = 'subjects'
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
247
1165.2.3 by Nick Chadwick
Added a new Admin view, which allows for the administration of projects
248
    def populate(self, req, ctx):
249
        self.plugin_styles[Plugin] = ["project.css"]
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
250
        self.plugin_scripts[Plugin] = ["project.js"]
1361 by William Grant
Remove the last +projectsets hardcoding.
251
        ctx['req'] = req
1165.2.3 by Nick Chadwick
Added a new Admin view, which allows for the administration of projects
252
        ctx['offering'] = self.context
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
253
        ctx['projectsets'] = []
1361 by William Grant
Remove the last +projectsets hardcoding.
254
        ctx['OfferingRESTView'] = OfferingRESTView
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
255
256
        #Open the projectset Fragment, and render it for inclusion
257
        #into the ProjectSets page
258
        #XXX: This could be a lot cleaner
259
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
260
261
        set_fragment = os.path.join(os.path.dirname(__file__),
262
                "templates/projectset_fragment.html")
263
        project_fragment = os.path.join(os.path.dirname(__file__),
264
                "templates/project_fragment.html")
265
266
        for projectset in self.context.project_sets:
267
            settmpl = loader.load(set_fragment)
268
            setCtx = Context()
1358 by William Grant
Use the publishing framework to generate URLs to projectsets.
269
            setCtx['req'] = req
1165.3.30 by William Grant
Clean out the projectset fragment context.
270
            setCtx['projectset'] = projectset
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
271
            setCtx['projects'] = []
1358 by William Grant
Use the publishing framework to generate URLs to projectsets.
272
            setCtx['GroupsView'] = GroupsView
273
            setCtx['ProjectSetRESTView'] = ProjectSetRESTView
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
274
275
            for project in projectset.projects:
276
                projecttmpl = loader.load(project_fragment)
277
                projectCtx = Context()
1358 by William Grant
Use the publishing framework to generate URLs to projectsets.
278
                projectCtx['req'] = req
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
279
                projectCtx['project'] = project
280
281
                setCtx['projects'].append(
282
                        projecttmpl.generate(projectCtx))
283
284
            ctx['projectsets'].append(settmpl.generate(setCtx))
285
286
287
class ProjectView(XHTMLView):
1165.2.3 by Nick Chadwick
Added a new Admin view, which allows for the administration of projects
288
    """View the submissions for a ProjectSet"""
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
289
    template = "templates/project.html"
290
    permission = "edit"
1165.3.18 by William Grant
Put the project listing and view in the Subjects tab.
291
    tab = 'subjects'
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
292
1165.3.61 by William Grant
Provide a Subversion command to grab each submission.
293
    def build_subversion_url(self, svnroot, submission):
294
        princ = submission.assessed.principal
295
296
        if isinstance(princ, User):
297
            path = 'users/%s' % princ.login
298
        else:
299
            path = 'groups/%s_%s_%s_%s' % (
300
                    princ.project_set.offering.subject.short_name,
301
                    princ.project_set.offering.semester.year,
302
                    princ.project_set.offering.semester.semester,
303
                    princ.name
304
                    )
305
        return urlparse.urljoin(
306
                    svnroot,
307
                    os.path.join(path, submission.path[1:] if
308
                                       submission.path.startswith(os.sep) else
309
                                       submission.path))
310
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
311
    def populate(self, req, ctx):
1165.3.66 by William Grant
Prettify the submissions table.
312
        self.plugin_styles[Plugin] = ["project.css"]
313
1375.1.4 by William Grant
Indicate when there is nobody assigned to a project, and link to the page to fix that.
314
        ctx['req'] = req
315
        ctx['GroupsView'] = GroupsView
316
        ctx['EnrolView'] = EnrolView
1165.3.14 by William Grant
Improve ProjectView's template substantially.
317
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
1165.3.61 by William Grant
Provide a Subversion command to grab each submission.
318
        ctx['build_subversion_url'] = self.build_subversion_url
319
        ctx['svn_addr'] = req.config['urls']['svn_addr']
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
320
        ctx['project'] = self.context
1165.3.61 by William Grant
Provide a Subversion command to grab each submission.
321
        ctx['user'] = req.user
1165.3.2 by Nick Chadwick
Created a new view for IVLE, allowing lecturers and tutors to
322
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
323
class Plugin(ViewPlugin, MediaPlugin):
1294.2.70 by William Grant
Split out ivle.webapp.admin's routes into annotated functions in ivle.webapp.traversal.
324
    forward_routes = (root_to_subject, subject_to_offering,
325
                      offering_to_project, offering_to_projectset)
326
    reverse_routes = (subject_url, offering_url, projectset_url, project_url)
1294.2.52 by William Grant
Port subject-related views to object traversal.
327
328
    views = [(ApplicationRoot, ('subjects', '+index'), SubjectsView),
1442.1.2 by William Grant
Add basic (ie. pretty much empty) offering index.
329
             (Offering, '+index', OfferingView),
1451.1.5 by William Grant
Add an OfferingEdit view, for setting the description and URL.
330
             (Offering, '+edit', OfferingEdit),
1365 by Matt Giuca
Added a new view under Offering/+enrolments to display all staff and students in an offering.
331
             (Offering, ('+enrolments', '+index'), EnrolmentsView),
1294.2.52 by William Grant
Port subject-related views to object traversal.
332
             (Offering, ('+enrolments', '+new'), EnrolView),
333
             (Offering, ('+projects', '+index'), OfferingProjectsView),
334
             (Project, '+index', ProjectView),
335
336
             (Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
337
             (ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
338
             ]
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
339
1294.2.94 by William Grant
Add a SubjectBreadcrumb.
340
    breadcrumbs = {Subject: SubjectBreadcrumb,
341
                   Offering: OfferingBreadcrumb,
1294.2.96 by William Grant
Add a UserBreadcrumb.
342
                   User: UserBreadcrumb,
1294.2.98 by William Grant
Add a ProjectBreadcrumb.
343
                   Project: ProjectBreadcrumb,
1294.2.89 by William Grant
Add an Offering breadcrumb.
344
                   }
345
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
346
    tabs = [
1118 by matt.giuca
Rewrote tooltips for the four tabs visible by default.
347
        ('subjects', 'Subjects',
348
         'View subject content and complete worksheets',
349
         'subjects.png', 'subjects', 5)
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
350
    ]
351
352
    media = 'subject-media'