[open-ils-commits] r1257 - in servres/trunk/conifer: integration syrup/views (gfawcett)

svn at svn.open-ils.org svn at svn.open-ils.org
Sat Mar 5 13:42:37 EST 2011


Author: gfawcett
Date: 2011-03-05 13:42:33 -0500 (Sat, 05 Mar 2011)
New Revision: 1257

Added:
   servres/trunk/conifer/integration/uwindsor_fuzzy_lookup.py
Modified:
   servres/trunk/conifer/integration/uwindsor.py
   servres/trunk/conifer/syrup/views/sites.py
Log:
uwindsor integration: LDAP fuzzy person-lookup rewritten in Python.

This used to require an external executable called SpeedLookup, whose
functionality is now replaced by uwindsor_fuzzy_lookup.py.

Modified: servres/trunk/conifer/integration/uwindsor.py
===================================================================
--- servres/trunk/conifer/integration/uwindsor.py	2011-03-02 18:16:25 UTC (rev 1256)
+++ servres/trunk/conifer/integration/uwindsor.py	2011-03-05 18:42:33 UTC (rev 1257)
@@ -17,6 +17,7 @@
 import traceback
 import subprocess
 import uwindsor_campus_info
+import uwindsor_fuzzy_lookup
 
 # 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
@@ -374,39 +375,32 @@
     else:
         return 'STUDT'
 
-FUZZY_LOOKUP_BIN = '/usr/local/bin/SpeedLookup'
+def fuzzy_person_lookup(query, include_students=False):
+    """
+    Given a query, return a list of users who probably match the
+    query. The result is a list of (userid, display), where userid
+    is the campus userid of the person, and display is a string
+    suitable for display in a results-list. Include_students
+    indicates that students, and not just faculty/staff, should be
+    included in the results.
+    """
+    # Note, our 'include_students' option only matches students on exact
+    # userids. That is, fuzzy matching only works for staff, faculty, and
+    # other non-student roles.
 
-if os.path.isfile(FUZZY_LOOKUP_BIN):
+    filter  = uwindsor_fuzzy_lookup.build_filter(query, include_students)
+    results = uwindsor_fuzzy_lookup.search(filter)
 
-    def fuzzy_person_lookup(query, include_students=False):
-        """
-        Given a query, return a list of users who probably match the
-        query. The result is a list of (userid, display), where userid
-        is the campus userid of the person, and display is a string
-        suitable for display in a results-list. Include_students
-        indicates that students, and not just faculty/staff, should be
-        included in the results.
-        """
+    out = []
+    for res in results:
+        if not 'employeeType' in res:
+            res['employeeType'] = 'Student' # a 99% truth!
+        display = ('%(givenName)s %(sn)s. %(employeeType)s, '
+                   '%(uwinDepartment)s. <%(mail)s>. [%(uid)s]') % res
+        out.append((res['uid'], display))
+    return out
 
-        cmd = [FUZZY_LOOKUP_BIN, query]
-        if include_students:
-            cmd.append('students')
 
