~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/admin/subject.py

  • Committer: Matt Giuca
  • Date: 2010-02-25 08:26:04 UTC
  • mfrom: (1710.1.25 new-project-ui)
  • Revision ID: matt.giuca@gmail.com-20100225082604-0fku57vycn5cgonf
Replaced AJAX UI for project creation with a standard one. Can now edit project sets, and edit/delete projects. Also many more links to such things, due to the fact that they have their own URLs now. Fixes Launchpad bug #493942.

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
import formencode
36
36
import formencode.validators
37
37
 
38
 
from ivle.webapp.base.forms import BaseFormView, URLNameValidator
 
38
from ivle.webapp.base.forms import (BaseFormView, URLNameValidator,
 
39
                                    DateTimeValidator)
39
40
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
40
41
from ivle.webapp.base.xhtml import XHTMLView
41
42
from ivle.webapp.errors import BadRequest
46
47
from ivle import util
47
48
import ivle.date
48
49
 
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,
57
 
            EnrolmentBreadcrumb)
 
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
688
687
 
689
688
    def populate(self, req, ctx):
690
689
        self.plugin_styles[Plugin] = ["project.css"]
691
 
        self.plugin_scripts[Plugin] = ["project.js"]
692
690
        ctx['req'] = req
693
691
        ctx['offering'] = self.context
694
692
        ctx['projectsets'] = []
695
 
        ctx['OfferingRESTView'] = OfferingRESTView
696
693
 
697
694
        #Open the projectset Fragment, and render it for inclusion
698
695
        #into the ProjectSets page
709
706
            setCtx['projectset'] = projectset
710
707
            setCtx['projects'] = []
711
708
            setCtx['GroupsView'] = GroupsView
712
 
            setCtx['ProjectSetRESTView'] = ProjectSetRESTView
 
709
            setCtx['ProjectSetEdit'] = ProjectSetEdit
 
710
            setCtx['ProjectNew'] = ProjectNew
713
711
 
714
712
            for project in \
715
713
                projectset.projects.order_by(ivle.database.Project.deadline):
717
715
                projectCtx = Context()
718
716
                projectCtx['req'] = req
719
717
                projectCtx['project'] = project
 
718
                projectCtx['ProjectEdit'] = ProjectEdit
 
719
                projectCtx['ProjectDelete'] = ProjectDelete
720
720
 
721
721
                setCtx['projects'].append(
722
722
                        projecttmpl.generate(projectCtx))
752
752
        self.plugin_styles[Plugin] = ["project.css"]
753
753
 
754
754
        ctx['req'] = req
 
755
        ctx['permissions'] = self.context.get_permissions(req.user,req.config)
755
756
        ctx['GroupsView'] = GroupsView
756
757
        ctx['EnrolView'] = EnrolView
757
758
        ctx['format_datetime'] = ivle.date.make_date_nice
760
761
        ctx['svn_addr'] = req.config['urls']['svn_addr']
761
762
        ctx['project'] = self.context
762
763
        ctx['user'] = req.user
 
764
        ctx['ProjectEdit'] = ProjectEdit
 
765
        ctx['ProjectDelete'] = ProjectDelete
 
766
 
 
767
class ProjectUniquenessValidator(formencode.FancyValidator):
 
768
    """A FormEncode validator that checks that a project short_name is unique
 
769
    in a given offering.
 
770
 
 
771
    The project referenced by state.existing_project is permitted to
 
772
    hold that short_name. If any other project holds it, the input is rejected.
 
773
    """
 
774
    def _to_python(self, value, state):
 
775
        if (state.store.find(
 
776
            Project,
 
777
            Project.short_name == unicode(value),
 
778
            Project.project_set_id == ProjectSet.id,
 
779
            ProjectSet.offering == state.offering).one() not in
 
780
            (None, state.existing_project)):
 
781
            raise formencode.Invalid(
 
782
                "A project with that URL name already exists in this offering."
 
783
                , value, state)
 
784
        return value
 
785
 
 
786
class ProjectSchema(formencode.Schema):
 
787
    name = formencode.validators.UnicodeString(not_empty=True)
 
788
    short_name = formencode.All(
 
789
        URLNameValidator(not_empty=True),
 
790
        ProjectUniquenessValidator())
 
791
    deadline = DateTimeValidator(not_empty=True)
 
792
    url = formencode.validators.URL(if_missing=None, not_empty=False)
 
793
    synopsis = formencode.validators.UnicodeString(not_empty=True)
 
794
 
 
795
class ProjectEdit(BaseFormView):
 
796
    """A form to edit a project."""
 
797
    template = 'templates/project-edit.html'
 
798
    tab = 'subjects'
 
799
    permission = 'edit'
 
800
 
 
801
    @property
 
802
    def validator(self):
 
803
        return ProjectSchema()
 
804
 
 
805
    def populate(self, req, ctx):
 
806
        super(ProjectEdit, self).populate(req, ctx)
 
807
        ctx['projectset'] = self.context.project_set
 
808
 
 
809
    def populate_state(self, state):
 
810
        state.offering = self.context.project_set.offering
 
811
        state.existing_project = self.context
 
812
 
 
813
    def get_default_data(self, req):
 
814
        return {
 
815
            'name':         self.context.name,
 
816
            'short_name':   self.context.short_name,
 
817
            'deadline':     self.context.deadline,
 
818
            'url':          self.context.url,
 
819
            'synopsis':     self.context.synopsis,
 
820
            }
 
