1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
# IVLE
# Copyright (C) 2007-2009 The University of Melbourne
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# Author: Will Grant
"""Project submissions user interface."""
import os.path
import datetime
from storm.locals import Store
from ivle import database
from ivle.database import (User, ProjectGroup, Offering, Subject, Semester,
ProjectSet, Project, Enrolment)
from ivle.webapp.errors import NotFound, BadRequest
from ivle.webapp.base.xhtml import XHTMLView
from ivle.webapp.base.plugins import ViewPlugin
from ivle.webapp import ApplicationRoot
import ivle.date
import ivle.chat
from ivle import util
class SubmitView(XHTMLView):
"""A view to submit a Subversion repository path for a project."""
template = 'submit.html'
tab = 'files'
permission = 'submit_project'
subpath_allowed = True
def __init__(self, req, context, subpath):
super(SubmitView, self).__init__(req, context, subpath)
if len(subpath) < 1:
raise NotFound()
name = subpath[0]
# We need to work out which entity owns the repository, so we look
# at the first two path segments. The first tells us the type.
self.context = self.get_repository_owner(req.store, name)
if self.context is None:
raise NotFound()
self.offering = self.get_offering()
def get_repository_owner(self, store, name):
"""Return the owner of the repository given the name and a Store."""
raise NotImplementedError()
def get_offering(self):
"""Return the offering that this path can be submitted to."""
raise NotImplementedError()
def populate(self, req, ctx):
if req.method == 'POST':
data = dict(req.get_fieldstorage())
if 'revision' not in data:
raise BadRequest('No revision selected.')
try:
revision = int(data['revision'])
except ValueError:
raise BadRequest('Revision must be an integer.')
if 'project' not in data:
raise BadRequest('No project selected.')
try:
projectid = int(data['project'])
except ValueError:
raise BadRequest('Project must be an integer.')
# True, if the user has confirmed to submit late
late = 'late' in data and data['late'] == "yes"
project = req.store.find(Project, Project.id == projectid).one()
# This view's offering will be the sole offering for which the
# path is permissible. We need to check that.
if project.project_set.offering is not self.offering:
raise BadRequest('Path is not permissible for this offering.')
if project is None:
raise BadRequest('Specified project does not exist.')
try:
ctx['submission'] = project.submit(self.context,
unicode(self.path), revision, req.user,
late=late)
except database.DeadlinePassed:
# Show late submission page
self.template = 'submit-late.html'
ctx['project'] = project
except database.SubmissionError, e:
raise BadRequest(str(e) + ".")
else:
# The Subversion configuration needs to be updated, to grant
# tutors and lecturers access to this submission. We have to
# commit early so usrmgt-server can see the new submission.
req.store.commit()
# Instruct usrmgt-server to rebuild the SVN group authz file.
msg = {'rebuild_svn_group_config': {}}
usrmgt = ivle.chat.chat(req.config['usrmgt']['host'],
req.config['usrmgt']['port'],
msg,
req.config['usrmgt']['magic'],
)
if usrmgt.get('response') in (None, 'failure'):
raise Exception("Failure creating repository: "
+ str(usrmgt))
# Instruct usrmgt-server to rebuild the SVN user authz file.
msg = {'rebuild_svn_config': {}}
usrmgt = ivle.chat.chat(req.config['usrmgt']['host'],
req.config['usrmgt']['port'],
msg,
req.config['usrmgt']['magic'],
)
if usrmgt.get('response') in (None, 'failure'):
raise Exception("Failure creating repository: "
+ str(usrmgt))
self.template = 'submitted.html'
ctx['project'] = project
ctx['req'] = req
ctx['principal'] = self.context
ctx['offering'] = self.offering
ctx['path'] = self.path
ctx['now'] = datetime.datetime.now()
ctx['format_submission_principal'] = util.format_submission_principal
ctx['format_datetime'] = ivle.date.make_date_nice
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
@property
def path(self):
return os.path.join('/', *self.subpath[1:]) if self.subpath else '/'
class UserSubmitView(SubmitView):
def get_repository_owner(self, store, name):
'''Resolve the user name into a user.'''
return User.get_by_login(store, name)
def get_offering(self):
return Store.of(self.context).find(Offering,
Offering.id == Enrolment.offering_id,
Enrolment.user_id == self.context.id,
Offering.semester_id == Semester.id,
Semester.state == u'current',
Offering.subject_id == Subject.id,
Subject.short_name == self.path.split('/')[1],
).one()
@property
def fullpath(self):
"""Get the original path of this request, after the +submit."""
return os.path.join('/users/', *self.subpath)
class GroupSubmitView(SubmitView):
def get_repository_owner(self, store, name):
'''Resolve the subject_year_semester_group name into a group.'''
namebits = name.split('_', 3)
if len(namebits) != 4:
return None
# Find the project group with the given name in any project set in the
# offering of the given subject in the semester with the given year
# and semester.
return store.find(ProjectGroup,
ProjectGroup.name == namebits[3],
ProjectGroup.project_set_id == ProjectSet.id,
ProjectSet.offering_id == Offering.id,
Offering.subject_id == Subject.id,
Subject.short_name == namebits[0],
Offering.semester_id == Semester.id,
Semester.year == namebits[1],
Semester.url_name == namebits[2]).one()
def get_offering(self):
return self.context.project_set.offering
@property
def fullpath(self):
"""Get the original path of this request, after the +submit."""
return os.path.join('/groups/', *self.subpath)
class Plugin(ViewPlugin):
views = [(ApplicationRoot, ('+submit', 'users'), UserSubmitView),
(ApplicationRoot, ('+submit', 'groups'), GroupSubmitView)]
help = {'Submitting a project': 'help.html'}
|