-        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
-        try:
-            rdr = csv.reader(p.stdout)
-            rdr.next()              # skip header row,
-            data = list(rdr)        # eagerly fetch the rest
-        finally:
-            p.stdout.close()
-
-        out = []
-        for uid, sn, given, role, dept, mail in data:
-            display = '%s %s. %s, %s. <%s>. [%s]' % (given, sn, role, dept, mail, uid)
-            out.append((uid, display))
-        return out
-
-
 def derive_group_code_from_section(site, section):
     """
     This function is used to simplify common-case permission setting

Added: servres/trunk/conifer/integration/uwindsor_fuzzy_lookup.py
===================================================================
--- servres/trunk/conifer/integration/uwindsor_fuzzy_lookup.py	                        (rev 0)
+++ servres/trunk/conifer/integration/uwindsor_fuzzy_lookup.py	2011-03-05 18:42:33 UTC (rev 1257)
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+"A rewrite of SpeedLookup in Python."
+
+import ldap
+import csv, sys
+
+MIN_QUERY_LENGTH = 3
+
+BASE  = "ou=people,dc=uwindsor,dc=ca"
+SCOPE = ldap.SCOPE_ONELEVEL
+ATTRS = ["uid", "sn", "eduPersonNickname", "employeeType",
+         "uwinDepartment", "mail", "givenName"]
+
+# fetch server connection details from /etc/ldap-agent
+
+tmp = [line.strip() for line in file('/etc/ldap-agent')]
+SERVER, USER, PWD = tmp[:3]
+
+# ---------------------------------------------------------------------------
+# filter construction
+
+def _or(*parts):
+    return '|%s' % ''.join(['(%s)' % p for p in parts])
+
+def _and(*parts):
+    return '&%s' % ''.join(['(%s)' % p for p in parts])
+
+def build_filter(query, include_students=False):
+    if len(query) < MIN_QUERY_LENGTH:
+        return None
+    else:
+        query = query.lower()
+        pattern = _and(_or('uid=%(query)s*',
+                           'sn=%(query)s*',
+                           'givenName=%(query)s*'),
+                       _or(*['employeeType=%s' % x for x in
+                             ('Faculty', 'Administration',
+                              'Staff', 'Librarian', 
+                              'Academic*')]))
+        if include_students:
+            # we only match students by uid.
+            pattern = _or('uid=%(query)s', pattern)
+        return '(%s)' % (pattern % locals())
+
+# ---------------------------------------------------------------------------
+# LDAP interaction
+
+def search(filt):
+    if not filt:
+        return []
+
+    conn = ldap.open(SERVER)
+    conn.simple_bind_s(USER, PWD)
+    results = conn.search_s(BASE, SCOPE, filt, ATTRS)
+
+    for (cn, dct) in results:
+        dct['cn'] = cn
+        if dct.get('eduPersonNickname'):
+            dct['givenName'] = dct['eduPersonNickname']
+        for attr in dct:
+            dct[attr] = dct[attr][0]
+    
+    dicts = [dct for cn,dct in results]
+    dicts.sort(key=lambda dct: (dct['sn'], dct['givenName']))
+    conn.unbind_s()
+    return dicts
+
+# ---------------------------------------------------------------------------
+# main
+
+if __name__ == '__main__':
+    # the headings to print, and their corresponding ldap attributes.
+    HEADINGS = ["uid", "surname", "given", "type", "department", "email"]
+    MAPPING  = ["uid", "sn", "givenName", "employeeType", "uwinDepartment", "mail"]
+
+    out      = csv.writer(sys.stdout)
+    query    = sys.argv[1]
+    students = 'students' in sys.argv
+    filt     = build_filter(query, include_students=students)
+    results  = search(filt)
+
+    # print them
+    out.writerow(HEADINGS)
+    for dct in results:
+        row = [dct.get(key,'') for key in MAPPING]
+        out.writerow(row)


Property changes on: servres/trunk/conifer/integration/uwindsor_fuzzy_lookup.py
___________________________________________________________________
Name: svn:executable
   + *

Modified: servres/trunk/conifer/syrup/views/sites.py
===================================================================
--- servres/trunk/conifer/syrup/views/sites.py	2011-03-02 18:16:25 UTC (rev 1256)
+++ servres/trunk/conifer/syrup/views/sites.py	2011-03-05 18:42:33 UTC (rev 1257)
@@ -214,8 +214,8 @@
 
 @admin_only
 def site_fuzzy_user_lookup(request):
-    query = request.POST.get('q').lower().strip()
-    include_students = (request.POST.get('includeStudents') == 'true')
+    query = request.REQUEST.get('q').lower().strip()
+    include_students = (request.REQUEST.get('includeStudents') == 'true')
     results = callhook('fuzzy_person_lookup', query, include_students) or []
     limit = 10
     resp = {'results': results[:limit], 



More information about the open-ils-commits mailing list