[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/index/text - control.py:1.1 configure.zcml:1.4 control.pt:1.7 interfaces.py:1.6

Guido van Rossum guido@python.org
Mon, 9 Dec 2002 14:47:17 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/index/text
In directory cvs.zope.org:/tmp/cvs-serv6296

Modified Files:
	configure.zcml control.pt interfaces.py 
Added Files:
	control.py 
Log Message:
Moved all the view-specific code to a separate vew module, control.py.
Batching now works.  (The default batch size is 2, for testing.)

=== Added File Zope3/lib/python/Zope/App/index/text/control.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Control view for the text index.

XXX longer description goes here.

$Id: control.py,v 1.1 2002/12/09 19:47:16 gvanrossum Exp $
"""

from __future__ import generators

from Zope.ComponentArchitecture import getService, queryAdapter
from Zope.Exceptions import NotFoundError
from Zope.Publisher.Browser.IBrowserView import IBrowserView

from Zope.App.Traversing import locationAsUnicode
from Zope.App.DublinCore.IZopeDublinCore import IZopeDublinCore
from Zope.App.index.text.interfaces import IQueryView

class ControlView(object):

    __implements__ = IBrowserView, IQueryView

    default_start = 0 # Don't change -- always start at first batch
    default_count = 2 # Default batch size -- tune as you please

    def __init__(self, context, request):
        self.index = self.context = context
        self.request = request
        self.hub = getService(context, "ObjectHub")

    def nextBatch(self):
        start = int(self.request.get('start', self.default_start))
        count = int(self.request.get('count', self.default_count))
        return start + count

    def prevBatch(self):
        start = int(self.request.get('start', self.default_start))
        count = int(self.request.get('count', self.default_count))
        return start - count

    def query(self, start=None):
        queryText = self.request.get('queryText', '')
        if start is None:
            start = int(self.request.get('start', self.default_start))
        count = int(self.request.get('count', self.default_count))
        results, total = self.index.query(queryText, start, count)
        nresults = len(results)
        first = start + 1
        last = first + len(results) - 1
        result = {
            'results': list(self._resultIterator(results)),
            'nresults': nresults,
            'first': first,
            'last': last,
            'total': total,
            }
        if start > 0:
            prev = max(0, start - count)
            result['prev'] = prev
        if last < total:
            next = start + count
            result['next'] = next
        return result

    def _resultIterator(self, results):
        for hubid, score in results:
            yield self._cookInfo(hubid, score)

    def _cookInfo(self, hubid, score):
        location = locationAsUnicode(self.hub.getLocation(hubid))
        scoreLabel = "%.1f%%" % (100.0 * score)
        result = {
            'location': location,
            'score': score,
            'scoreLabel': scoreLabel,
            }
        try:
            object = self.hub.getObject(hubid)
        except NotFoundError:
            pass
        else:
            dc = queryAdapter(object, IZopeDublinCore, context=self.index)
            if dc is not None:
                title = dc.title
                result['title'] = title
        return result


=== Zope3/lib/python/Zope/App/index/text/configure.zcml 1.3 => 1.4 ===
--- Zope3/lib/python/Zope/App/index/text/configure.zcml:1.3	Wed Dec  4 16:38:02 2002
+++ Zope3/lib/python/Zope/App/index/text/configure.zcml	Mon Dec  9 14:47:16 2002
@@ -32,8 +32,12 @@
 
   <browser:view
     for="Zope.TextIndex.TextIndexInterfaces.IStatistics"
-    permission="Zope.View"
+    permission="Zope.ManageServices"
     name="control.html"
-    template="control.pt" />
+    factory=".control.ControlView">
+
+    <browser:page name="index.html" template="control.pt" />
+
+  </browser:view>
 
 </zopeConfigure>


=== Zope3/lib/python/Zope/App/index/text/control.pt 1.6 => 1.7 ===
--- Zope3/lib/python/Zope/App/index/text/control.pt:1.6	Fri Dec  6 09:49:11 2002
+++ Zope3/lib/python/Zope/App/index/text/control.pt	Mon Dec  9 14:47:16 2002
@@ -29,11 +29,11 @@
     </span>
 
     <div>
-    	Documents: <span tal:replace="context/documentCount" />
+        Documents: <span tal:replace="context/documentCount" />
     </div>
 
     <div>
-    	Words: <span tal:replace="context/wordCount" />
+        Words: <span tal:replace="context/wordCount" />
     </div>
 
     <form method="POST">
@@ -52,34 +52,37 @@
     <form method="GET">
         <input type="text" name="queryText" value=""
                tal:attributes="value request/queryText|nothing" />
-        <input type="submit" value="Query" name="callQuery" />
+        <input type="submit" value="Query" />
     </form>
 
     <div tal:condition="request/queryText|nothing" tal:omit-tag="">
-        <span tal:define="
-            result_tuple python:context.query(request['queryText'],0,100);
-            total python:result_tuple[1];
-            result_list python:result_tuple[0]"
-            tal:omit-tag=""
-            tal:on-error="string:invalid query"
-            >
-        Search results: <span tal:replace="total" />
-            <div tal:repeat="match_tuple result_list">
-              <span tal:define="
-                    hubid    python:match_tuple[0];
-                    score    python:match_tuple[1];
-                    location python:context.hubid2location(hubid);
-                    title    python:context.hubid2title(hubid) or '(no title)';
-                    ">
-                 Location:
-                 <a href="location"
-                    tal:attributes="href location"
-                    tal:content="location">location</a>
-                 Title: <span tal:replace="title" />
-                 Score: <span tal:replace="score" />
-              </span>
-            </div>
-        </span>
+        <div tal:define="result view/query" tal:omit-tag="">
+	    <div tal:condition="not:result/total">
+	        No hits.  Please try another query.
+	    </div>
+	    <div tal:condition="result/total">
+		<div tal:content="
+		string:Hits ${result/first}-${result/last} of ${result/total}:"
+		/>
+		<div tal:repeat="info result/results">
+	            title=<span tal:replace="info/title" />;
+		    url=<a href="location"
+		            tal:attributes="href info/location"
+                            tal:content="info/location">url</a>;
+		    score=<span tal:replace="info/scoreLabel" />
+		</div>
+	    </div>
+	    <span tal:condition="exists:result/prev">
+                <a href="next"
+                   tal:attributes="href
+                       string:?queryText=${request/queryText}&start=${view/prevBatch}">&lt;-- PREVIOUS BATCH</a>
+	    </span>
+	    <span tal:condition="exists:result/next">
+                <a href="next"
+                   tal:attributes="href
+                       string:?queryText=${request/queryText}&start=${view/nextBatch}">NEXT BATCH --&gt;</a>
+	    </span>
+        </div>
     </div>
 
   </div>


=== Zope3/lib/python/Zope/App/index/text/interfaces.py 1.5 => 1.6 ===
--- Zope3/lib/python/Zope/App/index/text/interfaces.py:1.5	Fri Dec  6 09:49:11 2002
+++ Zope3/lib/python/Zope/App/index/text/interfaces.py	Mon Dec  9 14:47:16 2002
@@ -47,17 +47,42 @@
     def isSubscribed():
         """Return whether we are currently subscribed."""
 
-    # XXX The following are blatant view helpers.  Need refactoring.
+class IQueryView(Interface):
 
-    def hubid2location(hubid):
-        """Return a location string given a hubid."""
+    """Interface providing a query method that can be invoked from ZPT.
 
-    def hubid2object(hubid):
-        """Return an object given a hubid."""
+    XXX How to express that this is a browser interface?"""
 
-    def hubid2title(hubid):
-        """Return the Dublin Core title of the object from the hubid.
+    def query():
+        """Perform a batched query, based on request fields.
 
-        Return '' if there is no object or if it isn't adaptable to
-        IZopeDublinCore.
+        These request fields are used as input:
+
+        start -- batch start (0-based); defaults to 0
+        count -- batch size; defaults to 10
+        queryText -- query expression; must be given
+
+        Return a dict with fields:
+
+        results -- a list containing the requested batch
+        nresults -- number of items in results
+        first -- 1-based ordinal this batch's first item
+        last -- 1-based ordinal of if this batch's last item
+        next -- 0-based ordinal of next batch; not set if no next batch
+        prev -- 0-based ordinal of previous batch; not set if no prev batch
+        count -- requested batch size
+        total -- total number of matches (all batches together)
+
+        Each item in the results set has the following string fields:
+
+        location -- location of the object (usable as URL within the site)
+        title -- Dublin Core title of the object; not present if no title
+        score -- score, as a float in the range [0.0 ... 1.0]
+        scoreLabel -- score, formatted as a percentage and rounded to 0.1%
         """
+
+    def nextBatch():
+        """Return the start for the next batch."""
+
+    def prevBatch():
+        """Return the start for the previous batch."""