~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-07-04 11:54:26 UTC
  • mto: (1294.4.2 ui-the-third)
  • mto: This revision was merged to the branch mainline in revision 1353.
  • Revision ID: grantw@unimelb.edu.au-20090704115426-bq99fyr7zaopn7i6
Port subject-related views to object traversal.

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, Store
 
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
from ivle.webapp import ApplicationRoot
 
41
 
 
42
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
 
43
                          ProjectSet, Project, ProjectSubmission
 
44
from ivle import util
 
45
import ivle.date
 
46
 
 
47
from ivle.webapp.admin.projectservice import ProjectSetRESTView,\
 
48
                                             ProjectRESTView
 
49
from ivle.webapp.admin.offeringservice import OfferingRESTView
 
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 filter(self, stream, ctx):
 
104
        return stream | HTMLFormFiller(data=ctx['data'])
 
105
 
 
106
    def populate(self, req, ctx):
 
107
        if req.method == 'POST':
 
108
            data = dict(req.get_fieldstorage())
 
109
            try:
 
110
                validator = EnrolSchema()
 
111
                req.offering = self.context # XXX: Getting into state.
 
112
                data = validator.to_python(data, state=req)
 
113
                self.context.enrol(data['user'])
 
114
                req.store.commit()
 
115
                req.throw_redirect(req.uri)
 
116
            except formencode.Invalid, e:
 
117
                errors = e.unpack_errors()
 
118
        else:
 
119
            data = {}
 
120
            errors = {}
 
121
 
 
122
        ctx['data'] = data or {}
 
123
        ctx['offering'] = self.context
 
124
        ctx['errors'] = errors
 
125
 
 
126
class OfferingProjectsView(XHTMLView):
 
127
    """View the projects for an offering."""
 
128
    template = 'templates/offering_projects.html'
 
129
    permission = 'edit'
 
130
    tab = 'subjects'
 
131
 
 
132
    def project_url(self, projectset, project):
 
133
        return "/subjects/%s/%s/%s/+projects/%s" % (
 
134
                    self.context.subject.short_name,
 
135
                    self.context.semester.year,
 
136
                    self.context.semester.semester,
 
137
                    project.short_name
 
138
                    )
 
139
 
 
140
    def new_project_url(self, projectset):
 
141
        return "/api/subjects/" + self.context.subject.short_name + "/" +\
 
142
                self.context.semester.year + "/" + \
 
143
                self.context.semester.semester + "/+projectsets/" +\
 
144
                str(projectset.id) + "/+projects/+new"
 
145
    
 
146
    def populate(self, req, ctx):
 
147
        self.plugin_styles[Plugin] = ["project.css"]
 
148
        self.plugin_scripts[Plugin] = ["project.js"]
 
149
        ctx['offering'] = self.context
 
150
        ctx['projectsets'] = []
 
151
 
 
152
        #Open the projectset Fragment, and render it for inclusion
 
153
        #into the ProjectSets page
 
154
        #XXX: This could be a lot cleaner
 
155
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
 
156
 
 
157
        set_fragment = os.path.join(os.path.dirname(__file__),
 
158
                "templates/projectset_fragment.html")
 
159
        project_fragment = os.path.join(os.path.dirname(__file__),
 
160
                "templates/project_fragment.html")
 
161
 
 
162
        for projectset in self.context.project_sets:
 
163
            settmpl = loader.load(set_fragment)
 
164
            setCtx = Context()
 
165
            setCtx['projectset'] = projectset
 
166
            setCtx['new_project_url'] = self.new_project_url(projectset)
 
167
            setCtx['projects'] = []
 
168
 
 
169
            for project in projectset.projects:
 
170
                projecttmpl = loader.load(project_fragment)
 
171
                projectCtx = Context()
 
172
                projectCtx['project'] = project
 
173
                projectCtx['project_url'] = self.project_url(projectset, project)
 
174
 
 
175
                setCtx['projects'].append(
 
176
                        projecttmpl.generate(projectCtx))
 
177
 
 
178
            ctx['projectsets'].append(settmpl.generate(setCtx))
 
179
 
 
180
 
 
181
class ProjectView(XHTMLView):
 
182
    """View the submissions for a ProjectSet"""
 
183
    template = "templates/project.html"
 
184
    permission = "edit"
 
185
    tab = 'subjects'
 
186
 
 
187
    def build_subversion_url(self, svnroot, submission):
 
188
        princ = submission.assessed.principal
 
189
 
 
190
        if isinstance(princ, User):
 
191
            path = 'users/%s' % princ.login
 
192
        else:
 
193
            path = 'groups/%s_%s_%s_%s' % (
 
194
                    princ.project_set.offering.subject.short_name,
 
195
                    princ.project_set.offering.semester.year,
 
196
                    princ.project_set.offering.semester.semester,
 
197
                    princ.name
 
198
                    )
 
199
        return urlparse.urljoin(
 
200
                    svnroot,
 
201
                    os.path.join(path, submission.path[1:] if
 
202
                                       submission.path.startswith(os.sep) else
 
203
                                       submission.path))
 
204
 
 
205
    def populate(self, req, ctx):
 
206
        self.plugin_styles[Plugin] = ["project.css"]
 
207
 
 
208
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
 
209
        ctx['build_subversion_url'] = self.build_subversion_url
 
210
        ctx['svn_addr'] = req.config['urls']['svn_addr']
 
211
        ctx['project'] = self.context
 
212
        ctx['user'] = req.user
 
213
 
 
214
class OfferingEnrolmentSet(object):
 
215
    def __init__(self, offering):
 
216
        self.offering = offering
 
217
 
 
218
def root_to_subject(root, name):
 
219
    return root.store.find(Subject, short_name=name).one()
 
220
 
 
221
def subject_to_offering(subject, year, semester):
 
222
    return subject.offering_for_semester(year, semester)
 
223
 
 
224
def offering_to_project(offering, name):
 
225
    return Store.of(offering).find(Project,
 
226
                                   Project.project_set_id == ProjectSet.id,
 
227
                                   ProjectSet.offering == offering).one()
 
228
 
 
229
def offering_to_projectset(offering, name):
 
230
    return Store.of(offering).find(ProjectSet,
 
231
                                   ProjectSet.offering == offering).one()
 
232
 
 
233
class Plugin(ViewPlugin, MediaPlugin):
 
234
    forward_routes = [(ApplicationRoot, 'subjects', root_to_subject, 1),
 
235
                      (Subject, None, subject_to_offering, 2),
 
236
                      (Offering, '+projects', offering_to_project, 1),
 
237
                      (Offering, '+projectsets', offering_to_projectset, 1),
 
238
                      ]
 
239
 
 
240
    views = [(ApplicationRoot, ('subjects', '+index'), SubjectsView),
 
241
             (Offering, ('+enrolments', '+new'), EnrolView),
 
242
             (Offering, ('+projects', '+index'), OfferingProjectsView),
 
243
             (Project, '+index', ProjectView),
 
244
 
 
245
             (Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
 
246
             (ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
 
247
             (Project, '+index', ProjectRESTView, 'api'),
 
248
             ]
 
249
 
 
250
    tabs = [
 
251
        ('subjects', 'Subjects',
 
252
         'View subject content and complete worksheets',
 
253
         'subjects.png', 'subjects', 5)
 
254
    ]
 
255
 
 
256
    media = 'subject-media'