From 675c65205291f813c24c85be5197650bc1e2623c Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Sun, 1 Apr 2018 18:51:48 +0000 Subject: [PATCH] Additional changes to speed up the IESG agenda docs page: Changed telechat_page_count() to accept a list of documents if that's already been generated, to avoid double work. Changed the reviewed_by_teams list to provide acronyms directly, to avoid group lookups during template rendering. Provided page counts directly to the template instead of repeated (costly) filtering through telechat_page_count, with new document lookups. Removed the telechat_page_count template filter, due to its cost. Tweaked some lookups in fill_in_document_table_attributes() . Added to the select_related() list for documents in IESG agenda_documents(). - Legacy-Id: 14988 --- ietf/doc/forms.py | 2 +- ietf/doc/utils_search.py | 7 ++--- ietf/iesg/templatetags/__init__.py | 0 ietf/iesg/templatetags/iesg_filters.py | 9 ------ ietf/iesg/utils.py | 12 +++++--- ietf/iesg/views.py | 9 ++++-- ietf/templates/doc/search/status_columns.html | 4 +-- ietf/templates/iesg/agenda_documents.html | 7 ++--- ietf/utils/decorators.py | 28 ++++++++++++++++++- 9 files changed, 50 insertions(+), 28 deletions(-) delete mode 100644 ietf/iesg/templatetags/__init__.py delete mode 100644 ietf/iesg/templatetags/iesg_filters.py diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index cb9476d2d..c007d6998 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -26,7 +26,7 @@ class TelechatForm(forms.Form): self.page_count = {} choice_display = {} for d in dates: - self.page_count[d] = telechat_page_count(d).for_approval + self.page_count[d] = telechat_page_count(date=d).for_approval choice_display[d] = '%s (%s pages)' % (d.strftime("%Y-%m-%d"),self.page_count[d]) if d-datetime.date.today() < datetime.timedelta(days=13): choice_display[d] += ' : WARNING - this may not leave enough time for directorate reviews!' diff --git a/ietf/doc/utils_search.py b/ietf/doc/utils_search.py index 043b21e90..fcfcd1827 100644 --- a/ietf/doc/utils_search.py +++ b/ietf/doc/utils_search.py @@ -43,7 +43,7 @@ def fill_in_document_table_attributes(docs): seen.add(e.doc_id) # on agenda in upcoming meetings - presentations = SessionPresentation.objects.filter(session__timeslotassignments__timeslot__time__gte=datetime.datetime.today()).distinct().select_related('session', 'document') + presentations = SessionPresentation.objects.filter(session__meeting__date__gte=datetime.date.today()-datetime.timedelta(days=7)).select_related('session', 'document') session_list = [ (p.document, p.session) for p in presentations ] sessions = dict( (d, []) for (d, s) in session_list ) for (d, s) in session_list: @@ -74,9 +74,8 @@ def fill_in_document_table_attributes(docs): d.expirable = expirable_draft(d) if d.get_state_slug() != "rfc": - d.milestones = sorted((m for m in d.groupmilestone_set.all() if m.state_id == "active"), key=lambda m: m.time) - - d.reviewed_by_teams = sorted(set(r.team for r in d.reviewrequest_set.filter(state__in=["requested","accepted","part-completed","completed"])), key=lambda g: g.acronym) + d.milestones = [ m for (t, m) in sorted(((m.time, m) for m in d.groupmilestone_set.all() if m.state_id == "active")) ] + d.reviewed_by_teams = sorted(set(r.team.acronym for r in d.reviewrequest_set.filter(state__in=["requested","accepted","part-completed","completed"]).distinct().select_related('team'))) d.sessions = sessions[d] if d in sessions else [] diff --git a/ietf/iesg/templatetags/__init__.py b/ietf/iesg/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/iesg/templatetags/iesg_filters.py b/ietf/iesg/templatetags/iesg_filters.py deleted file mode 100644 index d20f5779d..000000000 --- a/ietf/iesg/templatetags/iesg_filters.py +++ /dev/null @@ -1,9 +0,0 @@ -from django import template - -from ietf.iesg.utils import telechat_page_count as page_counter - -register = template.Library() - -@register.filter -def telechat_page_count(telechat): - return page_counter(telechat['date']).for_approval diff --git a/ietf/iesg/utils.py b/ietf/iesg/utils.py index ed952a6d4..f16508277 100644 --- a/ietf/iesg/utils.py +++ b/ietf/iesg/utils.py @@ -1,15 +1,19 @@ from collections import namedtuple +import debug # pyflakes:ignore + from ietf.doc.models import Document, TelechatDocEvent, STATUSCHANGE_RELATIONS from ietf.iesg.agenda import get_doc_section TelechatPageCount = namedtuple('TelechatPageCount',['for_approval','for_action','related']) -def telechat_page_count(date): +def telechat_page_count(date=None, docs=None): + if not date and not docs: + return TelechatPageCount(0, 0, 0) - candidates = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct() - - docs = [ doc for doc in candidates if doc.latest_event(TelechatDocEvent,type='scheduled_for_telechat').telechat_date==date ] + if not docs: + candidates = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct() + docs = [ doc for doc in candidates if doc.latest_event(TelechatDocEvent,type='scheduled_for_telechat').telechat_date==date ] for_action =[d for d in docs if get_doc_section(d).endswith('.3')] diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index bbb8d2d77..b946bc383 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -94,7 +94,7 @@ def agenda_json(request, date=None): res = { "telechat-date": str(data["date"]), "as-of": str(datetime.datetime.utcnow()), - "page-counts": telechat_page_count(get_agenda_date(date))._asdict(), + "page-counts": telechat_page_count(date=get_agenda_date(date))._asdict(), "sections": {}, } @@ -358,7 +358,10 @@ def agenda_documents(request): dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4]) docs_by_date = dict((d, []) for d in dates) - for doc in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).select_related("stream", "group").distinct(): + for doc in (Document.objects + .filter(docevent__telechatdocevent__telechat_date__in=dates) + .select_related('stream', 'group', 'intended_std_level') + .distinct()): d = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date if d in docs_by_date: docs_by_date[d].append(doc) @@ -379,9 +382,11 @@ def agenda_documents(request): # the search_result_row view to display them (which expects them) fill_in_document_table_attributes(docs_by_date[date]) fill_in_agenda_docs(date, sections, docs_by_date[date]) + pages = telechat_page_count(docs=docs_by_date[date]).for_approval telechats.append({ "date":date, + "pages":pages, "sections": sorted((num, section) for num, section in sections.iteritems() if "2" <= num < "5") }) diff --git a/ietf/templates/doc/search/status_columns.html b/ietf/templates/doc/search/status_columns.html index 7b0c8362c..7d23eef49 100644 --- a/ietf/templates/doc/search/status_columns.html +++ b/ietf/templates/doc/search/status_columns.html @@ -44,8 +44,8 @@ {% if doc.reviewed_by_teams %}
Reviews: - {% for g in doc.reviewed_by_teams %} - {{ g.acronym }}{% if not forloop.last %}, {% endif %} + {% for acronym in doc.reviewed_by_teams %} + {{ acronym }}{% if not forloop.last %}, {% endif %} {% endfor %} {% endif %} diff --git a/ietf/templates/iesg/agenda_documents.html b/ietf/templates/iesg/agenda_documents.html index 9169ef03b..88a858209 100644 --- a/ietf/templates/iesg/agenda_documents.html +++ b/ietf/templates/iesg/agenda_documents.html @@ -4,7 +4,6 @@ {% load ballot_icon %} {% load ietf_filters %} -{% load iesg_filters %} {% block pagehead %} @@ -39,10 +38,8 @@ {% for t in telechats %}

