From f4e4f13f9e0ed46a54391f4f9c073289d335cb3d Mon Sep 17 00:00:00 2001 From: gfawcett Date: Thu, 15 Jul 2010 00:55:37 +0000 Subject: [PATCH] The Great Cleanup I've made some radical cuts and reorganizations to the source. Most functions are still working. git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@919 6d9bc8c9-1ec2-4278-b937-99fde70a366f --- .gitignore | 3 +- conifer/BRANCH-TODO.org | 38 - conifer/custom/README | 4 - conifer/doc/grahams-init-fixture.json | 1 + conifer/genshi_support.py | 52 - conifer/grahams-init-fixture.json | 1 - .../auth_evergreen}/__init__.py | 0 .../auth_evergreen/django.py} | 2 +- .../auth_evergreen/eg_xmlrpc.py} | 0 conifer/integration/{hooks.py => uwindsor.py} | 56 +- conifer/integration/uwindsor_campus_info.py | 14 + conifer/libsystems/evergreen/fm_IDL.xml | 3910 +------------------- conifer/libsystems/evergreen/item_status.py | 6 +- conifer/libsystems/evergreen/support.py | 1 + conifer/libsystems/z3950/marcxml.py | 13 +- conifer/libsystems/z3950/pyz3950_search.py | 2 + conifer/middleware/genshi_locals.py | 20 - conifer/{middleware => plumbing}/__init__.py | 0 conifer/plumbing/genshi_support.py | 84 + .../_hooksystem.py => plumbing/hooksystem.py} | 0 conifer/settings.py | 4 +- conifer/static/main.css | 13 +- conifer/syrup/admin.py | 3 - conifer/syrup/fuzzy_match.py | 53 - conifer/syrup/integration.py | 37 +- conifer/syrup/models.py | 23 +- conifer/syrup/urls.py | 10 +- conifer/syrup/user_lookup.py | 32 - conifer/syrup/views/__init__.py | 1 + conifer/syrup/views/_common.py | 168 +- conifer/syrup/views/{generics.py => _generics.py} | 2 +- conifer/syrup/views/admin.py | 2 +- conifer/syrup/views/auth.py | 42 + conifer/syrup/views/feeds.py | 2 +- conifer/{ => syrup/views}/genshi_namespace.py | 3 + conifer/syrup/views/items.py | 16 +- conifer/syrup/views/search.py | 19 +- conifer/syrup/views/sites.py | 2 - conifer/templates/auth/login.xhtml | 8 +- conifer/templates/browse_index.xhtml | 1 + conifer/templates/components/site.xhtml | 5 +- conifer/templates/edit_site.xhtml | 2 +- conifer/templates/feeds/site_atom.xml | 2 +- conifer/templates/item/item_add_cat_search.xhtml | 2 +- conifer/templates/master.xhtml | 3 - conifer/templates/search_results.xhtml | 78 +- conifer/templates/site_detail.xhtml | 13 +- conifer/templates/tabbar.xhtml | 3 +- conifer/templates/tabbar_anonymous.xhtml | 2 +- conifer/templates/zsearch.xhtml | 4 +- conifer/urls.py | 4 +- rfc_sql/reserves.sql | 151 - 52 files changed, 419 insertions(+), 4498 deletions(-) delete mode 100644 conifer/BRANCH-TODO.org delete mode 100644 conifer/custom/README create mode 100644 conifer/doc/grahams-init-fixture.json delete mode 100644 conifer/genshi_support.py delete mode 100644 conifer/grahams-init-fixture.json rename conifer/{custom => integration/auth_evergreen}/__init__.py (100%) rename conifer/{custom/auth_evergreen.py => integration/auth_evergreen/django.py} (97%) rename conifer/{custom/auth_evergreen_support.py => integration/auth_evergreen/eg_xmlrpc.py} (100%) rename conifer/integration/{hooks.py => uwindsor.py} (54%) create mode 100644 conifer/integration/uwindsor_campus_info.py delete mode 100644 conifer/middleware/genshi_locals.py rename conifer/{middleware => plumbing}/__init__.py (100%) create mode 100644 conifer/plumbing/genshi_support.py rename conifer/{integration/_hooksystem.py => plumbing/hooksystem.py} (100%) delete mode 100644 conifer/syrup/fuzzy_match.py delete mode 100644 conifer/syrup/user_lookup.py rename conifer/syrup/views/{generics.py => _generics.py} (96%) create mode 100644 conifer/syrup/views/auth.py rename conifer/{ => syrup/views}/genshi_namespace.py (93%) delete mode 100644 rfc_sql/reserves.sql diff --git a/.gitignore b/.gitignore index b365b13..63dd581 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ private_local_settings.py /conifer/.dired /conifer/local_settings.py /conifer/remodel.sqlite3 -*~ \ No newline at end of file +*~ +/conifer/test.db diff --git a/conifer/BRANCH-TODO.org b/conifer/BRANCH-TODO.org deleted file mode 100644 index 51f47f9..0000000 --- a/conifer/BRANCH-TODO.org +++ /dev/null @@ -1,38 +0,0 @@ -* Tasks for the =2010-02-campus-integration-reorg= branch - - The goal of this branch is to reorganize and document the two major - integration points in Syrup: the library systems and the campus - information systems. Both of these integrations existed prior to the - branch, but were undocumented and messy. - -** The Evergreen-or-not question. - - Prepare to sync with the =eg-schema-experiment= branch - - "in evergreen database" vs. "other database with OpenSRF calls" - -** Enumerate the ways that campus integration is currently used. - Put this in the campus-integration documentation. - -** A Library Integration module which is readable and documented - - integrate via local_settings.py - - Prepare to sync with the =eg-schema-experiment= branch - -** How much of the integration data belongs in the database? - Should the Django ADMINS list be pulled from the db? What about - Z39.50 targets? What are the deciding principles when figuring out - where to store config data? - -** Campus integration for departments. - - how to address the faculty/campus/dept/etc. hierarchy? - - list of departments - - look up department based on course-code - - instructors in a given department - -** question: Campus integration for terms? - Even just a "feed of terms you might not yet know about?" - -** question: when looking up membership info, always update membership table? - Should just asking an external campus system, 'What sections is - John in?' automatically add membership records for John, for - course-sites related to those sections? Should it also (only during - the active period of a term) drop John from current sections that - he's no longer part of? diff --git a/conifer/custom/README b/conifer/custom/README deleted file mode 100644 index a80b2b0..0000000 --- a/conifer/custom/README +++ /dev/null @@ -1,4 +0,0 @@ -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/doc/grahams-init-fixture.json b/conifer/doc/grahams-init-fixture.json new file mode 100644 index 0000000..3bdd0a7 --- /dev/null +++ b/conifer/doc/grahams-init-fixture.json @@ -0,0 +1 @@ +[{"pk": 22, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 8}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_message", "name": "Can add message", "content_type": 4}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_message", "name": "Can change message", "content_type": 4}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_message", "name": "Can delete message", "content_type": 4}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 5}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 6}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 7}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_migrationhistory", "name": "Can add migration history", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_migrationhistory", "name": "Can change migration history", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_migrationhistory", "name": "Can delete migration history", "content_type": 9}}, {"pk": 46, "model": "auth.permission", "fields": {"codename": "add_config", "name": "Can add config", "content_type": 16}}, {"pk": 40, "model": "auth.permission", "fields": {"codename": "add_course", "name": "Can add course", "content_type": 14}}, {"pk": 37, "model": "auth.permission", "fields": {"codename": "add_department", "name": "Can add department", "content_type": 13}}, {"pk": 52, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 18}}, {"pk": 58, "model": "auth.permission", "fields": {"codename": "add_item", "name": "Can add item", "content_type": 20}}, {"pk": 55, "model": "auth.permission", "fields": {"codename": "add_membership", "name": "Can add membership", "content_type": 19}}, {"pk": 31, "model": "auth.permission", "fields": {"codename": "add_servicedesk", "name": "Can add service desk", "content_type": 11}}, {"pk": 49, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 17}}, {"pk": 34, "model": "auth.permission", "fields": {"codename": "add_term", "name": "Can add term", "content_type": 12}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_userprofile", "name": "Can add user profile", "content_type": 10}}, {"pk": 43, "model": "auth.permission", "fields": {"codename": "add_z3950target", "name": "Can add z3950 target", "content_type": 15}}, {"pk": 47, "model": "auth.permission", "fields": {"codename": "change_config", "name": "Can change config", "content_type": 16}}, {"pk": 41, "model": "auth.permission", "fields": {"codename": "change_course", "name": "Can change course", "content_type": 14}}, {"pk": 38, "model": "auth.permission", "fields": {"codename": "change_department", "name": "Can change department", "content_type": 13}}, {"pk": 53, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 18}}, {"pk": 59, "model": "auth.permission", "fields": {"codename": "change_item", "name": "Can change item", "content_type": 20}}, {"pk": 56, "model": "auth.permission", "fields": {"codename": "change_membership", "name": "Can change membership", "content_type": 19}}, {"pk": 32, "model": "auth.permission", "fields": {"codename": "change_servicedesk", "name": "Can change service desk", "content_type": 11}}, {"pk": 50, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 17}}, {"pk": 35, "model": "auth.permission", "fields": {"codename": "change_term", "name": "Can change term", "content_type": 12}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_userprofile", "name": "Can change user profile", "content_type": 10}}, {"pk": 44, "model": "auth.permission", "fields": {"codename": "change_z3950target", "name": "Can change z3950 target", "content_type": 15}}, {"pk": 48, "model": "auth.permission", "fields": {"codename": "delete_config", "name": "Can delete config", "content_type": 16}}, {"pk": 42, "model": "auth.permission", "fields": {"codename": "delete_course", "name": "Can delete course", "content_type": 14}}, {"pk": 39, "model": "auth.permission", "fields": {"codename": "delete_department", "name": "Can delete department", "content_type": 13}}, {"pk": 54, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 18}}, {"pk": 60, "model": "auth.permission", "fields": {"codename": "delete_item", "name": "Can delete item", "content_type": 20}}, {"pk": 57, "model": "auth.permission", "fields": {"codename": "delete_membership", "name": "Can delete membership", "content_type": 19}}, {"pk": 33, "model": "auth.permission", "fields": {"codename": "delete_servicedesk", "name": "Can delete service desk", "content_type": 11}}, {"pk": 51, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 17}}, {"pk": 36, "model": "auth.permission", "fields": {"codename": "delete_term", "name": "Can delete term", "content_type": 12}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_userprofile", "name": "Can delete user profile", "content_type": 10}}, {"pk": 45, "model": "auth.permission", "fields": {"codename": "delete_z3950target", "name": "Can delete z3950 target", "content_type": 15}}, {"pk": 1, "model": "auth.user", "fields": {"username": "graham", "first_name": "Fawcett", "last_name": "Graham", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2010-07-14 13:03:40", "groups": [], "user_permissions": [], "password": "sha1$c1214$8cc63f8cfc851f04d1058f8448d895d0532a48ad", "email": "fawcett@uwindsor.ca", "date_joined": "2010-07-09 09:15:14"}}, {"pk": 2, "model": "auth.user", "fields": {"username": "art", "first_name": "Art", "last_name": "Rhyno", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2010-07-14 13:04:23", "groups": [], "user_permissions": [], "password": "sha1$d20ab$0c4d2b85765001fe8bfe5a206df3e5f208f9ebbe", "email": "artrhyno@uwindsor.ca", "date_joined": "2010-07-14 13:04:23"}}, {"pk": 3, "model": "auth.user", "fields": {"username": "mita", "first_name": "Mita", "last_name": "Williams", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2010-07-14 13:04:45", "groups": [], "user_permissions": [], "password": "sha1$02883$d2d17afdf8bbb55a755b8aad175290a526d931d3", "email": "mita@uwindsor.ca", "date_joined": "2010-07-14 13:04:45"}}, {"pk": 16, "model": "contenttypes.contenttype", "fields": {"model": "config", "name": "config", "app_label": "syrup"}}, {"pk": 5, "model": "contenttypes.contenttype", "fields": {"model": "contenttype", "name": "content type", "app_label": "contenttypes"}}, {"pk": 14, "model": "contenttypes.contenttype", "fields": {"model": "course", "name": "course", "app_label": "syrup"}}, {"pk": 13, "model": "contenttypes.contenttype", "fields": {"model": "department", "name": "department", "app_label": "syrup"}}, {"pk": 2, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "auth"}}, {"pk": 18, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "syrup"}}, {"pk": 20, "model": "contenttypes.contenttype", "fields": {"model": "item", "name": "item", "app_label": "syrup"}}, {"pk": 8, "model": "contenttypes.contenttype", "fields": {"model": "logentry", "name": "log entry", "app_label": "admin"}}, {"pk": 19, "model": "contenttypes.contenttype", "fields": {"model": "membership", "name": "membership", "app_label": "syrup"}}, {"pk": 4, "model": "contenttypes.contenttype", "fields": {"model": "message", "name": "message", "app_label": "auth"}}, {"pk": 9, "model": "contenttypes.contenttype", "fields": {"model": "migrationhistory", "name": "migration history", "app_label": "south"}}, {"pk": 1, "model": "contenttypes.contenttype", "fields": {"model": "permission", "name": "permission", "app_label": "auth"}}, {"pk": 11, "model": "contenttypes.contenttype", "fields": {"model": "servicedesk", "name": "service desk", "app_label": "syrup"}}, {"pk": 6, "model": "contenttypes.contenttype", "fields": {"model": "session", "name": "session", "app_label": "sessions"}}, {"pk": 7, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "sites"}}, {"pk": 17, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "syrup"}}, {"pk": 12, "model": "contenttypes.contenttype", "fields": {"model": "term", "name": "term", "app_label": "syrup"}}, {"pk": 3, "model": "contenttypes.contenttype", "fields": {"model": "user", "name": "user", "app_label": "auth"}}, {"pk": 10, "model": "contenttypes.contenttype", "fields": {"model": "userprofile", "name": "user profile", "app_label": "syrup"}}, {"pk": 15, "model": "contenttypes.contenttype", "fields": {"model": "z3950target", "name": "z3950 target", "app_label": "syrup"}}, {"pk": "7ab9e962df78ff5840a97773342df8bf", "model": "sessions.session", "fields": {"expire_date": "2010-07-23 09:20:53", "session_data": "gAJ9cQEoVQp0ZXN0Y29va2llcQJVBndvcmtlZHEDVRJfYXV0aF91c2VyX2JhY2tlbmRxBFUpZGph\nbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmRxBVUNX2F1dGhfdXNlcl9pZHEG\nSwF1LjI2YWFjNGJiMTNlZDE1ZjlmYjQxMzZlMWJjZGIxMDAw\n"}}, {"pk": "cd02d11771c381c73799afc5f75ea750", "model": "sessions.session", "fields": {"expire_date": "2010-07-28 13:03:40", "session_data": "gAJ9cQEoVRJfYXV0aF91c2VyX2JhY2tlbmRxAlUpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5k\ncy5Nb2RlbEJhY2tlbmRxA1UNX2F1dGhfdXNlcl9pZHEESwF1LjdlNTBmMDAyMWI4ZWQ0ZWUwOGE1\nZTdkMDUxZWNmZGZj\n"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 5, "model": "admin.logentry", "fields": {"action_flag": 2, "action_time": "2010-07-14 13:07:00", "object_repr": "graham", "object_id": "1", "change_message": "Changed first_name and last_name.", "user": 1, "content_type": 3}}, {"pk": 4, "model": "admin.logentry", "fields": {"action_flag": 2, "action_time": "2010-07-14 13:05:00", "object_repr": "mita", "object_id": "3", "change_message": "Changed first_name, last_name, email, is_staff and is_superuser.", "user": 1, "content_type": 3}}, {"pk": 3, "model": "admin.logentry", "fields": {"action_flag": 1, "action_time": "2010-07-14 13:04:45", "object_repr": "mita", "object_id": "3", "change_message": "", "user": 1, "content_type": 3}}, {"pk": 2, "model": "admin.logentry", "fields": {"action_flag": 2, "action_time": "2010-07-14 13:04:41", "object_repr": "art", "object_id": "2", "change_message": "Changed first_name, last_name, email, is_staff and is_superuser.", "user": 1, "content_type": 3}}, {"pk": 1, "model": "admin.logentry", "fields": {"action_flag": 1, "action_time": "2010-07-14 13:04:23", "object_repr": "art", "object_id": "2", "change_message": "", "user": 1, "content_type": 3}}, {"pk": 1, "model": "south.migrationhistory", "fields": {"applied": "2010-07-09 13:20:26", "app_name": "syrup", "migration": "0001_initial"}}, {"pk": 2, "model": "south.migrationhistory", "fields": {"applied": "2010-07-09 13:20:26", "app_name": "syrup", "migration": "0002_auto__add_field_site_term__add_field_site_course__add_unique_site_owne"}}, {"pk": 1, "model": "syrup.userprofile", "fields": {"last_email_notice": "2010-07-08 22:26:34", "created": "2010-07-08 22:26:34", "wants_email_notices": false, "last_modified": "2010-07-08 22:26:34", "user": 1, "ils_userid": null}}, {"pk": 1, "model": "syrup.servicedesk", "fields": {"active": true, "last_modified": "2010-07-08 22:30:22", "external_id": "", "name": "Leddy Library Reserves, Lower Level", "created": "2010-07-08 22:30:22"}}, {"pk": 1, "model": "syrup.term", "fields": {"code": "2010S", "name": "2010 Summer", "created": "2010-07-08 22:29:28", "finish": "2010-09-01", "start": "2010-05-01", "last_modified": "2010-07-08 22:29:28"}}, {"pk": 2, "model": "syrup.term", "fields": {"code": "2010F", "name": "2010 Fall", "created": "2010-07-08 22:29:50", "finish": "2010-12-31", "start": "2010-09-01", "last_modified": "2010-07-08 22:29:50"}}, {"pk": 3, "model": "syrup.term", "fields": {"code": "2011W", "name": "2011 Winter", "created": "2010-07-08 22:30:05", "finish": "2011-05-01", "start": "2011-01-01", "last_modified": "2010-07-08 22:30:05"}}, {"pk": 1, "model": "syrup.department", "fields": {"active": true, "service_desk": 1, "last_modified": "2010-07-08 22:30:34", "name": "Metaphysics", "created": "2010-07-08 22:30:34"}}, {"pk": 2, "model": "syrup.department", "fields": {"active": true, "service_desk": 1, "last_modified": "2010-07-08 22:30:44", "name": "Biology", "created": "2010-07-08 22:30:44"}}, {"pk": 3, "model": "syrup.department", "fields": {"active": true, "service_desk": 1, "last_modified": "2010-07-08 22:30:54", "name": "Computer Science", "created": "2010-07-08 22:30:54"}}, {"pk": 1, "model": "syrup.course", "fields": {"code": "99-100", "department": 1, "last_modified": "2010-07-08 22:31:24", "name": "Introduction to Thought", "created": "2010-07-08 22:31:24"}}, {"pk": 2, "model": "syrup.course", "fields": {"code": "99-200", "department": 1, "last_modified": "2010-07-08 22:31:36", "name": "Intermediate Thought", "created": "2010-07-08 22:31:36"}}, {"pk": 1, "model": "syrup.z3950target", "fields": {"name": "Concat/OWA", "database": "OWA", "syntax": "USMARC", "host": "zed.concat.ca", "active": true, "port": 210}}, {"pk": 2, "model": "syrup.z3950target", "fields": {"name": "LOC", "database": "Voyager", "syntax": "USMARC", "host": "z3950.loc.gov", "active": true, "port": 7090}}, {"pk": 1, "model": "syrup.config", "fields": {"name": "application.title", "value": "Reserv-o-Matic"}}, {"pk": 2, "model": "syrup.config", "fields": {"name": "default.desk", "value": "1"}}] diff --git a/conifer/genshi_support.py b/conifer/genshi_support.py deleted file mode 100644 index 3231fb1..0000000 --- a/conifer/genshi_support.py +++ /dev/null @@ -1,52 +0,0 @@ -import genshi_namespace -from django.http import HttpResponse, HttpRequest -from genshi.template import TemplateLoader -from genshi.filters import Translator -from genshi.builder import tag -import genshi.output -from django.conf import settings -import gettext -from conifer.middleware.genshi_locals import get_request -import genshi - -#------------------------------------------------------------ -# set up internationalization - -# if settings.USE_I18N: -# translations = gettext.GNUTranslations( -# file('locale/%s/LC_MESSAGES/conifer-syrup.mo' % settings.LANGUAGE_CODE)) -# _ = translations.ugettext -# else: -# _ = gettext.gettext - -from django.utils import translation -_ = translation.ugettext - -def template_loaded(template): - if settings.USE_I18N: - template.filters.insert(0, Translator(_)) - - -dirs = [settings.HERE('templates')] - -loader = TemplateLoader(dirs, auto_reload=True, callback=template_loaded) - -def template(tname): - return loader.load(tname) - - -def _inject_django_things_into_namespace(request, ns): - ns['_'] = _ - ns['request'] = request - ns['ROOT'] = request.META['SCRIPT_NAME'] - ns['user'] = getattr(request, 'user', None) - ns.update(genshi_namespace.__dict__) - -#------------------------------------------------------------ -# main API - -def render(tname, _django_type=HttpResponse, _serialization='xhtml', **kwargs): - request = get_request() - _inject_django_things_into_namespace(request, kwargs) - return _django_type(template(tname).generate(**kwargs).render(_serialization, doctype='xhtml')) - diff --git a/conifer/grahams-init-fixture.json b/conifer/grahams-init-fixture.json deleted file mode 100644 index ec1d296..0000000 --- a/conifer/grahams-init-fixture.json +++ /dev/null @@ -1 +0,0 @@ -[{"pk": 22, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 8}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_message", "name": "Can add message", "content_type": 4}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_message", "name": "Can change message", "content_type": 4}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_message", "name": "Can delete message", "content_type": 4}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 5}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 6}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 7}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_migrationhistory", "name": "Can add migration history", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_migrationhistory", "name": "Can change migration history", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_migrationhistory", "name": "Can delete migration history", "content_type": 9}}, {"pk": 46, "model": "auth.permission", "fields": {"codename": "add_config", "name": "Can add config", "content_type": 16}}, {"pk": 40, "model": "auth.permission", "fields": {"codename": "add_course", "name": "Can add course", "content_type": 14}}, {"pk": 37, "model": "auth.permission", "fields": {"codename": "add_department", "name": "Can add department", "content_type": 13}}, {"pk": 52, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 18}}, {"pk": 58, "model": "auth.permission", "fields": {"codename": "add_item", "name": "Can add item", "content_type": 20}}, {"pk": 55, "model": "auth.permission", "fields": {"codename": "add_membership", "name": "Can add membership", "content_type": 19}}, {"pk": 31, "model": "auth.permission", "fields": {"codename": "add_servicedesk", "name": "Can add service desk", "content_type": 11}}, {"pk": 49, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 17}}, {"pk": 34, "model": "auth.permission", "fields": {"codename": "add_term", "name": "Can add term", "content_type": 12}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_userprofile", "name": "Can add user profile", "content_type": 10}}, {"pk": 43, "model": "auth.permission", "fields": {"codename": "add_z3950target", "name": "Can add z3950 target", "content_type": 15}}, {"pk": 47, "model": "auth.permission", "fields": {"codename": "change_config", "name": "Can change config", "content_type": 16}}, {"pk": 41, "model": "auth.permission", "fields": {"codename": "change_course", "name": "Can change course", "content_type": 14}}, {"pk": 38, "model": "auth.permission", "fields": {"codename": "change_department", "name": "Can change department", "content_type": 13}}, {"pk": 53, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 18}}, {"pk": 59, "model": "auth.permission", "fields": {"codename": "change_item", "name": "Can change item", "content_type": 20}}, {"pk": 56, "model": "auth.permission", "fields": {"codename": "change_membership", "name": "Can change membership", "content_type": 19}}, {"pk": 32, "model": "auth.permission", "fields": {"codename": "change_servicedesk", "name": "Can change service desk", "content_type": 11}}, {"pk": 50, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 17}}, {"pk": 35, "model": "auth.permission", "fields": {"codename": "change_term", "name": "Can change term", "content_type": 12}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_userprofile", "name": "Can change user profile", "content_type": 10}}, {"pk": 44, "model": "auth.permission", "fields": {"codename": "change_z3950target", "name": "Can change z3950 target", "content_type": 15}}, {"pk": 48, "model": "auth.permission", "fields": {"codename": "delete_config", "name": "Can delete config", "content_type": 16}}, {"pk": 42, "model": "auth.permission", "fields": {"codename": "delete_course", "name": "Can delete course", "content_type": 14}}, {"pk": 39, "model": "auth.permission", "fields": {"codename": "delete_department", "name": "Can delete department", "content_type": 13}}, {"pk": 54, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 18}}, {"pk": 60, "model": "auth.permission", "fields": {"codename": "delete_item", "name": "Can delete item", "content_type": 20}}, {"pk": 57, "model": "auth.permission", "fields": {"codename": "delete_membership", "name": "Can delete membership", "content_type": 19}}, {"pk": 33, "model": "auth.permission", "fields": {"codename": "delete_servicedesk", "name": "Can delete service desk", "content_type": 11}}, {"pk": 51, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 17}}, {"pk": 36, "model": "auth.permission", "fields": {"codename": "delete_term", "name": "Can delete term", "content_type": 12}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_userprofile", "name": "Can delete user profile", "content_type": 10}}, {"pk": 45, "model": "auth.permission", "fields": {"codename": "delete_z3950target", "name": "Can delete z3950 target", "content_type": 15}}, {"pk": 1, "model": "auth.user", "fields": {"username": "graham", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2010-07-09 09:20:52", "groups": [], "user_permissions": [], "password": "sha1$c1214$8cc63f8cfc851f04d1058f8448d895d0532a48ad", "email": "fawcett@uwindsor.ca", "date_joined": "2010-07-09 09:15:14"}}, {"pk": 16, "model": "contenttypes.contenttype", "fields": {"model": "config", "name": "config", "app_label": "syrup"}}, {"pk": 5, "model": "contenttypes.contenttype", "fields": {"model": "contenttype", "name": "content type", "app_label": "contenttypes"}}, {"pk": 14, "model": "contenttypes.contenttype", "fields": {"model": "course", "name": "course", "app_label": "syrup"}}, {"pk": 13, "model": "contenttypes.contenttype", "fields": {"model": "department", "name": "department", "app_label": "syrup"}}, {"pk": 2, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "auth"}}, {"pk": 18, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "syrup"}}, {"pk": 20, "model": "contenttypes.contenttype", "fields": {"model": "item", "name": "item", "app_label": "syrup"}}, {"pk": 8, "model": "contenttypes.contenttype", "fields": {"model": "logentry", "name": "log entry", "app_label": "admin"}}, {"pk": 19, "model": "contenttypes.contenttype", "fields": {"model": "membership", "name": "membership", "app_label": "syrup"}}, {"pk": 4, "model": "contenttypes.contenttype", "fields": {"model": "message", "name": "message", "app_label": "auth"}}, {"pk": 9, "model": "contenttypes.contenttype", "fields": {"model": "migrationhistory", "name": "migration history", "app_label": "south"}}, {"pk": 1, "model": "contenttypes.contenttype", "fields": {"model": "permission", "name": "permission", "app_label": "auth"}}, {"pk": 11, "model": "contenttypes.contenttype", "fields": {"model": "servicedesk", "name": "service desk", "app_label": "syrup"}}, {"pk": 6, "model": "contenttypes.contenttype", "fields": {"model": "session", "name": "session", "app_label": "sessions"}}, {"pk": 7, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "sites"}}, {"pk": 17, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "syrup"}}, {"pk": 12, "model": "contenttypes.contenttype", "fields": {"model": "term", "name": "term", "app_label": "syrup"}}, {"pk": 3, "model": "contenttypes.contenttype", "fields": {"model": "user", "name": "user", "app_label": "auth"}}, {"pk": 10, "model": "contenttypes.contenttype", "fields": {"model": "userprofile", "name": "user profile", "app_label": "syrup"}}, {"pk": 15, "model": "contenttypes.contenttype", "fields": {"model": "z3950target", "name": "z3950 target", "app_label": "syrup"}}, {"pk": "7ab9e962df78ff5840a97773342df8bf", "model": "sessions.session", "fields": {"expire_date": "2010-07-23 09:20:53", "session_data": "gAJ9cQEoVQp0ZXN0Y29va2llcQJVBndvcmtlZHEDVRJfYXV0aF91c2VyX2JhY2tlbmRxBFUpZGph\nbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmRxBVUNX2F1dGhfdXNlcl9pZHEG\nSwF1LjI2YWFjNGJiMTNlZDE1ZjlmYjQxMzZlMWJjZGIxMDAw\n"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "south.migrationhistory", "fields": {"applied": "2010-07-09 13:20:26", "app_name": "syrup", "migration": "0001_initial"}}, {"pk": 2, "model": "south.migrationhistory", "fields": {"applied": "2010-07-09 13:20:26", "app_name": "syrup", "migration": "0002_auto__add_field_site_term__add_field_site_course__add_unique_site_owne"}}, {"pk": 1, "model": "syrup.userprofile", "fields": {"last_email_notice": "2010-07-08 22:26:34", "created": "2010-07-08 22:26:34", "wants_email_notices": false, "last_modified": "2010-07-08 22:26:34", "user": 1, "ils_userid": null}}, {"pk": 1, "model": "syrup.servicedesk", "fields": {"active": true, "last_modified": "2010-07-08 22:30:22", "external_id": "", "name": "Leddy Library Reserves, Lower Level", "created": "2010-07-08 22:30:22"}}, {"pk": 1, "model": "syrup.term", "fields": {"code": "2010S", "name": "2010 Summer", "created": "2010-07-08 22:29:28", "finish": "2010-09-01", "start": "2010-05-01", "last_modified": "2010-07-08 22:29:28"}}, {"pk": 2, "model": "syrup.term", "fields": {"code": "2010F", "name": "2010 Fall", "created": "2010-07-08 22:29:50", "finish": "2010-12-31", "start": "2010-09-01", "last_modified": "2010-07-08 22:29:50"}}, {"pk": 3, "model": "syrup.term", "fields": {"code": "2011W", "name": "2011 Winter", "created": "2010-07-08 22:30:05", "finish": "2011-05-01", "start": "2011-01-01", "last_modified": "2010-07-08 22:30:05"}}, {"pk": 1, "model": "syrup.department", "fields": {"active": true, "service_desk": 1, "last_modified": "2010-07-08 22:30:34", "name": "Metaphysics", "created": "2010-07-08 22:30:34"}}, {"pk": 2, "model": "syrup.department", "fields": {"active": true, "service_desk": 1, "last_modified": "2010-07-08 22:30:44", "name": "Biology", "created": "2010-07-08 22:30:44"}}, {"pk": 3, "model": "syrup.department", "fields": {"active": true, "service_desk": 1, "last_modified": "2010-07-08 22:30:54", "name": "Computer Science", "created": "2010-07-08 22:30:54"}}, {"pk": 1, "model": "syrup.course", "fields": {"code": "99-100", "department": 1, "last_modified": "2010-07-08 22:31:24", "name": "Introduction to Thought", "created": "2010-07-08 22:31:24"}}, {"pk": 2, "model": "syrup.course", "fields": {"code": "99-200", "department": 1, "last_modified": "2010-07-08 22:31:36", "name": "Intermediate Thought", "created": "2010-07-08 22:31:36"}}, {"pk": 1, "model": "syrup.z3950target", "fields": {"name": "zed/OWA", "database": "OWA", "syntax": "USMARC", "host": "zed.concat.ca", "active": true, "port": 210}}, {"pk": 1, "model": "syrup.config", "fields": {"name": "application.title", "value": "Reserv-o-Matic"}}, {"pk": 2, "model": "syrup.config", "fields": {"name": "default.desk", "value": "1"}}] diff --git a/conifer/custom/__init__.py b/conifer/integration/auth_evergreen/__init__.py similarity index 100% rename from conifer/custom/__init__.py rename to conifer/integration/auth_evergreen/__init__.py diff --git a/conifer/custom/auth_evergreen.py b/conifer/integration/auth_evergreen/django.py similarity index 97% rename from conifer/custom/auth_evergreen.py rename to conifer/integration/auth_evergreen/django.py index f40ed48..1cc5d83 100644 --- a/conifer/custom/auth_evergreen.py +++ b/conifer/integration/auth_evergreen/django.py @@ -1,4 +1,4 @@ -from auth_evergreen_support import EvergreenAuthServer +from eg_xmlrpc import EvergreenAuthServer from django.contrib.auth.models import User from django.conf import settings diff --git a/conifer/custom/auth_evergreen_support.py b/conifer/integration/auth_evergreen/eg_xmlrpc.py similarity index 100% rename from conifer/custom/auth_evergreen_support.py rename to conifer/integration/auth_evergreen/eg_xmlrpc.py diff --git a/conifer/integration/hooks.py b/conifer/integration/uwindsor.py similarity index 54% rename from conifer/integration/hooks.py rename to conifer/integration/uwindsor.py index 17fad18..651485d 100644 --- a/conifer/integration/hooks.py +++ b/conifer/integration/uwindsor.py @@ -1,9 +1,12 @@ from datetime import date from django.conf import settings -from conifer.libsystems.evergreen.support import initialize +from conifer.libsystems.evergreen.support import initialize, E1 from conifer.libsystems.z3950 import marcxml as M from conifer.libsystems.evergreen import item_status as I from conifer.libsystems.z3950 import pyz3950_search as PZ +from xml.etree import ElementTree as ET +import re + def department_course_catalogue(): """ @@ -51,9 +54,60 @@ def item_status(item): def cat_search(query, start=1, limit=10): if query.startswith(EG_BASE): + # query is an Evergreen URL results = M.marcxml_to_records(I.url_to_marcxml(query)) numhits = len(results) else: + # query is an actual Z39.50 query cat_host, cat_port, cat_db = settings.Z3950_CONFIG results, numhits = PZ.search(cat_host, cat_port, cat_db, query, start, limit) return results, numhits + +def bib_id_to_marcxml(bib_id): + """ + Given a bib_id, return a MARC record in MARCXML format. Return + None if the bib_id does not exist. + """ + try: + xml = I.bib_id_to_marcxml(bib_id) + return ET.fromstring(xml) + except: + return None + +def get_better_copy_of_marc(marc_string): + """ + This function takes a MARCXML record and returns either the same + record, or another instance of the same record from a different + source. + + This is a hack. There is currently at least one Z39.50 server that + returns a MARCXML record with broken character encoding. This + function declares a point at which we can work around that server. + """ + dct = M.marcxml_to_dictionary(marc_string) + bib_id = dct.get('901c') + better = bib_id_to_marcxml(bib_id) + return better or marc_string + +def marcxml_to_url(marc_string): + """ + Given a MARC record, return either a URL (representing the + electronic resource) or None. + + Typically this will be the 856$u value; but in Conifer, 856$9 and + 856$u form an associative array, where $9 holds the institution + codes and $u holds the URLs. + """ + LIBCODE = 'OWA' # Leddy + try: + dct = M.marcxml_to_dictionary(marc_string) + words = lambda string: re.findall(r'\S+', string) + keys = words(dct.get('8569')) + urls = words(dct.get('856u')) + print 'KEYS:', keys + print 'URLS:', urls + return urls[keys.index(LIBCODE)] + except: + return None + + diff --git a/conifer/integration/uwindsor_campus_info.py b/conifer/integration/uwindsor_campus_info.py new file mode 100644 index 0000000..2e1ed87 --- /dev/null +++ b/conifer/integration/uwindsor_campus_info.py @@ -0,0 +1,14 @@ +from urllib2 import * +from django.utils import simplejson + +CAMPUS_INFO_SERVICE = 'http://fawcett.medialab.uwindsor.ca/campus-info/' + +def call(name, *args): + url = '%s%s?%s' % (CAMPUS_INFO_SERVICE, name, simplejson.dumps(args)) + raw = urlopen(url).read() + return simplejson.loads(raw) + +if __name__ == '__main__': + print call('methods_supported') + print call('person_lookup', 'fawcett') + print call('membership_ids', 'dunn15') diff --git a/conifer/libsystems/evergreen/fm_IDL.xml b/conifer/libsystems/evergreen/fm_IDL.xml index 9d2b981..91542cf 100644 --- a/conifer/libsystems/evergreen/fm_IDL.xml +++ b/conifer/libsystems/evergreen/fm_IDL.xml @@ -1,3902 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +HTTP/1.1 200 OK +Date: Tue, 13 Jul 2010 20:42:08 GMT +Server: Apache/2.2.9 (Debian) mod_ssl/2.2.9 OpenSSL/0.9.8g mod_perl/2.0.4 Perl/v5.10.0 +Accept-Ranges: bytes +Cache-Control: max-age=2592000 +Expires: Thu, 12 Aug 2010 20:42:08 GMT +Content-Type: text/xml; charset=utf-8 + diff --git a/conifer/libsystems/evergreen/item_status.py b/conifer/libsystems/evergreen/item_status.py index 315e735..9ddd4ef 100644 --- a/conifer/libsystems/evergreen/item_status.py +++ b/conifer/libsystems/evergreen/item_status.py @@ -32,4 +32,8 @@ def url_to_marcxml(url): if __name__ == '__main__': support.initialize('http://www.concat.ca/') - print url_to_marcxml('http://www.concat.ca/opac/en-US/skin/default/xml/rdetail.xml?r=1082665&t=dylan%20thomas%20ralph&tp=keyword&d=0&hc=14&rt=keyword') + #print url_to_marcxml('http://www.concat.ca/opac/en-US/skin/default/xml/rdetail.xml?r=1082665&t=dylan%20thomas%20ralph&tp=keyword&d=0&hc=14&rt=keyword') + from xml.etree import ElementTree as ET + for t in ET.fromstring(bib_id_to_marcxml('2081089')).getiterator(): + print t.text + diff --git a/conifer/libsystems/evergreen/support.py b/conifer/libsystems/evergreen/support.py index cda0dc5..b28df74 100644 --- a/conifer/libsystems/evergreen/support.py +++ b/conifer/libsystems/evergreen/support.py @@ -22,6 +22,7 @@ def initialize(base): fields_for_class.update(dict(_fields())) def _fields(): + return fm_IDL_location = BASE + 'reports/fm_IDL.xml' tree = ElementTree.parse(urllib2.urlopen(fm_IDL_location)) NS = '{http://opensrf.org/spec/IDL/base/v1}' diff --git a/conifer/libsystems/z3950/marcxml.py b/conifer/libsystems/z3950/marcxml.py index 06ec460..140f210 100644 --- a/conifer/libsystems/z3950/marcxml.py +++ b/conifer/libsystems/z3950/marcxml.py @@ -65,8 +65,9 @@ def marcxml_dictionary_to_dc(dct): extract some Dublin Core elements from it. Fixme, I'm sure this could be way improved.""" out = {} - meta = [('245a', 'dc:title'), ('100a', 'dc:creator'), - ('260c', 'dc:date'), ('700a', 'dc:contributor')] + meta = [('245a', 'dc:title'), + ('260c', 'dc:date'), + ('700a', 'dc:contributor')] for marc, dc in meta: value = dct.get(marc) if value: @@ -79,12 +80,18 @@ def marcxml_dictionary_to_dc(dct): title = [v.strip() for k,v in sorted(dct.items()) if k in ('245a', '245b')] if title: out['dc:title'] = strip_punct(' '.join(title)) + + for k in ('100a', '110a', '700a', '710a'): + if dct.get(k): + out['dc:creator'] = strip_punct(dct[k]) + break + return out def strip_punct(s): # strip whitespace and trailing single punctuation characters s = s.strip() - if s and s[-1] in ',.;:/': + if s and (s[-1] in ',.;:/'): s = s[:-1] return s.strip() diff --git a/conifer/libsystems/z3950/pyz3950_search.py b/conifer/libsystems/z3950/pyz3950_search.py index 87abb63..e8b1909 100644 --- a/conifer/libsystems/z3950/pyz3950_search.py +++ b/conifer/libsystems/z3950/pyz3950_search.py @@ -76,6 +76,8 @@ def search(host, port, database, query, start=1, limit=10): # TODO: fix this ascii/replace, once our z3950/marc encoding # issues are sorted out. rec = unicode(rec, 'ascii', 'replace') + # replace multiple 'unknown' characters with a single one. + rec = re.sub(u'\ufffd+', u'\ufffd', rec) assert isinstance(rec, unicode) # this must be true. parsed.append(ET.fromstring(rec.encode('utf-8'))) diff --git a/conifer/middleware/genshi_locals.py b/conifer/middleware/genshi_locals.py deleted file mode 100644 index 2977cca..0000000 --- a/conifer/middleware/genshi_locals.py +++ /dev/null @@ -1,20 +0,0 @@ -# see http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser - -# threadlocals middleware - -try: - from threading import local -except ImportError: - from django.utils._threading_local import local - -_thread_locals = local() - -class ThreadLocals(object): - """Middleware that gets various objects from the - request object and saves them in thread local storage.""" - def process_request(self, request): - _thread_locals.request = request - -def get_request(): - return getattr(_thread_locals, 'request', None) - diff --git a/conifer/middleware/__init__.py b/conifer/plumbing/__init__.py similarity index 100% rename from conifer/middleware/__init__.py rename to conifer/plumbing/__init__.py diff --git a/conifer/plumbing/genshi_support.py b/conifer/plumbing/genshi_support.py new file mode 100644 index 0000000..a0fe935 --- /dev/null +++ b/conifer/plumbing/genshi_support.py @@ -0,0 +1,84 @@ +import os +from django.http import HttpResponse, HttpRequest +from genshi.template import TemplateLoader +from genshi.template import NewTextTemplate +from genshi.builder import tag +import genshi.output +from django.conf import settings +from warnings import warn + +try: + from threading import local +except ImportError: + from django.utils._threading_local import local + +#--------------------------------------------------------------------------- +# Middleware + +_THREAD_LOCALS = local() + +class GenshiMiddleware(object): + + def process_request(self, request): + _THREAD_LOCALS.request = request + +def get_request(): + return getattr(_THREAD_LOCALS, 'request') + + +#--------------------------------------------------------------------------- +# Templating support + +class TemplateSet(object): + + def __init__(self, basedir, namespace_module=None): + self.basedir = basedir + self.dirs = [self.basedir] + self.loader = TemplateLoader(self.dirs, + auto_reload=True, + callback=self.template_loaded) + self.namespace_module = namespace_module + + def file(self, name): + fn = os.path.join(self.basedir, name) + assert os.path.dirname(fn) == self.basedir + return file(fn) + + def template_loaded(self, template): + pass + + def template(self, tname): + return self.loader.load(tname) + + def text_template(self, tname): + return self.loader.load(tname, cls=NewTextTemplate) + + #------------------------------------------------------------ + + def _inject_django_things_into_namespace(self, request, ns): + ns['request'] = request + ns['user'] = getattr(request, 'user', None) + ns['ROOT'] = request and request.META['SCRIPT_NAME'] + if not 'errors' in ns: + ns['errors'] = None + if self.namespace_module is not None: + ns.update(self.namespace_module.__dict__) + + def render(self, tname, **kwargs): + request = get_request() + self._inject_django_things_into_namespace(request, kwargs) + return HttpResponse(self.template(tname).generate(**kwargs).render('xhtml')) + + def render_xml(self, tname, **kwargs): + request = get_request() + self._inject_django_things_into_namespace(request, kwargs) + content_type = kwargs.get('content_type', 'application/xml') + return HttpResponse(self.template(tname).generate(**kwargs).render('xml'), + content_type=content_type) + + def plaintext(self, tname, **kwargs): + request = get_request() + content_type = kwargs.get('content_type', 'text/plain') + self._inject_django_things_into_namespace(request, kwargs) + txt = self.text_template(tname).generate(**kwargs).render('text') + return txt diff --git a/conifer/integration/_hooksystem.py b/conifer/plumbing/hooksystem.py similarity index 100% rename from conifer/integration/_hooksystem.py rename to conifer/plumbing/hooksystem.py diff --git a/conifer/settings.py b/conifer/settings.py index cf49c44..1f0c6c7 100644 --- a/conifer/settings.py +++ b/conifer/settings.py @@ -67,7 +67,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'conifer.middleware.genshi_locals.ThreadLocals', + 'conifer.plumbing.genshi_support.GenshiMiddleware', 'django.middleware.locale.LocaleMiddleware', 'babeldjango.middleware.LocaleMiddleware', 'django.middleware.transaction.TransactionMiddleware', @@ -120,7 +120,7 @@ MANAGERS = ADMINS if EVERGREEN_AUTHENTICATION: AUTHENTICATION_BACKENDS.append( - 'conifer.custom.auth_evergreen.EvergreenAuthBackend') + 'conifer.integration.auth_evergreen.django.EvergreenAuthBackend') #---------- diff --git a/conifer/static/main.css b/conifer/static/main.css index 0da63f1..20c711e 100644 --- a/conifer/static/main.css +++ b/conifer/static/main.css @@ -21,8 +21,9 @@ li { margin-left: 16px; } /* remember to define focus styles! */ :focus { - outline: 0; + outline: gray 1px solid; } + body { line-height: 1; color: black; @@ -178,9 +179,11 @@ span.final_item { font-weight: bold; font-size: 110%; } .itemtree li { padding-left: 0; margin-left: 0; } -.itemtree li { margin: 12px 8px; } +.itemtree li { margin: 12px 8px; line-height: 115%; } .itemtree li .mainline { padding-left: 8px; } +.itemtree li .author_pub { padding-left: 8px; font-size: 90%; margin: 4px 0 16px 0; color: #111; } + .itemtree .metalink { padding-left: 8px; color: gray; } .itemtree .metalink a { color: gray; @@ -281,8 +284,10 @@ p.todo, div.todo { background-color: #fdd; padding: 6px; margin: 12px; border-le font-size: 80%; color: navy; } -.menublockopener { margin-left: 0.25em; color: #bbb !important; font-weight: normal !important; } -.menublock { background-color: #f2e4cc; font-size: 95%; padding: 1px 4px; } +.menublockopener { margin-left: 0.25em; color: #d44 !important; font-weight: normal !important; } +.menublock { color: gray; background-color: #844; font-size: 85%; padding: 4px 4px; margin-top: 2px; display: inline-block; } +.menublock a { color: white; padding: 8px; } +.menublock a:hover { color: yellow; } #sitebanner { background-color: #f2e4cc; margin: -12px -12px 12px -12px; padding: 8px; } #sitesearch { float: right; } diff --git a/conifer/syrup/admin.py b/conifer/syrup/admin.py index 94b1e2a..ebf34c9 100644 --- a/conifer/syrup/admin.py +++ b/conifer/syrup/admin.py @@ -1,6 +1,3 @@ -# to run standalone: From conifer directory: -# DJANGO_SETTINGS_MODULE=conifer.settings PYTHONPATH=.. python syrup/admin.py - from django.contrib import admin import django.db.models from conifer.syrup.models import * diff --git a/conifer/syrup/fuzzy_match.py b/conifer/syrup/fuzzy_match.py deleted file mode 100644 index 4e53917..0000000 --- a/conifer/syrup/fuzzy_match.py +++ /dev/null @@ -1,53 +0,0 @@ -from conifer.syrup import models -from django.db.models import Q - -#http://www.poromenos.org/node/87. Credit to Poromenos. It's under BSD. -def levenshtein_distance(first, second): - """Find the Levenshtein distance between two strings.""" - if len(first) > len(second): - first, second = second, first - if len(second) == 0: - return len(first) - first_length = len(first) + 1 - second_length = len(second) + 1 - distance_matrix = [range(second_length) for x in range(first_length)] - for i in xrange(1, first_length): - for j in range(1, second_length): - deletion = distance_matrix[i-1][j] + 1 - insertion = distance_matrix[i][j-1] + 1 - substitution = distance_matrix[i-1][j-1] - if first[i-1] != second[j-1]: - substitution += 1 - distance_matrix[i][j] = min(insertion, deletion, substitution) - - return distance_matrix[first_length-1][second_length-1] - -def rank_pending_items(dct): - title = dct.get('dc:title','') - author = dct.get('dc:creator','') - publisher = dct.get('dc:publisher','') - pubdate = dct.get('dc:pubdate','') - - # not right... also, prefetch metadata - all_items = models.Item.objects.select_related('metadata') - all_pending_items = all_items.filter(Q(item_type='PHYS'), - ~Q(metadata__name='syrup:barcode')) - all_pending_items = all_items.filter(Q(item_type='PHYS'), - ~Q(metadata__name='syrup:barcode', - metadata__value__in=[p.barcode for p in models.PhysicalObject.live_objects()])) - results = [] - # not sure I like these weights, but let's play a bit. - METRICS = (('dc:title', 1), ('dc:creator', 1), ('dc:publisher', 0.5), ('dc:pubdate', 0.25)) - for item in all_pending_items: - scores = [] - for heading, weight in METRICS: - try: - ival = item.metadata_set.get(name=heading).value or '' - except: - ival = '' - dist = levenshtein_distance(dct.get(heading) or '', ival) - scores.append(dist/weight) - score = sum(scores) - results.append((score, item)) - results.sort() - return results diff --git a/conifer/syrup/integration.py b/conifer/syrup/integration.py index 5911138..1b7a9d9 100644 --- a/conifer/syrup/integration.py +++ b/conifer/syrup/integration.py @@ -15,7 +15,6 @@ def can_create_sites(user): allowed to create new course-reserve sites. Note that users marked as 'staff' are always allowed to create new sites. """ - pass @disable @@ -25,7 +24,6 @@ def department_course_catalogue(): the departments to which they belong. Each row should be a tuple in the form: ('Department name', 'course-code', 'Course name'). """ - pass @disable @@ -36,7 +34,6 @@ def term_catalogue(): 'start-date', 'end-date'), where the dates are instances of the datetime.date class. """ - pass @disable @@ -69,3 +66,37 @@ def item_status(item): the item. The ServiceDesk object has an 'external_id' attribute which should represent the desk in the ILS. """ + + +@disable +def bib_id_to_marcxml(bib_id): + """ + Given a bib_id, return a MARC record in MARCXML format. Return + None if the bib_id does not exist. + """ + + +@disable +def get_better_copy_of_marc(marc_string): + """ + This function takes a MARCXML record and returns either the same + record, or another instance of the same record from a different + source. + + This is a hack. There is currently at least one Z39.50 server that + returns a MARCXML record with broken character encoding. This + function declares a point at which we can work around that server. + """ + + +@disable +def marcxml_to_url(marc_string): + """ + Given a MARC record, return either a URL (representing the + electronic resource) or None. + + Typically this will be the 856$u value; but in Conifer, 856$9 and + 856$u form an associative array, where $9 holds the institution + codes and $u holds the URLs. + """ + diff --git a/conifer/syrup/models.py b/conifer/syrup/models.py index 2d525d1..413b2f5 100644 --- a/conifer/syrup/models.py +++ b/conifer/syrup/models.py @@ -7,9 +7,9 @@ from django.utils.translation import ugettext as _ import re import random from django.utils import simplejson -from conifer.middleware import genshi_locals +from conifer.plumbing.genshi_support import get_request # campus and library integration -from conifer.integration._hooksystem import * +from conifer.plumbing.hooksystem import * from django.conf import settings campus = settings.CAMPUS_INTEGRATION # TODO: fixme, not sure if conifer.custom is a good parent. @@ -183,13 +183,10 @@ class Site(BaseModel): return u'%s: %s (%s, %s)' % ( self.course.code, self.course.name, self.owner.last_name or self.owner.username, - self.term) + self.term.name) def list_display(self): - if self.code: - return '%s: %s [%s]' % (self.term, self.title, self.code) - else: - return '%s: %s' % (self.term, self.title) + return '%s [%s, %s]' % (self.course.name, self.course.code, self.term.name) def items(self): return self.item_set.all() @@ -232,7 +229,7 @@ class Site(BaseModel): def site_url(self, suffix=''): # I'm not fond of this being here. I think I'll leave this and # item_url non-implemented, and monkey-patch them in views.py. - req = genshi_locals.get_request() + req = get_request() prefix = req.META['SCRIPT_NAME'] return '%s/site/%d/%s' % (prefix, self.id, suffix) @@ -292,6 +289,12 @@ class Site(BaseModel): user.id == self.owner_id \ or bool(self.members().filter(user=user))) + def is_open_to(self, user): + return self.access == 'ANON' \ + or (self.access == 'LOGIN' and user.is_authenticated()) \ + or user.is_staff \ + or self.is_member(user) + #------------------------------------------------------------ # User membership in sites @@ -499,7 +502,7 @@ class Item(BaseModel): def item_url(self, suffix='', force_local_url=False): # I'm not fond of this being here. I think I'll leave this and # site_url non-implemented, and monkey-patch them in views.py. - req = genshi_locals.get_request() + req = get_request() prefix = req.META['SCRIPT_NAME'] if self.item_type == 'ELEC' and suffix == '': return '%s/site/%d/item/%d/dl/%s' % ( @@ -542,7 +545,7 @@ class Item(BaseModel): return hl_title def author_hl(self, terms): - hl_author = self.author() + hl_author = self.author for term in terms: hl_author = highlight(hl_author,term) diff --git a/conifer/syrup/urls.py b/conifer/syrup/urls.py index afb2501..f8d36a7 100644 --- a/conifer/syrup/urls.py +++ b/conifer/syrup/urls.py @@ -52,11 +52,11 @@ urlpatterns = patterns('conifer.syrup.views', (r'^admin/update_depts_courses/$', 'admin_update_depts_courses'), (r'^admin/update_terms/$', 'admin_update_terms'), - (r'^phys/$', 'phys_index'), - (r'^phys/checkout/$', 'phys_checkout'), - (r'^phys/mark_arrived/$', 'phys_mark_arrived'), - (r'^phys/mark_arrived/match/$', 'phys_mark_arrived_match'), - (r'^phys/circlist/$', 'phys_circlist'), + # (r'^phys/$', 'phys_index'), + # (r'^phys/checkout/$', 'phys_checkout'), + # (r'^phys/mark_arrived/$', 'phys_mark_arrived'), + # (r'^phys/mark_arrived/match/$', 'phys_mark_arrived_match'), + # (r'^phys/circlist/$', 'phys_circlist'), (r'^site/(?P\d+)/reseq$', 'site_reseq'), (ITEM_PREFIX + r'reseq', 'item_heading_reseq'), diff --git a/conifer/syrup/user_lookup.py b/conifer/syrup/user_lookup.py deleted file mode 100644 index 5e0b8a7..0000000 --- a/conifer/syrup/user_lookup.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.contrib.auth.models import User -from django.contrib.auth import get_backends - -#---------------------------------------------------------------------- -# Initializing an external user account - -# TODO: does it make sense to put maybe_initialize_user on the -# authentication backends, or does it belong somewhere else? - -# For usernames that come from external authentication sources (LDAP, -# Evergreen, etc.) we need a general way to look up a user who may not -# yet have a Django account. For example, you might want to add user -# 'xsmith' as the instructor for a course. If 'xsmith' is in LDAP but -# not yet in Django, it would be nice if a Django record were lazily -# created for him upon lookup. - -# That's what 'maybe_initialize_user' is for: participating backends -# provide a 'maybe_initialize_user' method which creates a new User -# record if one doesn't exist. Otherwise, 'maybe_initialize_user' is -# equivalent to 'User.objects.get(username=username)'. - -_backends_that_can_initialize_users = [ - be for be in get_backends() if hasattr(be, 'maybe_initialize_user')] - -def maybe_initialize_user(username): - try: - return User.objects.get(username=username) - except User.DoesNotExist: - for be in _backends_that_can_initialize_users: - user = be.maybe_initialize_user(username, look_local=False) - if user: - return user diff --git a/conifer/syrup/views/__init__.py b/conifer/syrup/views/__init__.py index 2554718..f30317c 100644 --- a/conifer/syrup/views/__init__.py +++ b/conifer/syrup/views/__init__.py @@ -4,3 +4,4 @@ from items import * from search import * from admin import * from feeds import * +from auth import * diff --git a/conifer/syrup/views/_common.py b/conifer/syrup/views/_common.py index 24bf849..330fb87 100644 --- a/conifer/syrup/views/_common.py +++ b/conifer/syrup/views/_common.py @@ -1,129 +1,49 @@ -import warnings -from conifer.syrup import models -from datetime import datetime +#---------------------------------------------------------------------- +# Initialize the Genshi templating system. 'g' is the 'templating +# system object' used to render Genshi templates. 'genshi_namespace' +# is a module which acts as a global namespace when expanding a Genshi +# template. + +from conifer.here import HERE +from conifer.plumbing.genshi_support import TemplateSet +from . import genshi_namespace + +g = TemplateSet(HERE('templates'), genshi_namespace) + +#---------------------------------------------------------------------- +# Common imports shared by all view functions. + import django.conf -from django.contrib.auth import authenticate, login, logout -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User, SiteProfileNotAvailable -from django.core.paginator import Paginator -from django.db.models import Q -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound -from django.http import HttpResponseForbidden -from django.shortcuts import get_object_or_404 -from django.utils import simplejson -from generics import * -#from gettext import gettext as _ # fixme, is this the right function to import? -from django.utils.translation import ugettext as _ -import conifer.genshi_support as g import django.forms import re import sys -from django.forms.models import modelformset_factory -from conifer.libsystems.z3950.marcxml import (marcxml_to_dictionary, - marcxml_dictionary_to_dc) -from conifer.syrup.fuzzy_match import rank_pending_items -from django.core.urlresolvers import reverse -from conifer.here import HERE +import warnings import pdb -#----------------------------------------------------------------------------- -# Z39.50 Support -# -# This is experimental at this time, and requires some tricky Python -# imports as far as I can tell. For that reason, let's keep the Z39.50 -# support optional for now. If you have Ply and PyZ3950, we'll load -# and use it; if not, no worries, everything else will workk. - -try: - # Graham needs this import hackery to get PyZ3950 working. Presumably - # Art can 'import profile; import lex', so this hack won't run for - # him. - try: - import profile - import lex - import yacc - except ImportError: - sys.modules['profile'] = sys # just get something called 'profile'; - # it's not actually used. - import ply.lex - import ply.yacc # pyz3950 thinks these are toplevel modules. - sys.modules['lex'] = ply.lex - sys.modules['yacc'] = ply.yacc - - # for Z39.50 support, not sure whether this is the way to go yet but - # as generic as it gets - from PyZ3950 import zoom, zmarc -except: - warnings.warn('Could not load Z39.50 support.') - -#----------------------------------------------------------------------------- -# poor-man's logging. Not sure we need more yet. +from conifer.syrup import models +from datetime import datetime +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User, SiteProfileNotAvailable +from django.core.paginator import Paginator +from django.core.urlresolvers import reverse +from django.db.models import Q +from django.forms.models import modelformset_factory +from django.http import (HttpResponse, HttpResponseRedirect, + HttpResponseNotFound, + HttpResponseForbidden) +from django.shortcuts import get_object_or_404 +from django.utils import simplejson +from django.utils.translation import ugettext as _ +from _generics import * # TODO: should not import-star -def log(level, msg): - print >> sys.stderr, '[%s] %s: %s' % (datetime.now(), level.upper(), msg) +from conifer.libsystems.z3950.marcxml import (marcxml_to_dictionary, + marcxml_dictionary_to_dc) -#----------------------------------------------------------------------------- -# Authentication - -def auth_handler(request, path): - default_url = request.META['SCRIPT_NAME'] + '/' - if path == 'login/': - if request.method == 'GET': - next=request.GET.get('next', default_url) - if request.user.is_authenticated(): - return HttpResponseRedirect(next) - else: - return g.render('auth/login.xhtml', - next=request.GET.get('next')) - else: - userid, password = request.POST['userid'], request.POST['password'] - next = request.POST['next'] - user = authenticate(username=userid, password=password) - def _error_page(msg): - return g.render('auth/login.xhtml', err=msg, next=next) - if user is None: - return _error_page( - _('Invalid username or password. Please try again.')) - elif not user.is_active: - return _error_age( - _('Sorry, this account has been disabled.')) - else: - login(request, user) - # initialize the profile if it doesn't exist. - try: - user.get_profile() - except models.UserProfile.DoesNotExist: - profile = models.UserProfile.objects.create(user=user) - profile.save() - return HttpResponseRedirect( - request.POST.get('next', default_url)) - elif path == 'logout': - logout(request) - return HttpResponseRedirect(default_url) - else: - return HttpResponse('auth_handler: ' + path) #----------------------------------------------------------------------------- # Authorization -# TODO: this _fast_user_membership_query is broken. - -def _fast_user_membership_query(user_id, site_id, where=None): - # I use a raw SQL query here because I want the lookup to be as - # fast as possible. Caching would help too, but let's try this - # first. (todo, review later.) - return True # TODO: fixme!!!! - query = ('select count(*) from syrup_member ' - 'where user_id=%s and site_id=%s ') - if where: - query += (' and ' + where) - cursor = django.db.connection.cursor() - cursor.execute(query, [user_id, int(site_id)]) - res = cursor.fetchall() - cursor.close() - allowed = bool(res[0][0]) - return allowed - def _access_denied(request, message): if request.user.is_anonymous(): # then take them to login screen.... @@ -139,10 +59,8 @@ def _access_denied(request, message): # decorator def instructors_only(handler): def hdlr(request, site_id, *args, **kwargs): - allowed = request.user.is_superuser - if not allowed: - allowed = _fast_user_membership_query( - request.user.id, site_id, "role in ('INSTR','ASSIST')") + site = get_object_or_404(models.Site, pk=site_id) + allowed = site.can_edit(request.user) if allowed: return handler(request, site_id, *args, **kwargs) else: @@ -154,13 +72,8 @@ def instructors_only(handler): def members_only(handler): def hdlr(request, site_id, *args, **kwargs): user = request.user - allowed = user.is_superuser - if not allowed: - site = models.Site.objects.get(pk=site_id) - allowed = site.access=='ANON' or \ - (user.is_authenticated() and site.access=='LOGIN') - if not allowed: - allowed = _fast_user_membership_query(user.id, site_id) + site = get_object_or_404(models.Site, pk=site_id) + allowed = site.is_open_to(request.user) if allowed: return handler(request, site_id, *args, **kwargs) else: @@ -190,6 +103,7 @@ def public(handler): # that are supposed to be public. return handler + #----------------------------------------------------------------------------- # Simple Message: just a quick title-and-message web page. @@ -230,8 +144,8 @@ def user_filters(user): # have explicit Member-ship. filters = { 'items': (Q(site__access__in=('LOGIN','ANON')) \ - | Q(site__member__user=user)), - 'sites': (Q(access__in=('LOGIN','ANON')) | Q(member__user=user)), + | Q(site__group__membership__user=user)), + 'sites': (Q(access__in=('LOGIN','ANON')) | Q(group__membership__user=user)), 'instructors': Q(), # TODO: do we really need a filter here? } return filters diff --git a/conifer/syrup/views/generics.py b/conifer/syrup/views/_generics.py similarity index 96% rename from conifer/syrup/views/generics.py rename to conifer/syrup/views/_generics.py index fa0122e..deddb03 100644 --- a/conifer/syrup/views/generics.py +++ b/conifer/syrup/views/_generics.py @@ -1,9 +1,9 @@ -import conifer.genshi_support as g from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponseForbidden from django.shortcuts import get_object_or_404 from django.forms import ModelForm, ValidationError +from _common import g # the Genshi templating system def generic_handler(form, decorator=lambda x: x): def handler(request, obj_id=None, action=None): diff --git a/conifer/syrup/views/admin.py b/conifer/syrup/views/admin.py index cea88ce..fa21299 100644 --- a/conifer/syrup/views/admin.py +++ b/conifer/syrup/views/admin.py @@ -1,6 +1,6 @@ from _common import * from django.utils.translation import ugettext as _ -from conifer.integration._hooksystem import * +from conifer.plumbing.hooksystem import * from datetime import date #----------------------------------------------------------------------------- diff --git a/conifer/syrup/views/auth.py b/conifer/syrup/views/auth.py new file mode 100644 index 0000000..a81b89a --- /dev/null +++ b/conifer/syrup/views/auth.py @@ -0,0 +1,42 @@ +from _common import * + +#----------------------------------------------------------------------------- +# Authentication + +def auth_handler(request, path): + default_url = request.META['SCRIPT_NAME'] + '/' + if path == 'login/': + if request.method == 'GET': + next=request.GET.get('next', default_url) + if request.user.is_authenticated(): + return HttpResponseRedirect(next) + else: + return g.render('auth/login.xhtml', + next=request.GET.get('next')) + else: + userid, password = request.POST['userid'], request.POST['password'] + next = request.POST['next'] + user = authenticate(username=userid, password=password) + def _error_page(msg): + return g.render('auth/login.xhtml', err=msg, next=next) + if user is None: + return _error_page( + _('Invalid username or password. Please try again.')) + elif not user.is_active: + return _error_age( + _('Sorry, this account has been disabled.')) + else: + login(request, user) + # initialize the profile if it doesn't exist. + try: + user.get_profile() + except models.UserProfile.DoesNotExist: + profile = models.UserProfile.objects.create(user=user) + profile.save() + return HttpResponseRedirect( + request.POST.get('next', default_url)) + elif path == 'logout': + logout(request) + return HttpResponseRedirect(default_url) + else: + return HttpResponse('auth_handler: ' + path) diff --git a/conifer/syrup/views/feeds.py b/conifer/syrup/views/feeds.py index ce17953..5c5b136 100644 --- a/conifer/syrup/views/feeds.py +++ b/conifer/syrup/views/feeds.py @@ -15,7 +15,7 @@ def site_feeds(request, site_id, feed_type): def render_title(item): return item.title if feed_type == 'top-level': - items = items.filter(parent_heading=None).order_by('-sort_order') + items = items.filter(parent_heading=None).order_by('title') elif feed_type == 'recent-changes': items = items.order_by('-last_modified') elif feed_type == 'tree': diff --git a/conifer/genshi_namespace.py b/conifer/syrup/views/genshi_namespace.py similarity index 93% rename from conifer/genshi_namespace.py rename to conifer/syrup/views/genshi_namespace.py index be91276..f9e2d5b 100644 --- a/conifer/genshi_namespace.py +++ b/conifer/syrup/views/genshi_namespace.py @@ -8,6 +8,9 @@ import itertools from itertools import cycle from conifer.syrup import models import django.forms +from django.utils import translation + +_ = translation.ugettext # this probably ought to be a method on User, or another model class. def instructor_url(instructor, suffix=''): diff --git a/conifer/syrup/views/items.py b/conifer/syrup/views/items.py index 378149e..5c9d417 100644 --- a/conifer/syrup/views/items.py +++ b/conifer/syrup/views/items.py @@ -1,7 +1,8 @@ from _common import * from django.utils.translation import ugettext as _ -from xml.etree import ElementTree as E +from xml.etree import ElementTree as ET from conifer.syrup import integration +from conifer.plumbing.hooksystem import * @members_only @@ -173,9 +174,13 @@ def item_add_cat_search(request, site_id, item_id): if not site.can_edit(request.user): return _access_denied(_('You are not an editor.')) - pickitem = marcxml_to_dictionary(raw_pickitem) + if gethook('get_better_copy_of_marc'): + pickitem_xml = callhook('get_better_copy_of_marc', raw_pickitem) + raw_pickitem = unicode(ET.tostring(pickitem_xml)) + pickitem = marcxml_to_dictionary(pickitem_xml) + else: + pickitem = marcxml_to_dictionary(raw_pickitem) dublin = marcxml_dictionary_to_dc(pickitem) - assert dublin #TODO: this data munging does not belong here. @@ -183,8 +188,9 @@ def item_add_cat_search(request, site_id, item_id): # one last thing. If this picked item has an 856$9 field, then # it's an electronic resource, not a physical item. In that # case, we add it as a URL, not a PHYS. - if '8569' in pickitem: - dct = dict(item_type='URL', url=pickitem.get('856u')) + url = callhook('marcxml_to_url', raw_pickitem) + if url: + dct = dict(item_type='URL', url=url) else: dct = dict(item_type='PHYS') diff --git a/conifer/syrup/views/search.py b/conifer/syrup/views/search.py index 62f911c..6cee7c6 100644 --- a/conifer/syrup/views/search.py +++ b/conifer/syrup/views/search.py @@ -1,5 +1,5 @@ from _common import * -from django.utils.translation import ugettext as _ +from PyZ3950 import zoom, zmarc def normalize_query(query_string, findterms=re.compile(r'"([^"]+)"|(\S+)').findall, @@ -38,7 +38,6 @@ def get_query(query_string, search_fields): #----------------------------------------------------------------------------- # Search and search support - def search(request, in_site=None, with_instructor=None): ''' Need to work on this, the basic idea is - put an entry point for instructor and site listings @@ -88,7 +87,7 @@ def search(request, in_site=None, with_instructor=None): else: if not with_instructor: # Textual (non-numeric) queries. - item_query = get_query(query_string, ['title', 'metadata__value']) + item_query = get_query(query_string, ['title', 'author', 'publisher', 'marcxml']) #need to think about sort order here, probably better by author (will make sortable at display level) results_list = models.Item.objects.filter(item_query) @@ -103,7 +102,7 @@ def search(request, in_site=None, with_instructor=None): else: results_list = results_list.filter(user_filter_for_items) - results_list = results_list.distinct().order_by('title') + results_list = results_list.distinct() #.order_by('title') results_len = len(results_list) paginator = Paginator(results_list, count) @@ -112,10 +111,10 @@ def search(request, in_site=None, with_instructor=None): # then no site search is necessary. site_list = []; site_len = 0 else: - site_query = get_query(query_string, ['title', 'department__name']) + site_query = get_query(query_string, ['course__name', 'course__department__name']) # apply the search-filter and the user-filter site_results = models.Site.objects.filter(site_query).filter(user_filter_for_sites) - site_list = site_results.order_by('title') + site_list = site_results.order_by('course__name') site_len = len(site_results) #instructor search @@ -123,7 +122,7 @@ def search(request, in_site=None, with_instructor=None): instructor_list = []; instr_len = 0 else: instr_query = get_query(query_string, ['user__last_name']) - instructor_results = models.Member.objects.filter(instr_query).filter(role='INSTR') + instructor_results = models.Membership.objects.filter(instr_query).filter(role='INSTR') if in_site: instructor_results = instructor_results.filter(site=in_site) instructor_list = instructor_results.order_by('user__last_name')[0:5] @@ -137,7 +136,7 @@ def search(request, in_site=None, with_instructor=None): paginator = Paginator( results_list, count) site_results = models.Site.objects.filter(active=True) - site_list = site_results.order_by('title')[0:5] + site_list = site_results.order_by('course__name')[0:5] site_len = len(site_results) instructor_results = models.Member.objects.filter(role='INSTR') instructor_list = instructor_results.order_by('user__last_name')[0:5] @@ -180,7 +179,7 @@ def zsearch(request): tquery = request.POST['ztitle'] search_target= models.Z3950Target.objects.get(name=target) conn = zoom.Connection (search_target.host, search_target.port) - conn.databaseName = search_target.db + conn.databaseName = search_target.database conn.preferredRecordSyntax = search_target.syntax query = zoom.Query ('CCL', '%s="%s"' % ('ti',tquery)) res = conn.search (query) @@ -201,7 +200,7 @@ def zsearch(request): raw = r.data # Convert to MARC - marcdata = zmarc.MARC(raw) + marcdata = zmarc.MARC(raw, strict=False) #print marcdata # Convert to MARCXML diff --git a/conifer/syrup/views/sites.py b/conifer/syrup/views/sites.py index c0167fb..b635f59 100644 --- a/conifer/syrup/views/sites.py +++ b/conifer/syrup/views/sites.py @@ -220,8 +220,6 @@ def site_invitation(request): # invitation failures? They should be captured somehow, I # think. Should we temporarily disable accounts after # multiple failures? - log('WARN', 'Invitation failure, user %r gave code %r' % \ - (request.user.username, code)) error = _('The code you provided is not valid.') return g.render('site_invitation.xhtml', **locals()) diff --git a/conifer/templates/auth/login.xhtml b/conifer/templates/auth/login.xhtml index 16dc438..2d723df 100644 --- a/conifer/templates/auth/login.xhtml +++ b/conifer/templates/auth/login.xhtml @@ -1,5 +1,5 @@ ${title} - @@ -23,7 +23,7 @@ title = _('Syrup E-Reserves: Please log in') Password: - + diff --git a/conifer/templates/browse_index.xhtml b/conifer/templates/browse_index.xhtml index f06be1e..e31edf5 100644 --- a/conifer/templates/browse_index.xhtml +++ b/conifer/templates/browse_index.xhtml @@ -29,6 +29,7 @@ blocks = itertools.groupby(sites, lambda s: s.course.department)

