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

« back to all changes in this revision

Viewing changes to ivle/webapp/admin/subject.py

  • Committer: William Grant
  • Date: 2009-05-18 12:13:44 UTC
  • Revision ID: grantw@unimelb.edu.au-20090518121344-m69gxcfqwoeirtru
Fix make_path_segments' docstring.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
25
import os
 
26
import os.path
 
27
import urllib
 
28
import urlparse
 
29
import cgi
 
30
 
 
31
from storm.locals import Desc
 
32
import genshi
 
33
from genshi.filters import HTMLFormFiller
 
34
from genshi.template import Context, TemplateLoader
 
35
import formencode
 
36
 
 
37
from ivle.webapp.base.xhtml import XHTMLView
 
38
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
 
39
from ivle.webapp.errors import NotFound
 
40
 
 
41
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
 
42
                          ProjectSet, Project, ProjectSubmission
 
43
from ivle import util
 
44
import ivle.date
 
45
 
 
46
from ivle.webapp.admin.projectservice import ProjectSetRESTView,\
 
47
                                             ProjectRESTView
 
48
from ivle.webapp.admin.offeringservice import OfferingRESTView
 
49
 
 
50
 
 
51
class SubjectsView(XHTMLView):
 
52
    '''The view of the list of subjects.'''
 
53
    template = 'templates/subjects.html'
 
54
    tab = 'subjects'
 
55
 
 
56
    def authorize(self, req):
 
57
        return req.user is not None
 
58
 
 
59
    def populate(self, req, ctx):
 
60
        ctx['user'] = req.user
 
61
        ctx['semesters'] = []
 
62
        for semester in req.store.find(Semester).order_by(Desc(Semester.year),
 
63
                                                     Desc(Semester.semester)):
 
64
            enrolments = semester.enrolments.find(user=req.user)
 
65
            if enrolments.count():
 
66
                ctx['semesters'].append((semester, enrolments))
 
67
 
 
68
 
 
69
class UserValidator(formencode.FancyValidator):
 
70
    """A FormEncode validator that turns a username into a user.
 
71
 
 
72
    The state must have a 'store' attribute, which is the Storm store
 
73
    to use."""
 
74
    def _to_python(self, value, state):
 
75
        user = User.get_by_login(state.store, value)
 
76
        if user:
 
77
            return user
 
78
        else:
 
79
            raise formencode.Invalid('User does not exist', value, state)
 
80
 
 
81
 
 
82
class NoEnrolmentValidator(formencode.FancyValidator):
 
83
    """A FormEncode validator that ensures absence of an enrolment.
 
84
 
 
85
    The state must have an 'offering' attribute.
 
86
    """
 
87
    def _to_python(self, value, state):
 
88
        if state.offering.get_enrolment(value):
 
89
            raise formencode.Invalid('User already enrolled', value, state)
 
90
        return value
 
91
 
 
92
 
 
93
class EnrolSchema(formencode.Schema):
 
94
    user = formencode.All(NoEnrolmentValidator(), UserValidator())
 
95
 
 
96
 
 
97
class EnrolView(XHTMLView):
 
98
    """A form to enrol a user in an offering."""
 
99
    template = 'templates/enrol.html'
 
100
    tab = 'subjects'
 
101
    permission = 'edit'
 
102
 
 
103
    def __init__(self, req, subject, year, semester):
 
104
        """Find the given offering by subject, year and semester."""
 
105
        self.context = req.store.find(Offering,
 
106
            Offering.subject_id == Subject.id,
 
107
            Subject.short_name == subject,
 
108
            Offering.semester_id == Semester.id,
 
109
            Semester.year == year,
 
110
            Semester.semester == semester).one()
 
111
 
 
112
        if not self.context:
 
113
            raise NotFound()
 
114
 
 
115
    def filter(self, stream, ctx):
 
116
        return stream | HTMLFormFiller(data=ctx['data'])
 
117
 
 
118
    def populate(self, req, ctx):
 
119
        if req.method == 'POST':
 
120
            data = dict(req.get_fieldstorage())
 
121
            try:
 
122
                validator = EnrolSchema()
 
123
                req.offering = self.context # XXX: Getting into state.
 
124
                data = validator.to_python(data, state=req)
 
125
                self.context.enrol(data['user'])
 
126
                req.store.commit()
 
127
                req.throw_redirect(req.uri)
 
128
            except formencode.Invalid, e:
 
129
                errors = e.unpack_errors()
 
130
        else:
 
131
            data = {}
 
132
            errors = {}
 
133
 
 
134
        ctx['data'] = data or {}
 
135
        ctx['offering'] = self.context
 
136
        ctx['errors'] = errors
 
137
 
 
138
class OfferingProjectsView(XHTMLView):
 
139
    """View the projects for an offering."""
 
140
    template = 'templates/offering_projects.html'
 
141
    permission = 'edit'
 
142
    tab = 'subjects'
 
143
    
 
144
    def __init__(self, req, subject, year, semester):
 
145
        self.context = req.store.find(Offering,
 
146
            Offering.subject_id == Subject.id,
 
147
            Subject.short_name == subject,
 
148
            Offering.semester_id == Semester.id,
 
149
            Semester.year == year,
 
150
            Semester.semester == semester).one()
 
151
 
 
152
        if not self.context:
 
153
            raise NotFound()
 
154
 
 
155
    def project_url(self, projectset, project):
 
156
        return "/subjects/%s/%s/%s/+projects/%s" % (
 
157
                    self.context.subject.short_name,
 
158
                    self.context.semester.year,
 
159
                    self.context.semester.semester,
 
160
                    project.short_name
 
161
                    )
 
162
 
 
163
    def new_project_url(self, projectset):
 
164
        return "/api/subjects/" + self.context.subject.short_name + "/" +\
 
165
                self.context.semester.year + "/" + \
 
166
                self.context.semester.semester + "/+projectsets/" +\
 
167
                str(projectset.id) + "/+projects/+new"
 
168
    
 
169
    def populate(self, req, ctx):
 
170
        self.plugin_styles[Plugin] = ["project.css"]
 
171
        self.plugin_scripts[Plugin] = ["project.js"]
 
172
        ctx['offering'] = self.context
 
173
        ctx['projectsets'] = []
 
174
 
 
175
        #Open the projectset Fragment, and render it for inclusion
 
176
        #into the ProjectSets page
 
177
        #XXX: This could be a lot cleaner
 
178
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
 
179
 
 
180
        set_fragment = os.path.join(os.path.dirname(__file__),
 
181
                "templates/projectset_fragment.html")
 
182
        project_fragment = os.path.join(os.path.dirname(__file__),
 
183
                "templates/project_fragment.html")
 
184
 
 
185
        for projectset in self.context.project_sets:
 
186
            settmpl = loader.load(set_fragment)
 
187
            setCtx = Context()
 
188
            setCtx['projectset'] = projectset
 
189
            setCtx['new_project_url'] = self.new_project_url(projectset)
 
190
            setCtx['projects'] = []
 
191
 
 
192
            for project in projectset.projects:
 
193
                projecttmpl = loader.load(project_fragment)
 
194
                projectCtx = Context()
 
195
                projectCtx['project'] = project
 
196
                projectCtx['project_url'] = self.project_url(projectset, project)
 
197
 
 
198
                setCtx['projects'].append(
 
199
                        projecttmpl.generate(projectCtx))
 
200
 
 
201
            ctx['projectsets'].append(settmpl.generate(setCtx))
 
202
 
 
203
 
 
204
class ProjectView(XHTMLView):
 
205
    """View the submissions for a ProjectSet"""
 
206
    template = "templates/project.html"
 
207
    permission = "edit"
 
208
    tab = 'subjects'
 
209
 
 
210
    def __init__(self, req, subject, year, semester, project):
 
211
        self.context = req.store.find(Project,
 
212
                Project.short_name == project,
 
213
                Project.project_set_id == ProjectSet.id,
 
214
                ProjectSet.offering_id == Offering.id,
 
215
                Offering.semester_id == Semester.id,
 
216
                Semester.year == year,
 
217
                Semester.semester == semester,
 
218
                Offering.subject_id == Subject.id,
 
219
                Subject.short_name == subject).one()
 
220
        if self.context is None:
 
221
            raise NotFound()
 
222
 
 
223
    def build_subversion_url(self, svnroot, submission):
 
224
        princ = submission.assessed.principal
 
225
 
 
226
        if isinstance(princ, User):
 
227
            path = 'users/%s' % princ.login
 
228
        else:
 
229
            path = 'groups/%s_%s_%s_%s' % (
 
230
                    princ.project_set.offering.subject.short_name,
 
231
                    princ.project_set.offering.semester.year,
 
232
                    princ.project_set.offering.semester.semester,
 
233
                    princ.name
 
234
                    )
 
235
        return urlparse.urljoin(
 
236
                    svnroot,
 
237
                    os.path.join(path, submission.path[1:] if
 
238
                                       submission.path.startswith(os.sep) else
 
239
                                       submission.path))
 
240
 
 
241
    def populate(self, req, ctx):
 
242
        self.plugin_styles[Plugin] = ["project.css"]
 
243
 
 
244
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
 
245
        ctx['build_subversion_url'] = self.build_subversion_url
 
246
        ctx['svn_addr'] = req.config['urls']['svn_addr']
 
247
        ctx['project'] = self.context
 
248
        ctx['user'] = req.user
 
249
 
 
250
class Plugin(ViewPlugin, MediaPlugin):
 
251
    urls = [
 
252
        ('subjects/', SubjectsView),
 
253
        ('subjects/:subject/:year/:semester/+enrolments/+new', EnrolView),
 
254
        ('subjects/:subject/:year/:semester/+projects', OfferingProjectsView),
 
255
        ('subjects/:subject/:year/:semester/+projects/:project', ProjectView),
 
256
        #API Views
 
257
        ('api/subjects/:subject/:year/:semester/+projectsets/+new',
 
258
            OfferingRESTView),
 
259
        ('api/subjects/:subject/:year/:semester/+projectsets/:projectset/+projects/+new',
 
260
            ProjectSetRESTView),
 
261
        ('api/subjects/:subject/:year/:semester/+projects/:project', 
 
262
            ProjectRESTView),
 
263
 
 
264
    ]
 
265
 
 
266
    tabs = [
 
267
        ('subjects', 'Subjects',
 
268
         'View subject content and complete worksheets',
 
269
         'subjects.png', 'subjects', 5)
 
270
    ]
 
271
 
 
272
    media = 'subject-media'