use elasticsearch for label and free search
authorymh <ymh.work@gmail.com>
Wed, 05 Feb 2014 03:18:25 +0100
changeset 336 6ffca49f6a0c
parent 335 493fceed57ec
child 337 87f9e7d2b232
use elasticsearch for label and free search
.settings/org.eclipse.core.resources.prefs
src/jocondelab/cache_utils.py
src/jocondelab/config.py.tmpl
src/jocondelab/management/commands/import_dbpedia_fields.py
src/jocondelab/search_indexes.py
src/jocondelab/settings.py
src/jocondelab/templates/search/indexes/jocondelab/dbpediafields_text.txt
src/jocondelab/utils.py
src/jocondelab/views/ajax.py
src/jocondelab/views/front_office.py
virtualenv/res/lib/lib_create_env.py
virtualenv/res/src/Unidecode-0.04.14.tar.gz
virtualenv/res/src/django-haystack-2.0.0.tar.gz
virtualenv/res/src/django-haystack-2.1.0.tar.gz
virtualenv/res/src/pyelasticsearch-0.5.tar.gz
virtualenv/res/src/pyelasticsearch-0.6.1.tar.gz
virtualenv/res/src/requests-1.2.3.tar.gz
virtualenv/res/src/requests-2.2.1.tar.gz
virtualenv/res/src/simplejson-3.3.0.tar.gz
virtualenv/res/src/simplejson-3.3.2.tar.gz
virtualenv/web/res/requirement.txt
virtualenv/web/res/res_create_env.py
--- a/.settings/org.eclipse.core.resources.prefs	Mon Feb 03 02:43:13 2014 +0100
+++ b/.settings/org.eclipse.core.resources.prefs	Wed Feb 05 03:18:25 2014 +0100
@@ -26,6 +26,7 @@
 encoding//src/core/wp_utils.py=utf-8
 encoding//src/get_notice_years.py=utf-8
 encoding//src/jocondelab/admin.py=utf-8
+encoding//src/jocondelab/cache_utils.py=utf-8
 encoding//src/jocondelab/config.py=utf-8
 encoding//src/jocondelab/forms.py=utf-8
 encoding//src/jocondelab/management/__init__.py=utf-8
@@ -54,9 +55,11 @@
 encoding//src/jocondelab/migrations/0010_auto__add_contributable_term.py=utf-8
 encoding//src/jocondelab/migrations/0011_auto__add_tagcloud_term.py=utf-8
 encoding//src/jocondelab/migrations/0012_auto__add_notice_years.py=utf-8
+encoding//src/jocondelab/models/__init__.py=utf-8
 encoding//src/jocondelab/models/contribution.py=utf-8
 encoding//src/jocondelab/models/data.py=utf-8
 encoding//src/jocondelab/models/displaysettings.py=utf-8
