[CalendarServer-changes] [14901] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Jun 19 11:06:13 PDT 2015


Revision: 14901
          http://trac.calendarserver.org//changeset/14901
Author:   sagen at apple.com
Date:     2015-06-19 11:06:12 -0700 (Fri, 19 Jun 2015)
Log Message:
-----------
Adds ability to filter attendee lookup results so only records with names *starting* with all the search tokens are returned

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/extensions.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/twistedcaldav/test/test_upgrade.py
    CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml
    CalendarServer/trunk/txdav/who/directory.py
    CalendarServer/trunk/txdav/who/test/test_directory.py
    CalendarServer/trunk/txdav/who/test/test_util.py
    CalendarServer/trunk/txdav/who/util.py

Modified: CalendarServer/trunk/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/extensions.py	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/twistedcaldav/extensions.py	2015-06-19 18:06:12 UTC (rev 14901)
@@ -1041,7 +1041,9 @@
             applyTo = True
 
         elif child.qname() == (calendarserver_namespace, "search-token"):
-            tokens.append(child.toString())
+            tokenValue = child.toString().strip()
+            if tokenValue:
+                tokens.append(tokenValue)
 
         elif child.qname() == (calendarserver_namespace, "limit"):
             try:

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2015-06-19 18:06:12 UTC (rev 14901)
@@ -316,6 +316,10 @@
 
     "DirectoryRealmName": "",
 
+    # Apply an additional filter for attendee lookups where names must start
+    # with the search tokens rather than just contain them.
+    "DirectoryFilterStartsWith": False,
+
     #
     # Locations and Resources service
     #

Modified: CalendarServer/trunk/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_upgrade.py	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/twistedcaldav/test/test_upgrade.py	2015-06-19 18:06:12 UTC (rev 14901)
@@ -1739,8 +1739,8 @@
 UID:1E238CA1-3C95-4468-B8CD-C8A399F78C71
 DTSTART;TZID=US/Pacific:20090203T120000
 DTEND;TZID=US/Pacific:20090203T130000
-ATTENDEE;CN=Wilfredo Sanchez;CUTYPE=INDIVIDUAL;EMAIL=wsanchez at example.com;
- PARTSTAT=ACCEPTED:urn:x-uid:6423F94A-6B76-4A3A-815B-D52CFD77935D
+ATTENDEE;CN=Wilfredo Sanchez-Vega;CUTYPE=INDIVIDUAL;EMAIL=wsanchez at example
+ .com;PARTSTAT=ACCEPTED:urn:x-uid:6423F94A-6B76-4A3A-815B-D52CFD77935D
 ATTENDEE;CN=Double 'quotey' Quotes;CUTYPE=INDIVIDUAL;EMAIL=doublequotes at ex
  ample.com;PARTSTAT=ACCEPTED:urn:x-uid:8E04787E-336D-41ED-A70B-D233AD0DCE6
  F

Modified: CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml	2015-06-19 18:06:12 UTC (rev 14901)
@@ -30,7 +30,7 @@
     <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
     <short-name>wsanchez</short-name>
     <password>zehcnasw</password>
-    <full-name>Wilfredo Sanchez</full-name>
+    <full-name>Wilfredo Sanchez-Vega</full-name>
     <email>wsanchez at example.com</email>
   </record>
   <record type="user">

Modified: CalendarServer/trunk/txdav/who/directory.py
===================================================================
--- CalendarServer/trunk/txdav/who/directory.py	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/txdav/who/directory.py	2015-06-19 18:06:12 UTC (rev 14901)
@@ -200,10 +200,20 @@
         else:
             recordTypes = None
 
-        results = yield self.recordsFromExpression(
-            expression, recordTypes=recordTypes, limitResults=limitResults,
-            timeoutSeconds=timeoutSeconds
-        )
+        # If a filter has been set, pass self.recordsFromExpression to it for
+        # result processing
+        if getattr(self, "_resultFilter", None):
+            results = yield self._resultFilter(
+                self.recordsFromExpression, tokens, expression,
+                recordTypes=recordTypes, limitResults=limitResults,
+                timeoutSeconds=timeoutSeconds
+            )
+        else:
+            results = yield self.recordsFromExpression(
+                expression, recordTypes=recordTypes, limitResults=limitResults,
+                timeoutSeconds=timeoutSeconds
+            )
+
         log.debug(
             "Tokens ({t}) matched {n} records",
             t=tokens, n=len(results)
@@ -256,6 +266,18 @@
         )
 
 
