Commit c3a2099e authored by okhin's avatar okhin 🚴

Merge branch 'search_update' into 'master'

Search update

See merge request !197
parents 56b5ee5e 9119a4d1
Pipeline #1057 passed with stage
in 11 minutes and 4 seconds
......@@ -36,3 +36,4 @@ data/
# local setup
.memopol.alias
whoosh_index/
......@@ -43,6 +43,9 @@ memopol migrate
memopol loaddata data_sample.json
memopol refresh_scores
# Build index for Whoosh
memopol rebuild_index
echo
echo "You're all set!"
echo "To start the application run the following from the repository root ($REPOROOT):"
......
......@@ -30,7 +30,10 @@ setup(name='political-memory',
'pytz', # Always use up-to-date TZ data
'django-suit>=0.2,<0.3',
'psycopg2>=2,<3',
'alabaster==0.7.10',
'django-haystack==2.6.0',
'pysolr==3.6.0',
'Whoosh==2.7.4',
'alabaster==0.7.10',
],
extras_require={
# Full version hardcode for testing dependencies so that
......
......@@ -8,5 +8,6 @@ def main():
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
from haystack import indexes
from representatives.models import Representative
class RepresentativeIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.EdgeNgramField(document=True, use_template=True)
slug = indexes.CharField(model_attr='slug', faceted=True)
first_name = indexes.CharField(model_attr='first_name', faceted=True)
last_name = indexes.CharField(model_attr='last_name', faceted=True)
full_name = indexes.EdgeNgramField(model_attr='full_name')
ascii_name = indexes.NgramField(model_attr='ascii_name')
def get_model(self):
return Representative
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.all()
......@@ -58,6 +58,7 @@ INSTALLED_APPS = (
'fontawesome',
'rest_framework',
'taggit',
'haystack',
# memopol apps
'core',
......@@ -341,3 +342,19 @@ if os.path.exists(RAVEN_FILE):
with open(RAVEN_FILE, 'r') as f:
RAVEN_CONFIG = {'dsn': f.read().strip()}
# Haystack with Solr config
if not DEBUG:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
'URL': 'http://127.0.0.1:8080/solr',
},
}
else:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
},
}
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -20,6 +20,8 @@
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" type="text/css" />
<link rel="stylesheet" href="{% static 'libs/flag-icon-css/css/flag-icon.min.css' %}" type="text/css" />
<link rel="stylesheet" href="{% static 'css/jquery-ui.min.css' %}" type="text/css" />
{% load compress %}
{% compress css %}
......@@ -28,6 +30,7 @@
{% endcompress %}
<script type="text/javascript" src="{% static 'libs/jquery/dist/jquery.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery-ui.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/bootstrap.min.js' %}"></script>
{{ position_form.media }}
......@@ -86,5 +89,16 @@
$('[data-toggle="tooltip"]').tooltip()
})
</script>
<script>
$(function() {
$("#search").autocomplete({
source: "{% url 'representative-search-name-autocomplete' %}",
minLength: 2,
select: function(event, ui){
window.location.href = ui.item.link;
}
});
});
</script>
</body>
</html>
{{ object.id }}
{{ object.full_name }}
{{ object.slug }}$
{{ object.ascii_name}}
......@@ -26,6 +26,7 @@ from views.representative_detail_votes import RepresentativeDetailVotes
from views.representative_detail_mandates import RepresentativeDetailMandates
from views.representative_detail_positions import RepresentativeDetailPositions
from views.representative_list import RepresentativeList
from views.representative_search import search_autocomplete
from views.redirects import (
RedirectRepresentativeDetail,
......@@ -241,5 +242,10 @@ urlpatterns = [
ThemeDetailBase.as_view(),
name='theme-none'
),
url(
r'^representative/search/$',
search_autocomplete,
name="representative-search-name-autocomplete"
),
] + legacy_patterns
import unicodedata
def strip_accents(value):
return ''.join(
c for c in unicodedata.normalize('NFD', value)
if unicodedata.category(c) != 'Mn')
import json
from django.http import HttpResponse
from haystack.query import SearchQuerySet
from django.core.urlresolvers import reverse
from memopol.utils import strip_accents
def search_autocomplete(request):
if request.is_ajax():
q = strip_accents(request.GET.get('term', ''))
if q is not None:
json_results = []
sqs = SearchQuerySet().autocomplete(ascii_name=q)
for result in sqs:
result_json = {}
result_json['id'] = result.id
result_json['label'] = result.full_name
result_json['value'] = result.full_name
result_json['link'] = reverse(
'representative-detail', kwargs={'slug': result.slug})
json_results.append(result_json)
data = json.dumps(json_results)
else:
data = "Fail"
return HttpResponse(data, "application/json")
......@@ -8,4 +8,5 @@ class SettingAdmin(admin.ModelAdmin):
list_editable = ('key', 'value', 'comment')
list_filter = ('key',)
admin.site.register(Setting, SettingAdmin)
......@@ -5,6 +5,7 @@ from datetime import datetime
from django.db import models
from django.utils.encoding import smart_unicode
from django.utils.functional import cached_property
from memopol.utils import strip_accents
class TimeStampedModel(models.Model):
......@@ -76,6 +77,9 @@ class Representative(TimeStampedModel):
genders = {0: 'N/A', 1: 'F', 2: 'M'}
return genders[self.gender]
def ascii_name(self):
return strip_accents(self.full_name).lower()
class Meta:
ordering = ['last_name', 'first_name']
......
......@@ -11,6 +11,7 @@ def publish_positions(modeladmin, request, queryset):
"""Set published to True for the queryset"""
queryset.update(published=True)
publish_positions.short_description = 'Publish selected positions'
......@@ -18,6 +19,7 @@ def unpublish_positions(modeladmin, request, queryset):
"""Set published to False for the queryset"""
queryset.update(published=False)
unpublish_positions.short_description = 'Unpublish selected positions'
......
......@@ -13,4 +13,5 @@ class RecommendationsAdmin(admin.ModelAdmin):
'proposal__dossier__title')
form = RecommendationForm
admin.site.register(Recommendation, RecommendationsAdmin)
......@@ -31,4 +31,6 @@ def skip_votes(sender, vote_data=None, **kwargs):
if vote_data.get('epref', None) not in dossiers:
return False
vote_pre_import.connect(skip_votes)
......@@ -56,6 +56,7 @@ class VoteAdmin(admin.ModelAdmin):
def proposal_reference(self, obj):
return obj.proposal.reference
admin.site.register(Dossier, DossierAdmin)
admin.site.register(Document, DocumentAdmin)
admin.site.register(Proposal, ProposalAdmin)
......
......@@ -26,6 +26,7 @@ def _parse_date(date_str):
date_parse(date_str),
date_timezone('Europe/Brussels'))
JSON_URL = 'http://parltrack.euwiki.org/dumps/ep_votes.json.xz'
DESTINATION = join('/tmp', 'ep_votes.json')
RE_COMVOTE_REF = re.compile(r'&reference=([^&]+)')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment