diff --git a/requirements.pip b/requirements.pip index df5af99b..93acadbe 100644 --- a/requirements.pip +++ b/requirements.pip @@ -8,4 +8,5 @@ django==1.4.2 #south==0.7.6 git+git://github.com/petry/django-foundation.git git+git://github.com/yohanboniface/django-chickpea.git -git+git://github.com/frankban/django-endless-pagination.git \ No newline at end of file +git+git://github.com/frankban/django-endless-pagination.git +hg+https://bitbucket.org/liberation/sesql \ No newline at end of file diff --git a/youmap/sesql_config.py b/youmap/sesql_config.py new file mode 100644 index 00000000..3d898902 --- /dev/null +++ b/youmap/sesql_config.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) Pilot Systems and Libération, 2010-2011 + +# This file is part of SeSQL. + +# SeSQL is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# Foobar is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with SeSQL. If not, see . + +# +# Full text search configuration - we must define that before the imports, +# because those are used by the imports +# + +# Name of the PostgreSQL Text Search Configuration +TS_CONFIG_NAME = "simple_french" + +# Name of the stopwards file, must be plain ASCII +STOPWORDS_FILE = "ascii_french" + +# Global charset to use +CHARSET = "utf-8" + +from sesql.fields import * +from sesql.sources import * +from django.db.models import Q +from chickpea import models + +# +# Select the ORM to use +# +from sesql.orm.django import DjangoOrmAdapter +orm = DjangoOrmAdapter() + + +# +# Fields and tables configuration +# + +# Configuration of SeSQL search fields +FIELDS = (ClassField("classname"), + IntField("id"), + DateField("modified_at"), + FullTextField("name"), + FullTextField("fulltext", + ['name', 'description', + SubField("owner", ["username"]) + ], + primary=True, + ), + DateField('indexed_at', sql_default='NOW()'), + ) + +# Name of the global lookup table that should contain no real data +MASTER_TABLE_NAME = "sesql_index" + +# Type map, associating Django classes to SeSQL tables +TYPE_MAP = ((models.Map, "sesql_default"), ) + +# Additional indexes to create +CROSS_INDEXES = () + +# +# Cleanup configuration +# + +from htmlentitydefs import name2codepoint +from xml.sax import saxutils + +html_entities = dict([('&%s;' % k, unichr(v).encode(config.CHARSET)) for k,v in name2codepoint.items() ]) +ADDITIONAL_CLEANUP_FUNCTION = lambda value: saxutils.unescape(value, html_entities) + +# +# Query configuration +# + +# General condition to skip indexing content +SKIP_CONDITION = None + +# Default sort order for queries +DEFAULT_ORDER = ('-modified_at',) + +# Default LIMIT in short queries +DEFAULT_LIMIT = 20 + +# First we ask for the SMART_QUERY_INITIAL first sorted items +SMART_QUERY_INITIAL = 2500 +# Then, if we have at least SMART_QUERY_THRESOLD of our limit, we go on +SMART_QUERY_THRESOLD = 0.35 +# If we have a second query, we do * (wanted/result) * SMART_QUERY_RATIO +SMART_QUERY_RATIO = 3.0 + +# +# Long query cache configuration +# + +# Maximal number of queries to store in the long query cache +QUERY_CACHE_MAX_SIZE = 10000 +# Life time of a query in the query cache +QUERY_CACHE_EXPIRY = 24 * 3600 + +# +# Daemon configuration +# + +DAEMON_DEFAULT_CHUNK = 100 +DAEMON_DEFAULT_DELAY = 120 +DAEMON_DEFAULT_PID = '/var/run/sesql/update.pid' + +# +# Suggest/history configuration +# + +# default number of hit before including query in db +HISTORY_DEFAULT_FILTER = 5 + +# erode factor for time-based decay of recent searches score +HISTORY_ALPHA = 0.95 +# weight of frequency of the search in the final score +HISTORY_BETA = 1.0 +# weight of number of results in the final score +HISTORY_GAMMA = 1.0 + +# queries to ignore in history +HISTORY_BLACKLIST = [] + + +# +# Enable sesql searches from Django admin ? +# +ENABLE_SESQL_ADMIN = False + +# +# Enable to force all updates to be processed asynchronously by the daemon +# + +ASYNCHRONOUS_INDEXING = False diff --git a/youmap/settings/base.py b/youmap/settings/base.py index 7e0098a9..9f375ac1 100644 --- a/youmap/settings/base.py +++ b/youmap/settings/base.py @@ -29,6 +29,7 @@ INSTALLED_APPS = ( 'foundation', 'endless_pagination', 'youmap', + 'sesql', #'south', diff --git a/youmap/templates/youmap/navigation.html b/youmap/templates/youmap/navigation.html index 2ddaf3ce..9c1017a2 100644 --- a/youmap/templates/youmap/navigation.html +++ b/youmap/templates/youmap/navigation.html @@ -15,15 +15,11 @@ diff --git a/youmap/templates/youmap/search.html b/youmap/templates/youmap/search.html new file mode 100644 index 00000000..b6ae472b --- /dev/null +++ b/youmap/templates/youmap/search.html @@ -0,0 +1,33 @@ +{% extends "youmap/home.html" %} + +{% block content %} +
+
+ + + + {% include 'youmap/navigation.html' %} + + +
+
+ +
+
+

Search for maps containing «{{ q }}»

+
+
+
+
+
+ {% if maps %} + {% include "chickpea/map_list.html" %} + {% else %} +
+ Not map found. +
+ {% endif %} +
+
+
+{% endblock content %} diff --git a/youmap/urls.py b/youmap/urls.py index 95a4ef94..a05459ea 100644 --- a/youmap/urls.py +++ b/youmap/urls.py @@ -14,6 +14,7 @@ urlpatterns = patterns('', (r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^admin/', include(admin.site.urls)), url(r'^$', views.home, name="home"), + url(r'^search/$', views.search, name="search"), url(r'^user/(?P[-_\w]+)/$', views.user_maps, name='user_maps'), (r'', include('chickpea.urls')), ) diff --git a/youmap/views.py b/youmap/views.py index 98557a85..a04e53ef 100644 --- a/youmap/views.py +++ b/youmap/views.py @@ -1,6 +1,9 @@ from django.views.generic import TemplateView from django.contrib.auth.models import User from django.views.generic import DetailView +from django.db.models import Q + +from sesql.shortquery import shortquery from chickpea.models import Map @@ -53,3 +56,28 @@ class UserMaps(DetailView): return super(UserMaps, self).get_template_names() user_maps = UserMaps.as_view() + + +class Search(TemplateView): + template_name = "youmap/search.html" + list_template_name = "chickpea/map_list.html" + + def get_context_data(self, **kwargs): + q = self.request.GET['q'] + maps = shortquery(Q(fulltext__containswords=q)) + kwargs.update({ + 'maps': maps, + 'q': q + }) + return kwargs + + def get_template_names(self): + """ + Dispatch template according to the kind of request: ajax or normal. + """ + if self.request.is_ajax(): + return [self.list_template_name] + else: + return super(Search, self).get_template_names() + +search = Search.as_view()