more work on new-course; changes to Course model; various bugfixes.
authorgfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Sun, 8 Mar 2009 01:48:58 +0000 (01:48 +0000)
committergfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Sun, 8 Mar 2009 01:48:58 +0000 (01:48 +0000)
git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@143 6d9bc8c9-1ec2-4278-b937-99fde70a366f

conifer/syrup/models.py
conifer/syrup/views.py
conifer/templates/add_new_course.xhtml
conifer/templates/welcome.xhtml

index 7266eee..f76f292 100644 (file)
@@ -153,21 +153,36 @@ class Course(m.Model):
     term = m.ForeignKey(Term)
     title = m.CharField(max_length=1024)
     access = m.CharField(max_length=5,
-                         choices = [('ANON', _('World-accessible')),
-                                    ('LOGIN', _('Accessible to all logged-in users')),
-                                    ('STUDT', _('Accessible to course students')),
-                                    ('CLOSE', _('Accessible only to course owners'))],
+                         choices = [
+                                 ('ANON', _('World-accessible')),
+                                 ('LOGIN', _('Accessible to all logged-in users')),
+                                 ('STUDT', _('Accessible to course students (by section)')),
+                                 ('PASWD', _('Accessible to course students (by password)')),
+                                 ('CLOSE', _('Accessible only to course owners'))],
                          default='CLOSE')
 
-    # For sites that use a passphrase as an invitation (STUDT access).
-    passkey = m.CharField(db_index=True, unique=True, blank=True, null=True,
-                          max_length=255)
+    # For sites that use a passphrase as an invitation (PASWD access).
+    passkey = m.CharField(db_index=True, blank=True, null=True, max_length=255)
 
     # For sites that have registration-lists from an external system
     # (STUDT access).
     enrol_codes  = m.CharField(_('Registrar keys for class lists'),
                                max_length=4098, 
                                blank=True, null=True)
+
+
+    def save(self, force_insert=False, force_update=False):
+        # We need to override save() to ensure unique passkey
+        # values. Django (and some backend databases) will not allow
+        # multiple NULL values in a unique column.
+        if self.passkey:
+            try:
+                already = Course.objects.get(passkey=self.passkey)
+            except Course.DoesNotExist:
+                super(Course, self).save(force_insert, force_update)
+        else:
+            super(Course, self).save(force_insert, force_update)
+
     def __unicode__(self):
         return self.code or self.title
 
index 57e6b17..4412c52 100644 (file)
@@ -57,7 +57,7 @@ def welcome(request):
 def open_courses(request):
     page_num = int(request.GET.get('page', 1))
     count = int(request.GET.get('count', 5))
-    paginator = Paginator(models.Course.objects.filter(moderated=False), count)
+    paginator = Paginator(models.Course.objects.all(), count) # fixme, what filter?
     return g.render('open_courses.xhtml', paginator=paginator,
                     page_num=page_num,
                     count=count)
@@ -71,9 +71,9 @@ def instructors(request):
     if action == 'join':
         paginator = Paginator(models.UserProfile.active_instructors(), count)
     elif action == 'drop':
-        paginator = Paginator(models.Course.objects.filter(moderated=False), count)
+        paginator = Paginator(models.Course.objects.all(), count) # fixme, what filter?
     else:
