Add dynamic_search app API endpoints, simplify ModelSearch class and search views

This commit is contained in:
Roberto Rosario
2014-10-14 03:40:46 -04:00
parent 24a1a35a4c
commit 780c55e255
8 changed files with 113 additions and 46 deletions

View File

@@ -58,6 +58,7 @@ from .permissions import (PERMISSION_DOCUMENT_DELETE,
PERMISSION_DOCUMENT_VERSION_REVERT,
PERMISSION_DOCUMENT_VIEW)
from .settings import THUMBNAIL_SIZE
from .serializers import DocumentSerializer
from .statistics import DocumentStatistics, DocumentUsageStatistics
from .urls import api_urls
from .widgets import document_thumbnail
@@ -141,7 +142,7 @@ class_permissions(Document, [PERMISSION_DOCUMENT_DELETE,
PERMISSION_DOCUMENT_VIEW,
PERMISSION_HISTORY_VIEW])
document_search = SearchModel('documents', 'Document')
document_search = SearchModel('documents', 'Document', serializer=DocumentSerializer)
document_search.add_model_field('document_type__name', label=_(u'Document type'))
# TODO: move these to their respective apps

View File

@@ -1,10 +1,18 @@
from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
from navigation.api import register_links, register_top_menu
from rest_api.classes import APIEndPoint
from .links import search, search_advanced, search_again, search_menu
from .urls import api_urls
register_links(['search:search', 'search:search_advanced', 'search:results'], [search, search_advanced], menu_name='form_header')
register_links(['search:results'], [search_again], menu_name='sidebar')
register_top_menu('search', search_menu)
endpoint = APIEndPoint('search')
endpoint.register_urls(api_urls)
endpoint.add_endpoint('recentsearch-list', _(u'Returns a list of all recent searches.'))

View File

@@ -0,0 +1,61 @@
from __future__ import absolute_import
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from rest_framework import generics, status
from rest_framework.response import Response
from acls.models import AccessEntry
from permissions.models import Permission
from rest_api.filters import MayanObjectPermissionsFilter
from rest_api.permissions import MayanPermission
from .classes import SearchModel
from .models import RecentSearch
from .serializers import RecentSearchSerializer
class APIRecentSearchListView(generics.ListAPIView):
"""
Returns a list of all the recent searches.
"""
serializer_class = RecentSearchSerializer
queryset = RecentSearch.objects.all()
# TODO: Add filter_backend so that users can only see their own entries
class APIRecentSearchView(generics.RetrieveDestroyAPIView):
"""
Returns the selected recent search details.
"""
serializer_class = RecentSearchSerializer
queryset = RecentSearch.objects.all()
# TODO: Add filter_backend so that users can only see their own entries
class APISearchView(generics.ListAPIView):
"""
Perform a search operaton
q -- Term that will be used for the search.
"""
def get_queryset(self):
document_search = SearchModel.get('documents.Document')
self.serializer_class = document_search.serializer
if 'q' in self.request.GET:
# Simple query
query_string = self.request.GET.get('q', u'').strip()
queryset, ids, timedelta = document_search.simple_search(query_string)
else:
# Advanced search
queryset, ids, timedelta = document_search.advanced_search(self.request.GET)
RecentSearch.objects.add_query_for_user(self.request.user, self.request.GET, len(ids))
return queryset

View File

