~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: 2010-07-27 12:09:13 UTC
  • mto: This revision was merged to the branch mainline in revision 1826.
  • Revision ID: grantw@unimelb.edu.au-20100727120913-v0kfnwxzbiwrjnue
(simple)json always returns a unicode when decoding, while cjson returned a str where possible. This makes cPickle unhappy, so convert back to a str.

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
302
301
        ctx['OfferingCloneWorksheets'] = OfferingCloneWorksheets
303
302
        ctx['GroupsView'] = GroupsView
304
303
        ctx['EnrolmentsView'] = EnrolmentsView
 
304
        ctx['Project'] = ivle.database.Project
305
305
 
306
306
        # As we go, calculate the total score for this subject
307
307
        # (Assessable worksheets only, mandatory problems only)
308
308
 
309
309
        ctx['worksheets'], problems_total, problems_done = (
310
310
            ivle.worksheet.utils.create_list_of_fake_worksheets_and_stats(
311
 
                req.config, req.store, req.user, self.context))
 
311
                req.config, req.store, req.user, self.context,
 
312
                as_of=self.context.worksheet_cutoff))
312
313
 
313
314
        ctx['exercises_total'] = problems_total
314
315
        ctx['exercises_done'] = problems_done
385
386
    description = formencode.validators.UnicodeString(
386
387
        if_missing=None, not_empty=False)
387
388
    url = formencode.validators.URL(if_missing=None, not_empty=False)
 
389
    worksheet_cutoff = DateTimeValidator(if_missing=None, not_empty=False)
388
390
    show_worksheet_marks = formencode.validators.StringBoolean(
389
391
        if_missing=False)
390
392
 
427
429
                        self.context.semester.semester,
428
430
            'url': self.context.url,
429
431
            'description': self.context.description,
 
432
            'worksheet_cutoff': self.context.worksheet_cutoff,
430
433
            'show_worksheet_marks': self.context.show_worksheet_marks,
431
434
            }
432
435
 
436
439
            self.context.semester = data['semester']
437
440
        self.context.description = data['description']
438
441
        self.context.url = unicode(data['url']) if data['url'] else None
 
442
        self.context.worksheet_cutoff = data['worksheet_cutoff']
439
443
        self.context.show_worksheet_marks = data['show_worksheet_marks']
440
444
        return self.context
441
445
 
471
475
        new_offering.semester = data['semester']
472
476
        new_offering.description = data['description']
473
477
        new_offering.url = unicode(data['url']) if data['url'] else None
 
478
        new_offering.worksheet_cutoff = data['worksheet_cutoff']
474
479
        new_offering.show_worksheet_marks = data['show_worksheet_marks']
475
480
 
476
481
        req.store.add(new_offering)
687
692
 
688
693
    def populate(self, req, ctx):
689
694
        self.plugin_styles[Plugin] = ["project.css"]
690
 
        self.plugin_scripts[Plugin] = ["project.js"]
691
695
        ctx['req'] = req
692
696
        ctx['offering'] = self.context
693
697
        ctx['projectsets'] = []
694
 
        ctx['OfferingRESTView'] = OfferingRESTView
695
698
 
696
699
        #Open the projectset Fragment, and render it for inclusion
697
700
        #into the ProjectSets page
708
711
            setCtx['projectset'] = projectset
709
712
            setCtx['projects'] = []
710
713
            setCtx['GroupsView'] = GroupsView
711
 
            setCtx['ProjectSetRESTView'] = ProjectSetRESTView
 
714
            setCtx['ProjectSetEdit'] = ProjectSetEdit
 
715
            setCtx['ProjectNew'] = ProjectNew
712
716
 
713
717
            for project in \
714
718
                projectset.projects.order_by(ivle.database.Project.deadline):
716
720
                projectCtx = Context()
717
721
                projectCtx['req'] = req
718
722
                projectCtx['project'] = project
 
723
                projectCtx['ProjectEdit'] = ProjectEdit
 
724
                projectCtx['ProjectDelete'] = ProjectDelete
719
725
 
720
726
                setCtx['projects'].append(
721
727
                        projecttmpl.generate(projectCtx))
729
735
    permission = "view_project_submissions"
730
736
    tab = 'subjects'
731
737
 
732
 
    def build_subversion_url(self, svnroot, submission):
 