+    def setFilter(self, filter):
+        """
+        Assign a filter for post-processing recordsMatchingTokens and
+        recordsMatchingFields results.
+
+        @param filter: a callable taking the following args:
+            method, tokens, expression, recordTypes, limitResults, timeoutSeconds
+            ...and returning a deferred firing with the list of records
+        """
+        self._resultFilter = filter
+
+
     _oldRecordTypeNames = {
         "address": "addresses",
         "group": "groups",

Modified: CalendarServer/trunk/txdav/who/test/test_directory.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_directory.py	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/txdav/who/test/test_directory.py	2015-06-19 18:06:12 UTC (rev 14901)
@@ -23,6 +23,7 @@
 from twext.who.directory import DirectoryRecord
 from twext.who.idirectory import FieldName, RecordType
 from txdav.who.directory import CalendarDirectoryRecordMixin, AutoScheduleMode
+from txdav.who.util import startswithFilter
 from uuid import UUID
 from twext.who.expression import (
     MatchType, MatchFlags, MatchExpression
@@ -50,7 +51,7 @@
 
         expanded = yield record.expandedMembers()
         self.assertEquals(
-            set([u"Chris Lecroy", u"Cyrus Daboo", u"David Reid", u"Wilfredo Sanchez"]),
+            set([u"Chris Lecroy", u"Cyrus Daboo", u"David Reid", u"Wilfredo Sanchez-Vega"]),
             set([r.displayName for r in expanded])
         )
 
@@ -205,6 +206,53 @@
 
 
     @inlineCallbacks
+    def test_recordsMatchingTokensNoFilter(self):
+        """
+        Records with names containing the token are returned
+        """
+
+        records = (yield self.directory.recordsMatchingTokens(
+            [u"anche"]
+        ))
+        matchingShortNames = set()
+        for r in records:
+            for shortName in r.shortNames:
+                matchingShortNames.add(shortName)
+        self.assertTrue("dre" in matchingShortNames)
+        self.assertTrue("wsanchez" in matchingShortNames)
+
+
+    @inlineCallbacks
+    def test_recordsMatchingTokensStartswithFilter(self):
+        """
+        Records with names starting with the token are returned, because of
+        the filter installed.  Note that hyphens and spaces are used to split
+        fullname into names.
+        """
+        self.directory.setFilter(startswithFilter)
+
+        records = (yield self.directory.recordsMatchingTokens(
+            [u"anche"]
+        ))
+        matchingShortNames = set()
+        for r in records:
+            for shortName in r.shortNames:
+                matchingShortNames.add(shortName)
+        self.assertTrue("dre" not in matchingShortNames)
+        self.assertTrue("wsanchez" not in matchingShortNames)
+
+        records = (yield self.directory.recordsMatchingTokens(
+            [u"vega", u"wilf"]
+        ))
+        matchingShortNames = set()
+        for r in records:
+            for shortName in r.shortNames:
+                matchingShortNames.add(shortName)
+        self.assertTrue("dre" not in matchingShortNames)
+        self.assertTrue("wsanchez" in matchingShortNames)
+
+
+    @inlineCallbacks
     def test_getAutoScheduleMode(self):
 
         apollo = yield self.directory.recordWithUID(u"apollo")

Modified: CalendarServer/trunk/txdav/who/test/test_util.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_util.py	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/txdav/who/test/test_util.py	2015-06-19 18:06:12 UTC (rev 14901)
@@ -114,6 +114,7 @@
                     "InProcessCachingSeconds": 60,
                     "InSidecarCachingSeconds": 120,
                 },
+                "DirectoryFilterStartsWith": False,
             }
         )
 

Modified: CalendarServer/trunk/txdav/who/util.py
===================================================================
--- CalendarServer/trunk/txdav/who/util.py	2015-06-18 15:00:11 UTC (rev 14900)
+++ CalendarServer/trunk/txdav/who/util.py	2015-06-19 18:06:12 UTC (rev 14901)
@@ -14,7 +14,8 @@
 # limitations under the License.
 ##
 
-
+import re
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twext.python.log import Logger
 from twext.python.types import MappingProxyType
 from twext.who.aggregate import DirectoryService as AggregateDirectoryService
@@ -69,14 +70,15 @@
         config.AugmentService,
         config.Authentication.Wiki,
         serversDB=serversDB,
-        cachingSeconds=config.DirectoryProxy.InSidecarCachingSeconds
+        cachingSeconds=config.DirectoryProxy.InSidecarCachingSeconds,
+        filterStartsWith=config.DirectoryFilterStartsWith
     )
 
 
 
 def buildDirectory(
     store, dataRoot, servicesInfo, augmentServiceInfo, wikiServiceInfo,
-    serversDB=None, cachingSeconds=0
+    serversDB=None, cachingSeconds=0, filterStartsWith=False
 ):
     """
     Return a directory without using a config object; suitable for tests
@@ -315,6 +317,9 @@
     if serversDB is not None:
         augmented.setServersDB(serversDB)
 
+    if filterStartsWith:
+        augmented.setFilter(startswithFilter)
+
     return augmented
 
 
@@ -327,3 +332,49 @@
 
 <augments/>
 """
+
+
+ at inlineCallbacks
+def startswithFilter(
+    method, tokens, expression, recordTypes=None, records=None,
+    limitResults=None, timeoutSeconds=None
+):
+    """
+    Call the passed-in method to retrieve records from the directory, but
+    further filter the results by only returning records whose email addresses
+    and names actually start with the tokens.  Without this filter, it's only
+    required that the record's fullname *contains* the tokens. "Names" are split
+    from the record's fullname, delimited by whitespace and hypens.
+    """
+
+    tokens = [t.lower() for t in tokens]
+
+    results = []
+    records = yield method(
+        expression, recordTypes=recordTypes, limitResults=1000,
+        timeoutSeconds=timeoutSeconds
+    )
+    count = 0
+    for record in records:
+        try:
+            names = list(record.emailAddresses)
+        except AttributeError:
+            names = []
+        for fullName in record.fullNames:
+            names.extend(re.split(' |-', fullName))
+        match = True # assume it will match
+        for token in tokens:
+            for name in names:
+                if name.lower().startswith(token):
+                    break
+            else:
+                # there was no match for this token
+                match = False
+                break
+        if match:
+            results.append(record)
+            count += 1
+            if limitResults and count == limitResults:
+                break
+
+    returnValue(results)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150619/4541a95f/attachment-0001.html>


More information about the calendarserver-changes mailing list