-        paginator = Paginator(models.Course.objects.filter(moderated=False), count)
+        paginator = Paginator(models.Course.objects.all(), count) # fixme, what filter?
         
     return g.render('instructors.xhtml', paginator=paginator,
                     page_num=page_num,
@@ -117,9 +117,26 @@ def browse_courses(request, browse_option=''):
 def my_courses(request):
     return g.render('my_courses.xhtml')
 
+def course_detail(request, course_id):
+    course = get_object_or_404(models.Course, pk=course_id)
+    if course.access != 'ANON' and request.user.is_anonymous():
+        #fixme, don't stop access just if anonymous, but rather if not
+        #allowed to access. We need to set up a permissions model.
+        return login_required(lambda *args: None)(request)
+    return g.render('course_detail.xhtml', course=course)
+
+def course_search(request, course_id):
+    course = get_object_or_404(models.Course, pk=course_id)
+    return search(request, in_course=course)
+
+
+#------------------------------------------------------------
+# Creating a new course
+
 class NewCourseForm(ModelForm):
     class Meta:
         model = models.Course
+
     def clean_code(self):
         v = (self.cleaned_data.get('code') or '').strip()
         is_valid_func = models.course_codes.course_code_is_valid
@@ -128,6 +145,15 @@ class NewCourseForm(ModelForm):
         else:
             raise ValidationError, _('invalid course code')
 
+# I want different choice labels on the access screen than are given
+# in the model.
+NewCourseForm.base_fields['access'].widget.choices = [
+    ('CLOSE', _('For now, just myself and designated colleagues')),
+    ('STUDT', _('Students in my course (I will provide section numbers)')),
+    ('PASWD', _('Students in my course (I will share a password with them)')),
+    ('LOGIN', _('All Reserves patrons'))]
+
+
 # hack the new-course form if we have course-code lookup
 COURSE_CODE_LIST = bool(models.course_codes.course_code_list)
 COURSE_CODE_LOOKUP_TITLE = bool(models.course_codes.course_code_lookup_title)
@@ -153,23 +179,22 @@ def add_new_course(request):
         form = NewCourseForm(request.POST, instance=models.Course())
         if not form.is_valid():
             return g.render('add_new_course.xhtml', **locals())
+        else:
+            form.save()
+            course = form.instance
+            assert course.id
+            mbr = course.member_set.create(user=request.user, role='INSTR')
+            mbr.save()
+                                     
+            # fixme, need to ask about PASWD and STUDT settings before redirect.
+            return HttpResponseRedirect('../') # back to My Courses
 
 def add_new_course_ajax_title(request):
     course_code = request.GET['course_code']
     title = models.course_codes.course_code_lookup_title(course_code)
     return HttpResponse(simplejson.dumps({'title':title}))
 
-def course_detail(request, course_id):
-    course = get_object_or_404(models.Course, pk=course_id)
-    if course.moderated and request.user.is_anonymous():
-        #fixme, don't stop access just if anonymous, but rather if not
-        #allowed to access. We need to set up a permissions model.
-        return login_required(lambda *args: None)(request)
-    return g.render('course_detail.xhtml', course=course)
-
-def course_search(request, course_id):
-    course = get_object_or_404(models.Course, pk=course_id)
-    return search(request, in_course=course)
+#------------------------------------------------------------
 
 def instructor_detail(request, instructor_id):
     page_num = int(request.GET.get('page', 1))
@@ -319,7 +344,9 @@ def item_add(request, course_id, item_id):
 def item_edit(request, course_id, item_id):
     course = get_object_or_404(models.Course, pk=course_id)
     item = get_object_or_404(models.Item, pk=item_id, course__id=course_id)
-    template = 'item_add_%s.xhtml' % item.item_type.lower()
+    item_type = item.item_type
+    template = 'item_add_%s.xhtml' % item_type.lower()
+    parent_item = item.parent_heading
     if request.method == 'GET':
         return g.render(template, **locals())
     else:
index cdba3f4..3f39a6c 100644 (file)
@@ -11,17 +11,26 @@ title = _('Add a new course site')
 </head>
 <body>
   <h1>${title}</h1>
+  <ul py:for="fld in form" py:if="fld.errors">
+    <li py:for="err in fld.errors">${fld.name}: ${err}</li>
+  </ul>
   <form action="." method="POST">
-    <tr py:def="field_row(field)">
-      <th>${field.label}</th><td>${Markup(field)} !!!</td></tr>
+    <tr py:def="field_row(field, example=None)">
+      <th>${field.label}</th>
+      <td>
+       <ul py:if="field.errors" class="errorlist">
+         <li py:for="err in field.errors">${err}</li>
+       </ul>
+       ${Markup(field)}
+      </td>
+      <td class="example" py:if="example">e.g., ${example}</td>
+    </tr>
   <fieldset>
     <legend>General description</legend>
     <table class="formtable">
-    <tr><th>Course code</th><td>${Markup(form.code)}</td>
-    <td py:if="example">e.g. ${example}</td></tr>
-    <tr><th>Title</th><td>${Markup(form.title)} <span>${form.title.errors}</span></td></tr>
-    <tr><th>Term</th><td>${Markup(form.term)}</td></tr>
-    <tr><th>Access</th><td>${Markup(form.access)}</td></tr>
+    ${field_row(form.code, example)}
+    ${field_row(form.title)}
+    ${field_row(form.term)}
     ${field_row(form.department)}
     <!-- <tr><th>Department</th><td>${Markup(form.department)} ${errorlist(form.department)}</td></tr> -->
   </table>
@@ -29,12 +38,7 @@ title = _('Add a new course site')
   <fieldset>
     <legend>Access controls</legend>
     <p>This site will be available to:</p>
-    <select>
-      <option>For now, just myself and designated colleagues</option>
-      <option>Students in my course (I will provide section numbers)</option>
-      <option>Students in my course (I will share a password with them)</option>
-      <option>All Reserves patrons</option>
-    </select>
+    ${Markup(form.access)}
   </fieldset>
   <p><input type="submit" value="Continue"/></p>
   </form>
index 05ef1cc..7607d07 100644 (file)
@@ -13,7 +13,8 @@ title = _('Welcome!')
   <div class="newsitem" py:for="news in models.NewsItem.objects.all().order_by('-published','-id')[0:5]">
     <h2>${news.subject}</h2>
     <div>${news.generated_body()}</div>
-    <div class="newsdate">posted ${news.published.strftime('%c')}</div>
+    <div class="newsdate">posted ${news.published.strftime('%c')}
+    <span py:if="user.has_perm('change_newsitem')">&bull; <a href="admin/news/${news.id}/">edit</a></span></div>
   </div>
 </body>
 </html>