46
47
from ivle import util
49
from ivle.webapp.admin.projectservice import ProjectSetRESTView
50
from ivle.webapp.admin.offeringservice import OfferingRESTView
51
50
from ivle.webapp.admin.publishing import (root_to_subject, root_to_semester,
52
51
subject_to_offering, offering_to_projectset, offering_to_project,
53
52
offering_to_enrolment, subject_url, semester_url, offering_url,
54
53
projectset_url, project_url, enrolment_url)
55
54
from ivle.webapp.admin.breadcrumbs import (SubjectBreadcrumb,
56
55
OfferingBreadcrumb, UserBreadcrumb, ProjectBreadcrumb,
56
ProjectsBreadcrumb, EnrolmentBreadcrumb)
58
57
from ivle.webapp.core import Plugin as CorePlugin
59
58
from ivle.webapp.groups import GroupsView
60
59
from ivle.webapp.media import media_url
680
687
def populate(self, req, ctx):
681
688
self.plugin_styles[Plugin] = ["project.css"]
682
self.plugin_scripts[Plugin] = ["project.js"]
684
690
ctx['offering'] = self.context
685
691
ctx['projectsets'] = []
686
ctx['OfferingRESTView'] = OfferingRESTView
688
693
#Open the projectset Fragment, and render it for inclusion
689
694
#into the ProjectSets page
690
#XXX: This could be a lot cleaner
691
loader = genshi.template.TemplateLoader(".", auto_reload=True)
693
695
set_fragment = os.path.join(os.path.dirname(__file__),
694
696
"templates/projectset_fragment.html")
695
697
project_fragment = os.path.join(os.path.dirname(__file__),
696
698
"templates/project_fragment.html")
698
for projectset in self.context.project_sets:
699
settmpl = loader.load(set_fragment)
701
self.context.project_sets.order_by(ivle.database.ProjectSet.id):
702
settmpl = self._loader.load(set_fragment)
700
703
setCtx = Context()
701
704
setCtx['req'] = req
702
705
setCtx['projectset'] = projectset
703
706
setCtx['projects'] = []
704
707
setCtx['GroupsView'] = GroupsView
705
setCtx['ProjectSetRESTView'] = ProjectSetRESTView
708
setCtx['ProjectSetEdit'] = ProjectSetEdit
709
setCtx['ProjectNew'] = ProjectNew
707
for project in projectset.projects:
708
projecttmpl = loader.load(project_fragment)
712
projectset.projects.order_by(ivle.database.Project.deadline):
713
projecttmpl = self._loader.load(project_fragment)
709
714
projectCtx = Context()
710
715
projectCtx['req'] = req
711
716
projectCtx['project'] = project
747
752
ctx['GroupsView'] = GroupsView
748
753
ctx['EnrolView'] = EnrolView
754
ctx['format_datetime'] = ivle.date.make_date_nice
749
755
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
750
756
ctx['build_subversion_url'] = self.build_subversion_url
751
757
ctx['svn_addr'] = req.config['urls']['svn_addr']
752
758
ctx['project'] = self.context
753
759
ctx['user'] = req.user
761
class ProjectUniquenessValidator(formencode.FancyValidator):
762
"""A FormEncode validator that checks that a project short_name is unique
765
The project referenced by state.existing_project is permitted to
766
hold that short_name. If any other project holds it, the input is rejected.
768
def _to_python(self, value, state):
769
if (state.store.find(
771
Project.short_name == unicode(value),
772
Project.project_set_id == ProjectSet.id,
773
ProjectSet.offering == state.offering).one() not in
774
(None, state.existing_project)):
775
raise formencode.Invalid(
776
"A project with that URL name already exists in this offering."
780
class ProjectSchema(formencode.Schema):
781
name = formencode.validators.UnicodeString(not_empty=True)
782
short_name = formencode.All(
783
URLNameValidator(not_empty=True),
784
ProjectUniquenessValidator())
785
deadline = DateTimeValidator(not_empty=True)
786
url = formencode.validators.URL(if_missing=None, not_empty=False)
787
synopsis = formencode.validators.UnicodeString(not_empty=True)
789
class ProjectNew(BaseFormView):
790
"""A form to create a new project."""
791
template = 'templates/project-new.html'
797
return ProjectSchema()
799
def populate(self, req, ctx):
800
super(ProjectNew, self).populate(req, ctx)
801
ctx['projectset'] = self.context
803
def populate_state(self, state):
804
state.offering = self.context.offering
805
state.existing_project = None
807
def get_default_data(self, req):
810
def save_object(self, req, data):
811
new_project = Project()
812
new_project.project_set = self.context
813
new_project.name = data['name']
814
new_project.short_name = data['short_name']
815
new_project.deadline = data['deadline']
816
new_project.url = unicode(data['url']) if data['url'] else None
817
new_project.synopsis = data['synopsis']
818
req.store.add(new_project)
821
class ProjectSetSchema(formencode.Schema):
822
group_size = formencode.validators.Int(if_missing=None, not_empty=False)
824
class ProjectSetEdit(BaseFormView):
825
"""A form to edit a project set."""
826
template = 'templates/projectset-edit.html'
832
return ProjectSetSchema()
834
def populate(self, req, ctx):
835
super(ProjectSetEdit, self).populate(req, ctx)
837
def get_default_data(self, req):
839
'group_size': self.context.max_students_per_group,
842
def save_object(self, req, data):
843
self.context.max_students_per_group = data['group_size']
846
class ProjectSetNew(BaseFormView):
847
"""A form to create a new project set."""
848
template = 'templates/projectset-new.html'
851
breadcrumb_text = "Projects"
855
return ProjectSetSchema()
857
def populate(self, req, ctx):
858
super(ProjectSetNew, self).populate(req, ctx)
860
def get_default_data(self, req):
863
def save_object(self, req, data):
864
new_set = ProjectSet()
865
new_set.offering = self.context
866
new_set.max_students_per_group = data['group_size']
867
req.store.add(new_set)
755
870
class Plugin(ViewPlugin, MediaPlugin):
756
871
forward_routes = (root_to_subject, root_to_semester, subject_to_offering,
757
872
offering_to_project, offering_to_projectset,
777
892
(Enrolment, '+edit', EnrolmentEdit),
778
893
(Enrolment, '+delete', EnrolmentDelete),
779
894
(Offering, ('+projects', '+index'), OfferingProjectsView),
895
(Offering, ('+projects', '+new-set'), ProjectSetNew),
896
(ProjectSet, '+edit', ProjectSetEdit),
897
(ProjectSet, '+new', ProjectNew),
780
898
(Project, '+index', ProjectView),
782
(Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
783
(ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
786
901
breadcrumbs = {Subject: SubjectBreadcrumb,