+encoding//src/jocondelab/search_indexes.py=utf-8
 encoding//src/jocondelab/settings.py=utf-8
 encoding//src/jocondelab/utils.py=utf-8
 encoding//src/jocondelab/views/ajax.py=utf-8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jocondelab/cache_utils.py	Wed Feb 05 03:18:25 2014 +0100
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright Institut de Recherche et d'Innovation © 2014
+#
+# contact@iri.centrepompidou.fr
+#
+# Ce code a été développé pour un premier usage dans JocondeLab, projet du 
+# ministère de la culture et de la communication visant à expérimenter la
+# recherche sémantique dans la base Joconde
+# (http://jocondelab.iri-research.org/).
+#
+# Ce logiciel est régi par la licence CeCILL-C soumise au droit français et
+# respectant les principes de diffusion des logiciels libres. Vous pouvez
+# utiliser, modifier et/ou redistribuer ce programme sous les conditions
+# de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA 
+# sur le site "http://www.cecill.info".
+#
+# En contrepartie de l'accessibilité au code source et des droits de copie,
+# de modification et de redistribution accordés par cette licence, il n'est
+# offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
+# seule une responsabilité restreinte pèse sur l'auteur du programme,  le
+# titulaire des droits patrimoniaux et les concédants successifs.
+#
+# A cet égard  l'attention de l'utilisateur est attirée sur les risques
+# associés au chargement,  à l'utilisation,  à la modification et/ou au
+# développement et à la reproduction du logiciel par l'utilisateur étant 
+# donné sa spécificité de logiciel libre, qui peut le rendre complexe à 
+# manipuler et qui le réserve donc à des développeurs et des professionnels
+# avertis possédant  des  connaissances  informatiques approfondies.  Les
+# utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
+# logiciel à leurs besoins dans des conditions permettant d'assurer la
+# sécurité de leurs systèmes et ou de leurs données et, plus généralement, 
+# à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. 
+#
+# Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
+# pris connaissance de la licence CeCILL-C, et que vous en avez accepté les
+# termes.
+#
+'''
+
+@author: ymh
+'''
+
+import hashlib
+
+
+CONTROL_CHARACTERS = set([chr(i) for i in range(0,33)])
+CONTROL_CHARACTERS.add(chr(127))
+
+##
+# From django-cache-utils, https://bitbucket.org/kmike/django-cache-utils/
+#
+def sanitize_memcached_key(key, max_length=250):
+    """ Removes control characters and ensures that key will
+        not hit the memcached key length limit by replacing
+        the key tail with md5 hash if key is too long.
+    """
+    key = ''.join([c for c in key if c not in CONTROL_CHARACTERS])
+    if len(key) > max_length:
+        hash_str = hashlib.md5(key).hexdigest()
+        key = key[:max_length-33]+'-'+hash_str
+    return key
+
+def make_key(key, key_prefix, version):
+    return sanitize_memcached_key(':'.join([key_prefix, str(version), key]))
--- a/src/jocondelab/config.py.tmpl	Mon Feb 03 02:43:13 2014 +0100
+++ b/src/jocondelab/config.py.tmpl	Wed Feb 05 03:18:25 2014 +0100
@@ -189,13 +189,15 @@
 CACHES = {
     'default': {
         'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
-        'LOCATION': 'unique-snowflake'
+        'LOCATION': 'unique-snowflake',
+        'KEY_FUNCTION' : 'jocondelab.cache_utils.make_key'
     }
 }
 
 CACHE_MIDDLEWARE_SECONDS = 600
 HOME_CACHE_SECONDS = 30
 
+
 #PIPELINE_ENABLED = not DEBUG
 PIPELINE_CSS_COMPRESSOR = "pipeline.compressors.yuglify.YuglifyCompressor"
 PIPELINE_JS_COMPRESSOR = "pipeline.compressors.yuglify.YuglifyCompressor"
--- a/src/jocondelab/management/commands/import_dbpedia_fields.py	Mon Feb 03 02:43:13 2014 +0100
+++ b/src/jocondelab/management/commands/import_dbpedia_fields.py	Wed Feb 05 03:18:25 2014 +0100
@@ -41,21 +41,24 @@
 
 @author: ymh
 '''
+from optparse import make_option
+import sys
+import traceback
+import urllib2
+
 from SPARQLWrapper.Wrapper import RDF, SPARQLWrapper
-from core.models import Term
-from core.utils import show_progress
-from core.wp_utils import get_dbpedia_lang
 from django.conf import settings
 from django.core.management.base import NoArgsCommand
 from django.db import reset_queries, transaction
 from django.db.models.aggregates import Count
 from django.utils.http import urlunquote
+from rdflib.term import URIRef
+
+from core.models import Term
+from core.utils import show_progress
+from core.wp_utils import get_dbpedia_lang
 from jocondelab.models.data import TermLinks, DbpediaFields
-from optparse import make_option
-from rdflib.term import URIRef
-import sys
-import traceback
-import urllib2
+from django.core.management import call_command
 
 class Command(NoArgsCommand):
     '''
@@ -228,4 +231,6 @@
                 transaction.rollback()
 
         transaction.leave_transaction_management()
