Skip to content

Commit 9f1bd35

Browse files
committed
Rewrite search query building to support new schema, extend proxy support to search pages, fix a couple of bugs
- Legacy-Id: 2780
1 parent 19b572b commit 9f1bd35

9 files changed

Lines changed: 322 additions & 34 deletions

File tree

ietf/idrfc/idrfc_wrapper.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ def __init__(self, rfcindex, rfc=None, idinternal=None):
262262

263263
if not self._idinternal:
264264
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
265-
pub = rfcindex.latest_event(type="published_rfc")
266-
started = rfcindex.latest_event(type="started_iesg_process")
267-
if pub and started and pub.time < started.time:
265+
pub = rfcindex.rfc_published_date
266+
started = rfcindex.started_iesg_process if hasattr(rfcindex, 'started_iesg_process') else rfcindex.latest_event(type="started_iesg_process")
267+
if pub and started and pub < started.time.date():
268268
self._idinternal = rfcindex
269269
else:
270270
try:
@@ -282,7 +282,9 @@ def __init__(self, rfcindex, rfc=None, idinternal=None):
282282
if not self.maturity_level:
283283
self.maturity_level = "Unknown"
284284

285-
if settings.USE_DB_REDESIGN_PROXY_CLASSES and rfcindex.filename.startswith('rfc'):
285+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
286+
if not rfcindex.name.startswith('rfc'):
287+
self.draft_name = rfcindex.name
286288
return # we've already done the lookup while importing so skip the rest
287289

288290
ids = InternetDraft.objects.filter(rfc_number=self.rfc_number)
@@ -779,7 +781,7 @@ def position_to_string(position):
779781
return "No Record"
780782
p = None
781783
for k,v in positions.iteritems():
782-
if position.__dict__[k] > 0:
784+
if getattr(position, k) > 0:
783785
p = v
784786
if not p:
785787
p = "No Record"

ietf/idrfc/templatetags/ballot_icon.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040

4141
def get_user_adid(context):
4242
if 'user' in context and in_group(context['user'], "Area_Director"):
43+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
44+
# with the new design, we need the email address (FIXME:
45+
# we shouldn't go through the old classes though,
46+
# IESGLogin needs revamping)
47+
return context['user'].get_profile().person().email()[1]
4348
return context['user'].get_profile().iesg_login_id()
4449
else:
4550
return None

ietf/idrfc/views_ballot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
from django.template import RequestContext
1111
from django import forms
1212
from django.utils.html import strip_tags
13+
from django.conf import settings
1314

14-
from ietf import settings
1515
from ietf.utils.mail import send_mail_text, send_mail_preformatted
1616
from ietf.ietfauth.decorators import group_required
1717
from ietf.idtracker.templatetags.ietf_filters import in_group

ietf/idrfc/views_doc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
from django.utils.decorators import decorator_from_middleware
4343
from django.middleware.gzip import GZipMiddleware
4444
from django.core.urlresolvers import reverse as urlreverse
45-
46-
from ietf import settings
45+
from django.conf import settings
46+
4747
from ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, DocumentComment
4848
from ietf.idtracker.templatetags.ietf_filters import format_textarea, fill
4949
from ietf.idrfc import markup_txt

ietf/idrfc/views_edit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from django import forms
1212
from django.utils.html import strip_tags
1313
from django.db.models import Max
14+
from django.conf import settings
1415

15-
from ietf import settings
1616
from ietf.utils.mail import send_mail_text
1717
from ietf.ietfauth.decorators import group_required
1818
from ietf.idtracker.templatetags.ietf_filters import in_group

ietf/idrfc/views_search.py

Lines changed: 214 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
4242
from ietf.idrfc.idrfc_wrapper import IdWrapper,RfcWrapper,IdRfcWrapper
4343
from ietf.utils import normalize_draftname
44+
from django.conf import settings
4445

4546
class SearchForm(forms.Form):
4647
name = forms.CharField(required=False)
@@ -252,6 +253,206 @@ def search_query(query_original):
252253
meta['advanced'] = True
253254
return (results,meta)
254255

