Update dynamic_search app to work with organizations. Remove Recent search feature.

This commit is contained in:
Roberto Rosario
2016-06-30 18:46:26 -04:00
parent 1b8436add7
commit aa56e8ae14
18 changed files with 37 additions and 269 deletions

View File

@@ -95,6 +95,12 @@ class GenericViewTestCase(OrganizationTestCase):
data=data, follow=follow data=data, follow=follow
) )
def login_user(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
def login_superuser(self):
self.login(username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD)
class CommonViewTestCase(GenericViewTestCase): class CommonViewTestCase(GenericViewTestCase):
def test_about_view(self): def test_about_view(self):

View File

@@ -1,14 +0,0 @@
from __future__ import unicode_literals
from django.contrib import admin
from .models import RecentSearch
@admin.register(RecentSearch)
class RecentSearchAdmin(admin.ModelAdmin):
date_hierarchy = 'datetime_created'
list_display = ('user', 'query', 'datetime_created', 'hits')
list_display_links = ('user', 'query', 'datetime_created', 'hits')
list_filter = ('user',)
readonly_fields = ('user', 'query', 'datetime_created', 'hits')

View File

@@ -6,29 +6,6 @@ from rest_framework.exceptions import ParseError
from rest_api.filters import MayanObjectPermissionsFilter from rest_api.filters import MayanObjectPermissionsFilter
from .classes import SearchModel from .classes import SearchModel
from .filters import RecentSearchUserFilter
from .models import RecentSearch
from .serializers import RecentSearchSerializer
class APIRecentSearchListView(generics.ListAPIView):
"""
Returns a list of all the recent searches for the logged user.
"""
filter_backends = (RecentSearchUserFilter,)
queryset = RecentSearch.objects.all()
serializer_class = RecentSearchSerializer
class APIRecentSearchView(generics.RetrieveAPIView):
"""
Returns the selected recent search details.
"""
filter_backends = (RecentSearchUserFilter,)
queryset = RecentSearch.objects.all()
serializer_class = RecentSearchSerializer
class APISearchView(generics.ListAPIView): class APISearchView(generics.ListAPIView):

View File

@@ -2,10 +2,10 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_facet, menu_sidebar from common import MayanAppConfig, menu_facet
from rest_api.classes import APIEndPoint from rest_api.classes import APIEndPoint
from .links import link_search, link_search_advanced, link_search_again from .links import link_search, link_search_advanced
class DynamicSearchApp(MayanAppConfig): class DynamicSearchApp(MayanAppConfig):
@@ -26,6 +26,3 @@ class DynamicSearchApp(MayanAppConfig):
'search:search', 'search:search_advanced', 'search:results' 'search:search', 'search:search_advanced', 'search:results'
) )
) )
menu_sidebar.bind_links(
links=(link_search_again,), sources=('search:results',)
)

View File

@@ -12,7 +12,6 @@ from django.utils.module_loading import import_string
from acls.models import AccessControlList from acls.models import AccessControlList
from permissions import Permission from permissions import Permission
from .models import RecentSearch
from .settings import setting_limit from .settings import setting_limit
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -148,7 +147,7 @@ class SearchModel(object):
for query in field_query_list: for query in field_query_list:
logger.debug('query: %s', query) logger.debug('query: %s', query)
term_query_result_set = set( term_query_result_set = set(
model.objects.filter(query).values_list( model.on_organization.filter(query).values_list(
data['return_value'], flat=True data['return_value'], flat=True
) )
) )
@@ -183,7 +182,7 @@ class SearchModel(object):
datetime.datetime.now() - start_time datetime.datetime.now() - start_time
).split(':')[2] ).split(':')[2]
queryset = self.model.objects.filter( queryset = self.model.on_organization.filter(
pk__in=list(result_set)[:setting_limit.value] pk__in=list(result_set)[:setting_limit.value]
) )
@@ -195,10 +194,6 @@ class SearchModel(object):
self.permission, user, queryset self.permission, user, queryset
) )
RecentSearch.objects.add_query_for_user(
user, query_string, len(result_set)
)
return queryset, result_set, elapsed_time return queryset, result_set, elapsed_time
def assemble_query(self, terms, search_fields): def assemble_query(self, terms, search_fields):

View File

@@ -1,11 +0,0 @@
from __future__ import unicode_literals
from rest_framework.filters import BaseFilterBackend
class RecentSearchUserFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if request.user.is_staff or request.user.is_superuser:
return queryset
else:
return queryset.filter(user=self.request.user)

View File

@@ -8,4 +8,3 @@ link_search = Link(text=_('Search'), view='search:search')
link_search_advanced = Link( link_search_advanced = Link(
text=_('Advanced search'), view='search:search_advanced' text=_('Advanced search'), view='search:search_advanced'
) )
link_search_again = Link(text=_('Search again'), view='search:search_again')

View File

