Initial commit to support per page search.

This commit is contained in:
Roberto Rosario
2016-10-27 02:48:40 -04:00
parent f66f815ba6
commit b8b2e0e929
14 changed files with 279 additions and 96 deletions

View File

@@ -49,15 +49,17 @@
</div> </div>
</div> </div>
</div> </div>
{% comment %}
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<div class="well center-block"> <div class="well center-block">
<form action="{% url 'search:results' %}" method="get" role="search"> <form action="{% url 'search:results' search_model='documents.Document' %}" method="get" role="search">
<div class="input-group"> <div class="input-group">
<input class="form-control" name="q" placeholder="{% trans 'Space separated terms' %}" type="text" value="{{ search_terms|default:'' }}"> <input class="form-control" name="q" placeholder="{% trans 'Space separated terms' %}" type="text" value="{{ search_terms|default:'' }}">
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default" type="submit">{% trans 'Search' %}</button> <button class="btn btn-default" type="submit">{% trans 'Search' %}</button>
<a class="btn btn-primary" href="{% url 'search:search_advanced' %}">{% trans 'Advanced' %}</a> <a class="btn btn-primary" href="{% url 'search:search_advanced' search_model='documents.Document' %}">{% trans 'Advanced' %}</a>
</span> </span>
</div> </div>
</form> </form>
@@ -67,4 +69,48 @@
</div> </div>
</div> </div>
</div> </div>
{% endcomment %}
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default center-block">
<div class="panel-heading">{% trans 'Search documents' %}</div>
<div class="panel-body">
<form action="{% url 'search:results' search_model='documents.Document' %}" method="get" role="search">
<div class="input-group">
<input class="form-control" name="q" placeholder="{% trans 'Space separated terms' %}" type="text" value="{{ search_terms|default:'' }}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">{% trans 'Search' %}</button>
<a class="btn btn-primary" href="{% url 'search:search_advanced' search_model='documents.Document' %}">{% trans 'Advanced' %}</a>
</span>
</div>
</form>
{% if search_terms %}
{% include 'appearance/generic_list_subtemplate.html' %}
{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default center-block">
<div class="panel-heading">{% trans 'Search pages' %}</div>
<div class="panel-body">
<form action="{% url 'search:results' search_model='documents.DocumentPageResult' %}" method="get" role="search">
<div class="input-group">
<input class="form-control" name="q" placeholder="{% trans 'Space separated terms' %}" type="text" value="{{ search_terms|default:'' }}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">{% trans 'Search' %}</button>
<a class="btn btn-primary" href="{% url 'search:search_advanced' search_model='documents.DocumentPageResult' %}">{% trans 'Advanced' %}</a>
</span>
</div>
</form>
{% if search_terms %}
{% include 'appearance/generic_list_subtemplate.html' %}
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@@ -68,13 +68,14 @@ from .permissions import (
permission_document_trash, permission_document_version_revert, permission_document_trash, permission_document_version_revert,
permission_document_view permission_document_view
) )
from .search import document_search, document_page_search
from .settings import setting_thumbnail_size from .settings import setting_thumbnail_size
from .statistics import ( from .statistics import (
new_documents_per_month, new_document_pages_per_month, new_documents_per_month, new_document_pages_per_month,
new_document_versions_per_month, total_document_per_month, new_document_versions_per_month, total_document_per_month,
total_document_page_per_month, total_document_version_per_month total_document_page_per_month, total_document_version_per_month
) )
from .widgets import document_thumbnail from .widgets import document_html_widget, document_thumbnail
class DocumentsApp(MayanAppConfig): class DocumentsApp(MayanAppConfig):
@@ -90,6 +91,7 @@ class DocumentsApp(MayanAppConfig):
DeletedDocument = self.get_model('DeletedDocument') DeletedDocument = self.get_model('DeletedDocument')
Document = self.get_model('Document') Document = self.get_model('Document')
DocumentPage = self.get_model('DocumentPage') DocumentPage = self.get_model('DocumentPage')
DocumentPageResult = self.get_model('DocumentPageResult')
DocumentType = self.get_model('DocumentType') DocumentType = self.get_model('DocumentType')
DocumentTypeFilename = self.get_model('DocumentTypeFilename') DocumentTypeFilename = self.get_model('DocumentTypeFilename')
DocumentVersion = self.get_model('DocumentVersion') DocumentVersion = self.get_model('DocumentVersion')
@@ -159,6 +161,36 @@ class DocumentsApp(MayanAppConfig):
source=Document, label=_('Type'), attribute='document_type' source=Document, label=_('Type'), attribute='document_type'
) )
SourceColumn(
source=DocumentPage, label=_('Thumbnail'),
func=lambda context: document_html_widget(
document_page=context['object'],
click_view='documents:document_display',
click_view_arguments=(context['object'].document.pk,),
gallery_name='documents:document_page_list',
preview_click_view='documents:document_page_view',
size=setting_thumbnail_size.value,
title=unicode(context['object']),
)
)
SourceColumn(
source=DocumentPageResult, label=_('Thumbnail'),
func=lambda context: document_html_widget(
document_page=context['object'],
click_view='documents:document_display',
click_view_arguments=(context['object'].document.pk,),
gallery_name='documents:document_page_list',
preview_click_view='documents:document_page_view',
size=setting_thumbnail_size.value,
title=unicode(context['object']),
)
)
SourceColumn(
source=DocumentPageResult, label=_('Type'),
attribute='document_version.document.document_type'
)
SourceColumn( SourceColumn(
source=DocumentType, label=_('Documents'), source=DocumentType, label=_('Documents'),
func=lambda context: context['object'].get_document_count( func=lambda context: context['object'].get_document_count(

View File

@@ -774,6 +774,14 @@ class DocumentPage(models.Model):
return '{}-{}'.format(self.document_version.uuid, self.pk) return '{}-{}'.format(self.document_version.uuid, self.pk)
class DocumentPageResult(DocumentPage):
class Meta:
ordering = ('document_version__document', 'page_number')
proxy = True
verbose_name = _('Document page')
verbose_name_plural = _('Document pages')
class NewVersionBlock(models.Model): class NewVersionBlock(models.Model):
document = models.ForeignKey(Document, verbose_name=_('Document')) document = models.ForeignKey(Document, verbose_name=_('Document'))

View File

@@ -7,7 +7,8 @@ from dynamic_search.classes import SearchModel
from .permissions import permission_document_view from .permissions import permission_document_view
document_search = SearchModel( document_search = SearchModel(
'documents', 'Document', permission=permission_document_view, app_label='documents', model_name='Document',
permission=permission_document_view,
serializer_string='documents.serializers.DocumentSerializer' serializer_string='documents.serializers.DocumentSerializer'
) )
@@ -19,3 +20,24 @@ document_search.add_model_field(
) )
document_search.add_model_field(field='label', label=_('Label')) document_search.add_model_field(field='label', label=_('Label'))
document_search.add_model_field(field='description', label=_('Description')) document_search.add_model_field(field='description', label=_('Description'))
document_page_search = SearchModel(
app_label='documents', model_name='DocumentPageResult',
permission=permission_document_view,
serializer_string='documents.serializers.DocumentPageSerializer'
)
document_page_search.add_model_field(
field='document_version__document__document_type__label',
label=_('Document type')
)
document_page_search.add_model_field(
field='document_version__document__versions__mimetype',
label=_('MIME type')
)
document_page_search.add_model_field(
field='document_version__document__label', label=_('Label')
)
document_page_search.add_model_field(
field='document_version__document__description', label=_('Description')
)

View File

@@ -83,7 +83,7 @@ class DocumentPagesCarouselWidget(forms.widgets.Widget):
def document_thumbnail(document, **kwargs): def document_thumbnail(document, **kwargs):
return document_html_widget( return document_html_widget(
document.latest_version.pages.first(), document_page=document.latest_version.pages.first(),
click_view='documents:document_display', **kwargs click_view='documents:document_display', **kwargs
) )
@@ -94,7 +94,7 @@ def document_link(document):
) )
def document_html_widget(document_page, click_view=None, click_view_arguments=None, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, gallery_name=None, fancybox_class='fancybox', image_class='lazy-load', title=None, size=setting_thumbnail_size.value, nolazyload=False, post_load_class=None, disable_title_link=False): def document_html_widget(document_page, click_view=None, click_view_arguments=None, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, gallery_name=None, fancybox_class='fancybox', image_class='lazy-load', title=None, size=setting_thumbnail_size.value, nolazyload=False, post_load_class=None, disable_title_link=False, preview_click_view=None):
result = [] result = []
alt_text = _('Document page image') alt_text = _('Document page image')
@@ -110,6 +110,7 @@ def document_html_widget(document_page, click_view=None, click_view_arguments=No
'zoom': zoom, 'zoom': zoom,
'rotation': rotation, 'rotation': rotation,
'size': size, 'size': size,
'page': document_page.page_number
} }
if gallery_name: if gallery_name:
@@ -132,7 +133,13 @@ def document_html_widget(document_page, click_view=None, click_view_arguments=No
if title: if title:
if not disable_title_link: if not disable_title_link:
if not preview_click_view:
preview_click_link = document.get_absolute_url() preview_click_link = document.get_absolute_url()
else:
preview_click_link = reverse(
preview_click_view, args=(document_page.pk,)
)
title_template = 'data-caption="<a class=\'a-caption\' href=\'{url}\'>{title}</a>"'.format( title_template = 'data-caption="<a class=\'a-caption\' href=\'{url}\'>{title}</a>"'.format(
title=strip_tags(title), url=preview_click_link or '#' title=strip_tags(title), url=preview_click_link or '#'
) )

View File

@@ -33,23 +33,55 @@ class SearchModel(object):
self.app_label = app_label self.app_label = app_label
self.model_name = model_name self.model_name = model_name
self.search_fields = [] self.search_fields = []
self.model = None # Lazy self._model = None # Lazy
self.label = label self._label = label
self.serializer_string = serializer_string self.serializer_string = serializer_string
self.permission = permission self.permission = permission
self.__class__.registry[self.get_full_name()] = self self.__class__.registry[self.get_full_name()] = self
def get_full_name(self): @property
return '%s.%s' % (self.app_label, self.model_name) def model(self):
if not self._model:
self._model = apps.get_model(self.app_label, self.model_name)
return self._model
@property
def label(self):
if not self._label:
self._label = self.model._meta.verbose_name
return self._label
def add_model_field(self, *args, **kwargs):
"""
Add a search field that directly belongs to the parent SearchModel
"""
search_field = SearchField(self, *args, **kwargs)
self.search_fields.append(search_field)
def assemble_query(self, terms, search_fields):
"""
Returns a query, that is a combination of Q objects. That combination
aims to search keywords within a model by testing the given search
fields.
"""
queries = []
for term in terms:
or_query = None
for field in search_fields:
q = Q(**{'%s__%s' % (field, 'icontains'): term})
if or_query is None:
or_query = q
else:
or_query = or_query | q
queries.append(or_query)
return queries
def get_all_search_fields(self): def get_all_search_fields(self):
return self.search_fields return self.search_fields
def get_search_field(self, full_name): def get_full_name(self):
try: return '%s.%s' % (self.app_label, self.model_name)
return self.search_fields[full_name]
except KeyError:
raise KeyError('No search field named: %s' % full_name)
def get_fields_simple_list(self): def get_fields_simple_list(self):
""" """
@@ -61,12 +93,11 @@ class SearchModel(object):
return result return result
def add_model_field(self, *args, **kwargs): def get_search_field(self, full_name):
""" try:
Add a search field that directly belongs to the parent SearchModel return self.search_fields[full_name]
""" except KeyError:
search_field = SearchField(self, *args, **kwargs) raise KeyError('No search field named: %s' % full_name)
self.search_fields.append(search_field)
def normalize_query(self, query_string, def normalize_query(self, query_string,
findterms=re.compile(r'"([^"]+)"|(\S+)').findall, findterms=re.compile(r'"([^"]+)"|(\S+)').findall,
@@ -88,11 +119,6 @@ class SearchModel(object):
result_set = set() result_set = set()
search_dict = {} search_dict = {}
if not self.model:
self.model = apps.get_model(self.app_label, self.model_name)
if not self.label:
self.label = self.model._meta.verbose_name
if 'q' in query_string: if 'q' in query_string:
# Simple search # Simple search
for search_field in self.get_all_search_fields(): for search_field in self.get_all_search_fields():
@@ -110,7 +136,6 @@ class SearchModel(object):
} }
) )
else: else:
for search_field in self.get_all_search_fields(): for search_field in self.get_all_search_fields():
if search_field.field in query_string and query_string[search_field.field]: if search_field.field in query_string and query_string[search_field.field]:
search_dict.setdefault(search_field.get_model(), { search_dict.setdefault(search_field.get_model(), {
@@ -183,6 +208,8 @@ class SearchModel(object):
datetime.datetime.now() - start_time datetime.datetime.now() - start_time
).split(':')[2] ).split(':')[2]
logger.debug('elapsed_time: %s', elapsed_time)
queryset = self.model.objects.filter( queryset = self.model.objects.filter(
pk__in=list(result_set)[:setting_limit.value] pk__in=list(result_set)[:setting_limit.value]
) )
@@ -201,25 +228,6 @@ class SearchModel(object):
return queryset, result_set, elapsed_time return queryset, result_set, elapsed_time
def assemble_query(self, terms, search_fields):
"""
Returns a query, that is a combination of Q objects. That combination
aims to search keywords within a model by testing the given search
fields.
"""
queries = []
for term in terms:
or_query = None
for field in search_fields:
q = Q(**{'%s__%s' % (field, 'icontains'): term})
if or_query is None:
or_query = q
else:
or_query = or_query | q
queries.append(or_query)
return queries
# SearchField classes # SearchField classes
class SearchField(object): class SearchField(object):

View File

@@ -5,6 +5,14 @@ from django.utils.translation import ugettext_lazy as _
class AdvancedSearchForm(forms.Form): class AdvancedSearchForm(forms.Form):
_match_all = forms.BooleanField(
label=_('Match all'), help_text=_(
'When checked, only results that match all fields will be '
'returned. When unchecked results that match at least one field '
'will be returned.'
), required=False
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.search_model = kwargs.pop('search_model') self.search_model = kwargs.pop('search_model')
super(AdvancedSearchForm, self).__init__(*args, **kwargs) super(AdvancedSearchForm, self).__init__(*args, **kwargs)

View File

@@ -4,8 +4,14 @@ from django.utils.translation import ugettext_lazy as _
from navigation import Link from navigation import Link
link_search = Link(text=_('Search'), view='search:search') link_search = Link(
link_search_advanced = Link( text=_('Search'), view='search:search', args='search_model.get_full_name'
text=_('Advanced search'), view='search:search_advanced' )
link_search_advanced = Link(
text=_('Advanced search'), view='search:search_advanced',
args='search_model.get_full_name'
)
link_search_again = Link(
text=_('Search again'), view='search:search_again',
args='search_model.get_full_name', keep_query=True
) )
link_search_again = Link(text=_('Search again'), view='search:search_again')

View File

@@ -6,9 +6,6 @@ from smart_settings import Namespace
namespace = Namespace(name='dynamic_search', label=_('Search')) namespace = Namespace(name='dynamic_search', label=_('Search'))
setting_show_object_type = namespace.add_setting(
global_name='SEARCH_SHOW_OBJECT_TYPE', default=False
)
setting_limit = namespace.add_setting( setting_limit = namespace.add_setting(
global_name='SEARCH_LIMIT', default=100, global_name='SEARCH_LIMIT', default=100,
help_text=_('Maximum amount search hits to fetch and display.') help_text=_('Maximum amount search hits to fetch and display.')

View File

@@ -5,14 +5,25 @@ from django.conf.urls import patterns, url
from .api_views import ( from .api_views import (
APIRecentSearchListView, APIRecentSearchView, APISearchView APIRecentSearchListView, APIRecentSearchView, APISearchView
) )
from .views import AdvancedSearchView, ResultsView, SearchView from .views import (
AdvancedSearchView, ResultsView, SearchAgainView, SearchView
)
urlpatterns = patterns( urlpatterns = patterns(
'dynamic_search.views', 'dynamic_search.views',
url(r'^$', SearchView.as_view(), name='search'), url(r'^(?P<search_model>[\.\w]+)/$', SearchView.as_view(), name='search'),
url(r'^advanced/$', AdvancedSearchView.as_view(), name='search_advanced'), url(
url(r'^again/$', 'search_again', name='search_again'), r'^advanced/(?P<search_model>[\.\w]+)/$', AdvancedSearchView.as_view(),
url(r'^results/$', ResultsView.as_view(), name='results'), name='search_advanced'
),
url(
r'^again/(?P<search_model>[\.\w]+)/$', SearchAgainView.as_view(),
name='search_again'
),
url(
r'^results/(?P<search_model>[\.\w]+)/$', ResultsView.as_view(),
name='results'
),
) )
api_urls = patterns( api_urls = patterns(

View File

@@ -7,12 +7,13 @@ from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import RedirectView
from common.generics import SimpleView, SingleObjectListView from common.generics import SimpleView, SingleObjectListView
from .classes import SearchModel from .classes import SearchModel
from .forms import SearchForm, AdvancedSearchForm from .forms import SearchForm, AdvancedSearchForm
from .settings import setting_limit, setting_show_object_type from .settings import setting_limit
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -21,40 +22,52 @@ class ResultsView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
context = { context = {
'hide_links': True, 'hide_links': True,
'search_model': self.search_model,
'search_results_limit': setting_limit.value, 'search_results_limit': setting_limit.value,
'title': _('Search results'), 'title': _('Search results for: %s') % self.search_model.label,
} }
if setting_show_object_type.value:
context.update({
'extra_columns': (
{
'name': _('Type'),
'attribute': lambda x: x._meta.verbose_name[0].upper() + x._meta.verbose_name[1:]
},
)
})
return context return context
def get_queryset(self): def get_queryset(self):
document_search = SearchModel.get('documents.Document') self.search_model = self.get_search_model()
if self.request.GET: if self.request.GET:
# Only do search if there is user input, otherwise just render # Only do search if there is user input, otherwise just render
# the template with the extra_context # the template with the extra_context
queryset, ids, timedelta = document_search.search( if self.request.GET.get('_match_all', 'off') == 'on':
self.request.GET, self.request.user global_and_search=True
else:
global_and_search=False
queryset, ids, timedelta = self.search_model.search(
query_string=self.request.GET, user=self.request.user,
global_and_search=global_and_search
) )
return queryset return queryset
def get_search_model(self):
return SearchModel.get(self.kwargs['search_model'])
class SearchView(SimpleView): class SearchView(SimpleView):
template_name = 'appearance/generic_form.html' template_name = 'appearance/generic_form.html'
title = _('Search') title = _('Search')
def get_extra_context(self):
self.search_model = self.get_search_model()
return {
'form': self.get_form(),
'form_action': reverse('search:results', args=(self.search_model.get_full_name(),)),
'search_model': self.search_model,
'submit_icon': 'fa fa-search',
'submit_label': _('Search'),
'submit_method': 'GET',
'title': _('Search for: %s') % self.search_model.label,
}
def get_form(self): def get_form(self):
if ('q' in self.request.GET) and self.request.GET['q'].strip(): if ('q' in self.request.GET) and self.request.GET['q'].strip():
query_string = self.request.GET['q'] query_string = self.request.GET['q']
@@ -62,32 +75,19 @@ class SearchView(SimpleView):
else: else:
return SearchForm() return SearchForm()
def get_extra_context(self): def get_search_model(self):
return { return SearchModel.get(self.kwargs['search_model'])
'form': self.get_form(),
'form_action': reverse('search:results'),
'submit_icon': 'fa fa-search',
'submit_label': _('Search'),
'submit_method': 'GET',
'title': self.title,
}
class AdvancedSearchView(SearchView): class AdvancedSearchView(SearchView):
title = _('Advanced search') title = _('Advanced search')
def get_form(self): def get_form(self):
document_search = SearchModel.get('documents.Document')
return AdvancedSearchForm( return AdvancedSearchForm(
data=self.request.GET, search_model=document_search data=self.request.GET, search_model=self.get_search_model()
) )
def search_again(request): class SearchAgainView(RedirectView):
query = urlparse.urlparse( pattern_name = 'search:search_advanced'
request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)) query_string = True
).query
return HttpResponseRedirect(
'{}?{}'.format(reverse('search:search_advanced'), query)
)

View File

@@ -15,7 +15,7 @@ from common import (
) )
from common.classes import ModelAttribute, Filter from common.classes import ModelAttribute, Filter
from common.widgets import two_state_template from common.widgets import two_state_template
from documents.search import document_search from documents.search import document_page_search, document_search
from documents.signals import post_document_type_change from documents.signals import post_document_type_change
from documents.permissions import permission_document_view from documents.permissions import permission_document_view
from mayan.celery import app from mayan.celery import app
@@ -57,6 +57,9 @@ class MetadataApp(MayanAppConfig):
Document = apps.get_model( Document = apps.get_model(
app_label='documents', model_name='Document' app_label='documents', model_name='Document'
) )
DocumentPageResult = apps.get_model(
app_label='documents', model_name='DocumentPageResult'
)
DocumentType = apps.get_model( DocumentType = apps.get_model(
app_label='documents', model_name='DocumentType' app_label='documents', model_name='DocumentType'
@@ -143,6 +146,13 @@ class MetadataApp(MayanAppConfig):
func=lambda context: get_metadata_string(context['object']) func=lambda context: get_metadata_string(context['object'])
) )
SourceColumn(
source=DocumentPageResult, label=_('Metadata'),
func=lambda context: get_metadata_string(
context['object'].document
)
)
SourceColumn( SourceColumn(
source=DocumentMetadata, label=_('Value'), source=DocumentMetadata, label=_('Value'),
attribute='value' attribute='value'
@@ -176,6 +186,15 @@ class MetadataApp(MayanAppConfig):
field='metadata__value', label=_('Metadata value') field='metadata__value', label=_('Metadata value')
) )
document_page_search.add_model_field(
field='document_version__document__metadata__metadata_type__name',
label=_('Metadata type')
)
document_page_search.add_model_field(
field='document_version__document__metadata__value',
label=_('Metadata value')
)
menu_facet.bind_links(links=(link_metadata_view,), sources=(Document,)) menu_facet.bind_links(links=(link_metadata_view,), sources=(Document,))
menu_multi_item.bind_links( menu_multi_item.bind_links(
links=( links=(

View File

@@ -15,7 +15,7 @@ from common import (
menu_tools menu_tools
) )
from common.settings import settings_db_sync_task_delay from common.settings import settings_db_sync_task_delay
from documents.search import document_search from documents.search import document_search, document_page_search
from documents.signals import post_version_upload from documents.signals import post_version_upload
from documents.widgets import document_link from documents.widgets import document_link
from mayan.celery import app from mayan.celery import app
@@ -115,6 +115,10 @@ class OCRApp(MayanAppConfig):
field='versions__pages__ocr_content__content', label=_('OCR') field='versions__pages__ocr_content__content', label=_('OCR')
) )
document_page_search.add_model_field(
field='ocr_content__content', label=_('OCR')
)
menu_facet.bind_links( menu_facet.bind_links(
links=(link_document_content,), sources=(Document,) links=(link_document_content,), sources=(Document,)
) )

View File

@@ -10,7 +10,7 @@ from common import (
MayanAppConfig, menu_facet, menu_secondary, menu_object, menu_main, MayanAppConfig, menu_facet, menu_secondary, menu_object, menu_main,
menu_multi_item, menu_sidebar menu_multi_item, menu_sidebar
) )
from documents.search import document_search from documents.search import document_page_search, document_search
from navigation import SourceColumn from navigation import SourceColumn
from rest_api.classes import APIEndPoint from rest_api.classes import APIEndPoint
@@ -39,6 +39,10 @@ class TagsApp(MayanAppConfig):
app_label='documents', model_name='Document' app_label='documents', model_name='Document'
) )
DocumentPageResult = apps.get_model(
app_label='documents', model_name='DocumentPageResult'
)
DocumentTag = self.get_model('DocumentTag') DocumentTag = self.get_model('DocumentTag')
Tag = self.get_model('Tag') Tag = self.get_model('Tag')
@@ -76,6 +80,14 @@ class TagsApp(MayanAppConfig):
) )
) )
SourceColumn(
source=DocumentPageResult, label=_('Tags'),
func=lambda context: widget_document_tags(
document=context['object'].document,
user=context['request'].user
)
)
SourceColumn( SourceColumn(
source=Tag, label=_('Preview'), source=Tag, label=_('Preview'),
func=lambda context: widget_single_tag(context['object']) func=lambda context: widget_single_tag(context['object'])
@@ -87,6 +99,9 @@ class TagsApp(MayanAppConfig):
) )
) )
document_page_search.add_model_field(
field='document_version__document__tags__label', label=_('Tags')
)
document_search.add_model_field(field='tags__label', label=_('Tags')) document_search.add_model_field(field='tags__label', label=_('Tags'))
menu_facet.bind_links( menu_facet.bind_links(