Campus integration module now specified in local_settings; default is in 'conifer...
authorgfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Fri, 26 Feb 2010 02:58:37 +0000 (02:58 +0000)
committergfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Fri, 26 Feb 2010 02:58:37 +0000 (02:58 +0000)
I haven't changed any of the campus integration functions: I've just
moved them from 'custom' into 'integration', joined them into a single
module, and given an example of how to extend the default (null)
integration. It's a start.

Library integration will be similar, but bitchier.

git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/branches/2010-02-campus-integration-reorg@801 6d9bc8c9-1ec2-4278-b937-99fde70a366f

14 files changed:
.gitignore
conifer/custom/README [new file with mode: 0644]
conifer/custom/course_codes.py [deleted file]
conifer/custom/course_sections.py [deleted file]
conifer/integration/COURSE_CODES.txt [new file with mode: 0644]
conifer/integration/COURSE_SECTIONS.txt [new file with mode: 0644]
conifer/integration/__init__.py [new file with mode: 0644]
conifer/integration/default.py [new file with mode: 0644]
conifer/integration/example.py [new file with mode: 0644]
conifer/local_settings.py.example
conifer/settings.py
conifer/syrup/models.py
conifer/syrup/views/courses.py
conifer/templates/edit_course_permissions.xhtml

index 7b80a14..b634ce8 100644 (file)
@@ -8,3 +8,4 @@ PyZ3950_parsetab.py
 xsip
 TAGS
 private_local_settings.py