@@ -1,40 +0,0 @@
import urlparse
from django.contrib.auth.models import AnonymousUser
from django.db import models
from django.utils.http import urlencode
from .settings import setting_recent_count
class RecentSearchManager(models.Manager):
def add_query_for_user(self, user, query_string, hits):
parsed_query = urlparse.parse_qs(
urlencode(dict(query_string.items()))
)
for key, value in parsed_query.items():
parsed_query[key] = ' '.join(value)
if 'q' in query_string:
# Is a simple query
if not query_string['q']:
# Don't store empty simple searches
return
else:
# Cleanup query string and only store the q parameter
parsed_query = {'q': parsed_query['q']}
if parsed_query and not isinstance(user, AnonymousUser):
# If the URL query has at least one variable with a value
new_recent, created = self.model.objects.get_or_create(
user=user, query=urlencode(parsed_query),
defaults={'hits': hits}
)
if not created:
new_recent.hits = hits
new_recent.save()
for recent_to_delete in self.model.objects.filter(user=user)[setting_recent_count.value:]:
recent_to_delete.delete()

View File

@@ -15,7 +15,10 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='recentsearch', model_name='recentsearch',
name='user', name='user',
field=models.ForeignKey(editable=False, to=settings.AUTH_USER_MODEL, verbose_name='User'), field=models.ForeignKey(
editable=False, to=settings.AUTH_USER_MODEL,
verbose_name='User'
),
preserve_default=True, preserve_default=True,
), ),
] ]

View File