256+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
257+
from doc.models import *
258+
from person.models import *
259+
from group.models import *
260+
261+
class SearchForm(forms.Form):
262+
name = forms.CharField(required=False)
263+
rfcs = forms.BooleanField(required=False,initial=True)
264+
activeDrafts = forms.BooleanField(required=False,initial=True)
265+
oldDrafts = forms.BooleanField(required=False,initial=False)
266+
lucky = forms.BooleanField(required=False,initial=False)
267+
268+
by = forms.ChoiceField(choices=[(x,x) for x in ('author','group','area','ad','state')], required=False, initial='wg', label='Foobar')
269+
author = forms.CharField(required=False)
270+
group = forms.CharField(required=False)
271+
area = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), empty_label="any area", required=False)
272+
ad = forms.ChoiceField(choices=(), required=False)
273+
# FIXME: state needs a sort
274+
state = forms.ModelChoiceField(IesgDocStateName.objects.all(), empty_label="any state", required=False)
275+
subState = forms.ChoiceField(choices=(), required=False)
276+
277+
def __init__(self, *args, **kwargs):
278+
super(SearchForm, self).__init__(*args, **kwargs)
279+
responsible = Document.objects.values_list('ad', flat=True).distinct()
280+
active_ads = list(Email.objects.filter(role__name="ad",
281+
role__group__type="area",
282+
role__group__state="active")
283+
.select_related('person'))
284+
inactive_ads = list(Email.objects.filter(pk__in=responsible)
285+
.exclude(pk__in=[x.pk for x in active_ads])
286+
.select_related('person'))
287+
extract_last_name = lambda x: x.get_name().split(' ')[-1]
288+
active_ads.sort(key=extract_last_name)
289+
inactive_ads.sort(key=extract_last_name)
290+
291+
# FIXME: -99
292+
self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.get_name()) for ad in active_ads] + [('-99', '------------------')] + [(ad.pk, ad.get_name()) for ad in inactive_ads]
293+
self.fields['subState'].choices = [('', 'any substate'), ('0', 'no substate')] + [(n.slug, n.name) for n in DocInfoTagName.objects.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))]
294+
def clean_name(self):
295+
value = self.cleaned_data.get('name','')
296+
return normalize_draftname(value)
297+
def clean(self):
298+
q = self.cleaned_data
299+
# Reset query['by'] if needed
300+
for k in ('author','group','area','ad'):
301+
if (q['by'] == k) and not q[k]:
302+
q['by'] = None
303+
if (q['by'] == 'state') and not (q['state'] or q['subState']):
304+
q['by'] = None
305+
# Reset other fields
306+
for k in ('author','group','area','ad'):
307+
if q['by'] != k:
308+
self.data[k] = ""
309+
q[k] = ""
310+
if q['by'] != 'state':
311+
self.data['state'] = ""
312+
self.data['subState'] = ""
313+
q['state'] = ""
314+
q['subState'] = ""
315+
return q
316+
317+
def search_query(query_original):
318+
query = dict(query_original.items())
319+
drafts = query['activeDrafts'] or query['oldDrafts']
320+
if (not drafts) and (not query['rfcs']):
321+
return ([], {})
322+
323+
# Non-ASCII strings don't match anything; this check
324+
# is currently needed to avoid complaints from MySQL.
325+
# FIXME: this should be fixed if it's still a problem
326+
for k in ['name','author','group']:
327+
try:
328+
tmp = str(query.get(k, ''))
329+
except:
330+
query[k] = '*NOSUCH*'
331+
332+
# Start by search InternetDrafts
333+
idresults = []
334+
rfcresults = []
335+
MAX = 500
336+
337+
docs = InternetDraft.objects.all()
338+
339+
# name
340+
if query["name"]:
341+
docs = docs.filter(Q(docalias__name__icontains=query["name"]) |
342+
Q(title__icontains=query["name"])).distinct()
343+
344+
# rfc/active/old check buttons
345+
allowed = []
346+
disallowed = []
347+
348+
def add(allow, states):
349+
l = allowed if allow else disallowed
350+
l.extend(states)
351+
352+
add(query["rfcs"], ['rfc'])
353+
add(query["activeDrafts"], ['active'])
354+
add(query["oldDrafts"], ['repl', 'expired', 'auth-rm', 'ietf-rm'])
355+
356+
docs = docs.filter(state__in=allowed).exclude(state__in=disallowed)
357+
358+
# radio choices
359+
by = query["by"]
360+
if by == "author":
361+
# FIXME: this is full name, not last name as hinted in the HTML
362+
docs = docs.filter(authors__person__name__icontains=query["author"])
363+
elif by == "group":
364+
docs = docs.filter(group__acronym=query["group"])
365+
elif by == "area":
366+
docs = docs.filter(Q(group__parent=query["area"]) |
367+
Q(ad__role__name="ad",
368+
ad__role__group=query["area"]))
369+
elif by == "ad":
370+
docs = docs.filter(ad=query["ad"])
371+
elif by == "state":
372+
if query["state"]:
373+
docs = docs.filter(iesg_state=query["state"])
374+
if query["subState"]:
375+
docs = docs.filter(tags=query["subState"])
376+
377+
# evaluate and fill in values with aggregate queries to avoid
378+
# too many individual queries
379+
results = list(docs.select_related("state", "iesg_state", "ad", "ad__person", "std_level", "intended_std_level", "group")[:MAX])
380+
381+
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[r.pk for r in results]).values_list("document_id", "name"))
382+
# canonical name
383+
for r in results:
384+
if r.pk in rfc_aliases:
385+
r.canonical_name = rfc_aliases[r.pk]
386+
else:
387+
r.canonical_name = r.name
388+
389+
result_map = dict((r.pk, r) for r in results)
390+
391+
# events
392+
event_types = ("published_rfc",
393+
"changed_ballot_position",
394+
"started_iesg_process",
395+
"new_revision")
396+
for d in rfc_aliases.keys():
397+
for e in event_types:
398+
setattr(result_map[d], e, None)
399+
400+
for e in Event.objects.filter(doc__in=rfc_aliases.keys(), type__in=event_types).order_by('-time'):
401+
r = result_map[e.doc_id]
402+
if not getattr(r, e.type):
403+
# sets e.g. r.published_date = e for use in proxy wrapper
404+
setattr(r, e.type, e)
405+
406+
# obsoleted/updated by
407+
for d in rfc_aliases:
408+
r = result_map[d]
409+
r.obsoleted_by_list = []
410+
r.updated_by_list = []
411+
412+
xed_by = RelatedDocument.objects.filter(doc_alias__name__in=rfc_aliases.values(), relationship__in=("obs", "updates")).select_related('doc_alias__document_id')
413+
rel_rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[rel.document_id for rel in xed_by]).values_list('document_id', 'name'))
414+
for rel in xed_by:
415+
r = result_map[rel.doc_alias.document_id]
416+
if rel.relationship_id == "obs":
417+
attr = "obsoleted_by_list"
418+
else:
419+
attr = "updated_by_list"
420+
421+
getattr(r, attr).append(int(rel_rfc_aliases[rel.document_id][3:]))
422+
423+
424+
# sort
425+
def sort_key(d):
426+
if d.canonical_name.startswith('rfc'):
427+
return (2, "%06d" % int(d.canonical_name[3:]))
428+
elif d.state_id == "active":
429+
return (1, d.canonical_name)
430+
else:
431+
return (3, d.canonical_name)
432+
433+
results.sort(key=sort_key)
434+
435+
meta = {}
436+
if len(docs) == MAX:
437+
meta['max'] = MAX
438+
if query['by']:
439+
meta['advanced'] = True
440+
441+
# finally wrap in old wrappers
442+
443+
wrapped_results = []
444+
for r in results:
445+
draft = None
446+
rfc = None
447+
if not r.name.startswith('rfc'):
448+
draft = IdWrapper(r)
449+
if r.name.startswith('rfc') or r.pk in rfc_aliases:
450+
rfc = RfcWrapper(r)
451+
wrapped_results.append(IdRfcWrapper(draft, rfc))
452+
453+
return (wrapped_results, meta)
454+
455+
255456
def search_results(request):
256457
if len(request.REQUEST.items()) == 0:
257458
return search_main(request)
@@ -288,6 +489,8 @@ def by_ad(request, name):
288489
break
289490
if not ad_id:
290491
raise Http404
492+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
493+
ad_id = i.person.email()[1]
291494
form = SearchForm({'by':'ad','ad':ad_id,
292495
'rfcs':'on', 'activeDrafts':'on', 'oldDrafts':'on'})
293496
if not form.is_valid():
@@ -298,11 +501,17 @@ def by_ad(request, name):
298501

