Added searching capabilities.
This commit is contained in:
commit
f82002d255
@ -48,6 +48,8 @@ def make_map():
|
|||||||
map.connect('/archive/{year}/{month}/{day}', controller='blog', action='archive')
|
map.connect('/archive/{year}/{month}/{day}', controller='blog', action='archive')
|
||||||
map.connect('/archive/{year}/{month}/{day}/{url}', controller='blog', action='view')
|
map.connect('/archive/{year}/{month}/{day}/{url}', controller='blog', action='view')
|
||||||
|
|
||||||
|
map.connect('/search', controller='blog', action='search')
|
||||||
|
|
||||||
map.connect('/tag/{id}', controller='blog', action='tag')
|
map.connect('/tag/{id}', controller='blog', action='tag')
|
||||||
|
|
||||||
map.connect('/{controller}/{action}')
|
map.connect('/{controller}/{action}')
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from sqlalchemy.sql import or_
|
||||||
|
|
||||||
from scribeengine.lib.base import *
|
from scribeengine.lib.base import *
|
||||||
from scribeengine.lib import utils
|
from scribeengine.lib import utils
|
||||||
from scribeengine.model import Post, Comment, Tag
|
from scribeengine.model import Post, Comment, Tag
|
||||||
@ -111,3 +113,16 @@ class BlogController(BaseController):
|
|||||||
h.flash.set_message(u'Successfully submitted your comment.', u'success')
|
h.flash.set_message(u'Successfully submitted your comment.', u'success')
|
||||||
h.redirect_to(h.url_for_post(post))
|
h.redirect_to(h.url_for_post(post))
|
||||||
|
|
||||||
|
def search(self):
|
||||||
|
c.querystring = request.GET.get(u'q')
|
||||||
|
if not c.querystring:
|
||||||
|
h.flash.set_message(u'You didn\'t supply anything to search for.', u'error')
|
||||||
|
h.redirect_to('/')
|
||||||
|
keywords = [or_(Post.body.contains(kw.strip()), Post.title.contains(kw.strip())) for kw in c.querystring.split(',')]
|
||||||
|
c.page_title = u'Search'
|
||||||
|
c.posts = Session.query(Post)
|
||||||
|
for or_clause in keywords:
|
||||||
|
c.posts = c.posts.filter(or_clause)
|
||||||
|
c.posts = c.posts.all()
|
||||||
|
return render(u'/blog/search.mako')
|
||||||
|
|
||||||
|
@ -32,6 +32,76 @@ from turbomail import Message
|
|||||||
|
|
||||||
from scribeengine.lib.base import render, h
|
from scribeengine.lib.base import render, h
|
||||||
|
|
||||||
|
class KeywordProcessor(object):
|
||||||
|
"""Process user-supplied keywords, tags, or search terms.
|
||||||
|
|
||||||
|
This tries to be as flexible as possible while being efficient.
|
||||||
|
The vast majority of the work is done in the regular expression."""
|
||||||
|
|
||||||
|
def __init__(self, separators=' \t', quotes=['"', "'"], groups=[], group=False, normalize=None, sort=False, result=list):
|
||||||
|
"""Configure the processor.
|
||||||
|
|
||||||
|
separators: A list of acceptable separator characters. The first will be used for joins.
|
||||||
|
quotes: Pass a list or tuple of allowable quotes. E.g. ["\"", "'"] or None to disable.
|
||||||
|
groups: Pass a string, list, or tuple of allowable prefixes. E.g. '+-' or None to disable.
|
||||||
|
group: Pass in the type you want to group by, e.g. list, tuple, or dict.
|
||||||
|
normalize: Pass a function which will normalize the results. E.g. lambda s: s.lower().strip(' \"')
|
||||||
|
sort: Sort the resulting list (or lists) alphabeticlly.
|
||||||
|
result: The return type. One of set, tuple, list.
|
||||||
|
|
||||||
|
If groups are defined, and group is not, the result will be a list/tuple/set of tuples, e.g. [('+', "foo"), ...]
|
||||||
|
"""
|
||||||
|
|
||||||
|
separators = list(separators)
|
||||||
|
|
||||||
|
self.pattern = ''.join((
|
||||||
|
('[\s%s]*' % (''.join(separators), )), # Trap possible leading space or separators.
|
||||||
|
'(',
|
||||||
|
('[%s]%s' % (''.join([i for i in list(groups) if i is not None]), '?' if None in groups else '')) if groups else '', # Pass groups=('+','-') to handle optional leading + or -.
|
||||||
|
''.join([(r'%s[^%s]+%s|' % (i, i, i)) for i in quotes]) if quotes else '', # Match any amount of text (that isn't a quote) inside quotes.
|
||||||
|
('[^%s]+' % (''.join(separators), )), # Match any amount of text that isn't whitespace.
|
||||||
|
')',
|
||||||
|
('[%s]*' % (''.join(separators), )), # Match possible separator character.
|
||||||
|
))
|
||||||
|
self.regex = re.compile(self.pattern)
|
||||||
|
|
||||||
|
self.groups = list(groups)
|
||||||
|
self.group = dict if group is True else group
|
||||||
|
self.normalize = normalize
|
||||||
|
self.sort = sort
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
def split(self, value):
|
||||||
|
if not isinstance(value, basestring): raise TypeError("Invalid type for argument 'value'.")
|
||||||
|
|
||||||
|
matches = self.regex.findall(value)
|
||||||
|
|
||||||
|
if callable(self.normalize): matches = [self.normalize(i) for i in matches]
|
||||||
|
if self.sort: matches.sort()
|
||||||
|
if not self.groups: return self.result(matches)
|
||||||
|
|
||||||
|
groups = dict([(i, list()) for i in self.groups])
|
||||||
|
if None not in groups.iterkeys(): groups[None] = list() # To prevent errors.
|
||||||
|
|
||||||
|
for i in matches:
|
||||||
|
if i[0] in self.groups:
|
||||||
|
groups[i[0]].append(i[1:])
|
||||||
|
else:
|
||||||
|
groups[None].append(i)
|
||||||
|
|
||||||
|
if self.group is dict: return groups
|
||||||
|
|
||||||
|
if self.group is False or self.group is None:
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for group in self.groups:
|
||||||
|
results.extend([(group, match) for match in groups[group]])
|
||||||
|
|
||||||
|
return self.result(results)
|
||||||
|
|
||||||
|
return self.group([[match for match in groups[group]] for group in self.groups])
|
||||||
|
|
||||||
|
|
||||||
def send_mail(template, mail_to, mail_from, subject, variables={}, attachments=[]):
|
def send_mail(template, mail_to, mail_from, subject, variables={}, attachments=[]):
|
||||||
"""
|
"""
|
||||||
Sends an e-mail using the template ``template``.
|
Sends an e-mail using the template ``template``.
|
||||||
|
26
scribeengine/templates/blog/search.mako
Normal file
26
scribeengine/templates/blog/search.mako
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<%inherit file="/base.mako"/>
|
||||||
|
<%include file="/flash.mako"/>
|
||||||
|
<h2 class="title">Searching for: "${c.querystring}"</h2>
|
||||||
|
% if len(c.posts) > 0:
|
||||||
|
% for post in c.posts:
|
||||||
|
<div class="post">
|
||||||
|
<h3 class="title"><a href="${h.url_for_post(post)}">${post.title}</a></h3>
|
||||||
|
<div class="entry">
|
||||||
|
${h.literal(h.teaser(post.body))}
|
||||||
|
</div>
|
||||||
|
<p class="meta">
|
||||||
|
<span class="byline">Posted by ${post.user.nick} on ${post.created.strftime('%B %d, %Y')}</span>
|
||||||
|
<a href="${h.url_for_post(post)}" class="read-more">Read more</a>
|
||||||
|
% if len(post.comments) == 0:
|
||||||
|
<a href="${h.url_for_post(post)}#comments" class="comments">No comments</a>
|
||||||
|
% elif len(post.comments) == 1:
|
||||||
|
<a href="${h.url_for_post(post)}#comments" class="comments">1 comment</a>
|
||||||
|
% else:
|
||||||
|
<a href="${h.url_for_post(post)}#comments" class="comments">${len(post.comments)} comments</a>
|
||||||
|
% endif
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
% endfor
|
||||||
|
% else:
|
||||||
|
<div class="post"><p>Sorry, there seem to be no results for your search query.</p></div>
|
||||||
|
% endif
|
@ -3,9 +3,9 @@
|
|||||||
<li id="search">
|
<li id="search">
|
||||||
<form id="searchform" method="get" action="/search">
|
<form id="searchform" method="get" action="/search">
|
||||||
<div>
|
<div>
|
||||||
<input type="text" name="keywords" id="s" size="15" />
|
<input type="text" name="q" id="s" size="15" />
|
||||||
<br />
|
<br />
|
||||||
<input name="submit" type="submit" value="Search" />
|
<input type="submit" value="Search" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
|
Reference in New Issue
Block a user