@@ -1,75 +0,0 @@
from __future__ import unicode_literals
import urllib
import urlparse
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import (
python_2_unicode_compatible, smart_str, smart_unicode
)
from django.utils.translation import ugettext_lazy as _
from .managers import RecentSearchManager
@python_2_unicode_compatible
class RecentSearch(models.Model):
"""
Keeps a list of the [n] most recent search keywords for a given user
"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL, editable=False, verbose_name=_('User')
)
query = models.TextField(editable=False, verbose_name=_('Query'))
datetime_created = models.DateTimeField(
auto_now=True, db_index=True, verbose_name=_('Datetime created')
)
hits = models.IntegerField(editable=False, verbose_name=_('Hits'))
objects = RecentSearchManager()
def __str__(self):
# TODO: Fix this hack, store the search model name in the recent
# search entry
from .classes import SearchModel
document_search = SearchModel.get('documents.Document')
query_dict = urlparse.parse_qs(
urllib.unquote_plus(smart_str(self.query))
)
if self.is_advanced():
# Advanced search
advanced_string = []
for key, value in query_dict.items():
search_field = document_search.get_search_field(key)
advanced_string.append(
'%s: %s' % (
search_field.label, smart_unicode(' '.join(value))
)
)
display_string = ', '.join(advanced_string)
else:
# Is a simple search
display_string = smart_unicode(' '.join(query_dict['q']))
return '%s (%s)' % (display_string, self.hits)
def save(self, *args, **kwargs):
super(RecentSearch, self).save(*args, **kwargs)
def url(self):
view = 'search:results' if self.is_advanced() else 'search:search'
return '%s?%s' % (reverse(view), self.query)
def is_advanced(self):
return 'q' not in urlparse.parse_qs(self.query)
class Meta:
ordering = ('-datetime_created',)
verbose_name = _('Recent search')
verbose_name_plural = _('Recent searches')

View File

@@ -1,19 +0,0 @@
from __future__ import unicode_literals
from rest_framework import serializers
from user_management.serializers import UserSerializer
from .models import RecentSearch
class RecentSearchSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='rest_api:recentsearch-detail'
)
user = UserSerializer()
class Meta:
fields = ('datetime_created', 'hits', 'query', 'url', 'user')
model = RecentSearch
read_only_fields = ('datetime_created', 'hits', 'query', 'user')

View File

@@ -1,38 +1,21 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from json import loads
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import override_settings from django.test import override_settings
from rest_framework.test import APITestCase
from documents.models import DocumentType from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from user_management.tests import ( from rest_api.tests import GenericAPITestCase
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
@override_settings(OCR_AUTO_OCR=False) @override_settings(OCR_AUTO_OCR=False)
class SearchAPITestCase(APITestCase): class SearchAPITestCase(GenericAPITestCase):
""" """
Test the search API endpoints Test the search API endpoints
""" """
def setUp(self):
self.admin_user = get_user_model().objects.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
self.client.login(
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
)
def test_search(self): def test_search(self):
document_type = DocumentType.objects.create( document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE label=TEST_DOCUMENT_TYPE
) )
@@ -45,6 +28,5 @@ class SearchAPITestCase(APITestCase):
'{}?q={}'.format(reverse('rest_api:search-view'), document.label) '{}?q={}'.format(reverse('rest_api:search-view'), document.label)
) )
content = loads(response.content) self.assertEqual(response.data['results'][0]['label'], document.label)
self.assertEqual(content['results'][0]['label'], document.label) self.assertEqual(response.data['count'], 1)
self.assertEqual(content['count'], 1)

View File

@@ -1,23 +1,25 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import TestCase
from documents.models import DocumentType from documents.models import DocumentType
from documents.search import document_search from documents.search import document_search
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from organizations.tests import OrganizationTestCase
from user_management.tests import ( from user_management.tests import (
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL
) )
class DocumentSearchTestCase(TestCase): class DocumentSearchTestCase(OrganizationTestCase):
def setUp(self): def setUp(self):
self.admin_user = get_user_model().objects.create_superuser( super(DocumentSearchTestCase, self).setUp()
self.admin_user = get_user_model().on_organization.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD password=TEST_ADMIN_PASSWORD
) )
self.document_type = DocumentType.objects.create( self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE label=TEST_DOCUMENT_TYPE
) )
@@ -28,6 +30,7 @@ class DocumentSearchTestCase(TestCase):
def tearDown(self): def tearDown(self):
self.document_type.delete() self.document_type.delete()
super(DocumentSearchTestCase, self).tearDown()
def test_simple_search_after_related_name_change(self): def test_simple_search_after_related_name_change(self):
""" """

View File

@@ -1,39 +1,26 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import Client
from common.tests.test_views import GenericViewTestCase
from documents.models import DocumentType from documents.models import DocumentType
from documents.search import document_search from documents.search import document_search
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from user_management.tests import (
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL
)
class Issue46TestCase(TestCase): class Issue46TestCase(GenericViewTestCase):
""" """
Functional tests to make sure issue 46 is fixed Functional tests to make sure issue 46 is fixed
""" """
def setUp(self): def setUp(self):
self.admin_user = get_user_model().objects.create_superuser( super(Issue46TestCase, self).setUp()
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD self.login_superuser()
)
self.client = Client()
# Login the admin user
logged_in = self.client.login(
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
)
self.assertTrue(logged_in)
self.assertTrue(self.admin_user.is_authenticated())
self.document_count = 4 self.document_count = 4
self.document_type = DocumentType.objects.create( self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE label=TEST_DOCUMENT_TYPE
) )
@@ -46,9 +33,11 @@ class Issue46TestCase(TestCase):
) )
def tearDown(self): def tearDown(self):
for document_type in DocumentType.objects.all(): for document_type in DocumentType.on_organization.all():
document_type.delete() document_type.delete()
super(Issue46TestCase, self).tearDown()
def test_advanced_search_past_first_page(self): def test_advanced_search_past_first_page(self):
# Make sure all documents are returned by the search # Make sure all documents are returned by the search
model_list, result_set, elapsed_time = document_search.search( model_list, result_set, elapsed_time = document_search.search(

View File

@@ -2,28 +2,17 @@ from __future__ import unicode_literals
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from .api_views import ( from .api_views import APISearchView
APIRecentSearchListView, APIRecentSearchView, APISearchView
)
from .views import AdvancedSearchView, ResultsView, SearchView from .views import AdvancedSearchView, ResultsView, SearchView
urlpatterns = patterns( urlpatterns = patterns(
'dynamic_search.views', 'dynamic_search.views',
url(r'^$', SearchView.as_view(), name='search'), url(r'^$', SearchView.as_view(), name='search'),
url(r'^advanced/$', AdvancedSearchView.as_view(), name='search_advanced'), url(r'^advanced/$', AdvancedSearchView.as_view(), name='search_advanced'),
url(r'^again/$', 'search_again', name='search_again'),
url(r'^results/$', ResultsView.as_view(), name='results'), url(r'^results/$', ResultsView.as_view(), name='results'),
) )
api_urls = patterns( 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'), url(r'^search/$', APISearchView.as_view(), name='search-view'),
) )

View File

@@ -1,11 +1,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import logging import logging
import urlparse
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.generics import SimpleView, SingleObjectListView from common.generics import SimpleView, SingleObjectListView
@@ -82,12 +79,3 @@ class AdvancedSearchView(SearchView):
return AdvancedSearchForm( return AdvancedSearchForm(
data=self.request.GET, search_model=document_search data=self.request.GET, search_model=document_search
) )
def search_again(request):
query = urlparse.urlparse(
request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))
).query
return HttpResponseRedirect(
'{}?{}'.format(reverse('search:search_advanced'), query)
)

View File

@@ -1 +1 @@
from .base import OrganizationTestCase #NOQA from .base import OrganizationTestCase # NOQA

View File

@@ -24,7 +24,6 @@ class GenericAPITestCase(APITestCase):
username=self.admin_user.username, password=password username=self.admin_user.username, password=password
) )
def tearDown(self): def tearDown(self):
super(GenericAPITestCase, self).tearDown() super(GenericAPITestCase, self).tearDown()
self.admin_user.delete() self.admin_user.delete()