+/conifer/.dired
diff --git a/conifer/custom/README b/conifer/custom/README
new file mode 100644 (file)
index 0000000..a80b2b0
--- /dev/null
@@ -0,0 +1,4 @@
+This directory is going away.
+
+Default integrations are being moved to 'conifer.integration'. The
+active integration modules are to be specified in local_settings.
diff --git a/conifer/custom/course_codes.py b/conifer/custom/course_codes.py
deleted file mode 100644 (file)
index 7482ad0..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-# 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'),
-#          ('LIB201', 'Intro to Library Science'),
-#          ('WEB203', 'Advanced Web Design'),]
-
-_codes = [('ART99-100', 'Art History'),
-          ('BIOL55-350', 'Molecular Cell Biology'),
-          ('CRIM48-567', 'Current Issues in Criminology'),
-          ('ENGL26-280', 'Contemporary Literary Theory'),
-          ('ENGL26-420', 'Word and Image: The Contemporary Graphic Novel'),
-          ('SOCWK47-457', 'Advanced Social Work Research'),]
-
-_crosslists = set(['ENGL26-280', 'ENGL26-420'])
-
-
-course_code_is_valid = None
-
-course_code_example = 'BIOL55-350; SOCWK47-457'
-
-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]))
-
diff --git a/conifer/custom/course_sections.py b/conifer/custom/course_sections.py
deleted file mode 100644 (file)
index 4003e0b..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-# Operations on course-section identifiers
-
-# A course section is an instance of a course offered in a term. 
-
-# A section is specified by a 'section-id', a 3-tuple (course-code,
-# term, section-code), where section-code is usually a short
-# identifier (e.g., "1" representing "section 1 in this term"). Note
-# that multiple sections of the same course are possible in a given
-# term.
-
-# Within the reserves system, a course-site can be associated with
-# zero or more sections, granting access to students in those
-# sections. We need two representations of a section-id.
-
-# The section_tuple_delimiter must be a string which will never appear
-# in a course-code, term, or section-code in your database. It may be
-# a nonprintable character (e.g. NUL or CR). It is used to delimit
-# parts of the tuples in a course's database record.
-
-#------------------------------------------------------------
-# Notes on the interface
-#
-# 'sections_taught_by(username)' returns a set of sections for which
-# username is an instructor. It is acceptable if 'sections_taught_by'
-# only returns current and future sections: historical information is
-# not required by the reserves system.
-#
-# It is expected that the reserves system will be able to resolve any
-# usernames into user records. If there are students on a section-list
-# which do not resolve into user accounts, they will probably be
-# ignored and will not get access to their course sites. So if you're
-# updating your users and sections in a batch-run, you might want to
-# update your users first.
-#
-#------------------------------------------------------------
-# Implementations
-
-# The reserves system will work with a null-implementation of the
-# course-section interface, but tasks related to course-sections will
-# be unavailable. 
-
-# ------------------------------------------------------------ 
-# The null implementation:
-#
-#    sections_tuple_delimiter   = None
-#    sections_taught_by         = None
-#    students_in                = None
-#    instructors_in             = None
-#    sections_for_code_and_term = None
-
-# ------------------------------------------------------------ 
-#
-# The minimal non-null implementation. At the least you must provide
-# sections_tuple_delimiter and students_in. Lookups for instructors
-# may be skipped. Note that sections passed to students_in are
-# (term, course-code, section-code) tuples (string, string, string).
-#
-#    sections_tuple_delimiter   = '|'
-#
-#    def students_in(*sections):
-#        ...
-#        return set_of_usernames
-#
-#    instructors_in             = None
-#    sections_for_code_and_term = None
-
-# ------------------------------------------------------------
-# A complete implementation, with a static database.
-
-#    sections_tuple_delimiter = '|'
-#    
-#    _db = [
-#        ('fred', ('2009W', 'ENG203', '1'), 'jim joe jack ellen ed'),
-#        ('fred', ('2009W', 'ENG327', '1'), 'ed paul bill'),
-#        ('bill', ('2009S', 'BIO323', '1'), 'alan june jack'),
-#        ('bill', ('2009S', 'BIO323', '2'), 'emmet'),
-#    ]
-#    
-#    def sections_taught_by(username):
-#        return set([s[1] for s in _db if s[0] == username])
-#    
-#    def students_in(*sections):
-#        def inner():
-#            for instr, sec, studs in _db:
-#                if sec in sections:
-#                    for s in studs.split(' '):
-#                        yield s
-#        return set(inner())
-#    
-#    def instructors_in(*sections):
-#        def inner():
-#            for instr, sec, studs in _db:
-#                if sec in sections:
-#                    yield instr
-#        return set(inner())
-#    
-#    def sections_for_code_and_term(code, term):
-#        return [(t, c, s) for (instr, (t, c, s), ss) in _db \
-#                    if c == code and t == term]
-#
-
-
-# ------------------------------------------------------------
-# Provide your own implementation below.
-
-sections_tuple_delimiter   = None
-sections_taught_by         = None
-students_in                = None
-instructors_in             = None
-sections_for_code_and_term = None
-
-
-
-# ------------------------------------------------------------
-# a temporary implementation, while I write up the UI.
-
-sections_tuple_delimiter = '|'
-
-# For any of the students to actually appear in a course site, they
-# must also exist as Django users (or be in an authentication backend
-# that supports 'maybe_initialize_user'; see auth_evergreen.py).
-
-_db = [
-    #(instructor, (term, code, sec-code), 'student1 student2 ... studentN'),
-    ('fred', ('2009W', 'ENG203', '1'), 'jim joe jack ellen ed'),
-    ('fred', ('2009W', 'ENG327', '1'), 'ed paul bill'),
-    ('art',  ('2009W', 'LIB201', '1'), 'graham bill ed'),
-    ('graham', ('2009S', 'ART108', '1'), 'alan june jack'),
-    ('graham', ('2009S', 'ART108', '2'), 'emmet'),
-    ('graham', ('2009S', 'ART108', '3'), 'freda hugo bill'),
-]
-
-def sections_taught_by(username):
-    return set([s[1] for s in _db if s[0] == username])
-
-def students_in(*sections):
-    def inner():
-        for instr, sec, studs in _db:
-            if sec in sections:
-                for s in studs.split(' '):
-                    yield s
-    return set(inner())
-
-def instructors_in(*sections):
-    def inner():
-        for instr, sec, studs in _db:
-            if sec in sections:
-                yield instr
-    return set(inner())
-
-def sections_for_code_and_term(code, term):
-    return [(t, c, s) for (instr, (t, c, s), ss) in _db \
-                if c == code and t == term]
diff --git a/conifer/integration/COURSE_CODES.txt b/conifer/integration/COURSE_CODES.txt
new file mode 100644 (file)
index 0000000..7482ad0
--- /dev/null
@@ -0,0 +1,140 @@
+# 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'),
+#          ('LIB201', 'Intro to Library Science'),
+#          ('WEB203', 'Advanced Web Design'),]
+
+_codes = [('ART99-100', 'Art History'),
+          ('BIOL55-350', 'Molecular Cell Biology'),
+          ('CRIM48-567', 'Current Issues in Criminology'),
+          ('ENGL26-280', 'Contemporary Literary Theory'),
+          ('ENGL26-420', 'Word and Image: The Contemporary Graphic Novel'),
+          ('SOCWK47-457', 'Advanced Social Work Research'),]
+
+_crosslists = set(['ENGL26-280', 'ENGL26-420'])
+
+
+course_code_is_valid = None
+
+course_code_example = 'BIOL55-350; SOCWK47-457'
+
+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]))
+
diff --git a/conifer/integration/COURSE_SECTIONS.txt b/conifer/integration/COURSE_SECTIONS.txt
new file mode 100644 (file)
index 0000000..4003e0b
--- /dev/null
@@ -0,0 +1,153 @@
+# Operations on course-section identifiers
+
+# A course section is an instance of a course offered in a term. 
+
+# A section is specified by a 'section-id', a 3-tuple (course-code,
+# term, section-code), where section-code is usually a short
+# identifier (e.g., "1" representing "section 1 in this term"). Note
+# that multiple sections of the same course are possible in a given
+# term.
+
+# Within the reserves system, a course-site can be associated with
+# zero or more sections, granting access to students in those
+# sections. We need two representations of a section-id.
+
+# The section_tuple_delimiter must be a string which will never appear
+# in a course-code, term, or section-code in your database. It may be
+# a nonprintable character (e.g. NUL or CR). It is used to delimit
+# parts of the tuples in a course's database record.
+
+#------------------------------------------------------------
+# Notes on the interface
+#
+# 'sections_taught_by(username)' returns a set of sections for which
+# username is an instructor. It is acceptable if 'sections_taught_by'
+# only returns current and future sections: historical information is
+# not required by the reserves system.
+#
+# It is expected that the reserves system will be able to resolve any
+# usernames into user records. If there are students on a section-list
+# which do not resolve into user accounts, they will probably be
+# ignored and will not get access to their course sites. So if you're
+# updating your users and sections in a batch-run, you might want to
+# update your users first.
+#
+#------------------------------------------------------------
+# Implementations
+
+# The reserves system will work with a null-implementation of the
+# course-section interface, but tasks related to course-sections will
+# be unavailable. 
+
+# ------------------------------------------------------------ 
+# The null implementation:
+#
+#    sections_tuple_delimiter   = None
+#    sections_taught_by         = None
+#    students_in                = None
+#    instructors_in             = None
+#    sections_for_code_and_term = None
+
+# ------------------------------------------------------------ 
+#
+# The minimal non-null implementation. At the least you must provide
+# sections_tuple_delimiter and students_in. Lookups for instructors
+# may be skipped. Note that sections passed to students_in are
+# (term, course-code, section-code) tuples (string, string, string).
+#
+#    sections_tuple_delimiter   = '|'
+#
+#    def students_in(*sections):
+#        ...
+#        return set_of_usernames
+#
+#    instructors_in             = None
+#    sections_for_code_and_term = None
+
+# ------------------------------------------------------------
+# A complete implementation, with a static database.
+
+#    sections_tuple_delimiter = '|'
+#    
+#    _db = [
+#        ('fred', ('2009W', 'ENG203', '1'), 'jim joe jack ellen ed'),
+#        ('fred', ('2009W', 'ENG327', '1'), 'ed paul bill'),
+#        ('bill', ('2009S', 'BIO323', '1'), 'alan june jack'),
+#        ('bill', ('2009S', 'BIO323', '2'), 'emmet'),
+#    ]
+#    
+#    def sections_taught_by(username):
+#        return set([s[1] for s in _db if s[0] == username])
+#    
+#    def students_in(*sections):
+#        def inner():
+#            for instr, sec, studs in _db:
+#                if sec in sections:
+#                    for s in studs.split(' '):
+#                        yield s
+#        return set(inner())
+#    
+#    def instructors_in(*sections):
+#        def inner():
+#            for instr, sec, studs in _db:
+#                if sec in sections:
+#                    yield instr
+#        return set(inner())
+#    
+#    def sections_for_code_and_term(code, term):
+#        return [(t, c, s) for (instr, (t, c, s), ss) in _db \
+#                    if c == code and t == term]
+#
+
+
+# ------------------------------------------------------------
+# Provide your own implementation below.
+
+sections_tuple_delimiter   = None
+sections_taught_by         = None
+students_in                = None
+instructors_in             = None
+sections_for_code_and_term = None
+
+
+
+# ------------------------------------------------------------
+# a temporary implementation, while I write up the UI.
+
+sections_tuple_delimiter = '|'
+
+# For any of the students to actually appear in a course site, they
+# must also exist as Django users (or be in an authentication backend
+# that supports 'maybe_initialize_user'; see auth_evergreen.py).
+
+_db = [
+    #(instructor, (term, code, sec-code), 'student1 student2 ... studentN'),
+    ('fred', ('2009W', 'ENG203', '1'), 'jim joe jack ellen ed'),
+    ('fred', ('2009W', 'ENG327', '1'), 'ed paul bill'),
+    ('art',  ('2009W', 'LIB201', '1'), 'graham bill ed'),
+    ('graham', ('2009S', 'ART108', '1'), 'alan june jack'),
+    ('graham', ('2009S', 'ART108', '2'), 'emmet'),
+    ('graham', ('2009S', 'ART108', '3'), 'freda hugo bill'),
+]
+
+def sections_taught_by(username):
+    return set([s[1] for s in _db if s[0] == username])
+
+def students_in(*sections):
+    def inner():
+        for instr, sec, studs in _db:
+            if sec in sections:
+                for s in studs.split(' '):
+                    yield s
+    return set(inner())
+
+def instructors_in(*sections):
+    def inner():
+        for instr, sec, studs in _db:
+            if sec in sections:
+                yield instr
+    return set(inner())
+
+def sections_for_code_and_term(code, term):
+    return [(t, c, s) for (instr, (t, c, s), ss) in _db \
+                if c == code and t == term]
diff --git a/conifer/integration/__init__.py b/conifer/integration/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/conifer/integration/default.py b/conifer/integration/default.py
new file mode 100644 (file)
index 0000000..b553b28
--- /dev/null
@@ -0,0 +1,18 @@
+# Do not edit this file: make your own, instead.
+
+# See COURSE_CODES.txt for information.
+
+course_code_is_valid       = None
+course_code_example        = None
+course_code_list           = None
+course_code_lookup_title   = None
+course_code_cross_listings = None
+
+# See COURSE_SECTIONS.txt for information.
+
+sections_tuple_delimiter   = None
+sections_taught_by         = None
+students_in                = None
+instructors_in             = None
+sections_for_code_and_term = None
+
diff --git a/conifer/integration/example.py b/conifer/integration/example.py
new file mode 100644 (file)
index 0000000..2426949
--- /dev/null
@@ -0,0 +1,72 @@
+from default import *
+
+#----------------------------------------------------------------------
+# Course Codes
+
+_codes = [('ART99-100', 'Art History'),
+          ('BIOL55-350', 'Molecular Cell Biology'),
+          ('CRIM48-567', 'Current Issues in Criminology'),
+          ('ENGL26-280', 'Contemporary Literary Theory'),
+          ('ENGL26-420', 'Word and Image: The Contemporary Graphic Novel'),
+          ('SOCWK47-457', 'Advanced Social Work Research'),]
+
+_crosslists = set(['ENGL26-280', 'ENGL26-420'])
+
+
+course_code_is_valid = None
+
+course_code_example = 'BIOL55-350; SOCWK47-457'
+
+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]))
+
+
+#----------------------------------------------------------------------
+# Course Sections
+
+sections_tuple_delimiter = '|'
+
+# For any of the students to actually appear in a course site, they
+# must also exist as Django users (or be in an authentication backend
+# that supports 'maybe_initialize_user'; see auth_evergreen.py).
+
+_db = [
+    #(instructor, (term, code, sec-code), 'student1 student2 ... studentN'),
+    ('fred', ('2009W', 'ENG203', '1'), 'jim joe jack ellen ed'),
+    ('fred', ('2009W', 'ENG327', '1'), 'ed paul bill'),
+    ('art',  ('2009W', 'LIB201', '1'), 'graham bill ed'),
+    ('graham', ('2009S', 'ART108', '1'), 'alan june jack'),
+    ('graham', ('2009S', 'ART108', '2'), 'emmet'),
+    ('graham', ('2009S', 'ART108', '3'), 'freda hugo bill'),
+]
+
+def sections_taught_by(username):
+    return set([s[1] for s in _db if s[0] == username])
+
+def students_in(*sections):
+    def inner():
+        for instr, sec, studs in _db:
+            if sec in sections:
+                for s in studs.split(' '):
+                    yield s
+    return set(inner())
+
+def instructors_in(*sections):
+    def inner():
+        for instr, sec, studs in _db:
+            if sec in sections:
+                yield instr
+    return set(inner())
+
+def sections_for_code_and_term(code, term):
+    return [(t, c, s) for (instr, (t, c, s), ss) in _db \
+                if c == code and t == term]
+
+
index dafbae6..659612b 100644 (file)
@@ -34,6 +34,11 @@ SECRET_KEY = 'replace-with-your-own-super-random-key-@vv(tuvt2+yu2r-$dxs$s7=iqjz
 EVERGREEN_AUTHENTICATION = False
 
 #----------------------------------------------------------------------