-            
+        print("Ddpedia fields imported. launching index recreation, no questions asked.")
+        call_command("rebuild_index", interactive=False)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jocondelab/search_indexes.py	Wed Feb 05 03:18:25 2014 +0100
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright Institut de Recherche et d'Innovation © 2014
+#
+# contact@iri.centrepompidou.fr
+#
+# Ce code a été développé pour un premier usage dans JocondeLab, projet du 
+# ministère de la culture et de la communication visant à expérimenter la
+# recherche sémantique dans la base Joconde
+# (http://jocondelab.iri-research.org/).
+#
+# Ce logiciel est régi par la licence CeCILL-C soumise au droit français et
+# respectant les principes de diffusion des logiciels libres. Vous pouvez
+# utiliser, modifier et/ou redistribuer ce programme sous les conditions
+# de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA 
+# sur le site "http://www.cecill.info".
+#
+# En contrepartie de l'accessibilité au code source et des droits de copie,
+# de modification et de redistribution accordés par cette licence, il n'est
+# offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
+# seule une responsabilité restreinte pèse sur l'auteur du programme,  le
+# titulaire des droits patrimoniaux et les concédants successifs.
+#
+# A cet égard  l'attention de l'utilisateur est attirée sur les risques
+# associés au chargement,  à l'utilisation,  à la modification et/ou au
+# développement et à la reproduction du logiciel par l'utilisateur étant 
+# donné sa spécificité de logiciel libre, qui peut le rendre complexe à 
+# manipuler et qui le réserve donc à des développeurs et des professionnels
+# avertis possédant  des  connaissances  informatiques approfondies.  Les
+# utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
+# logiciel à leurs besoins dans des conditions permettant d'assurer la
+# sécurité de leurs systèmes et ou de leurs données et, plus généralement, 
+# à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. 
+#
+# Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
+# pris connaissance de la licence CeCILL-C, et que vous en avez accepté les
+# termes.
+#
+from unidecode import unidecode
+'''
+@author: ymh
+'''
+
+from haystack import indexes
+from jocondelab.models import DbpediaFields
+
+class DbpediaFieldsIndex(indexes.SearchIndex, indexes.Indexable):
+    text = indexes.CharField(document=True, use_template=True)
+    label = indexes.CharField(model_attr='label', null=True)
+    label_trans = indexes.CharField(model_attr='label', null=True, indexed=True, stored=False)
+    label_ngram = indexes.NgramField(model_attr='label', null=True)
+    label_edge = indexes.EdgeNgramField(model_attr='label', null=True)
+    language_code = indexes.CharField(model_attr='language_code', null=True)
+    dbpedia_uri = indexes.CharField(model_attr='dbpedia_uri', null=True, indexed=False, stored=True)
+    
+    def get_model(self):
+        return DbpediaFields
+    
+    def index_queryset(self, using=None):
+        """Used when the entire index for model is updated."""
+        return self.get_model().objects.filter(term__nb_illustrated_notice__gt=0)
+    
+    def prepare_label_trans(self, obj):
+        if obj.label is not None:
+            return unidecode(obj.label.lower())
+        else:
+            return None 
--- a/src/jocondelab/settings.py	Mon Feb 03 02:43:13 2014 +0100
+++ b/src/jocondelab/settings.py	Wed Feb 05 03:18:25 2014 +0100
@@ -205,7 +205,8 @@
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'django_extensions',
-    'django.contrib.admin',    
+    'django.contrib.admin',
+    'haystack',
     'south',
     'mptt',
     'pipeline',
@@ -313,6 +314,17 @@
 CACHE_MIDDLEWARE_SECONDS = 600
 HOME_CACHE_SECONDS = 30
 
