[Python-checkins] r86754 - in tracker/instances/python-dev: extensions/search_id.py html/osd.xml html/page.html scripts/suggest.py

martin.v.loewis python-checkins at python.org
Thu Nov 25 21:47:55 CET 2010


Author: martin.v.loewis
Date: Thu Nov 25 21:47:55 2010
New Revision: 86754

Log:
Add Open Search Description and suggester script.


Added:
   tracker/instances/python-dev/html/osd.xml   (contents, props changed)
   tracker/instances/python-dev/scripts/suggest.py   (contents, props changed)
Modified:
   tracker/instances/python-dev/extensions/search_id.py
   tracker/instances/python-dev/html/page.html

Modified: tracker/instances/python-dev/extensions/search_id.py
==============================================================================
--- tracker/instances/python-dev/extensions/search_id.py	(original)
+++ tracker/instances/python-dev/extensions/search_id.py	Thu Nov 25 21:47:55 2010
@@ -1,3 +1,4 @@
+import cgi
 from roundup.cgi.actions import Action
 from roundup.cgi import exceptions
 
@@ -13,8 +14,24 @@
                 if self.db.hasnode('issue', id):
                     raise exceptions.Redirect('issue'+id)
         if len(split) > 50:
-            # Postgres crashes on log queries
+            # Postgres crashes on long queries
             raise exceptions.FormError("too many search terms")
 
+class OpenSearchAction(SearchIDAction):
+    """Action referred to in the Open Search Description.
+    This has just a single query parameter (in addition to the action
+    name), and fills out the rest here.
+    """
+    def handle(self):
+        # Check for IDs first
+        SearchIDAction.handle(self)
+        # regular search, fill out query parameters
+        for k, v in [('@columns', 'id,activity,title,creator,assignee,status,type'), #columns_showall
+                     ('@sort', '-activity'),
+                     ('ignore', 'file:content')]:
+            self.form.value.append(cgi.MiniFieldStorage(k, v))
+
+
 def init(instance):
     instance.registerAction('searchid', SearchIDAction)
+    instance.registerAction('opensearch', OpenSearchAction)

Added: tracker/instances/python-dev/html/osd.xml
==============================================================================
--- (empty file)
+++ tracker/instances/python-dev/html/osd.xml	Thu Nov 25 21:47:55 2010
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+   <ShortName>Python Bugs</ShortName>
+   <Description>Python Bug Tracker Search</Description>
+   <Tags>python bugtracker</Tags>
+   <Contact>tracker-discuss at python.org</Contact>
+   <Url type="text/html" 
+        template="http://bugs.python.org/?@action=opensearch&amp;@search_text={searchTerms}"/>
+   <Url type="application/x-suggestions+json"
+	template="http://bugs.python.org/suggest?{searchTerms}"/>
+   <LongName>Python Bug Tracker Search</LongName>
+   <Query role="example" searchTerms="python" />
+   <Language>en-us</Language>
+</OpenSearchDescription>

Modified: tracker/instances/python-dev/html/page.html
==============================================================================
--- tracker/instances/python-dev/html/page.html	(original)
+++ tracker/instances/python-dev/html/page.html	Thu Nov 25 21:47:55 2010
@@ -10,6 +10,9 @@
 <link media="screen" href="http://python.org/styles/largestyles.css" type="text/css" rel="alternate stylesheet" title="large text" />
 <link media="screen" href="http://python.org/styles/defaultfonts.css" type="text/css" rel="alternate stylesheet" title="default fonts" />
 <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+<link rel="search" type="application/opensearchdescription+xml" 
+      href="@@file/osd.xml"
+      title="Python bug tracker search" />
 <meta http-equiv="Content-Type"
  tal:attributes="content string:text/html;; charset=${request/client/charset}" />
 <script tal:replace="structure request/base_javascript">

Added: tracker/instances/python-dev/scripts/suggest.py
==============================================================================
--- (empty file)
+++ tracker/instances/python-dev/scripts/suggest.py	Thu Nov 25 21:47:55 2010
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+# WSGI server to implement search suggestions
+import ConfigParser, psycopg2, os, urllib, json
+
+# copied from indexer_common
+STOPWORDS = set([
+    "A", "AND", "ARE", "AS", "AT", "BE", "BUT", "BY",
+    "FOR", "IF", "IN", "INTO", "IS", "IT",
+    "NO", "NOT", "OF", "ON", "OR", "SUCH",
+    "THAT", "THE", "THEIR", "THEN", "THERE", "THESE",
+    "THEY", "THIS", "TO", "WAS", "WILL", "WITH"
+])
+
+cfg = ConfigParser.ConfigParser({'password':'', 'port':''})
+cfg.read(os.path.dirname(__file__)+"/../config.ini")
+pgname = cfg.get('rdbms', 'name')
+pguser = cfg.get('rdbms', 'user')
+pgpwd = cfg.get('rdbms', 'password')
+pghost = cfg.get('rdbms', 'host')
+pgport = cfg.get('rdbms', 'port')
+
+optparams = {}
+if pghost: optparams['host'] = pghost
+if pgport: optparams['port'] = pgport
+
+conn = psycopg2.connect(database=pgname,
+                        user=pguser,
+                        password=pgpwd,
+                        **optparams)
+c = conn.cursor()
+
+def escape_sql_like(s):
+    return s.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
+
+def suggest(query):
+    words = query.upper().split()
+    fullwords = [w for w in words[:-1]
+                 if w not in STOPWORDS]
+    partialword = words[-1]
+    if fullwords:
+        sql = 'select distinct(_textid) from __words where _word=%s'
+        intersect = '\nINTERSECT\n'.join([sql]*len(fullwords))
+        constraint = ' and _textid in ('+intersect+')'
+    else:
+        constraint = ''
+    sql = ('select _word from __words where _word like %s '+constraint+
+           'group by _word order by count(*) desc limit 10')
+    c.execute(sql, (escape_sql_like(partialword)+'_%',)+tuple(fullwords))
+    words = [w[0].lower() for w in c.fetchall()]
+    conn.rollback()
+    return json.dumps([query, words])    
+
+def application(environ, start_response):
+    # don't care about the URL here - just consider
+    # the q query parameter
+    query = urllib.unquote(environ['QUERY_STRING'])
+    if not query:
+        start_response('404 Not Found',[('Content-Type', 'text/html')])
+        return ['<html><head><title>Not found</title></head>',
+                '<body>The q query parameter is missing</body></html>']
+    result = suggest(query)
+    start_response('200 OK', 
+                   [('Content-type', 'application/x-suggestions+json'),
+                    ('Content-length', str(len(result)))])
+    return [result]
+
+if __name__=='__main__':
+    from wsgiref.simple_server import make_server
+    httpd = make_server('', 8086, application)
+    print "Serving on port 8086..."
+    httpd.serve_forever()


More information about the Python-checkins mailing list