738
    def build_subversion_url(self, req, submission):
733
739
        princ = submission.assessed.principal
734
740
 
735
 
        if isinstance(princ, User):
736
 
            path = 'users/%s' % princ.login
737
 
        else:
738
 
            path = 'groups/%s_%s_%s_%s' % (
739
 
                    princ.project_set.offering.subject.short_name,
740
 
                    princ.project_set.offering.semester.year,
741
 
                    princ.project_set.offering.semester.semester,
742
 
                    princ.name
743
 
                    )
744
 
        return urlparse.urljoin(
745
 
                    svnroot,
746
 
                    os.path.join(path, submission.path[1:] if
747
 
                                       submission.path.startswith(os.sep) else
748
 
                                       submission.path))
 
741
        return os.path.join(princ.get_svn_url(req.config, req),
 
742
                            submission.path[1:] if
 
743
                                submission.path.startswith(os.sep) else
 
744
                                submission.path)
749
745
 
750
746
    def populate(self, req, ctx):
751
747
        self.plugin_styles[Plugin] = ["project.css"]
752
748
 
753
749
        ctx['req'] = req
 
750
        ctx['permissions'] = self.context.get_permissions(req.user,req.config)
754
751
        ctx['GroupsView'] = GroupsView
755
752
        ctx['EnrolView'] = EnrolView
756
753
        ctx['format_datetime'] = ivle.date.make_date_nice
757
754
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
758
755
        ctx['build_subversion_url'] = self.build_subversion_url
759
 
        ctx['svn_addr'] = req.config['urls']['svn_addr']
760
756
        ctx['project'] = self.context
761
757
        ctx['user'] = req.user
 
758
        ctx['ProjectEdit'] = ProjectEdit
 
759
        ctx['ProjectDelete'] = ProjectDelete
 
760
 
 
761
class ProjectUniquenessValidator(formencode.FancyValidator):
 
762
    """A FormEncode validator that checks that a project short_name is unique
 
763
    in a given offering.
 
764
 
 
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.
 
767
    """
 
768
    def _to_python(self, value, state):
 
769
        if (state.store.find(
 
770
            Project,
 
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."
 
777
                , value, state)
 
778
        return value
 
779
 
 
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)
 
788
 
 
789
class ProjectEdit(BaseFormView):
 
790
    """A form to edit a project."""
 
791
    template = 'templates/project-edit.html'
 
792
    tab = 'subjects'
 
793
    permission = 'edit'
 
794
 
 
795
    @property
 
796
    def validator(self):
 
797
        return ProjectSchema()
 
798
 
 
799
    def populate(self, req, ctx):
 
800
        super(ProjectEdit, self).populate(req, ctx)
 
801
        ctx['projectset'] = self.context.project_set
 
802
 
 
803
    def populate_state(self, state):
 
804
        state.offering = self.context.project_set.offering
 
805
        state.existing_project = self.context
 
806
 
 
807
    def get_default_data(self, req):
 
808
        return {
 
809
            'name':         self.context.name,
 
810
            'short_name':   self.context.short_name,
 
811
            'deadline':     self.context.deadline,
 
812
            'url':          self.context.url,
 
813
            'synopsis':     self.context.synopsis,
 
814
            }
 
815
 
 
816
    def save_object(self, req, data):
 
817
        self.context.name = data['name']
 
818
        self.context.short_name = data['short_name']
 
819
        self.context.deadline = data['deadline']
 
820
        self.context.url = unicode(data['url']) if data['url'] else None
 
821
        self.context.synopsis = data['synopsis']
 
822
        return self.context
 
823
 
 
824
class ProjectNew(BaseFormView):
 
825
    """A form to create a new project."""
 
826
    template = 'templates/project-new.html'
 
827
    tab = 'subjects'
 
828
    permission = 'edit'
 
829
 
 
830
    @property
 
831
    def validator(self):
 
832
        return ProjectSchema()
 
833
 
 
834
    def populate(self, req, ctx):
 
835
        super(ProjectNew, self).populate(req, ctx)
 
836
        ctx['projectset'] = self.context
 
837
 
 
838
    def populate_state(self, state):
 
839
        state.offering = self.context.offering
 
840
        state.existing_project = None
 
841
 
 
842
    def get_default_data(self, req):
 
843
        return {}
 
844
 
 
845
    def save_object(self, req, data):
 
