Summary: Add a view for /doc/foo-bar-baz that tries to redirect

helpfully instead of just throwing a 404 in case in an inexact match.
Right now, it knows how to do prefix matches and handles revisions and
extensions. If it can't find a unique match, it redirects to the
search page.

Branch ready for merge.
 - Legacy-Id: 10158
This commit is contained in:
Ole Laursen 2015-10-09 14:20:27 +00:00
parent cfefc0ae58
commit 4ac99a9dc4
3 changed files with 100 additions and 22 deletions

View file

@ -10,6 +10,7 @@ else:
from pyquery import PyQuery
from tempfile import NamedTemporaryFile
from Cookie import SimpleCookie
import urlparse
from django.core.urlresolvers import reverse as urlreverse
from django.conf import settings
@ -28,7 +29,7 @@ from ietf.utils.test_data import make_test_data
from ietf.utils.test_utils import login_testing_unauthorized
from ietf.utils.test_utils import TestCase
class SearchTestCase(TestCase):
class SearchTests(TestCase):
def test_search(self):
draft = make_test_data()
@ -104,6 +105,46 @@ class SearchTestCase(TestCase):
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
def test_search_for_name(self):
draft = make_test_data()
save_document_in_history(draft)
prev_rev = draft.rev
draft.rev = "%02d" % (int(prev_rev) + 1)
draft.save()
# exact match
r = self.client.get(urlreverse("doc_search_for_name", kwargs=dict(name=draft.name)))
self.assertEqual(r.status_code, 302)
self.assertEqual(urlparse.urlparse(r["Location"]).path, urlreverse("doc_view", kwargs=dict(name=draft.name)))
# prefix match
r = self.client.get(urlreverse("doc_search_for_name", kwargs=dict(name="-".join(draft.name.split("-")[:-1]))))
self.assertEqual(r.status_code, 302)
self.assertEqual(urlparse.urlparse(r["Location"]).path, urlreverse("doc_view", kwargs=dict(name=draft.name)))
# match with revision
r = self.client.get(urlreverse("doc_search_for_name", kwargs=dict(name=draft.name + "-" + prev_rev)))
self.assertEqual(r.status_code, 302)
self.assertEqual(urlparse.urlparse(r["Location"]).path, urlreverse("doc_view", kwargs=dict(name=draft.name, rev=prev_rev)))
# match with non-existing revision
r = self.client.get(urlreverse("doc_search_for_name", kwargs=dict(name=draft.name + "-09")))
self.assertEqual(r.status_code, 302)
self.assertEqual(urlparse.urlparse(r["Location"]).path, urlreverse("doc_view", kwargs=dict(name=draft.name)))
# match with revision and extension
r = self.client.get(urlreverse("doc_search_for_name", kwargs=dict(name=draft.name + "-" + prev_rev + ".txt")))
self.assertEqual(r.status_code, 302)
self.assertEqual(urlparse.urlparse(r["Location"]).path, urlreverse("doc_view", kwargs=dict(name=draft.name, rev=prev_rev)))
# no match
r = self.client.get(urlreverse("doc_search_for_name", kwargs=dict(name="draft-ietf-doesnotexist-42")))
self.assertEqual(r.status_code, 302)
parsed = urlparse.urlparse(r["Location"])
self.assertEqual(parsed.path, urlreverse("doc_search"))
self.assertEqual(urlparse.parse_qs(parsed.query)["name"][0], "draft-ietf-doesnotexist-42")
def test_frontpage(self):
make_test_data()
r = self.client.get("/")

View file

