[open-ils-commits] r142 - in servres/trunk/conifer: custom static syrup templates (gfawcett)
svn at svn.open-ils.org
svn at svn.open-ils.org
Sat Mar 7 20:48:43 EST 2009
Author: gfawcett
Date: 2009-03-07 20:48:42 -0500 (Sat, 07 Mar 2009)
New Revision: 142
Added:
servres/trunk/conifer/custom/course_codes.py
servres/trunk/conifer/static/add_new_course.js
Modified:
servres/trunk/conifer/syrup/models.py
servres/trunk/conifer/syrup/urls.py
servres/trunk/conifer/syrup/views.py
servres/trunk/conifer/templates/add_new_course.xhtml
Log:
external course-lookup; continuing work on new-course-site creation.
custom/course_codes.py generalizes the validation of course-codes and
lookup (in an external system) of information related to course-codes.
Added: servres/trunk/conifer/custom/course_codes.py
===================================================================
--- servres/trunk/conifer/custom/course_codes.py (rev 0)
+++ servres/trunk/conifer/custom/course_codes.py 2009-03-08 01:48:42 UTC (rev 142)
@@ -0,0 +1,132 @@
+# Validation and lookup of course codes.
+
+# This modules specifies an "course-code interface" and a null
+# implementation of that interface. If your local system has rules for
+# valid course codes, and a mechanism for looking up details of these
+# codes, you can implement the interface according to your local
+# rules.
+
+
+# ------------------------------------------------------------
+# Overview and definitions
+
+# A course code identifies a specific course offering. Course codes
+# map 1:N onto formal course titles: by looking up a code, we can
+# derive a formal title (in theory, though it may not be possible for
+# external reasons).
+
+# A course code is insufficient to specify a class list: we need a
+# course section for that. A section ties a course code and term to an
+# instructor(s) and a list of students.
+
+# Course codes may have cross-listings, i.e., other codes which refer
+# to the same course, but which appear under a different department
+# for various academic purposes. In our system, we make no attempt to
+# subordinate cross-listings to a "primary" course code.
+
+
+#------------------------------------------------------------
+# Notes on the interface
+#
+# The `course_code_is_valid` function will be used ONLY if
+# course_code_list() returns None (it is a null implementation). If a
+# course-list is available, the system will use a membership test for
+# course-code validity.
+#
+# `course_code_lookup_title` will be used ONLY if `course_code_list`
+# is implemented.
+#
+#
+# "types" of the interface members
+#
+# course_code_is_valid (string) --> boolean.
+# course_code_example : a string constant.
+# course_code_list () --> list of strings
+# course_code_lookup_title (string) --> string, or None.
+# course_code_cross_listings (string) --> list of strings
+#
+# For each member, you MUST provide either a valid implementation, or
+# set the member to None. See the null implementation below.
+
+#------------------------------------------------------------
+# Implementations
+
+# ------------------------------------------------------------
+# Here is a 'null implementation' of the course-code interface. No
+# validation is done, nor are lookups.
+#
+# course_code_is_valid = None # anything is OK;
+# course_code_example = None # no examples;
+# course_code_lookup_title = None # no codes to list;
+# course_code_cross_listings = None # no cross lists.
+
+# ------------------------------------------------------------
+# This one specifies a valid course-code format using a regular
+# expression, and offers some example codes, but does not have a
+# lookup system.
+#
+# import re
+#
+# def course_code_is_valid(course_code):
+# pattern = re.compile(r'^\d{2}-\d{3}$')
+# return bool(pattern.match(course_code))
+#
+# course_code_example = '55-203; 99-105'
+#
+# course_code_list = None
+# course_code_lookup_title = None
+# course_code_cross_listings = None
+
+
+
+# ------------------------------------------------------------
+# This is a complete implementation, based on a hard-coded list of
+# course codes and titles, and two cross-listed course codes.
+#
+# _codes = [('ENG100', 'Introduction to English'),
+# ('ART108', 'English: An Introduction'),
+# ('FRE238', 'Modern French Literature'),
+# ('WEB203', 'Advanced Web Design'),]
+#
+# _crosslists = set(['ENG100', 'ART108'])
+#
+# course_code_is_valid = None
+# course_code_example = 'ENG100; FRE238'
+#
+# def course_code_list():
+# return [a for (a,b) in _codes]
+#
+# def course_code_lookup_title(course_code):
+# return dict(_codes).get(course_code)
+#
+# def course_code_cross_listings(course_code):
+# if course_code in _crosslists:
+# return list(_crosslists - set([course_code]))
+
+
+# ------------------------------------------------------------
+# Provide your own implementation below.
+
+
+_codes = [('ENG100', 'Introduction to English'),
+ ('ART108', 'English: An Introduction'),
+ ('FRE238', 'Modern French Literature'),
+ ('WEB203', 'Advanced Web Design'),]
+
+_crosslists = set(['ENG100', 'ART108'])
+
+
+course_code_is_valid = None
+
+course_code_example = 'ENG100; FRE238'
+
+def course_code_list():
+ return [a for (a,b) in _codes]
+
+def course_code_lookup_title(course_code):
+ return dict(_codes).get(course_code)
+
+def course_code_cross_listings(course_code):
+ if course_code in _crosslists:
+ return list(_crosslists - set([course_code]))
+
Added: servres/trunk/conifer/static/add_new_course.js
===================================================================
--- servres/trunk/conifer/static/add_new_course.js (rev 0)
+++ servres/trunk/conifer/static/add_new_course.js 2009-03-08 01:48:42 UTC (rev 142)
@@ -0,0 +1,16 @@
+function do_init() {
+ if ($('#id_code')[0].tagName == 'SELECT') {
+ // code is a SELECT, so we add a callback to lookup titles.
+ $('#id_code').change(function() {
+ $('#id_title')[0].disabled=true;
+ $.getJSON('ajax_title', {course_code: $(this).val()},
+ function(resp) {
+ $('#id_title').val(resp.title)
+ $('#id_title')[0].disabled=false;
+
+ });
+ });
+ }
+}
+
+$(do_init);
Modified: servres/trunk/conifer/syrup/models.py
===================================================================
--- servres/trunk/conifer/syrup/models.py 2009-03-06 17:25:22 UTC (rev 141)
+++ servres/trunk/conifer/syrup/models.py 2009-03-08 01:48:42 UTC (rev 142)
@@ -5,7 +5,7 @@
from datetime import datetime
from genshi import Markup
from gettext import gettext as _ # fixme, is this the right function to import?
-
+from conifer.custom import course_codes # fixme, not sure if conifer.custom is a good parent.
import re
def highlight(text, phrase,
@@ -159,11 +159,13 @@
('CLOSE', _('Accessible only to course owners'))],
default='CLOSE')
- # For sites that use a passphrase as an invitation.
- passkey = m.CharField(db_index=True, unique=True, blank=True, null=True)
+ # 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 have registration-lists from an external system.
- enrol_codes = m.CharField(_('Registrar keys for class lists, pipe-separated'),
+ # 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 __unicode__(self):
@@ -414,7 +416,7 @@
choices = (('plain', _('plain text')),
('html', _('HTML')),
('markdown', _('Markdown'))),
- default = 'html')
+ default = 'plain')
def __unicode__(self):
return u'%s (%s)' % (self.subject, self.published)
@@ -426,4 +428,3 @@
return Markup(self.body)
elif self.encoding == 'markdown':
return Markup(do_markdown(self.body))
-
Modified: servres/trunk/conifer/syrup/urls.py
===================================================================
--- servres/trunk/conifer/syrup/urls.py 2009-03-06 17:25:22 UTC (rev 141)
+++ servres/trunk/conifer/syrup/urls.py 2009-03-08 01:48:42 UTC (rev 142)
@@ -10,6 +10,7 @@
(r'^$', 'welcome'),
(r'^course/$', 'my_courses'),
(r'^course/new/$', 'add_new_course'),
+ (r'^course/new/ajax_title$', 'add_new_course_ajax_title'),
(r'^browse/$', 'browse_courses'),
(r'^browse/(?P<browse_option>.*)/$', 'browse_courses'),
(r'^prefs/$', 'user_prefs'),
Modified: servres/trunk/conifer/syrup/views.py
===================================================================
--- servres/trunk/conifer/syrup/views.py 2009-03-06 17:25:22 UTC (rev 141)
+++ servres/trunk/conifer/syrup/views.py 2009-03-08 01:48:42 UTC (rev 142)
@@ -12,7 +12,9 @@
from datetime import datetime
from generics import *
from gettext import gettext as _ # fixme, is this the right function to import?
+from django.utils import simplejson
+
#------------------------------------------------------------
# Authentication
@@ -102,7 +104,8 @@
page_num=page_num,
count=count)
elif browse_option == 'courses':
- paginator = Paginator(models.Course.objects.filter(active=True), count)
+ # fixme, course filter should not be (active=True) but based on user identity.
+ paginator = Paginator(models.Course.objects.all(), count)
return g.render('courses.xhtml', paginator=paginator,
page_num=page_num,
@@ -117,12 +120,45 @@
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
+ if (not is_valid_func) or is_valid_func(v):
+ return v
+ else:
+ raise ValidationError, _('invalid course code')
+# 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)
+
+if COURSE_CODE_LIST:
+ from django.forms import Select
+ course_list = models.course_codes.course_code_list()
+ choices = [(a,a) for a in course_list]
+ choices.sort()
+ empty_label = u'---------'
+ choices.insert(0, (0, empty_label))
+ NewCourseForm.base_fields['code'].widget = Select(
+ choices = choices)
+ NewCourseForm.base_fields['code'].empty_label = empty_label
+
@login_required
def add_new_course(request):
- form = NewCourseForm(instance=models.Course())
- return g.render('add_new_course.xhtml', **locals())
+ example = models.course_codes.course_code_example
+ if request.method != 'POST':
+ form = NewCourseForm(instance=models.Course())
+ return g.render('add_new_course.xhtml', **locals())
+ else:
+ form = NewCourseForm(request.POST, instance=models.Course())
+ if not form.is_valid():
+ return g.render('add_new_course.xhtml', **locals())
+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():
@@ -386,7 +422,7 @@
course_query = get_query(query_string, ['title', 'department__name'])
print 'course_query'
print course_query
- course_results = models.Course.objects.filter(course_query).filter(active=True)
+ course_results = models.Course.objects.filter(course_query).all()
# course_list = models.Course.objects.filter(course_query).filter(active=True).order_by('title')[0:5]
course_list = course_results.order_by('title')[0:5]
#there might be a better way of doing this, though instr and course tables should not be expensive to query
Modified: servres/trunk/conifer/templates/add_new_course.xhtml
===================================================================
--- servres/trunk/conifer/templates/add_new_course.xhtml 2009-03-06 17:25:22 UTC (rev 141)
+++ servres/trunk/conifer/templates/add_new_course.xhtml 2009-03-08 01:48:42 UTC (rev 142)
@@ -7,17 +7,23 @@
<xi:include href="master.xhtml"/>
<head>
<title>${title}</title>
+ <script type="text/javascript" src="/static/add_new_course.js"/>
</head>
<body>
<h1>${title}</h1>
<form action="." method="POST">
+ <tr py:def="field_row(field)">
+ <th>${field.label}</th><td>${Markup(field)} !!!</td></tr>
<fieldset>
<legend>General description</legend>
- <table class="formtable">
- <tr><th>Course code</th><td><input type="text"/></td></tr>
- <tr><th>Title</th><td><input type="text"/></td></tr>
+ <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>Department</th><td>${Markup(form.department)}</td></tr>
+ <tr><th>Access</th><td>${Markup(form.access)}</td></tr>
+ ${field_row(form.department)}
+ <!-- <tr><th>Department</th><td>${Markup(form.department)} ${errorlist(form.department)}</td></tr> -->
</table>
</fieldset>
<fieldset>
More information about the open-ils-commits
mailing list