Choose from one of the options below:

+ (currently broken)
  • Browse by Course Name
  • diff --git a/conifer/templates/components/site.xhtml b/conifer/templates/components/site.xhtml index 3fbc49c..9b158c8 100644 --- a/conifer/templates/components/site.xhtml +++ b/conifer/templates/components/site.xhtml @@ -55,9 +55,12 @@ searchtext = _('search this site...') - about • edit • relocate + about this item edit relocate delete
+
+ ${item.author}. ${item.publisher}. +
${show_tree(subs, edit)} diff --git a/conifer/templates/edit_site.xhtml b/conifer/templates/edit_site.xhtml index 2a79949..0e4cc2f 100644 --- a/conifer/templates/edit_site.xhtml +++ b/conifer/templates/edit_site.xhtml @@ -1,6 +1,6 @@ diff --git a/conifer/templates/feeds/site_atom.xml b/conifer/templates/feeds/site_atom.xml index 49b72ff..a0c6182 100644 --- a/conifer/templates/feeds/site_atom.xml +++ b/conifer/templates/feeds/site_atom.xml @@ -2,7 +2,7 @@ - Reserves (${feed_type}) for ${site.list_display()} + Reserves (${feed_type}) for ${site} details

- Electronic resource. view + Electronic resource. view

${dc.get(k) or '—'} diff --git a/conifer/templates/master.xhtml b/conifer/templates/master.xhtml index b35580d..c885ebe 100644 --- a/conifer/templates/master.xhtml +++ b/conifer/templates/master.xhtml @@ -21,9 +21,6 @@ import os -->