IESG telechat {{t.date}} - {% with t|telechat_page_count as pages %} - {{pages}} page{{pages|pluralize}} - {% endwith %} -

+ {{t.pages}} page{{t.pages|pluralize}} +

diff --git a/ietf/utils/decorators.py b/ietf/utils/decorators.py index ef7428664..6cce65a6e 100644 --- a/ietf/utils/decorators.py +++ b/ietf/utils/decorators.py @@ -2,7 +2,7 @@ import datetime -from decorator import decorator +from decorator import decorator, decorate from django.conf import settings from django.contrib.auth import login @@ -74,3 +74,29 @@ def require_api_key(f, request, *args, **kwargs): PersonApiKeyEvent.objects.create(person=person, type='apikey_login', key=key, desc="Logged in with key ID %s, endpoint %s" % (key.id, key.endpoint)) # Execute decorated function return f(request, *args, **kwargs) + + +def _memoize(func, self, *args, **kwargs): + ''''Memoize wrapper for instance methouds. Use @lru_cache for functions.''' + if kwargs: # frozenset is used to ensure hashability + key = args, frozenset(kwargs.items()) + else: + key = args + # instance method, set up cache if needed + if not hasattr(self, '_cache'): + self._cache = {} + if not func in self._cache: + self._cache[func] = {} + # + cache = self._cache[func] + if key not in cache: + cache[key] = func(self, *args, **kwargs) + return cache[key] +def memoize(func): + if not hasattr(func, '__class__'): + raise NotImplementedError("Use @lru_cache instead of memoize() for funcitons.") + # For methods, we want the cache on the object, not on the class, in order + # to not having to think about cache bloat and content becoming stale, so + # we cannot set up the cache here. + return decorate(func, _memoize) +