~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-12-08 05:26:00 UTC
  • Revision ID: grantw@unimelb.edu.au-20091208052600-j5jpe25vgjtypex9
Fix missing GroupsView when creating a project set.

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