+#MAX number of term in case search term does not recognize a wikipedia label 
+MAX_TERMS_QUERY = 200
+
+HAYSTACK_CONNECTIONS = {
+    'default': {
+        'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
+        'KEY_FUNCTION' : 'jocondelab.utils.make_key'
+    },
+}
+
+
 BASE_CSS = ('jocondelab/css/smoothness/jquery-ui-1.10.3.custom.min.css', 'jocondelab/lib/jquery.tagit.css', 'jocondelab/css/front-common.css')
 
 PIPELINE_CSS = {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jocondelab/templates/search/indexes/jocondelab/dbpediafields_text.txt	Wed Feb 05 03:18:25 2014 +0100
@@ -0,0 +1,2 @@
+{{ object.label }}
+{{ object.language_code }}
\ No newline at end of file
--- a/src/jocondelab/utils.py	Mon Feb 03 02:43:13 2014 +0100
+++ b/src/jocondelab/utils.py	Wed Feb 05 03:18:25 2014 +0100
@@ -37,7 +37,6 @@
 # termes.
 #
 '''
-Created on Jun 16, 2013
 
 @author: ymh
 '''
@@ -127,4 +126,5 @@
             cache.set(self._get_count_key(), self._count, settings.DB_QUERY_CACHE_TIME)
         return self._count
     
-    count = property(_get_count_cached)
\ No newline at end of file
+    count = property(_get_count_cached)
+
--- a/src/jocondelab/views/ajax.py	Mon Feb 03 02:43:13 2014 +0100
+++ b/src/jocondelab/views/ajax.py	Wed Feb 05 03:18:25 2014 +0100
@@ -44,20 +44,20 @@
 
 import json
 import logging
-import re
 
 from django.conf import settings
+from django.core.cache import cache
 from django.db.models import Sum
 from django.http import HttpResponse
 from django.http.response import HttpResponseBadRequest
 from django.views.generic import TemplateView
+from haystack.query import SearchQuerySet
 import requests
 
 from core.models import Notice, Thesaurus
 from jocondelab.models import (DbpediaYears, DbpediaGeo, DbpediaFields, 
     ContributedTerm, ContributedFields, Contribution)
 
-
 logger = logging.getLogger(__name__)
 
 def terms(request):
@@ -65,14 +65,30 @@
     lang = request.GET.get('lang', request.LANGUAGE_CODE)[:2]
     q = request.GET.get('term', None)
     count = request.GET.get('count', 20)
-    qs = DbpediaFields.objects.filter(term__nb_illustrated_notice__gt=0,language_code=lang)
-    if lang in [ "fr", "en", "de", "it", "es", "pt", "ca", "br", "eu", "oc" ]:
-        qs = qs.filter(label__iregex=r"\y%s"%re.sub("(\W)",lambda m: u'\\'+m.group(1),q, flags=re.UNICODE))
-    else:
-        qs = qs.filter(label__icontains=q)
-    qs = qs.values('dbpedia_uri','label').distinct().order_by('label')[:count]
-    res = [{"dbpedia_uri": r['dbpedia_uri'], "label": r['label']} for r in qs]
-    return HttpResponse(content=json.dumps(res), mimetype='application/json')
+    
+    cachekey = "--".join((q,lang,str(count)))
+    res_list = cache.get(cachekey)
+    
+    if not res_list:
+
+        if lang in [ "fr", "en", "de", "it", "es", "pt", "ca", "br", "eu", "oc" ]:
+            fields_index_qs = SearchQuerySet().models(DbpediaFields).filter(language_code__exact=lang).autocomplete(label_ngram=q)
+        else:
+            fields_index_qs = SearchQuerySet().models(DbpediaFields).filter(language_code__exact=lang).autocomplete(label_edge=q)
+    
+        res_dict = {}
+        for r in fields_index_qs[:count*5]:
+            dbpedia_uri = r.get_stored_fields()['dbpedia_uri']
+            label = r.get_stored_fields()['label']
+            if not dbpedia_uri:
+                continue
+            res_entry = res_dict.setdefault(dbpedia_uri, {'label': None, 'score':0, 'uri':dbpedia_uri})
+            res_entry['label'] = label if label and not res_entry.get('label', None) else res_entry['label']
+            res_entry['score'] += r.score
+        
+        res_list = sorted([ res_entry for res_entry in res_dict.values()],key=lambda r: r.get('score',0), reverse=True)
+        cache.set(cachekey, res_list)
+    return HttpResponse(content=json.dumps([{"dbpedia_uri":r.get('uri'), "label": r.get('label','')} for r in res_list]), mimetype='application/json')
 
 def years(request):
     
--- a/src/jocondelab/views/front_office.py	Mon Feb 03 02:43:13 2014 +0100
+++ b/src/jocondelab/views/front_office.py	Wed Feb 05 03:18:25 2014 +0100
@@ -55,6 +55,8 @@
 from django.utils.http import urlencode
 from django.utils.translation import ugettext
 from django.views.generic import DetailView, TemplateView
+from haystack.query import SearchQuerySet
+from unidecode import unidecode
 
 from core.models import Notice, Term, TERM_WK_LINK_SEMANTIC_LEVEL_DICT
 from core.models.term import Thesaurus
@@ -168,10 +170,13 @@
                     queryobj = {'dbpedia_uri': dbpedia_uri}
                     fs = list(DbpediaFields.objects.filter(dbpedia_uri__in=dbpedia_uris, language_code=lang).exclude(label__isnull=True))
                     searchterms = set([fields.label for fields in fs])
+                    operator = "and"
                 elif queryterms:
                     searchterms = queryterms
                     queryobj = {'q': querystr}
-                    fs = list(DbpediaFields.objects.filter(label__in=queryterms, language_code=lang).exclude(label__isnull=True))
+                    label_search = SearchQuerySet().models(DbpediaFields).filter(language_code__exact=lang).auto_query(" ".join([unidecode(q.lower()) for q in queryterms]), "label_trans")[:getattr(settings, 'MAX_TERMS_QUERY', 200)]
+                    fs = list(DbpediaFields.objects.filter(id__in=[r.pk for r in label_search], language_code=lang).exclude(label__isnull=True))
+                    operator = 'or'
                 # If fs is empty, qs has to be empty
                 if len(fs)==0:
                     qs = qs.none()
@@ -182,8 +187,14 @@
                 uri_cache.update(dict([(fields.label.lower() if fields.label else "", fields.dbpedia_uri) for fields in fs]))
                 if page == 1 and len(dbpedia_uris) == 1 and len(fs) > 0:
                     context["wkinfo"] = fs[0]
+                term_filters = Q()
                 for term_ids in fields_hash.values():
-                    qs = qs.filter(noticeterm__term_id__in=term_ids)
+                    term_filter_term = Q(noticeterm__term_id__in=term_ids)
+                    if operator == "and":
+                        term_filters &= term_filter_term
+                    else:
+                        term_filters |= term_filter_term
+                qs = qs.filter(term_filters)
             
             count_qs = qs
             qs = qs.filter(noticeterm__term__validated=True)
--- a/virtualenv/res/lib/lib_create_env.py	Mon Feb 03 02:43:13 2014 +0100
+++ b/virtualenv/res/lib/lib_create_env.py	Wed Feb 05 03:18:25 2014 +0100
@@ -23,14 +23,14 @@
     'SOUTH': { 'setup': 'South', 'url':'http://www.aeracode.org/releases/south/south-0.7.6.tar.gz', 'local':"south-0.7.6.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
     'LIBJPEG': {'setup': None, 'url':'jpegsrc.v9.tar.gz', 'local':'jpegsrc.v9.tar.gz', 'install': {'method': 'install_libjpeg', 'option_str': None, 'dict_extra_env': None}},
     'ZLIB': {'setup': None, 'url':'zlib-1.2.8.tar.gz', 'local':'zlib-1.2.8.tar.gz', 'install': {'method': 'install_zlib', 'option_str': None, 'dict_extra_env': None}},
-    'HAYSTACK': {'setup': 'django-haystack', 'url': 'https://github.com/toastdriven/django-haystack/archive/v2.0.0.tar.gz', 'local': 'django-haystack-2.0.0.tar.gz', 'install':{'method':'pip', 'option_str': None, 'dict_extra_env': None}},
-    'PYELASTICSEARCH': {'setup': 'pyelasticsearch', 'url':'https://github.com/rhec/pyelasticsearch/archive/0.5.tar.gz', 'local':'pyelasticsearch-0.5.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+    'HAYSTACK': {'setup': 'django-haystack', 'url': 'https://github.com/toastdriven/django-haystack/archive/v2.1.0.tar.gz', 'local': 'django-haystack-2.1.0.tar.gz', 'install':{'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+    'PYELASTICSEARCH': {'setup': 'pyelasticsearch', 'url':'https://github.com/rhec/pyelasticsearch/archive/0.6.1.tar.gz', 'local':'pyelasticsearch-0.6.1.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'PYTHON-DATEUTIL' : {'setup':'python-dateutil', 'url':'https://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-2.1.tar.gz', 'local': 'python-dateutil-2.1.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'RDFLIB' : {'setup':'rdflib', 'url':'https://github.com/RDFLib/rdflib/archive/4.0.1.tar.gz', 'local': 'rdflib-4.0.1.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'SQLALCHEMY' : {'setup':'sqlalchemy', 'url':'https://pypi.python.org/packages/source/S/SQLAlchemy/SQLAlchemy-0.8.1.tar.gz', 'local': 'SQLAlchemy-0.8.1.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'RDFLIB-SQLALCHEMY' : {'setup':'rdflib-sqlalchemy', 'url':'https://github.com/editorsnotes/rdflib-sqlalchemy/archive/9996e2fd7cdd87e35384c529196407808c9f60ee.tar.gz', 'local': 'rdflib-sqlalchemy-0.2.dev.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
-    'REQUESTS': {'setup': 'requests', 'url':'https://github.com/kennethreitz/requests/archive/v1.2.3.tar.gz', 'local':'requests-1.2.3.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
-    'SIMPLEJSON': {'setup': 'simplejson','url':'https://github.com/simplejson/simplejson/archive/v3.3.0.tar.gz', 'local': 'simplejson-3.3.0.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+    'REQUESTS': {'setup': 'requests', 'url':'https://github.com/kennethreitz/requests/archive/v2.2.1.tar.gz', 'local':'requests-2.2.1.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+    'SIMPLEJSON': {'setup': 'simplejson','url':'https://github.com/simplejson/simplejson/archive/v3.3.2.tar.gz', 'local': 'simplejson-3.3.2.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'ISODATE': {'setup': 'isodate','url':'https://pypi.python.org/packages/source/i/isodate/isodate-0.4.9.tar.gz', 'local': 'isodate-0.4.9.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'PYPARSING': {'setup': 'pyparsing','url':'http://downloads.sourceforge.net/project/pyparsing/pyparsing/pyparsing-1.5.7/pyparsing-1.5.7.tar.gz', 'local': 'pyparsing-1.5.7.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'SPARQLWRAPPER': {'setup': 'sparqlwrapper','url':'http://downloads.sourceforge.net/project/sparql-wrapper/sparql-wrapper-python/1.5.2/SPARQLWrapper-1.5.2.tar.gz', 'local': 'SPARQLWrapper-1.5.2.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
@@ -39,6 +39,7 @@
     'MPTT' : { 'setup': 'django-mptt', 'url': 'https://codeload.github.com/django-mptt/django-mptt/tar.gz/master', 'local': 'django-mptt-0.5.-dev.tar.bz2', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
     'FUTURES' : { 'setup': 'futures', 'url': 'https://pypi.python.org/packages/source/f/futures/futures-2.1.6.tar.gz', 'local': 'futures-2.1.6.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
     'PIPELINE' : { 'setup': 'django-pipeline', 'url': 'https://github.com/cyberdelia/django-pipeline/archive/1.3.20.tar.gz', 'local': 'django-pipeline-1.3.20.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+    'UNIDECODE' : { 'setup': 'unidecode', 'url': 'https://pypi.python.org/packages/source/U/Unidecode/Unidecode-0.04.14.tar.gz', 'local': 'Unidecode-0.04.14.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
 }
 
 if system_str == 'Windows':
Binary file virtualenv/res/src/Unidecode-0.04.14.tar.gz has changed
Binary file virtualenv/res/src/django-haystack-2.0.0.tar.gz has changed
Binary file virtualenv/res/src/django-haystack-2.1.0.tar.gz has changed
Binary file virtualenv/res/src/pyelasticsearch-0.5.tar.gz has changed
Binary file virtualenv/res/src/pyelasticsearch-0.6.1.tar.gz has changed
Binary file virtualenv/res/src/requests-1.2.3.tar.gz has changed
Binary file virtualenv/res/src/requests-2.2.1.tar.gz has changed
Binary file virtualenv/res/src/simplejson-3.3.0.tar.gz has changed
Binary file virtualenv/res/src/simplejson-3.3.2.tar.gz has changed
--- a/virtualenv/web/res/requirement.txt	Mon Feb 03 02:43:13 2014 +0100
+++ b/virtualenv/web/res/requirement.txt	Wed Feb 05 03:18:25 2014 +0100
@@ -3,9 +3,10 @@
 SPARQLWrapper==1.5.2
 SQLAlchemy==0.8.1
 South==0.7.6
+Unidecode==0.04.14
 distribute==0.6.34
 django-extensions==1.1.1
-django-haystack==2.0.0
+django-haystack==2.1.0
 django-mptt==0.5.-dev
 django-pipeline==1.3.20
 futures==2.1.6
@@ -13,13 +14,13 @@
 isodate==0.4.9
 lxml==3.2.1
 psycopg2==2.5
-pyelasticsearch==0.5
+pyelasticsearch==0.6.1
 pyparsing==1.5.7
 python-dateutil==2.1
 rdflib==4.0.1
 rdflib-sqlalchemy==0.2.dev
-requests==1.2.3
-simplejson==3.3.0
+requests==2.2.1
+simplejson==3.3.2
 six==1.3.0
 wikitools==1.1.1
 wsgiref==0.1.2
\ No newline at end of file
--- a/virtualenv/web/res/res_create_env.py	Mon Feb 03 02:43:13 2014 +0100
+++ b/virtualenv/web/res/res_create_env.py	Wed Feb 05 03:18:25 2014 +0100
@@ -31,6 +31,7 @@
 #    'WIKITOOLS',
 #    'MPTT',
 #    'PIPELINE',
+#    'UNIDECODE',
 ]
 
 if system_str == "Linux":