299502
@cache_page(15*60) # 15 minutes
300503
def all(request):
301-
active = InternetDraft.objects.all().filter(status=1).order_by("filename").values('filename')
302-
rfc1 = InternetDraft.objects.all().filter(status=3).order_by("filename").values('filename','rfc_number')
303-
rfc_numbers1 = InternetDraft.objects.all().filter(status=3).values_list('rfc_number', flat=True)
304-
rfc2 = RfcIndex.objects.all().exclude(rfc_number__in=rfc_numbers1).order_by('rfc_number').values('rfc_number','draft')
305-
dead = InternetDraft.objects.all().exclude(status__in=[1,3]).order_by("filename").select_related('status__status')
504+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
505+
active = (dict(filename=n) for n in InternetDraft.objects.filter(state="active").order_by("name").values_list('name', flat=True))
506+
rfc1 = (dict(filename=d, rfc_number=int(n[3:])) for d, n in DocAlias.objects.filter(document__state="rfc", name__startswith="rfc").exclude(document__name__startswith="rfc").order_by("document__name").values_list('document__name','name').distinct())
507+
rfc2 = (dict(rfc_number=r, draft=None) for r in sorted(int(n[3:]) for n in Document.objects.filter(name__startswith="rfc").values_list('name', flat=True)))
508+
dead = InternetDraft.objects.exclude(state__in=("active", "rfc")).select_related("state").order_by("name")
509+
else:
510+
active = InternetDraft.objects.all().filter(status=1).order_by("filename").values('filename')
511+
rfc1 = InternetDraft.objects.all().filter(status=3).order_by("filename").values('filename','rfc_number')
512+
rfc_numbers1 = InternetDraft.objects.all().filter(status=3).values_list('rfc_number', flat=True)
513+
rfc2 = RfcIndex.objects.all().exclude(rfc_number__in=rfc_numbers1).order_by('rfc_number').values('rfc_number','draft')
514+
dead = InternetDraft.objects.all().exclude(status__in=[1,3]).order_by("filename").select_related('status__status')
306515
return render_to_response('idrfc/all.html', {'active':active, 'rfc1':rfc1, 'rfc2':rfc2, 'dead':dead}, context_instance=RequestContext(request))
307516

308517
@cache_page(15*60) # 15 minutes

0 commit comments

Comments
 (0)