mirror of
https://github.com/postgres/pgweb.git
synced 2025-07-29 11:59:36 +00:00
Add views and templates to perform searches from the main web app
This makes it possible to render the search results on the main engine. We still run the query on the seprate search server, so once has to be configured in settings_local.py with the key SEARCH_DSN (standard PostgreSQL/psycopg2 connection string)
This commit is contained in:
0
pgweb/search/__init__.py
Normal file
0
pgweb/search/__init__.py
Normal file
2
pgweb/search/models.py
Normal file
2
pgweb/search/models.py
Normal file
@ -0,0 +1,2 @@
|
||||
from django.db import models
|
||||
|
217
pgweb/search/views.py
Normal file
217
pgweb/search/views.py
Normal file
@ -0,0 +1,217 @@
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.http import HttpResponse, Http404, HttpResponseRedirect
|
||||
from django.conf import settings
|
||||
|
||||
from pgweb.util.decorators import cache
|
||||
|
||||
import datetime
|
||||
import urllib
|
||||
import psycopg2
|
||||
|
||||
from lists.models import MailingList, MailingListGroup
|
||||
|
||||
|
||||
def generate_pagelinks(pagenum, totalpages, querystring):
|
||||
# Generate a list of links to page through a search result
|
||||
# We generate these in HTML from the python code because it's
|
||||
# simply too ugly to try to do it in the template.
|
||||
if totalpages < 2:
|
||||
return
|
||||
|
||||
if pagenum > 1:
|
||||
# Prev link
|
||||
yield '<a href="%s&p=%s">Prev</a>' % (querystring, pagenum-1)
|
||||
|
||||
if pagenum > 10:
|
||||
start = pagenum - 10
|
||||
else:
|
||||
start = 1
|
||||
|
||||
for i in range(start, min(start+20, totalpages + 1)):
|
||||
if i == pagenum:
|
||||
yield "%s" % i
|
||||
else:
|
||||
yield '<a href="%s&p=%s">%s</a>' % (querystring, i, i)
|
||||
|
||||
if pagenum != min(start+20, totalpages):
|
||||
yield '<a href="%s&p=%s">Next</a>' % (querystring, pagenum+1)
|
||||
|
||||
|
||||
@cache(minutes=15)
|
||||
def search(request):
|
||||
# Perform a general web search
|
||||
# Since this lives in a different database, we open a direct
|
||||
# connection with psycopg, thus bypassing everything that has to do
|
||||
# with django.
|
||||
|
||||
# constants that we might eventually want to make configurable
|
||||
hitsperpage = 20
|
||||
|
||||
if request.REQUEST.has_key('m') and request.REQUEST['m'] == '1':
|
||||
searchlists = True
|
||||
|
||||
if request.REQUEST.has_key('l'):
|
||||
if request.REQUEST['l'] != '':
|
||||
listid = int(request.REQUEST['l'])
|
||||
else:
|
||||
listid = None
|
||||
else:
|
||||
listid = None
|
||||
|
||||
if request.REQUEST.has_key('d'):
|
||||
dateval = int(request.REQUEST['d'])
|
||||
else:
|
||||
dateval = None
|
||||
|
||||
if request.REQUEST.has_key('s'):
|
||||
listsort = request.REQUEST['s']
|
||||
else:
|
||||
listsort = 'r'
|
||||
|
||||
if dateval == -1:
|
||||
firstdate = None
|
||||
else:
|
||||
firstdate = datetime.datetime.today()-datetime.timedelta(days=dateval)
|
||||
|
||||
sortoptions = (
|
||||
{'val':'r', 'text': 'Rank', 'selected': not (request.REQUEST.has_key('s') and request.REQUEST['s'] == 'd')},
|
||||
{'val':'d', 'text': 'Date', 'selected': request.REQUEST.has_key('s') and request.REQUEST['s'] == 'd'},
|
||||
)
|
||||
dateoptions = (
|
||||
{'val': -1, 'text': 'anytime'},
|
||||
{'val': 1, 'text': 'within last day'},
|
||||
{'val': 7, 'text': 'within last week'},
|
||||
{'val': 31, 'text': 'within last month'},
|
||||
{'val': 186, 'text': 'within last 6 months'},
|
||||
{'val': 365, 'text': 'within last year'},
|
||||
)
|
||||
else:
|
||||
searchlists = False
|
||||
if request.REQUEST.has_key('u'):
|
||||
suburl = request.REQUEST['u']
|
||||
else:
|
||||
suburl = None
|
||||
|
||||
if request.REQUEST.has_key('a'):
|
||||
allsites = (request.REQUEST['a'] == "1")
|
||||
else:
|
||||
allsites = False
|
||||
|
||||
# Check that we actually have something to search for
|
||||
if not request.REQUEST.has_key('q') or request.REQUEST['q'] == '':
|
||||
if searchlists:
|
||||
return render_to_response('search/listsearch.html', {
|
||||
'search_error': "No search term specified.",
|
||||
'sortoptions': sortoptions,
|
||||
'lists': MailingList.objects.all().order_by("group__sortkey"),
|
||||
'listid': listid,
|
||||
'dates': dateoptions,
|
||||
'dateval': dateval,
|
||||
})
|
||||
else:
|
||||
return render_to_response('search/sitesearch.html', {
|
||||
'search_error': "No search term specified.",
|
||||
})
|
||||
query = request.REQUEST['q']
|
||||
|
||||
# Is the request being paged?
|
||||
if request.REQUEST.has_key('p'):
|
||||
try:
|
||||
pagenum = int(request.REQUEST['p'])
|
||||
except:
|
||||
pagenum = 1
|
||||
else:
|
||||
pagenum = 1
|
||||
|
||||
firsthit = (pagenum - 1) * hitsperpage + 1
|
||||
|
||||
# Get ourselves a connection
|
||||
try:
|
||||
conn = psycopg2.connect(settings.SEARCH_DSN)
|
||||
curs = conn.cursor()
|
||||
except:
|
||||
return render_to_response('search/sitesearch.html', {
|
||||
'search_error': 'Could not connect to search database.'
|
||||
})
|
||||
|
||||
if searchlists:
|
||||
# perform the query for list archives search
|
||||
curs.execute("SELECT * from archives_search(%(query)s, %(listid)s, %(firstdate)s, NULL, %(firsthit)s, %(hitsperpage)s, %(sort)s)", {
|
||||
'query': query,
|
||||
'firsthit': firsthit - 1,
|
||||
'hitsperpage': hitsperpage,
|
||||
'listid': listid,
|
||||
'firstdate': firstdate,
|
||||
'sort': listsort,
|
||||
})
|
||||
hits = curs.fetchall()
|
||||
conn.close()
|
||||
totalhits = int(hits[-1][1])
|
||||
querystr = "?m=1&q=%s&l=%s&d=%s&s=%s" % (
|
||||
urllib.quote_plus(query),
|
||||
listid or '',
|
||||
dateval,
|
||||
listsort
|
||||
)
|
||||
return render_to_response('search/listsearch.html', {
|
||||
'hitcount': totalhits,
|
||||
'firsthit': firsthit,
|
||||
'lasthit': min(totalhits, firsthit+hitsperpage-1),
|
||||
'query': request.REQUEST['q'],
|
||||
'pagelinks': " ".join(
|
||||
generate_pagelinks(pagenum,
|
||||
totalhits / hitsperpage + 1,
|
||||
querystr)),
|
||||
'hits': [{
|
||||
'list': h[0],
|
||||
'year': h[1],
|
||||
'month': h[2],
|
||||
'msgnum': "%05d" % h[3],
|
||||
'date': h[4],
|
||||
'subject': h[5],
|
||||
'author': h[6],
|
||||
'abstract': h[7].replace("[[[[[[", "<b>").replace("]]]]]]","</b>"),
|
||||
'rank': h[8],
|
||||
} for h in hits[:-1]],
|
||||
'sortoptions': sortoptions,
|
||||
'lists': MailingList.objects.all().order_by("group__sortkey"),
|
||||
'listid': listid,
|
||||
'dates': dateoptions,
|
||||
'dateval': dateval,
|
||||
})
|
||||
else:
|
||||
# perform the query for general web search
|
||||
curs.execute("SELECT * FROM site_search(%(query)s, %(firsthit)s, %(hitsperpage)s, %(allsites)s, %(suburl)s)", {
|
||||
'query': query,
|
||||
'firsthit': firsthit - 1,
|
||||
'hitsperpage': hitsperpage,
|
||||
'allsites': allsites,
|
||||
'suburl': suburl
|
||||
})
|
||||
|
||||
hits = curs.fetchall()
|
||||
conn.close()
|
||||
totalhits = int(hits[-1][5])
|
||||
querystr = "?q=%s&a=%s&u=%s" % (
|
||||
urllib.quote_plus(query),
|
||||
allsites and "1" or "0",
|
||||
suburl and urllib.quote_plus(suburl) or '',
|
||||
)
|
||||
|
||||
return render_to_response('search/sitesearch.html', {
|
||||
'suburl': suburl,
|
||||
'allsites': allsites,
|
||||
'hitcount': totalhits,
|
||||
'firsthit': firsthit,
|
||||
'lasthit': min(totalhits, firsthit+hitsperpage-1),
|
||||
'query': request.REQUEST['q'],
|
||||
'pagelinks': " ".join(
|
||||
generate_pagelinks(pagenum,
|
||||
totalhits / hitsperpage + 1,
|
||||
querystr)),
|
||||
'hits': [{
|
||||
'title': h[3],
|
||||
'url': "%s%s" % (h[1], h[2]),
|
||||
'abstract': h[4].replace("[[[[[[", "<b>").replace("]]]]]]","</b>"),
|
||||
'rank': h[5]} for h in hits[:-1]],
|
||||
})
|
@ -106,6 +106,7 @@ INSTALLED_APPS = [
|
||||
'pgweb.misc',
|
||||
'pgweb.featurematrix',
|
||||
'pgweb.pwn',
|
||||
'pgweb.search',
|
||||
]
|
||||
|
||||
|
||||
|
@ -55,6 +55,8 @@ urlpatterns = patterns('',
|
||||
(r'^community/weeklynews/$', 'pwn.views.index'),
|
||||
(r'^community/weeklynews/pwn(\d{4})(\d{2})(\d{2})/$', 'pwn.views.post'),
|
||||
|
||||
(r'^search/$', 'search.views.search'),
|
||||
|
||||
(r'^support/professional_(support|hosting)/$', 'profserv.views.root'),
|
||||
(r'^support/professional_(support|hosting)[/_](.*)/$', 'profserv.views.region'),
|
||||
(r'^support/submitbug/$', 'misc.views.submitbug'),
|
||||
|
61
templates/search/listsearch.html
Normal file
61
templates/search/listsearch.html
Normal file
@ -0,0 +1,61 @@
|
||||
{%extends "base/page.html"%}
|
||||
{%block title%}Search results{%endblock%}
|
||||
{%block contents%}
|
||||
|
||||
<form method="get" action="/search/">
|
||||
<input type="hidden" name="m" value="1">
|
||||
<div class="tblBasic">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="tblBasicGrey" width="590">
|
||||
<tr>
|
||||
<td class="colFirst">Search for</td>
|
||||
<th class="colLast"><input type="text" name="q" value="{{query}}" /> <input type="submit" value="Search" /></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="colFirstR">List:</td>
|
||||
<td class="colLast"><select name="l">
|
||||
<option value="">-- All lists</option>
|
||||
{%for l in lists %}
|
||||
{%ifchanged l.group%}
|
||||
<option value="{{l.group.negid}}"{%if l.group.negid = listid%} SELECTED{%endif%}>-- {{l.group}}</option>
|
||||
{%endifchanged%}
|
||||
<option value="{{l.id}}"{%if l.id = listid%} SELECTED{%endif%}>{{l}}</option>
|
||||
{%endfor%}
|
||||
</select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="colFirstR">Post date:</td>
|
||||
<td class="colLast"><select name="d">{%for d in dates%}
|
||||
<option value="{{d.val}}"{%if d.val = dateval %} SELECTED{%endif%}>{{d.text}}</option>{%endfor%}
|
||||
</select></td>
|
||||
</tr>
|
||||
<tr class="lastrow">
|
||||
<td class="colFirst" align="right">Sort by:</td>
|
||||
<td class="colLast"><select name="s">{%for s in sortoptions%}
|
||||
<option value="{{s.val}}"{%if s.selected%} SELECTED{%endif%}>{{s.text}}</option>{%endfor%}
|
||||
</select></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="pgSearchContent">
|
||||
{%if search_error %}
|
||||
<div>{{search_error}}</div>
|
||||
{%else%}
|
||||
<!-- docbot goes here -->
|
||||
{%if hitcount = 0 %}
|
||||
<p>Your search for <b>{{query}}</b> returned no hits.</p>
|
||||
{%else%}
|
||||
<h2>Results {{firsthit}}-{{lasthit}} of {%if hitcount = 1000%}more than 1000{%else%}{{hitcount}}{%endif%}.</h2>
|
||||
{%if pagelinks %}Result pages: {{pagelinks|safe}}<br/><br/>{%endif%}
|
||||
{%for hit in hits %}
|
||||
{{forloop.counter0|add:firsthit}}. <a href="http://archives.postgresql.org/{{hit.list}}/{{hit.year}}-{{hit.month}}/msg{{hit.msgnum}}.php">{{hit.subject}}</a> [{{hit.rank}}]<br/>
|
||||
{{hit.abstract|safe}}<br/>
|
||||
<a href="http://archives.postgresql.org/{{hit.list}}/{{hit.year}}-{{hit.month}}/msg{{hit.msgnum}}.php">http://archives.postgresql.org/{{hit.list}}/{{hit.year}}-{{hit.month}}/msg{{hit.msgnum}}.php</a><br/>
|
||||
<br/>
|
||||
{%endfor%}
|
||||
{%if pagelinks %}Result pages: {{pagelinks|safe}}<br/><br/>{%endif%}
|
||||
{%endif%}
|
||||
{%endif%}
|
||||
</div>
|
||||
{%endblock%}
|
43
templates/search/sitesearch.html
Normal file
43
templates/search/sitesearch.html
Normal file
@ -0,0 +1,43 @@
|
||||
{%extends "base/page.html"%}
|
||||
{%block title%}Search results{%endblock%}
|
||||
{%block contents%}
|
||||
|
||||
<form method="get" action="/search/">
|
||||
{%if suburl%}
|
||||
<input type="hidden" name="u" value="{{suburl}}">
|
||||
{%endif%}
|
||||
<div class="tblBasic">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="tblBasicGrey" width="590">
|
||||
<tr>
|
||||
<td class="colFirst">Search for</td>
|
||||
<th class="colLast"><input type="text" name="q" value="{{query}}" /> <input type="submit" value="Search" /></th>
|
||||
</tr>
|
||||
<tr class="lastrow">
|
||||
<td class="colFirst"></td>
|
||||
<td class="colLast"><input type="checkbox" name="a" value="1" {%if allsites%}CHECKED{%endif%}/>Include community sites</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="pgSearchContent">
|
||||
{%if search_error %}
|
||||
<div>{{search_error}}</div>
|
||||
{%else%}
|
||||
<!-- docbot goes here -->
|
||||
{%if hitcount = 0 %}
|
||||
<p>Your search for <b>{{query}}</b> returned no hits.</p>
|
||||
{%else%}
|
||||
<h2>Results {{firsthit}}-{{lasthit}} of {%if hitcount = 1000%}more than 1000{%else%}{{hitcount}}{%endif%}.</h2>
|
||||
{%if pagelinks %}Result pages: {{pagelinks|safe}}<br/><br/>{%endif%}
|
||||
{%for hit in hits %}
|
||||
{{forloop.counter0|add:firsthit}}. <a href="{{hit.url}}">{{hit.title}}</a> [{{hit.rank}}]<br/>
|
||||
<div>...{{hit.abstract|safe}}...</div>
|
||||
<a href="{{hit.url}}">{{hit.url}}</a><br/>
|
||||
<br/>
|
||||
{%endfor%}
|
||||
{%if pagelinks %}Result pages: {{pagelinks|safe}}<br/><br/>{%endif%}
|
||||
{%endif%}
|
||||
{%endif%}
|
||||
</div>
|
||||
{%endblock%}
|
Reference in New Issue
Block a user