@ -39,6 +39,7 @@ from ietf.doc import views_doc
urlpatterns = patterns('',
(r'^/?$', views_search.search),
url(r'^(?P<name>[A-Za-z0-9\._\+\-]+)$', views_search.search_for_name, name="doc_search_for_name"),
url(r'^search/$', views_search.search, name="doc_search"),
url(r'^in-last-call/$', views_search.drafts_in_last_call, name="drafts_in_last_call"),
url(r'^ad/(?P<name>[A-Za-z0-9.-]+)/$', views_search.docs_for_ad, name="docs_for_ad"),

View file

@ -30,20 +30,20 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import datetime
import datetime, re
from django import forms
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import render_to_response
from django.core.urlresolvers import reverse as urlreverse
from django.shortcuts import render
from django.db.models import Q
from django.template import RequestContext
from django.http import Http404, HttpResponseBadRequest, HttpResponse
from django.http import Http404, HttpResponseBadRequest, HttpResponse, HttpResponseRedirect
import debug # pyflakes:ignore
from ietf.community.models import CommunityList
from ietf.doc.models import ( Document, DocAlias, State, RelatedDocument, DocEvent,
LastCallDocEvent, TelechatDocEvent, IESG_SUBSTATE_TAGS )
from ietf.doc.models import ( Document, DocHistory, DocAlias, State, RelatedDocument,
DocEvent, LastCallDocEvent, TelechatDocEvent, IESG_SUBSTATE_TAGS )
from ietf.doc.expire import expirable_draft
from ietf.doc.fields import select2_id_doc_name_json
from ietf.group.models import Group
@ -384,13 +384,50 @@ def search(request):
doc_is_tracked = get_doc_is_tracked(request, results)
return render_to_response('doc/search/search.html',
{'form':form, 'docs':results, 'doc_is_tracked':doc_is_tracked, 'meta':meta, },
context_instance=RequestContext(request))
return render(request, 'doc/search/search.html', {
'form':form, 'docs':results, 'doc_is_tracked':doc_is_tracked, 'meta':meta, },
)
def frontpage(request):
form = SearchForm()
return render_to_response('doc/frontpage.html', {'form':form}, context_instance=RequestContext(request))
return render(request, 'doc/frontpage.html', {'form':form})
def search_for_name(request, name):
def find_unique(n):
exact = DocAlias.objects.filter(name=n).first()
if exact:
return exact.name
aliases = DocAlias.objects.filter(name__startswith=n)[:2]
if len(aliases) == 1:
return aliases[0].name
return None
n = name
# chop away extension
extension_split = re.search("^(.+)\.(txt|ps|pdf)$", n)
if extension_split:
n = extension_split.group(1)
redirect_to = find_unique(name)
if redirect_to:
return HttpResponseRedirect(urlreverse("doc_view", kwargs={ "name": redirect_to }))
else:
# check for embedded rev - this may be ambigious, so don't
# chop it off if we don't find a match
rev_split = re.search("^(.+)-([0-9]{2})$", n)
if rev_split:
redirect_to = find_unique(rev_split.group(1))
if redirect_to:
rev = rev_split.group(2)
# check if we can redirect directly to the rev
if DocHistory.objects.filter(doc__docalias__name=redirect_to, rev=rev).exists():
return HttpResponseRedirect(urlreverse("doc_view", kwargs={ "name": redirect_to, "rev": rev }))
else:
return HttpResponseRedirect(urlreverse("doc_view", kwargs={ "name": redirect_to }))
return HttpResponseRedirect(urlreverse("doc_search") + "?name=%s&rfcs=on&activedrafts=on" % n)
def ad_dashboard_group(doc):
@ -500,18 +537,18 @@ def docs_for_ad(request, name):
for d in results:
d.search_heading = ad_dashboard_group(d)
#
return render_to_response('doc/drafts_for_ad.html',
{ 'form':form, 'docs':results, 'meta':meta, 'ad_name': ad.plain_name() },
context_instance=RequestContext(request))
return render(request, 'doc/drafts_for_ad.html', {
'form':form, 'docs':results, 'meta':meta, 'ad_name': ad.plain_name()
})
def drafts_in_last_call(request):
lc_state = State.objects.get(type="draft-iesg", slug="lc").pk
form = SearchForm({'by':'state','state': lc_state, 'rfcs':'on', 'activedrafts':'on'})
results, meta = retrieve_search_results(form)
return render_to_response('doc/drafts_in_last_call.html',
{ 'form':form, 'docs':results, 'meta':meta },
context_instance=RequestContext(request))
return render(request, 'doc/drafts_in_last_call.html', {
'form':form, 'docs':results, 'meta':meta
})
def drafts_in_iesg_process(request, last_call_only=None):
if last_call_only:
@ -535,11 +572,11 @@ def drafts_in_iesg_process(request, last_call_only=None):
grouped_docs.append((s, docs))
return render_to_response('doc/drafts_in_iesg_process.html', {
return render(request, 'doc/drafts_in_iesg_process.html', {
"grouped_docs": grouped_docs,
"title": title,
"last_call_only": last_call_only,
}, context_instance=RequestContext(request))
})
def index_all_drafts(request):
# try to be efficient since this view returns a lot of data
@ -582,13 +619,12 @@ def index_all_drafts(request):
len(names),
"<br>".join(names)
))
return render_to_response('doc/index_all_drafts.html', { "categories": categories },
context_instance=RequestContext(request))
return render(request, 'doc/index_all_drafts.html', { "categories": categories })
def index_active_drafts(request):
groups = active_drafts_index_by_group()
return render_to_response("doc/index_active_drafts.html", { 'groups': groups }, context_instance=RequestContext(request))
return render(request, "doc/index_active_drafts.html", { 'groups': groups })
def ajax_select2_search_docs(request, model_name, doc_type):
if model_name == "docalias":