~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-26 03:06:53 UTC
  • Revision ID: grantw@unimelb.edu.au-20090526030653-axxawt0o5ws4icbt
Remove ivle.conf usage from ivle.studpath.

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
 
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
39
from ivle.webapp.errors import NotFound
32
 
from ivle.database import Subject
 
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
 
45
 
 
46
from ivle.webapp.admin.projectservice import ProjectSetRESTView,\
 
47
                                             ProjectRESTView
 
48
from ivle.webapp.admin.offeringservice import OfferingRESTView
34
49
 
35
50
 
36
51
class SubjectsView(XHTMLView):
37
52
    '''The view of the list of subjects.'''
38
 
    template = 'subjects.html'
39
 
    appname = 'subjects' # XXX
 
53
    template = 'templates/subjects.html'
 
54
    tab = 'subjects'
40
55
 
41
56
    def authorize(self, req):
42
57
        return req.user is not None
43
58
 
44
59
    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
 
 
 
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
66
249
 
67
250
class Plugin(ViewPlugin, MediaPlugin):
68
251
    urls = [
69
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
 
70
264
    ]
71
265
 
72
266
    tabs = [
73
 
        ('subjects', 'Subjects', 'Announcements and information about the '
74
 
         'subjects you are enrolled in.', 'subjects.png', 'subjects', 5)
 
267
        ('subjects', 'Subjects',
 
268
         'View subject content and complete worksheets',
 
269
         'subjects.png', 'subjects', 5)
75
270
    ]
76
271
 
77
272
    media = 'subject-media'