+#Campus integration
+
+CAMPUS_INTEGRATION_MODULE = 'conifer.integration.default'
+
+#----------------------------------------------------------------------
 # Stuff that probably belongs in a config table in the database, with
 # a nice UI to maintain it all.
 
index 51cf117..39f68ea 100644 (file)
@@ -8,11 +8,7 @@ sys.path.append(HERE('..'))
 
 DEBUG = False
 
-ADMINS = (
-    # ('Your Name', 'your_email@domain.com'),
-)
-
-MANAGERS = ADMINS
+ADMINS = []
 
 DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
 DATABASE_NAME = ''   # Or path to database file if using sqlite3.
@@ -97,6 +93,8 @@ AUTHENTICATION_BACKENDS = [
     'django.contrib.auth.backends.ModelBackend'
 ]
 
+CAMPUS_INTEGRATION_MODULE = 'conifer.integration.default'
+
 #---------------------------------------------------------------------------
 # local_settings.py
 
@@ -115,9 +113,22 @@ except:
 # Further settings that depend upon local_settings.
 
 TEMPLATE_DEBUG = DEBUG
+MANAGERS       = ADMINS
+
+#----------
 
 if EVERGREEN_AUTHENTICATION:
-    AUTHENTICATION_BACKENDS.extend(
-        ['conifer.custom.auth_evergreen.EvergreenAuthBackend',
-         ])
+    AUTHENTICATION_BACKENDS.append(
+        'conifer.custom.auth_evergreen.EvergreenAuthBackend')
+
+#----------
+
+try:
+    exec 'import %s as CAMPUS_INTEGRATION' % CAMPUS_INTEGRATION_MODULE
+except:
+    raise Exception('There is an error in your campus integration module (%s)! '
+                    'Please investigate and repair.' % CAMPUS_INTEGRATION_MODULE,
+                    sys.exc_value)
+
+#----------
 
index 03a3ea1..5bfd74c 100644 (file)
@@ -5,14 +5,17 @@ from django.contrib.auth import get_backends
 from datetime import datetime
 from genshi import Markup
 from django.utils.translation import ugettext as _
-from conifer.custom import course_codes # fixme, not sure if conifer.custom is a good parent.
-from conifer.custom import course_sections # fixme, not sure if conifer.custom is a good parent.
-from conifer.custom import lib_integration
 import re
 import random
 from django.utils import simplejson
 from conifer.middleware import genshi_locals
 
+# campus and library integration
+from django.conf import settings
+campus = settings.CAMPUS_INTEGRATION
+from conifer.custom import lib_integration # fixme, not sure if conifer.custom is a good parent.
+
+
 def highlight(text, phrase,
               highlighter='<strong class="highlight">\\1</strong>'):
     ''' This may be a lame way to do this, but will want to highlight matches somehow
@@ -262,7 +265,7 @@ class Course(m.Model):
                 break
 
     def sections(self):
-        delim = course_sections.sections_tuple_delimiter
+        delim = campus.sections_tuple_delimiter
         if not delim:
             return []
         else:
@@ -302,16 +305,16 @@ class Course(m.Model):
 
 
 def _merge_sections(secs):
-    delim = course_sections.sections_tuple_delimiter
+    delim = campus.sections_tuple_delimiter
     return delim.join(delim.join(sec) for sec in secs)
 
 def section_decode_safe(secstring):
     if not secstring:
         return None
-    return tuple(secstring.decode('base64').split(course_sections.sections_tuple_delimiter))
+    return tuple(secstring.decode('base64').split(campus.sections_tuple_delimiter))
 
 def section_encode_safe(section):
-    return course_sections.sections_tuple_delimiter.join(section).encode('base64').strip()
+    return campus.sections_tuple_delimiter.join(section).encode('base64').strip()
 
 class Member(m.Model):
     class Meta:
index 8a778ce..e0bc9a7 100644 (file)
@@ -12,7 +12,7 @@ class NewCourseForm(ModelForm):
 
     def clean_code(self):
         v = (self.cleaned_data.get('code') or '').strip()
-        is_valid_func = models.course_codes.course_code_is_valid
+        is_valid_func = models.campus.course_code_is_valid
         if (not is_valid_func) or is_valid_func(v):
             return v
         else:
@@ -20,12 +20,12 @@ class NewCourseForm(ModelForm):
 
 # if we have course-code lookup, hack lookup support into the new-course form.
 
-COURSE_CODE_LIST = bool(models.course_codes.course_code_list)
-COURSE_CODE_LOOKUP_TITLE = bool(models.course_codes.course_code_lookup_title)
+COURSE_CODE_LIST = bool(models.campus.course_code_list)
+COURSE_CODE_LOOKUP_TITLE = bool(models.campus.course_code_lookup_title)
 
 if COURSE_CODE_LIST:
     from django.forms import Select
-    course_list = models.course_codes.course_code_list()
+    course_list = models.campus.course_code_list()
     choices = [(a,a) for a in course_list]
     choices.sort()
     empty_label = u'---------'
@@ -52,7 +52,7 @@ def _add_or_edit_course(request, instance=None):
     if is_add:
         instance = models.Course()
     current_access_level = not is_add and instance.access or None
-    example = models.course_codes.course_code_example
+    example = models.campus.course_code_example
     if request.method != 'POST':
         form = NewCourseForm(instance=instance)
         return g.render('edit_course.xhtml', **locals())
@@ -81,7 +81,7 @@ def _add_or_edit_course(request, instance=None):
 # no access-control needed to protect title lookup.
 def add_new_course_ajax_title(request):
     course_code = request.GET['course_code']
-    title = models.course_codes.course_code_lookup_title(course_code)
+    title = models.campus.course_code_lookup_title(course_code)
     return HttpResponse(simplejson.dumps({'title':title}))
 
 @instructors_only
@@ -96,7 +96,7 @@ def edit_course_permissions(request, course_id):
         (u'STUDT', _(u'Students in my course -- I will provide section numbers')),
         (u'INVIT', _(u'Students in my course -- I will share an Invitation Code with them')),
         (u'LOGIN', _(u'All Reserves patrons'))]
-    if models.course_sections.sections_tuple_delimiter is None:
+    if models.campus.sections_tuple_delimiter is None:
         # no course-sections support? Then STUDT cannot be an option.
         del choices[1]
     choose_access = django.forms.Select(choices=choices)
@@ -172,7 +172,7 @@ def edit_course_permissions(request, course_id):
                                  for name in POST \
                                  if name.startswith('remove_section_')]
                 course.drop_sections(*to_remove)
-                student_names = models.course_sections.students_in(*course.sections())
+                student_names = models.campus.students_in(*course.sections())
                 for name in student_names:
                     user = models.maybe_initialize_user(name)
                     if user:
index e8adbe2..98510a8 100644 (file)
@@ -66,8 +66,8 @@ instructors = [m for m in models.Member.objects.filter(course=course) if m.role
       <h3>Course section numbers</h3>
        <?python
          current_sections = course.sections()
-         my_sections = [s for s in models.course_sections.sections_taught_by(user.username) if not s in current_sections]
-         ct_sections = [s for s in models.course_sections.sections_for_code_and_term(course.code, course.term.code) \
+         my_sections = [s for s in models.campus.sections_taught_by(user.username) if not s in current_sections]
+         ct_sections = [s for s in models.campus.sections_for_code_and_term(course.code, course.term.code) \
                          if not s in current_sections]
           encode = lambda t,c,s: models.section_encode_safe((t,c,s))
        ?>