Add dynamic_search app API endpoints, simplify ModelSearch class and search views
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.'))
|
||||
|
||||
61
mayan/apps/dynamic_search/api_views.py
Normal file
61
mayan/apps/dynamic_search/api_views.py
Normal 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
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
11
mayan/apps/dynamic_search/serializers.py
Normal file
11
mayan/apps/dynamic_search/serializers.py
Normal 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')
|
||||
@@ -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'),
|
||||
)
|
||||
|
||||
@@ -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'),
|
||||
|
||||
Reference in New Issue
Block a user