예제 #1
0
    def test_validation(self):
        # no data - not valid
        form = AdvancedSearchForm(data={})
        self.assertFalse(form.is_valid(),
            'advanced search form is not valid when no fields are specified')
        self.assertTrue(form.non_field_errors(),
            'a non-field error is displayed when no search terms are entered')
        # any one field - valid
        form = AdvancedSearchForm(data={'keywords': 'foo'})
        self.assertTrue(form.is_valid(),
            'advanced search form is valid when only keywords are specified')
        form = AdvancedSearchForm(data={'subject': 'bar'})
        self.assertTrue(form.is_valid(),
            'advanced search form is valid when only subject is specified')
        # FIXME: this could break due to caching when testing & running a dev site at the same time
        # adjust the test to clear the cache or use a separate cache for tests

        # grab a valid choice from the current options in the form:
        repo = form.fields['repository'].choices[0][0]  # id value of first option tuple
        form = AdvancedSearchForm(data={'repository': repo})
        self.assertTrue(form.is_valid(),
            'advanced search form is valid when only repository is specified')
예제 #2
0
def search(request):
    "Simple keyword search - runs exist full-text terms query on all terms included."

    form = AdvancedSearchForm(request.GET)
    query_error = False

    if form.is_valid():
        # form validation requires that at least one of subject & keyword is not empty
        subject = form.cleaned_data['subject']
        keywords = form.cleaned_data['keywords']
        repository = form.cleaned_data['repository']
        dao = form.cleaned_data['dao']
        page = request.GET.get('page', 1)

        # initialize findingaid queryset - filters will be added based on search terms
        findingaids = FindingAid.objects

        # local copy of return fields (fulltext-score may be added-- don't modify master copy!)
        return_fields = fa_listfields[:]

        try:
            if subject:
                # if a subject was specified, filter on subject
                findingaids = findingaids.filter(subject__fulltext_terms=subject).order_by('list_title')
                # order by list title when searching by subject only
                # (if keywords are specified, fulltext score ordering will override this)
            if repository:
                # if repository is set, filter finding aids by requested repository
                # expecting repository value to come in as exact phrase
                findingaids = findingaids.filter(repository__fulltext_terms=repository).order_by('list_title')
            if keywords:
                # if keywords were specified, do a fulltext search
                return_fields.append('fulltext_score')
                findingaids = findingaids.filter(
                    # first do a full-text search to restrict to relevant documents
                    fulltext_terms=keywords
                ).or_filter(
                    # do an OR search on boosted fields, so that relevance score
                    # will be calculated based on boosted field values
                    fulltext_terms=keywords,
                    boostfields__fulltext_terms=keywords,
                    highlight=False,    # disable highlighting in search results list
                ).order_by('-fulltext_score')

            # optional filter: restrict to items with digital archival objects
            if dao:
                findingaids = findingaids.filter(daos__exists=True)

                # if user does not have permission to view internal daos,
                # restrict to public daos only
                if not request.user.has_perm('fa_admin.can_view_internal_dao'):
                    findingaids = findingaids.filter(public_dao_count__gte=1)

                # NOTE: using >= filter to force a where clause because this works
                # when what seems to be the same filter on the xpath does not
                # (possibly an indexing issue?)

            findingaids = findingaids.only(*return_fields)
            result_subset, paginator = paginate_queryset(request, findingaids,
                                                         per_page=10, orphans=5)
            # when searching by subject only, use alpha pagination
            if subject and not keywords:
                page_labels = alpha_pagelabels(paginator, findingaids,
                                               label_attribute='list_title')
            else:
                page_labels = {}
            show_pages = pages_to_show(paginator, result_subset.number, page_labels)
            # query_times = findingaids.queryTime()

            # select non-empty form values for use in template
            search_params = dict((key, value) for key, value in form.cleaned_data.iteritems()
                                 if value)

            # set query and last page in session and set it to expire on browser close
            for key, val in search_params.iteritems():
                if key == 'dao':
                    search_params[key] = val
                else:
                    search_params[key] = val.encode('utf-8')
            last_search = search_params.copy()
            # pagination url params should NOT include page
            if 'page' in last_search:
                del(last_search['page'])
            url_params = urlencode(last_search)

            # store the current page (even if not specified in URL) for saved search
            last_search['page'] = page
            last_search = "%s?%s" % (reverse("fa:search"), urlencode(last_search))
            last_search = {"url": last_search, "txt": "Return to Search Results"}
            request.session["last_search"] = last_search
            request.session.set_expiry(0)  # set to expire when browser closes

            # ONLY keywords - not page or subject - should be included in
            # document url for search term highlighting
            if 'keywords' in search_params:
                highlight_params = urlencode({'keywords': search_params['keywords']})
            else:
                highlight_params = None

            response_context = {
                'findingaids': result_subset,
                'search_params': search_params,    # actual search terms, for display
                'url_params': url_params,   # url opts for pagination
                'highlight_params': highlight_params,  # keyword highlighting
                # 'querytime': [query_times],
                'show_pages': show_pages
            }
            if page_labels:     # if there are page labels to show, add to context
                # other page labels handled by show_pages, but first & last are special
                response_context['first_page_label'] = page_labels[1]
                response_context['last_page_label'] = page_labels[paginator.num_pages]

            return render(request, 'fa/search_results.html', response_context)

        except ExistDBException as e:
            # for an invalid full-text query (e.g., missing close quote), eXist
            # error reports 'Cannot parse' and 'Lexical error'
            # FIXME: could/should this be a custom eXist exception class?
            query_error = True
            if 'Cannot parse' in e.message():
                messages.error(request,
                               'Your search query could not be parsed.  ' +
                               'Please revise your search and try again.')
            else:
                # generic error message for any other exception
                messages.error(request, 'There was an error processing your search.')
    elif 'keywords' not in request.GET and 'subject' not in request.GET:
        # if form was not valid and nothing was submitted, re-initialize
        # don't tell the user that fields are required if they haven't submitted anything!
        form = AdvancedSearchForm()

    # if form is invalid (no search terms) or there was an error, display search form
    response = render(request, 'fa/search_form.html',
                      {'form': form, 'request': request})
    # if query could not be parsed, set a 'Bad Request' status code on the response
    if query_error:
        response.status_code = 400
    return response