846
        new_project = Project()
 
847
        new_project.project_set = self.context
 
848
        new_project.name = data['name']
 
849
        new_project.short_name = data['short_name']
 
850
        new_project.deadline = data['deadline']
 
851
        new_project.url = unicode(data['url']) if data['url'] else None
 
852
        new_project.synopsis = data['synopsis']
 
853
        req.store.add(new_project)
 
854
        return new_project
 
855
 
 
856
class ProjectDelete(XHTMLView):
 
857
    """A form to delete a project."""
 
858
    template = 'templates/project-delete.html'
 
859
    tab = 'subjects'
 
860
    permission = 'edit'
 
861
 
 
862
    def populate(self, req, ctx):
 
863
        # If post, delete the project, or display a message explaining that
 
864
        # the project cannot be deleted
 
865
        if self.context.can_delete:
 
866
            if req.method == 'POST':
 
867
                self.context.delete()
 
868
                self.template = 'templates/project-deleted.html'
 
869
        else:
 
870
            # Can't delete
 
871
            self.template = 'templates/project-undeletable.html'
 
872
 
 
873
        # If get and can delete, display a delete confirmation page
 
874
 
 
875
        # Variables for the template
 
876
        ctx['req'] = req
 
877
        ctx['project'] = self.context
 
878
        ctx['OfferingProjectsView'] = OfferingProjectsView
 
879
 
 
880
class ProjectSetSchema(formencode.Schema):
 
881
    group_size = formencode.validators.Int(if_missing=None, not_empty=False)
 
882
 
 
883
class ProjectSetEdit(BaseFormView):
 
884
    """A form to edit a project set."""
 
885
    template = 'templates/projectset-edit.html'
 
886
    tab = 'subjects'
 
887
    permission = 'edit'
 
888
 
 
889
    @property
 
890
    def validator(self):
 
891
        return ProjectSetSchema()
 
892
 
 
893
    def populate(self, req, ctx):
 
894
        super(ProjectSetEdit, self).populate(req, ctx)
 
895
 
 
896
    def get_default_data(self, req):
 
897
        return {
 
898
            'group_size': self.context.max_students_per_group,
 
899
            }
 
900
 
 
901
    def save_object(self, req, data):
 
902
        self.context.max_students_per_group = data['group_size']
 
903
        return self.context
 
904
 
 
905
class ProjectSetNew(BaseFormView):
 
906
    """A form to create a new project set."""
 
907
    template = 'templates/projectset-new.html'
 
908
    tab = 'subjects'
 
909
    permission = 'edit'
 
910
    breadcrumb_text = "Projects"
 
911
 
 
912
    @property
 
913
    def validator(self):
 
914
        return ProjectSetSchema()
 
915
 
 
916
    def populate(self, req, ctx):
 
917
        super(ProjectSetNew, self).populate(req, ctx)
 
918
 
 
919
    def get_default_data(self, req):
 
920
        return {}
 
921
 
 
922
    def save_object(self, req, data):
 
923
        new_set = ProjectSet()
 
924
        new_set.offering = self.context
 
925
        new_set.max_students_per_group = data['group_size']
 
926
        req.store.add(new_set)
 
927
        return new_set
762
928
 
763
929
class Plugin(ViewPlugin, MediaPlugin):
764
930
    forward_routes = (root_to_subject, root_to_semester, subject_to_offering,
785
951
             (Enrolment, '+edit', EnrolmentEdit),
786
952
             (Enrolment, '+delete', EnrolmentDelete),
787
953
             (Offering, ('+projects', '+index'), OfferingProjectsView),
 
954
             (Offering, ('+projects', '+new-set'), ProjectSetNew),
 
955
             (ProjectSet, '+edit', ProjectSetEdit),
 
956
             (ProjectSet, '+new', ProjectNew),
788
957
             (Project, '+index', ProjectView),
789
 
 
790
 
             (Offering, ('+projectsets', '+new'), OfferingRESTView, 'api'),
791
 
             (ProjectSet, ('+projects', '+new'), ProjectSetRESTView, 'api'),
 
958
             (Project, '+edit', ProjectEdit),
 
959
             (Project, '+delete', ProjectDelete),
792
960
             ]
793
961
 
794
962
    breadcrumbs = {Subject: SubjectBreadcrumb,