~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-02-26 02:55:51 UTC
  • Revision ID: grantw@unimelb.edu.au-20090226025551-hofgfw6e7h3pt54i
Tags: 0.1.9.2, 0.1.9.3, 0.1.9.4
Move the group admin view to per-offering.

Show diffs side-by-side

added added

removed removed

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