From a8fcbb38daa25247ca9a52032336a0fb788b23db Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 12 Jan 2024 19:49:48 -0400 Subject: [PATCH 1/2] refactor: Avoid module-level queries --- ietf/doc/views_search.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index 0117886f702..cb40de1ed62 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -416,27 +416,23 @@ def state_name(doc_type, state, shorten=True): return name.strip() -STATE_SLUGS = { - dt: {state_name(dt, ds, shorten=False): ds for ds in AD_WORKLOAD[dt]} # type: ignore - for dt in AD_WORKLOAD -} - - -def state_to_doc_type(state): - for dt in STATE_SLUGS: - if state in STATE_SLUGS[dt]: - return dt - return None - - -IESG_STATES = State.objects.filter(type="draft-iesg").values_list("name", flat=True) - - def date_to_bucket(date, now, num_buckets): return num_buckets - int((now.date() - date.date()).total_seconds() / 60 / 60 / 24) def ad_workload(request): + IESG_STATES = State.objects.filter(type="draft-iesg").values_list("name", flat=True) + STATE_SLUGS = { + dt: {state_name(dt, ds, shorten=False): ds for ds in AD_WORKLOAD[dt]} # type: ignore + for dt in AD_WORKLOAD + } + + def _state_to_doc_type(state): + for dt in STATE_SLUGS: + if state in STATE_SLUGS[dt]: + return dt + return None + # number of days (= buckets) to show in the graphs days = 120 if has_role(request.user, ["Area Director", "Secretariat"]) else 1 now = timezone.now() @@ -512,7 +508,7 @@ def ad_workload(request): to_state = "RFC" if dt == "rfc": - new_dt = state_to_doc_type(to_state) + new_dt = _state_to_doc_type(to_state) if new_dt is not None and new_dt != dt: dt = new_dt From 871924331e001c354063519cedd8e7c8536eed1e Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Mon, 15 Jan 2024 13:17:39 -0400 Subject: [PATCH 2/2] refactor: Avoid repeated State queries --- ietf/doc/views_search.py | 85 ++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index cb40de1ed62..4bde5b0dd82 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -39,7 +39,9 @@ import copy import operator +from collections import defaultdict from functools import reduce + from django import forms from django.conf import settings from django.core.cache import cache, caches @@ -360,32 +362,52 @@ def cached_redirect(cache_key, url): return cached_redirect(cache_key, urlreverse('ietf.doc.views_search.search') + search_args) -def state_name(doc_type, state, shorten=True): - name = "" - # Note doc_type rfc here is _not_ necessarily Document.type - for some callers - # it is a type derived from draft... The ad_workload view needs more rework so that - # the code isn't having to shadow-box so much. - if doc_type == "rfc": - if state == "rfc": - name = "RFC" - if name == "": - s = State.objects.filter(type="rfc",slug=state).first() - if s: - name = s.name - if name == "": - name = State.objects.get(type__in=["draft", "draft-iesg"], slug=state).name - elif doc_type == "draft" and state not in ["rfc", "expired"]: - name = State.objects.get(type__in=["draft", "draft-iesg"], slug=state).name - elif doc_type == "draft" and state == "rfc": - name = "RFC" - elif doc_type == "conflrev" and state.startswith("appr"): - name = "Approved" - else: - name = State.objects.get(type=doc_type, slug=state).name +def get_state_name_calculator(): + """Get a function to calculate state names + + Queries the database once when called, then uses cached look-up table for name calculations. + """ + # state_lut always has at least rfc, draft, and draft-iesg keys + state_lut = defaultdict(dict, **{"rfc": {}, "draft":{}, "draft-iesg": {}}) + for state in State.objects.filter(used=True): + state_lut[state.type_id][state.slug] = state.name + state_lut = dict(state_lut) # convert to dict to freeze key changes + + def _get_state_name(doc_type, state_slug): + """Get display name for a doc type / state slug + + Note doc_type rfc here is _not_ necessarily Document.type - for some callers + it is a type derived from draft... The ad_workload view needs more rework so that + the code isn't having to shadow-box so much. + """ + if doc_type == "rfc": + if state_slug == "rfc": + return "RFC" + elif state_slug in state_lut["rfc"]: + return state_lut["rfc"][state_slug] + else: + return state_lut["draft"].get( + state_slug, + state_lut["draft-iesg"][state_slug], + ) + elif doc_type == "draft" and state_slug not in ["rfc", "expired"]: + return state_lut["draft"].get( + state_slug, + state_lut["draft-iesg"][state_slug], + ) + elif doc_type == "draft" and state_slug == "rfc": + return "RFC" + elif doc_type == "conflrev" and state_slug.startswith("appr"): + return "Approved" + else: + return state_lut[doc_type][state_slug] + + # return the function as a closure + return _get_state_name - if not shorten: - return name +def shorten_state_name(name): + """Get abbreviated display name for a state""" for pat, sub in [ (r" \(Internal Steering Group/IAB Review\)", ""), ("Writeup", "Write-up"), @@ -412,7 +434,6 @@ def state_name(doc_type, state, shorten=True): (r"\(Message to Community, Selected by Secretariat\)", ""), ]: name = re.sub(pat, sub, name) - return name.strip() @@ -421,10 +442,11 @@ def date_to_bucket(date, now, num_buckets): def ad_workload(request): + _calculate_state_name = get_state_name_calculator() IESG_STATES = State.objects.filter(type="draft-iesg").values_list("name", flat=True) STATE_SLUGS = { - dt: {state_name(dt, ds, shorten=False): ds for ds in AD_WORKLOAD[dt]} # type: ignore - for dt in AD_WORKLOAD + dt: {_calculate_state_name(dt, ds): ds for ds in AD_WORKLOAD[dt]} # type: ignore + for dt in AD_WORKLOAD.keys() } def _state_to_doc_type(state): @@ -479,7 +501,7 @@ def _state_to_doc_type(state): to_state = None if dt == "charter": if e.type == "closed_ballot": - to_state = state_name(dt, state, shorten=False) + to_state = _calculate_state_name(dt, state) elif e.desc.endswith("has been replaced"): # stop tracking last = e.time @@ -545,7 +567,9 @@ def _state_to_doc_type(state): metadata = [ { "type": (dt, doc_type_name(dt)), - "states": [(state, state_name(dt, state)) for state in ad.buckets[dt]], + "states": [ + (state, shorten_state_name(_calculate_state_name(dt, state))) for state in ad.buckets[dt] + ], "ads": ads, } for dt in AD_WORKLOAD @@ -612,9 +636,10 @@ def sort_key(doc): ) ] + _calculate_state_name = get_state_name_calculator() for d in results: dt = d.type.slug - d.search_heading = state_name(dt, doc_state(d), shorten=False) + d.search_heading = _calculate_state_name(dt, doc_state(d)) if d.search_heading != "RFC": d.search_heading += f" {doc_type_name(dt)}"