821
 
 
822
    def save_object(self, req, data):
 
823
        self.context.name = data['name']
 
824
        self.context.short_name = data['short_name']
 
825
        self.context.deadline = data['deadline']
 
826
        self.context.url = unicode(data['url']) if data['url'] else None
 
827
        self.context.synopsis = data['synopsis']
 
828
        return self.context
 
829
 
 
830
class ProjectNew(BaseFormView):
 
831
    """A form to create a new project."""
 
832
    template = 'templates/project-new.html'
 
833
    tab = 'subjects'
 
834
    permission = 'edit'
 
835
 
 
836
    @property
 
837
    def validator(self):
 
838
        return ProjectSchema()
 
839
 
 
840
    def populate(self, req, ctx):
 
841
        super(ProjectNew, self).populate(req, ctx)
 
842
        ctx['projectset'] = self.context
 
843
 
 
844
    def populate_state(self, state):
 
845
        state.offering = self.context.offering
 
846
        state.existing_project = None
 
847
 
 
848
    def get_default_data(self, req):
 
849
        return {}
 
850
 
 
851
    def save_object(self, req, data):
 
852
        new_project = Project()
 
853
        new_project.project_set = self.context
 
854
        new_project.name = data['name']
 
855
        new_project.short_name = data['short_name']
 
856
        new_project.deadline = data['deadline']
 
857
        new_project.url = unicode(data['url']) if data['url'] else None
 
858
        new_project.synopsis = data['synopsis']
 
859
        req.store.add(new_project)
 
860
        return new_project
 
861
 
 
862
class ProjectDelete(XHTMLView):
 
863
    """A form to delete a project."""
 
864
    template = 'templates/project-delete.html'
 
865
    tab = 'subjects'
 
866
    permission = 'edit'
 
867
 
 
868
    def populate(self, req, ctx):
 
869
        # If post, delete the project, or display a message explaining that
 
870
        # the project cannot be deleted
 
871
        if self.context.can_delete:
 
872
            if req.method == 'POST':
 
873
                self.context.delete()
 
874
                self.template = 'templates/project-deleted.html'
 
875
        else:
 
876
            # Can't delete
 
877
            self.template = 'templates/project-undeletable.html'
 
878
 
 
879
        # If get and can delete, display a delete confirmation page
 
880
 
 
881
        # Variables for the template
 
882
        ctx['req'] = req
 
883
        ctx['project'] = self.context
 
884
        ctx['OfferingProjectsView'] = OfferingProjectsView
 
885
 
 
886
class ProjectSetSchema(formencode.Schema):
 
887
    group_size = formencode.validators.Int(if_missing=None, not_empty=False)
 
888
 
 
889
class ProjectSetEdit(BaseFormView):
 
890
    """A form to edit a project set."""
 
891
    template = 'templates/projectset-edit.html'
 
892
    tab = 'subjects'
 
893
    permission = 'edit'
 
894
 
 
895
    @property
 
896
    def validator(self):
 
897
        return ProjectSetSchema()
 
898
 
 
899
    def populate(self, req, ctx):
 
900
        super(ProjectSetEdit, self).populate(req, ctx)
 
901
 
 
902
    def get_default_data(self, req):
 
903
        return {
 
904
            'group_size': self.context.max_students_per_group,
 
905
            }
 
906
 
 
907
    def save_object(self, req, data):
 
908
        self.context.max_students_per_group = data['group_size']
 
909
        return self.context
 
910
 
 
911
class ProjectSetNew(BaseFormView):
 
912
    """A form to create a new project set."""
 
913
    template = 'templates/projectset-new.html'
 
914
    tab = 'subjects'
 
915
    permission = 'edit'
 
916
    breadcrumb_text = "Projects"
 
917
 
 
918
    @property
 
919
    def validator(self):
 
920
        return ProjectSetSchema()
 
921
 
 
922
    def populate(self, req, ctx):
 
923
        super(ProjectSetNew, self).populate(req, ctx)
 
924
 
 
925
    def get_default_data(self, req):
 
926
        return {}
 
927
 
 
928
    def save_object(self, req, data):
 
929
        new_set = ProjectSet()
 
930
        new_set.offering = self.context
 
931
        new_set.max_students_per_group = data['group_size']
 
932
        req.store.add(new_set)
 
933
        return new_set
763
934
 
764
935
class Plugin(ViewPlugin, MediaPlugin):
765
936
    forward_routes = (root_to_subject, root_to_semester, subject_to_offering,
786
957
             (Enrolment, '+edit', EnrolmentEdit),
787
958
             (Enrolment, '+delete', EnrolmentDelete),
788
959
             (Offering, ('+projects', '+index'), OfferingProjectsView),
 
960
             (Offering, ('+projects', '+new-set'), ProjectSetNew),
 
961
             (ProjectSet, '+edit', ProjectSetEdit),
 
962
             (ProjectSet, '+new', ProjectNew),
789
963
             (Project, '+index', ProjectView),
790
 
 
791
 
             (Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
792
 
             (ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
 
964
             (Project, '+edit', ProjectEdit),
 
965
             (Project, '+delete', ProjectDelete),
793
966
             ]
794
967
 
795
968
    breadcrumbs = {Subject: SubjectBreadcrumb,