@@ -23,12 +23,13 @@ class SearchModel(object):
def get(cls, full_name):
return cls.registry[full_name]
def __init__(self, app_label, model_name, label=None):
def __init__(self, app_label, model_name, serializer, label=None):
self.app_label = app_label
self.model_name = model_name
self.search_fields = {}
self.model = get_model(app_label, model_name)
self.label = label or self.model._meta.verbose_name
self.serializer = serializer
self.__class__.registry[self.get_full_name()] = self
def get_full_name(self):
@@ -126,12 +127,9 @@ class SearchModel(object):
return self.execute_search(search_dict, global_and_search=True)
def execute_search(self, search_dict, global_and_search=False):
model_list = {}
flat_list = []
result_count = 0
shown_result_count = 0
elapsed_time = 0
start_time = datetime.datetime.now()
result_set = set()
for model, data in search_dict.items():
logger.debug('model: %s' % model)
@@ -175,30 +173,11 @@ class SearchModel(object):
else:
model_result_set |= field_result_set
logger.debug('model_result_set: %s' % model_result_set)
# Update the search result total count
result_count += len(model_result_set)
# Search the field results return values (PK) in the SearchModel's model
results = self.model.objects.in_bulk(list(model_result_set)[: LIMIT]).values()
logger.debug('query model_result_set: %s' % model_result_set)
# Update the search result visible count (limited by LIMIT config option)
shown_result_count += len(results)
if results:
model_list[data['label']] = results
for result in results:
if result not in flat_list:
flat_list.append(result)
logger.debug('model_list: %s' % model_list)
logger.debug('flat_list: %s' % flat_list)
result_set = result_set | model_result_set
elapsed_time = unicode(datetime.datetime.now() - start_time).split(':')[2]
return model_list, flat_list, shown_result_count, result_count, elapsed_time
return self.model.objects.in_bulk(list(result_set)[: LIMIT]).values(), result_set, elapsed_time
def assemble_query(self, terms, search_fields):
"""

View File

@@ -18,7 +18,12 @@ class RecentSearch(models.Model):
"""
Keeps a list of the n most recent search keywords for a given user
"""
user = models.ForeignKey(User, verbose_name=_(u'User'), editable=False)
user = models.ForeignKey(User, verbose_name=_(u'User'), editable=True)
# Setting editable to True to workaround Django REST framework issue
# 1604 - https://github.com/tomchristie/django-rest-framework/issues/1604
# Should be fixed by DRF v2.4.4
# TODO: Fix after upgrade to DRF v2.4.4
query = models.TextField(verbose_name=_(u'Query'), editable=False)
datetime_created = models.DateTimeField(verbose_name=_(u'Datetime created'), editable=False)
hits = models.IntegerField(verbose_name=_(u'Hits'), editable=False)

View File

@@ -0,0 +1,11 @@
from __future__ import absolute_import
from rest_framework import serializers
from .models import RecentSearch
class RecentSearchSerializer(serializers.ModelSerializer):
class Meta:
model = RecentSearch
read_only_fields = ('user', 'query', 'datetime_created', 'hits')

View File

@@ -1,8 +1,17 @@
from django.conf.urls import patterns, url
from .api_views import (APIRecentSearchListView, APIRecentSearchView,
APISearchView)
urlpatterns = patterns('dynamic_search.views',
url(r'^$', 'search', (), 'search'),
url(r'^advanced/$', 'search', {'advanced': True}, 'search_advanced'),
url(r'^again/$', 'search_again', (), 'search_again'),
url(r'^results/$', 'results', (), 'results'),
)
api_urls = patterns('',
url(r'^recent_searches/$', APIRecentSearchListView.as_view(), name='recentsearch-list'),
url(r'^recent_searches/(?P<pk>[0-9]+)/$', APIRecentSearchView.as_view(), name='recentsearch-detail'),
url(r'^search/$', APISearchView.as_view(), name='search-view'),
)

View File

@@ -34,36 +34,28 @@ def results(request, extra_context=None):
# Simple query
logger.debug('simple search')
query_string = request.GET.get('q', u'').strip()
model_list, flat_list, shown_result_count, result_count, elapsed_time = document_search.simple_search(query_string)
queryset, ids, timedelta = document_search.simple_search(query_string)
else:
# Advanced search
logger.debug('advanced search')
model_list, flat_list, shown_result_count, result_count, elapsed_time = document_search.advanced_search(request.GET)
if shown_result_count != result_count:
title = _(u'Results, (showing only %(shown_result_count)s out of %(result_count)s)') % {
'shown_result_count': shown_result_count,
'result_count': result_count}
else:
title = _(u'Results')
queryset, ids, timedelta = document_search.advanced_search(request.GET)
# Update the context with the search results
context.update({
'found_entries': model_list,
'object_list': flat_list,
'title': title,
'time_delta': elapsed_time,
'object_list': queryset,
'time_delta': timedelta,
'title': _(u'Results'),
})
RecentSearch.objects.add_query_for_user(request.user, request.GET, result_count)
RecentSearch.objects.add_query_for_user(request.user, request.GET, len(ids))
if extra_context:
context.update(extra_context)
if SHOW_OBJECT_TYPE:
context.update({'extra_columns':
[{'name': _(u'Type'), 'attribute': lambda x: x._meta.verbose_name[0].upper() + x._meta.verbose_name[1:]}]})
context.update({
'extra_columns': [{'name': _(u'Type'), 'attribute': lambda x: x._meta.verbose_name[0].upper() + x._meta.verbose_name[1:]}]
})
return render_to_response('search_results.html', context,
context_instance=RequestContext(request))
@@ -72,7 +64,8 @@ def results(request, extra_context=None):
def search(request, advanced=False):
if advanced:
form = AdvancedSearchForm(data=request.GET, search_model=document_search)
return render_to_response('main/generic_form.html',
return render_to_response(
'main/generic_form.html',
{
'form': form,
'title': _(u'Advanced search'),