From: gfawcett Date: Sun, 17 Apr 2011 21:29:27 +0000 (+0000) Subject: Evergreen authentication working again; overhauled local_settings, moved EG-specific... X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=72423bcc07050e1055c7f6a91d91cff6fdc56e7f;p=Syrup.git Evergreen authentication working again; overhauled local_settings, moved EG-specific stuff into EG module. git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@1376 6d9bc8c9-1ec2-4278-b937-99fde70a366f --- diff --git a/conifer/integration/auth_evergreen/__init__.py b/conifer/integration/auth_evergreen/__init__.py index e69de29..759a4b4 100644 --- a/conifer/integration/auth_evergreen/__init__.py +++ b/conifer/integration/auth_evergreen/__init__.py @@ -0,0 +1,52 @@ +from .eg_http import EvergreenAuthServer +from django.contrib.auth.models import User + + +class EvergreenAuthBackend(EvergreenAuthServer): + + def __init__(self): + super(EvergreenAuthBackend, self).__init__() + + def authenticate(self, username=None, password=None): + login_token = self.login(username, password) + if login_token: + return self.maybe_initialize_user( + username, login_token=login_token) + return None + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None + + def maybe_initialize_user(self, username, login_token=None, look_local=True): + """Look up user in Django db; if not found, fetch user detail + from backend and set up a local user object. Return None if no + such user exists in either Django or the backend. + + Setting look_local=False skips the Django search and heads + straight to the backend; this shaves a database call when + walking a set of backends to initialize a user. Skipping + look_local on a username that already exists in Django will + certainly lead to an integrity error. + + This method is NOT part of the Django backend interface. + """ + user = None + username = self.djangoize_username(username) + if look_local: + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + pass + if user is None: + u = self.lookup(username, login_token) + if u: # user found in Evergreen. + user = User(username = username, + first_name = u['first_name'], + last_name = u['last_name'], + email = u['email']) + user.set_unusable_password() + user.save() + return user diff --git a/conifer/integration/auth_evergreen/dj.py b/conifer/integration/auth_evergreen/dj.py deleted file mode 100644 index 1cc5d83..0000000 --- a/conifer/integration/auth_evergreen/dj.py +++ /dev/null @@ -1,53 +0,0 @@ -from eg_xmlrpc import EvergreenAuthServer -from django.contrib.auth.models import User -from django.conf import settings - -class EvergreenAuthBackend(EvergreenAuthServer): - - def __init__(self): - assert settings.EVERGREEN_GATEWAY_SERVER, \ - 'EvergreenAuthBackend requires settings.EVERGREEN_GATEWAY_SERVER' - EvergreenAuthServer.__init__( - self, settings.EVERGREEN_GATEWAY_SERVER) - - def authenticate(self, username=None, password=None): - pwd_valid = self.login(username, password) - if pwd_valid: - return self.maybe_initialize_user(username) - return None - - def get_user(self, user_id): - try: - return User.objects.get(pk=user_id) - except User.DoesNotExist: - return None - - def maybe_initialize_user(self, username, look_local=True): - """Look up user in Django db; if not found, fetch user detail - from backend and set up a local user object. Return None if no - such user exists in either Django or the backend. - - Setting look_local=False skips the Django search and heads - straight to the backend; this shaves a database call when - walking a set of backends to initialize a user. Skipping - look_local on a username that already exists in Django will - certainly lead to an integrity error. - - This method is NOT part of the Django backend interface. - """ - user = None - if look_local: - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - pass - if user is None: - u = self.lookup(username) - if u: # user found in Evergreen. - user = User(username=username, - first_name = u['first_name'], - last_name = u['last_name'], - email = u['email']) - user.set_unusable_password() - user.save() - return user diff --git a/conifer/integration/auth_evergreen/eg_http.py b/conifer/integration/auth_evergreen/eg_http.py new file mode 100644 index 0000000..e5a1b23 --- /dev/null +++ b/conifer/integration/auth_evergreen/eg_http.py @@ -0,0 +1,67 @@ +# auth_evergreen_support -- Authentication and user lookup against an +# Evergreen XML-RPC server. + +# This is the Evergreen-specific stuff, with no Django dependencies. + +from hashlib import md5 +import warnings +import time +from conifer.libsystems.evergreen.support import ER, E1, initialize +import re + +#---------------------------------------------------------------------- +# support + +def _hsh(s): + return md5(s).hexdigest() + +#---------------------------------------------------------------------- +# main interface + +class EvergreenAuthServer(object): + + def __init__(self): + pass + + def login(self, username, password, workstation='OWA-proxyloc'): # fixme! + """Return True if the username/password are good, False otherwise.""" + + seed = E1('open-ils.auth.authenticate.init', username) + + result = E1('open-ils.auth.authenticate.complete', { + 'workstation' : workstation, + 'username' : username, + 'password' : _hsh(seed + _hsh(password)), + 'type' : 'staff' + }) + try: + authkey = result['payload']['authtoken'] + return authkey + except: + return None + + def djangoize_username(self, username): + """ + Transform username so it is valid as a Django username. For Django + 1.2, only [a-zA-Z0-9_] are allowed in userids. + """ + pat = re.compile('[^a-zA-Z0-9_]+') + return pat.sub('_', username) + + def lookup(self, username, login_token=None): + """Given a username, return a dict, or None. The dict must have + four keys (first_name, last_name, email, external_username), where + external_username value is the username parameter.""" + + # for now, this backend only returns a user if a login_token is + # provided; in other words, the only time your personal info is + # fetched is when you log in. + if login_token: + resp = E1('open-ils.auth.session.retrieve', login_token) + person = dict((j, resp.get(k)) + for j,k in [('first_name', 'first_given_name'), + ('last_name', 'family_name'), + ('email', 'email'), + ('external_username', 'usrname')]) + return person + diff --git a/conifer/integration/auth_evergreen/eg_xmlrpc.py b/conifer/integration/auth_evergreen/eg_xmlrpc.py deleted file mode 100644 index 7915dde..0000000 --- a/conifer/integration/auth_evergreen/eg_xmlrpc.py +++ /dev/null @@ -1,86 +0,0 @@ -# auth_evergreen_support -- Authentication and user lookup against an -# Evergreen XML-RPC server. - -# This is the Evergreen-specific stuff, with no Django dependencies. - -import xmlrpclib -import md5 -import warnings -import time - -#---------------------------------------------------------------------- -# support - -def do_request(proxy, method, *args): - # Against my test server, I would get intermittent - # ProtcolErrors. If we get one, try again, backing off gradually. - for attempt in range(5): - try: - return getattr(proxy, method)(*args) - except xmlrpclib.ProtocolError, pe: - warnings.warn('open-ils xml-rpc protocol error: trying again: ' + method) - time.sleep(0.1 * attempt) # back off a bit and try again - -def _hsh(s): - return md5.new(s).hexdigest() - -#---------------------------------------------------------------------- -# main interface - -class EvergreenAuthServer(object): - - def __init__(self, address, verbose=False): - self.address = address - self.verbose = verbose - - def proxy(self, service): - server = xmlrpclib.Server( - 'http://%s/xml-rpc/%s' % (self.address, service), - verbose=self.verbose) - def req(method, *args): - return do_request(server, method, *args) - return req - - def login(self, username, password): - """Return True if the username/password are good, False otherwise.""" - prx = self.proxy('open-ils.auth') - seed = prx('open-ils.auth.authenticate.init', username) - resp = prx('open-ils.auth.authenticate.complete', - dict(username=username, - password=_hsh(seed + _hsh(password)), - type='reserves')) - try: - # do we need the authkey for anything? - authkey = resp['payload']['authtoken'] - return True - except KeyError: - return False - - def lookup(self, username): - """Given a username, return a dict, or None. The dict must have - four keys (first_name, last_name, email, external_username), where - external_username value is the username parameter.""" - - prx = self.proxy('open-ils.actor') - r = prx('open-ils.actor.user.search.username', 'admin') - if not r: - return None - else: - r = r[0]['__data__'] - f = lambda k: r.get(k) - person = dict((j, f(k)) for j,k in [('first_name', 'first_given_name'), - ('last_name', 'family_name'), - ('email', 'email'), - ('external_username', 'usrname')]) - return person - -#---------------------------------------------------------------------- -# testing - -if __name__ == '__main__': - from pprint import pprint - address = '192.168.1.10' - egreen = EvergreenAuthServer(address) - username, password = 'admin', 'open-ils' - print egreen.login(username, password) - pprint(egreen.lookup('admin')) diff --git a/conifer/integration/evergreen_site.py b/conifer/integration/evergreen_site.py index e23415a..0d030de 100644 --- a/conifer/integration/evergreen_site.py +++ b/conifer/integration/evergreen_site.py @@ -11,6 +11,19 @@ import re import time import traceback +# If the Python OpenSRF library is installed, we want to know about it. It +# isn't needed for our read-only ILS operations, only for updates. + +try: + import osrf + OSRF_LIB_INSTALLED = True +except ImportError: + OSRF_LIB_INSTALLED = False + +if OSRF_LIB_INSTALLED: + from conifer.libsystems.evergreen.startup import ils_startup + + OPENSRF_AUTHENTICATE = "open-ils.auth.authenticate.complete" OPENSRF_AUTHENTICATE_INIT = "open-ils.auth.authenticate.init" OPENSRF_BATCH_UPDATE = "open-ils.cat.asset.copy.fleshed.batch.update" @@ -30,16 +43,67 @@ OPENSRF_FLESHEDCOPY_CALL = "open-ils.search.asset.copy.fleshed.batch.retrieve.a def disable(func): return None + + class EvergreenIntegration(object): - EG_BASE = 'http://%s/' % settings.EVERGREEN_GATEWAY_SERVER - initialize(EG_BASE) + # Either specify EVERGREEN_SERVERin your local_settings, or override + # EVERGREEN_SERVER and OPAC_URL, etc. in your subclass of this class. + + EVERGREEN_SERVER = getattr(settings, 'EVERGREEN_SERVER', '') + + + # ---------------------------------------------------------------------- + # These variables depend on EVERGREEN_SERVER, or else you need to override + # them in your subclass. + + # OPAC_URL: the base URL for the OPAC's Web interface. + # default: http://your-eg-server/ + # local_settings variable: EVERGREEN_OPAC_URL + + if hasattr(settings, 'EVERGREEN_OPAC_URL'): + OPAC_URL = settings.EVERGREEN_OPAC_URL + else: + assert EVERGREEN_SERVER + OPAC_URL = 'http://%s/' % EVERGREEN_SERVER + + # IDL_URL: where is your fm_IDL.xml file located? For faster process + # startup, it's recommended you use a file:// URL, pointing to a local + # copy of the file. default: http://your-eg-server/reports/fm_IDL.xml + # local_settings variable: EVERGREEN_IDL_URL + + IDL_URL = getattr(settings, 'EVERGREEN_IDL_URL', + 'http://%s/reports/fm_IDL.xml' % EVERGREEN_SERVER) + + # GATEWAY_URL: where is your HTTP gateway? + # default: http://your-eg-server/osrf-gateway-v1' + # variable: EVERGREEN_GATEWAY_URL + + GATEWAY_URL = getattr(settings, 'EVERGREEN_GATEWAY_URL', + 'http://%s/osrf-gateway-v1' % EVERGREEN_SERVER) + + # end of variables dependent on EVERGREEN_SERVER + # ---------------------------------------------------------------------- + + + # OPAC_LANG and OPAC_SKIN: localization skinning for your OPAC + + OPAC_LANG = getattr(settings, 'EVERGREEN_OPAC_LANG', 'en-CA') + OPAC_SKIN = getattr(settings, 'EVERGREEN_OPAC_SKIN', 'default') + + # RESERVES_DESK_NAME: this will be going away, but for now, it's the full + # ILS-side name of the reserves desk. This needs to be replaced with a + # database-driven lookup for the correct reserves desk in the context of + # the given item. + + RESERVES_DESK_NAME = getattr(settings, 'RESERVES_DESK_NAME', None) + # USE_Z3950: if True, use Z39.50 for catalogue search; if False, use OpenSRF. # Don't set this value directly here: rather, if there is a valid Z3950_CONFIG # settings in local_settings.py, then Z39.50 will be used. - USE_Z3950 = getattr(settings, 'Z3950_CONFIG', None) is not None + USE_Z3950 = bool(getattr(settings, 'Z3950_CONFIG', None)) TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" DUE_FORMAT = "%b %d %Y, %r" @@ -48,13 +112,36 @@ class EvergreenIntegration(object): # call number IS_ATTACHMENT = re.compile('\w*DVD\s?|\w*CD\s?|\w[Gg]uide\s?|\w[Bb]ooklet\s?|\w*CD\-ROM\s?') - # Item status stuff - _STATUS_DECODE = [(str(x['id']), x['name']) - for x in E1('open-ils.search.config.copy_status.retrieve.all')] + # Used if you're doing updates to Evergreen from Syrup. + + UPDATE_CHOICES = [ + ('One', 'Syrup only'), + ('Cat', 'Catalogue'), + ('Zap', 'Remove from Syrup'), + ] + + + + # ---------------------------------------------------------------------- + + + + def __init__(self): + # establish our OpenSRF connection. + initialize(self) + + if OSRF_LIB_INSTALLED: + ils_startup(self.EVERGREEN_SERVER, + self.IDL_URL) + + # set up the available/reshelving codes, for the item_status routine. + status_decode = [(str(x['id']), x['name']) + for x in E1('open-ils.search.config.copy_status.retrieve.all')] + + self.AVAILABLE = [id for id, name in status_decode if name == 'Available'][0] + self.RESHELVING = [id for id, name in status_decode if name == 'Reshelving'][0] - AVAILABLE = [id for id, name in _STATUS_DECODE if name == 'Available'][0] - RESHELVING = [id for id, name in _STATUS_DECODE if name == 'Reshelving'][0] def item_status(self, item): """ @@ -85,6 +172,7 @@ class EvergreenIntegration(object): # bindings, I am not sure there is a use case where an evergreen # site would not have access to these but will leave for now # since there are no hardcoded references + assert self.RESERVES_DESK_NAME, 'No RESERVES_DESK_NAME specified!' try: counts = E1(OPENSRF_COPY_COUNTS, bib_id, 1, 0) lib = desk = avail = vol = 0 @@ -108,7 +196,7 @@ class EvergreenIntegration(object): # attachment test attachtest = re.search(self.IS_ATTACHMENT, callnum) - if loc == settings.RESERVES_DESK_NAME: + if loc == self.RESERVES_DESK_NAME: desk += anystatus_here avail += avail_here dueinfo = '' @@ -138,7 +226,7 @@ class EvergreenIntegration(object): if thisloc: thisloc = thisloc.get("name") - if thisloc == settings.RESERVES_DESK_NAME: + if thisloc == self.RESERVES_DESK_NAME: bringfw = attachtest # multiple volumes @@ -224,7 +312,7 @@ class EvergreenIntegration(object): bibid = '' is_barcode = re.search('\d{14}', query) - if query.startswith(self.EG_BASE): + if query.startswith(self.OPAC_URL): # query is an Evergreen URL # snag the bibid at this point params = dict([x.split("=") for x in query.split("&")]) @@ -302,10 +390,11 @@ class EvergreenIntegration(object): """ Given a bib ID, return either a URL for examining the bib record, or None. """ - # TODO: move this to local_settings if bib_id: - return ('%sopac/en-CA' - '/skin/uwin/xml/rdetail.xml?r=%s&l=1&d=0' % (self.EG_BASE, bib_id)) + url = '%sopac/%s/skin/%s/xml/rdetail.xml?l=1&d=0&r=%s' % ( + self.OPAC_URL, self.OPAC_LANG, self.OPAC_SKIN, + bib_id) + return url if USE_Z3950: # only if we are using Z39.50 for catalogue search. Against our Conifer diff --git a/conifer/integration/uwindsor.py b/conifer/integration/uwindsor.py index 8b2d3bc..5cd4df9 100644 --- a/conifer/integration/uwindsor.py +++ b/conifer/integration/uwindsor.py @@ -13,6 +13,12 @@ class UWindsorIntegration(EvergreenIntegration): OSRF_CAT_SEARCH_ORG_UNIT = 106 + + OPAC_LANG = 'en-CA' + OPAC_SKIN = 'uwin' + + RESERVES_DESK_NAME = 'Leddy: Course Reserves - Main Bldng - 1st Flr - Reserve Counter at Circulation Desk' + SITE_DEFAULT_ACCESS_LEVEL = 'RESTR' #--------------------------------------------------------------------------- # proxy server integration @@ -84,6 +90,11 @@ class UWindsorIntegration(EvergreenIntegration): 'group': a group-code, externally defined; 'role': the user's role in that group, one of (INSTR, ASSIST, STUDT). """ + if '@' in userid: + # If there's an at-sign in the userid, then it's not a UWin ID. + # Maybe you've got Evergreen authentication turned on? + return [] + memberships = self._campus_info('membership_ids', userid) for m in memberships: m['role'] = self._decode_role(m['role']) diff --git a/conifer/libsystems/evergreen/fm_IDL.xml b/conifer/libsystems/evergreen/fm_IDL.xml deleted file mode 100644 index 36f38ca..0000000 --- a/conifer/libsystems/evergreen/fm_IDL.xml +++ /dev/null @@ -1,5384 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SELECT t.* - FROM action.transit_copy t - JOIN actor.org_unit AS s ON (t.source = s.id) - JOIN actor.org_unit AS d ON (t.dest = d.id) - WHERE s.parent_ou <> d.parent_ou - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/conifer/libsystems/evergreen/item_status.py b/conifer/libsystems/evergreen/item_status.py index cd5b656..61608e9 100644 --- a/conifer/libsystems/evergreen/item_status.py +++ b/conifer/libsystems/evergreen/item_status.py @@ -27,16 +27,10 @@ def url_to_marcxml(url): item_id = m and m.group(1) or None if item_id: marc_url = ("%s/opac/extras/unapi?" - "id=tag:concat.ca,9999:biblio-record_entry/" + "id=tag:concat.ca,9999:biblio-record_entry/" # FIMXE, concat.ca reference! "%s/-&format=marcxml-full" % (support.BASE, item_id)) xml = unicode(urllib2.urlopen(marc_url).read(), 'utf-8') return xml if __name__ == '__main__': - EG_BASE = 'http://%s/' % settings.EVERGREEN_GATEWAY_SERVER - support.initialize(EG_BASE) - print url_to_marcxml('http://windsor.concat.ca/opac/en-CA/skin/uwin/xml/rdetail.xml?r=1971331&t=evergreen&tp=keyword&l=106&d=1&hc=210&rt=keyword') - # from xml.etree import ElementTree as ET - # for t in ET.fromstring(bib_id_to_marcxml('2081089')).getiterator(): - # print t.text - + support.initialize(settings) diff --git a/conifer/libsystems/evergreen/startup.py b/conifer/libsystems/evergreen/startup.py index ec7dd4b..b3806ac 100644 --- a/conifer/libsystems/evergreen/startup.py +++ b/conifer/libsystems/evergreen/startup.py @@ -11,7 +11,8 @@ import sys import tempfile import urllib2 -def load_idl(osrf_http, gateway_server, idl_url): +# http://eg-training.cwmars.org/reports/fm_IDL.xml +def load_idl(idl_url): """ Loads the fieldmapper IDL, registering class hints for the defined objects @@ -26,10 +27,8 @@ def load_idl(osrf_http, gateway_server, idl_url): # Get the fm_IDL.xml file from the server try: - print '%s://%s/%s' % (osrf_http, gateway_server, idl_url) - idl = urllib2.urlopen('%s://%s/%s' % - (osrf_http, gateway_server, idl_url) - ) + print idl_url + idl = urllib2.urlopen(idl_url) idlfile.write(idl.read()) # rewind to the beginning of the file idlfile.seek(0) @@ -45,14 +44,14 @@ def load_idl(osrf_http, gateway_server, idl_url): parser.set_IDL(idlfile) parser.parse_IDL() -def ils_startup(EVERGREEN_GATEWAY_SERVER, OSRF_HTTP, IDL_URL): +def ils_startup(evergreen_server, full_idl_url): """ Put any housekeeping for ILS interactions here, the definitions come from local_settings in the call itself rather than an import """ # Set the host for our requests - osrf.gateway.GatewayRequest.setDefaultHost(EVERGREEN_GATEWAY_SERVER) + osrf.gateway.GatewayRequest.setDefaultHost(evergreen_server) # Pull all of our object definitions together - load_idl(OSRF_HTTP, EVERGREEN_GATEWAY_SERVER, IDL_URL) + load_idl(full_idl_url) diff --git a/conifer/libsystems/evergreen/support.py b/conifer/libsystems/evergreen/support.py index e94c317..a81c060 100644 --- a/conifer/libsystems/evergreen/support.py +++ b/conifer/libsystems/evergreen/support.py @@ -6,29 +6,26 @@ from xml.etree import ElementTree import re import sys, os -LOCALE = 'en-US' - #------------------------------------------------------------ # parse fm_IDL, to build a field-name-lookup service. fields_for_class = {} -BASE = None +GATE = None +LOCALE = 'en-US' # fixme, this shouldn't be here. + -def initialize(base): - global BASE - if not BASE: - assert base.endswith('/') - BASE = base - fields_for_class.update(dict(_fields())) +def initialize(integration_object): + global GATE + if not GATE: + GATE = integration_object.GATEWAY_URL + fm_idl_url = integration_object.IDL_URL + fields_for_class.update(dict(_fields(fm_idl_url))) -def _fields(): - fm_idl_file = os.path.join(os.path.dirname(__file__), 'fm_IDL.xml') - f = open(fm_idl_file) - # to get around 2.5.x python installations - # with open(fm_idl_file) as f: +def _fields(fm_idl_url): + print 'Loading fm_IDL from %s' % fm_idl_url + f = urllib2.urlopen(fm_idl_url) tree = ElementTree.parse(f) - # fm_IDL_location = BASE + 'reports/fm_IDL.xml' - # tree = ElementTree.parse(urllib2.urlopen(fm_IDL_location)) + f.close() NS = '{http://opensrf.org/spec/IDL/base/v1}' for c in tree.findall('%sclass' % NS): cid = c.attrib['id'] @@ -58,16 +55,18 @@ def evergreen_request(method, *args, **kwargs): kwargs.update({'service':service, 'method':method}) params = ['%s=%s' % (k,quote(v)) for k,v in kwargs.items()] params += ['param=%s' % quote(json.dumps(a)) for a in args] - url = '%sosrf-gateway-v1?%s' % (BASE, '&'.join(params)) + url = '%s?%s' % (GATE, '&'.join(params)) # fixme, OSRF_HTTP, IDL_URL + #print '--->', url req = urllib2.urlopen(url) resp = json.load(req) - assert resp['status'] == 200, 'error during evergreen request' + if resp['status'] != 200: + raise Exception('error during evergren request', resp) payload = resp['payload'] - #print '----', payload + #print '<---', payload return evergreen_object(payload) -def evergreen_request_single_result(method, *args): - resp = evergreen_request(method, *args) +def evergreen_request_single_result(method, *args, **kwargs): + resp = evergreen_request(method, *args, **kwargs) if not resp: return None elif len(resp) > 1: diff --git a/conifer/local_settings.py.example b/conifer/local_settings.py.example index feec57b..a367724 100644 --- a/conifer/local_settings.py.example +++ b/conifer/local_settings.py.example @@ -1,14 +1,12 @@ # -*- mode: python -*- -import os -from here import HERE - DEBUG = False #---------------------------------------------------------------------- # You may need to set the PYTHON_EGG_CACHE directory, depending on how # you installed Syrup. +# import os # os.environ['PYTHON_EGG_CACHE'] = '/tmp/eggs' #---------------------------------------------------------------------- @@ -32,17 +30,14 @@ SECRET_KEY = 'replace-with-your-own-super-random-key-@vv(tuvt2+yu2r-$dxs$s7=iqjz #---------------------------------------------------------------------- # EZproxy integration -EZPROXY_HOST = 'ezproxy.library.org' -EZPROXY_PASSWORD = '' - -#---------------------------------------------------------------------- -# For campus information - ignore if not applicable for your organization -CAMPUS_INFO_SERVICE = 'http://sample.campus.info' +EZPROXY_HOST = 'your-ezproxy.example.net' +EZPROXY_PASSWORD = 'yourpass' #---------------------------------------------------------------------- # Authentication systems EVERGREEN_AUTHENTICATION = False # Evergreen ILS authentication + SAKAI_LINKTOOL_AUTHENTICATION = False # Sakai LMS Linktool authentication SAKAI_LINKTOOL_AUTH_URL = 'https://...' # fixme, add documentation @@ -52,32 +47,13 @@ SAKAI_LINKTOOL_AUTH_URL = 'https://...' # fixme, add documentation CAS_AUTHENTICATION = False CAS_SERVER_URL = 'https://uwinid.uwindsor.ca/cas/' -#---------------------------------------------------------------------- -# Stuff that probably belongs in a config table in the database, with -# a nice UI to maintain it all. - -EVERGREEN_GATEWAY_SERVER = 'www.concat.ca' -Z3950_CONFIG = ('zed.concat.ca', 210, 'OWA') #OWA,OSUL,CONIFER +EVERGREEN_SERVER = 'www.concat.ca' -# Note, in the UWindsor integration, commenting out Z3950_CONFIG or setting it +# Note, in the Evergreen integration, commenting out Z3950_CONFIG or setting it # equal to None will result in OpenSRF being used for catalogue search instead # of Z39.50. -#---------------------------------------------------------------------- -# SITE_DEFAULT_ACCESS_LEVEL: by default, all new sites are -# world-readable. Possible default values are ANON (world readable), -# LOGIN (any logged in user), MEMBR (only explicit members of the -# site), and CLOSE (only instructors/owners of the site). - -#---------------------------------------------------------------------- -# UPDATE_CHOICES: these options are only surfaced when a site -# enables updates from syrup to the catalogue - -UPDATE_CHOICES = [ - ('One', 'Syrup-only'), - ('Cat', 'Catalogue'), - ('Zap', 'Remove from Syrup'), - ] +Z3950_CONFIG = ('zed.concat.ca', 210, 'OWA') #OWA,OSUL,CONIFER #---------------------------------------------------------------------- # INTEGRATION_CLASS: name of a class to instantiate after the database models @@ -85,5 +61,4 @@ UPDATE_CHOICES = [ # other late initializations. See the 'conifer.syrup.integration' module for # more information. -#INTEGRATION_CLASS = 'conifer.integration.uwindsor.UWindsorIntegration' - +# INTEGRATION_CLASS = 'conifer.integration.uwindsor.UWindsorIntegration' diff --git a/conifer/plumbing/hooksystem.py b/conifer/plumbing/hooksystem.py index 16b4281..bcb35db 100644 --- a/conifer/plumbing/hooksystem.py +++ b/conifer/plumbing/hooksystem.py @@ -5,7 +5,9 @@ __all__ = ['callhook', 'callhook_required', 'gethook', 'initialize_hooks'] def initialize_hooks(obj): global HOOKS - assert HOOKS is None + assert HOOKS is None, ('Cannot load hooksystem twice. ' + 'Probably you are importing "models" ' + 'using two different module paths.') HOOKS = obj def gethook(name, default=None): diff --git a/conifer/settings.py b/conifer/settings.py index 4d75d04..6a49a4b 100644 --- a/conifer/settings.py +++ b/conifer/settings.py @@ -127,7 +127,7 @@ MANAGERS = ADMINS if EVERGREEN_AUTHENTICATION: AUTHENTICATION_BACKENDS.append( - 'conifer.integration.auth_evergreen.dj.EvergreenAuthBackend') + 'conifer.integration.auth_evergreen.EvergreenAuthBackend') if SAKAI_LINKTOOL_AUTHENTICATION: AUTHENTICATION_BACKENDS.append( diff --git a/conifer/syrup/migrations/0015_auto__chg_field_item_evergreen_update.py b/conifer/syrup/migrations/0015_auto__chg_field_item_evergreen_update.py new file mode 100644 index 0000000..bbe97f3 --- /dev/null +++ b/conifer/syrup/migrations/0015_auto__chg_field_item_evergreen_update.py @@ -0,0 +1,194 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Item.evergreen_update' + db.alter_column('syrup_item', 'evergreen_update', self.gf('django.db.models.fields.CharField')(max_length=4, blank=True)) + + + def backwards(self, orm): + + # Changing field 'Item.evergreen_update' + db.alter_column('syrup_item', 'evergreen_update', self.gf('django.db.models.fields.CharField')(max_length=4)) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'syrup.config': { + 'Meta': {'object_name': 'Config'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '8192'}) + }, + 'syrup.course': { + 'Meta': {'object_name': 'Course'}, + 'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Department']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) + }, + 'syrup.declaration': { + 'Meta': {'object_name': 'Declaration'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Item']"}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'syrup.department': { + 'Meta': {'object_name': 'Department'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'service_desk': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.ServiceDesk']"}) + }, + 'syrup.group': { + 'Meta': {'object_name': 'Group'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'external_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Site']"}) + }, + 'syrup.item': { + 'Meta': {'object_name': 'Item'}, + 'author': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '8192', 'null': 'True', 'blank': 'True'}), + 'barcode': ('django.db.models.fields.CharField', [], {'max_length': '14', 'null': 'True', 'blank': 'True'}), + 'bib_id': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'circ_desk': ('django.db.models.fields.CharField', [], {'default': "'631'", 'max_length': '5', 'blank': 'True'}), + 'circ_modifier': ('django.db.models.fields.CharField', [], {'default': "'RSV2'", 'max_length': '10', 'blank': 'True'}), + 'copyright_status': ('django.db.models.fields.CharField', [], {'default': "'UK'", 'max_length': '2'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'evergreen_update': ('django.db.models.fields.CharField', [], {'default': "'One'", 'max_length': '4', 'blank': 'True'}), + 'fileobj': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'fileobj_mimetype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'fileobj_origname': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'isbn': ('django.db.models.fields.CharField', [], {'max_length': '17', 'null': 'True', 'blank': 'True'}), + 'issue': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '7'}), + 'itemtype': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '1', 'null': 'True', 'blank': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'marcxml': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'orig_callno': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'pages': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'parent_heading': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Item']", 'null': 'True', 'blank': 'True'}), + 'published': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'publisher': ('django.db.models.fields.CharField', [], {'max_length': '8192', 'null': 'True', 'blank': 'True'}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Site']"}), + 'source_title': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '8192', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '8192', 'db_index': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}), + 'volume': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}) + }, + 'syrup.membership': { + 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'Membership'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'role': ('django.db.models.fields.CharField', [], {'default': "'STUDT'", 'max_length': '6'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'syrup.servicedesk': { + 'Meta': {'object_name': 'ServiceDesk'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'syrup.site': { + 'Meta': {'unique_together': "(('course', 'start_term', 'owner'),)", 'object_name': 'Site'}, + 'access': ('django.db.models.fields.CharField', [], {'default': "'RESTR'", 'max_length': '5'}), + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Course']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'end_term': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'end_term'", 'to': "orm['syrup.Term']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'service_desk': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.ServiceDesk']"}), + 'start_term': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'start_term'", 'to': "orm['syrup.Term']"}), + 'uwindsor_bookbag': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}), + 'uwindsor_eres': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}) + }, + 'syrup.term': { + 'Meta': {'object_name': 'Term'}, + 'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'finish': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'start': ('django.db.models.fields.DateField', [], {}) + }, + 'syrup.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'external_memberships_checked': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ils_userid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'last_email_notice': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'wants_email_notices': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'syrup.z3950target': { + 'Meta': {'object_name': 'Z3950Target'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'database': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'host': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'port': ('django.db.models.fields.IntegerField', [], {'default': '210'}), + 'syntax': ('django.db.models.fields.CharField', [], {'default': "'USMARC'", 'max_length': '10'}) + } + } + + complete_apps = ['syrup'] diff --git a/conifer/syrup/models.py b/conifer/syrup/models.py index 7793f4f..dc92076 100644 --- a/conifer/syrup/models.py +++ b/conifer/syrup/models.py @@ -15,6 +15,18 @@ from django.utils.translation import ugettext as _ from genshi import Markup #---------------------------------------------------------------------- +# +# Load, but don't instantiate, the local integration module. This way, we can +# refer to static values in the module. + +integration_class = None + +if hasattr(settings, 'INTEGRATION_CLASS'): + modname, klassname = settings.INTEGRATION_CLASS.rsplit('.', 1) # e.g. 'foo.bar.baz.MyClass' + mod = __import__(modname, fromlist=['']) + integration_class = getattr(mod, klassname) + +#---------------------------------------------------------------------- class BaseModel(m.Model): class Meta: @@ -277,7 +289,7 @@ class Site(BaseModel): ('MEMBR', _('Accessible to course-site members')), ('CLOSE', _('Accessible only to course-site owners'))] - ACCESS_DEFAULT = getattr(settings, 'SITE_DEFAULT_ACCESS_LEVEL', 'ANON') + ACCESS_DEFAULT = getattr(integration_class, 'SITE_DEFAULT_ACCESS_LEVEL', 'ANON') assert ACCESS_DEFAULT in [x[0] for x in ACCESS_CHOICES] access = m.CharField(max_length=5, @@ -654,11 +666,12 @@ class Item(BaseModel): # TODO: all of the choices should probably go in settings, as per EVERGREEN_UPDATE_CHOICES # Options for evergreen updates - EVERGREEN_UPDATE_CHOICES = settings.UPDATE_CHOICES + EVERGREEN_UPDATE_CHOICES = getattr(integration_class, 'UPDATE_CHOICES', + [('', 'n/a')]) - evergreen_update = m.CharField(max_length=4, + evergreen_update = m.CharField(max_length=4, blank=True, choices=EVERGREEN_UPDATE_CHOICES, - default='One') + default=EVERGREEN_UPDATE_CHOICES[0][0]) # As per discussion with Art Rhyno and Joan Dalton, Leddy Library. COPYRIGHT_STATUS_CHOICES = [ @@ -942,13 +955,11 @@ def highlight(text, phrase, return highlight_re.sub(highlighter, text) #---------------------------------------------------------------------- -# Activate the local integration module. +# Activate the local integration module. We loaded the module at the top of +# models.py, now we instantiate it. -if hasattr(settings, 'INTEGRATION_CLASS'): - modname, klassname = settings.INTEGRATION_CLASS.rsplit('.', 1) # e.g. 'foo.bar.baz.MyClass' - mod = __import__(modname, fromlist=['']) - klass = getattr(mod, klassname) - initialize_hooks(klass()) +if integration_class: + initialize_hooks(integration_class()) #----------------------------------------------------------------------------- # this can't be imported until Membership is defined...