Skip to content

Commit aec77c2

Browse files
committed
Rewrote Document.href() to not do database queries when possible, as that has a big performance impact. Fixed a number of tests which relied on href() not doing the right thing for simplified test data. Added caching of canonical_name(), which can be quite heavy. Additional refactoring in a number of places, to use better test data and avoid test failures for good code :-)
- Legacy-Id: 12226
1 parent a1934d1 commit aec77c2

10 files changed

Lines changed: 84 additions & 68 deletions

File tree

ietf/doc/models.py

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def get_file_path(self):
9292
else:
9393
return settings.DOCUMENT_PATH_PATTERN.format(doc=self)
9494

95-
def href(self):
95+
def href(self, meeting=None):
9696
"""
9797
Returns an url to the document text. This differs from .get_absolute_url(),
9898
which returns an url to the datatracker page for the document.
@@ -105,31 +105,44 @@ def href(self):
105105
# better named, or regularize the filename based on self.name
106106
if not hasattr(self, '_cached_href'):
107107
validator = URLValidator()
108-
try:
109-
validator(self.external_url)
110-
return self.external_url
111-
except ValidationError:
112-
pass
113-
114-
meeting_related = self.meeting_related()
108+
if self.external_url and self.external_url.split(':')[0] in validator.schemes:
109+
try:
110+
validator(self.external_url)
111+
return self.external_url
112+
except ValidationError:
113+
pass
115114

116-
settings_var = settings.DOC_HREFS
117-
if meeting_related:
118-
settings_var = settings.MEETING_DOC_HREFS
119115

120-
try:
121-
format = settings_var[self.type_id]
122-
except KeyError:
116+
if self.type_id in settings.DOC_HREFS and self.type_id in settings.MEETING_DOC_HREFS:
117+
if self.meeting_related():
118+
self.is_meeting_related = True
119+
format = settings.MEETING_DOC_HREFS[self.type_id]
120+
else:
121+
self.is_meeting_related = False
122+
format = settings.DOC_HREFS[self.type_id]
123+
elif self.type_id in settings.DOC_HREFS:
124+
self.is_meeting_related = False
125+
format = settings.DOC_HREFS[self.type_id]
126+
elif self.type_id in settings.MEETING_DOC_HREFS:
127+
self.is_meeting_related = True
128+
format = settings.MEETING_DOC_HREFS[self.type_id]
129+
else:
123130
if len(self.external_url):
124131
return self.external_url
125-
return None
126-
127-
meeting = None
128-
if meeting_related:
129-
doc = self.doc if isinstance(self, DocHistory) else self
130-
meeting = doc.session_set.first().meeting
132+
else:
133+
return None
134+
135+
if self.is_meeting_related:
136+
if not meeting:
137+
# we need to do this because DocHistory items don't have
138+
# any session_set entry:
139+
doc = self.doc if isinstance(self, DocHistory) else self
140+
meeting = doc.session_set.first().meeting
141+
info = dict(doc=self, meeting=meeting)
142+
else:
143+
info = dict(doc=self)
131144

132-
self._cached_href = format.format(doc=self,meeting=meeting)
145+
self._cached_href = format.format(**info)
133146
return self._cached_href
134147

135148
def set_state(self, state):
@@ -424,20 +437,21 @@ def latest_event(self, *args, **filter_args):
424437
return e[0] if e else None
425438

426439
def canonical_name(self):
427-
from ietf.doc.utils_charter import charter_name_for_group # Imported locally to avoid circular imports
428-
if hasattr(self, '_canonical_name'):
429-
return self._canonical_name
430-
name = self.name
431-
if self.type_id == "draft" and self.get_state_slug() == "rfc":
432-
a = self.docalias_set.filter(name__startswith="rfc")
433-
if a:
434-
name = a[0].name
435-
elif self.type_id == "charter":
436-
try:
437-
name = charter_name_for_group(self.chartered_group)
438-
except Group.DoesNotExist:
439-
pass
440-
return name
440+
if not hasattr(self, '_canonical_name'):
441+
name = self.name
442+
if self.type_id == "draft" and self.get_state_slug() == "rfc":
443+
a = self.docalias_set.filter(name__startswith="rfc")
444+
if a:
445+
name = a[0].name
446+
elif self.type_id == "charter":
447+
from ietf.doc.utils_charter import charter_name_for_group # Imported locally to avoid circular imports
448+
try:
449+
name = charter_name_for_group(self.chartered_group)
450+
except Group.DoesNotExist:
451+
pass
452+
self._canonical_name = name
453+
return self._canonical_name
454+
441455

442456
def canonical_docalias(self):
443457
return self.docalias_set.get(name=self.name)

ietf/doc/tests.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from ietf.group.factories import GroupFactory
2828
from ietf.meeting.models import Meeting, Session, SessionPresentation
2929
from ietf.meeting.factories import SessionFactory
30+
from ietf.meeting.test_data import make_meeting_test_data
3031
from ietf.name.models import SessionStatusName
3132
from ietf.person.models import Person
3233
from ietf.person.factories import PersonFactory
@@ -117,6 +118,7 @@ def test_search(self):
117118

118119
def test_search_for_name(self):
119120
draft = make_test_data()
121+
make_meeting_test_data()
120122
draft.save_with_history([DocEvent.objects.create(doc=draft, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
121123

122124
prev_rev = draft.rev
@@ -559,6 +561,7 @@ def test_document_draft(self):
559561

560562
def test_document_primary_and_history_views(self):
561563
make_test_data()
564+
make_meeting_test_data()
562565

563566
# Ensure primary views of both current and historic versions of documents works
564567
for docname in ["draft-imaginary-independent-submission",
@@ -567,7 +570,7 @@ def test_document_primary_and_history_views(self):
567570
"charter-ietf-mars",
568571
"agenda-42-mars",
569572
"minutes-42-mars",
570-
"slides-42-mars-1",
573+
"slides-42-mars-1-active",
571574
# TODO: add
572575
#"bluesheets-42-mars-1",
573576
#"recording-42-mars-1-00",

ietf/liaisons/views.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from django.http import HttpResponse, HttpResponseForbidden
1010
from django.shortcuts import render, get_object_or_404, redirect
1111

12+
import debug # pyflakes:ignore
13+
1214
from ietf.doc.models import Document
1315
from ietf.ietfauth.utils import role_required, has_role
1416
from ietf.group.models import Group, Role

ietf/meeting/models.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,9 +1020,12 @@ def agenda(self):
10201020
return self._agenda_cache
10211021

10221022
def minutes(self):
1023-
return self.get_material("minutes", only_one=True)
1023+
if not hasattr(self, '_cached_minutes'):
1024+
self._cached_minutes = self.get_material("minutes", only_one=True)
1025+
return self._cached_minutes
10241026

10251027
def recordings(self):
1028+
10261029
return list(self.get_material("recording", only_one=False))
10271030

10281031
def bluesheets(self):

ietf/meeting/test_data.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import datetime
22

3-
from ietf.doc.models import Document, State
3+
from ietf.doc.factories import DocumentFactory
44
from ietf.group.models import Group
55
from ietf.meeting.models import (Meeting, Room, TimeSlot, Session, Schedule, SchedTimeSessAssignment,
66
ResourceAssociation, SessionPresentation, UrlResource)
@@ -96,22 +96,22 @@ def make_meeting_test_data(meeting=None):
9696
meeting.unofficial_schedule = unofficial_schedule
9797

9898

99-
doc = Document.objects.create(name='agenda-mars-ietf-42', type_id='agenda', title="Agenda", external_url="agenda-mars.txt",group=mars,rev='00')
100-
doc.set_state(State.objects.get(type=doc.type_id, slug="active"))
101-
mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev))
99+
doc = DocumentFactory.create(name='agenda-42-mars', type_id='agenda', title="Agenda",
100+
external_url="agenda-42-mars.txt", group=mars, rev='00', states=[('draft','active')])
101+
mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev)) #
102102

103-
doc = Document.objects.create(name='minutes-mars-ietf-42', type_id='minutes', title="Minutes", external_url="minutes-mars.txt",group=mars,rev='00')
104-
doc.set_state(State.objects.get(type=doc.type_id, slug="active"))
103+
doc = DocumentFactory.create(name='minutes-42-mars', type_id='minutes', title="Minutes",
104+
external_url="minutes-42-mars.txt", group=mars, rev='00', states=[('minutes','active')])
105105
mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev))
106106

107-
doc = Document.objects.create(name='slides-mars-ietf-42', type_id='slides', title="Slideshow", external_url="slides-mars.txt",group=mars,rev='00')
108-
doc.set_state(State.objects.get(type=doc.type_id, slug="active"))
109-
doc.set_state(State.objects.get(type='reuse_policy',slug='single'))
107+
doc = DocumentFactory.create(name='slides-42-mars-1-active', type_id='slides', title="Slideshow",
108+
external_url="slides-42-mars.txt", group=mars, rev='00',
109+
states=[('slides','active'), ('reuse_policy', 'single')])
110110
mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev))
111111

112-
doc = Document.objects.create(name='slides-mars-ietf-42-deleted', type_id='slides', title="Bad Slideshow", external_url="slides-mars-deleted.txt",group=mars,rev='00')
113-
doc.set_state(State.objects.get(type=doc.type_id, slug="deleted"))
114-
doc.set_state(State.objects.get(type='reuse_policy',slug='single'))
112+
doc = DocumentFactory.create(name='slides-42-mars-2-deleted', type_id='slides',
113+
title="Bad Slideshow", external_url="slides-42-mars-2-deleted.txt", group=mars, rev='00',
114+
states=[('slides','deleted'), ('reuse_policy', 'single')])
115115
mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev))
116116

117117
# Future Interim Meetings

ietf/meeting/views.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,10 @@ def materials(request, num=None):
109109

110110
#sessions = Session.objects.filter(meeting__number=meeting.number, timeslot__isnull=False)
111111
schedule = get_schedule(meeting, None)
112-
sessions = Session.objects.filter(meeting__number=meeting.number,
113-
timeslotassignments__schedule=schedule).select_related('meeting__agenda','status','group__state','group__parent')
112+
sessions = ( Session.objects
113+
.filter(meeting__number=meeting.number, timeslotassignments__schedule=schedule)
114+
.select_related('meeting__agenda','status','group__state','group__parent', )
115+
)
114116
for session in sessions:
115117
session.past_cutoff_date = past_cutoff_date
116118
plenaries = sessions.filter(name__icontains='plenary')

ietf/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ def skip_unreadable_post(record):
636636
# Set debug apps in DEV_APPS settings_local
637637
DEV_APPS = ()
638638
DEV_MIDDLEWARE_CLASSES = ()
639+
DEV_TEMPLATE_CONTEXT_PROCESSORS = ()
639640

640641
# Domain which hosts draft and wg alias lists
641642
DRAFT_ALIAS_DOMAIN = IETF_DOMAIN
@@ -742,6 +743,7 @@ def skip_unreadable_post(record):
742743
# Add DEV_APPS to INSTALLED_APPS
743744
INSTALLED_APPS += DEV_APPS
744745
MIDDLEWARE_CLASSES += DEV_MIDDLEWARE_CLASSES
746+
TEMPLATE_CONTEXT_PROCESSORS += DEV_TEMPLATE_CONTEXT_PROCESSORS
745747

746748

747749
# We provide a secret key only for test and development modes. It's

ietf/templates/meeting/edit_materials_button.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{% load ietf_filters session_filters %}
2-
{% if user|has_role:"Secretariat" or session|can_manage_materials:user and not session.past_cutoff_date %}
2+
{% if session|can_manage_materials:user and not session.past_cutoff_date %}
33
{% with gt=session.group.type_id %}
44
<a class="button btn-default btn-sm" href="{% url 'ietf.meeting.views.session_details' num=session.meeting.number acronym=session.group.acronym %}{% if gt == 'wg' or gt == 'rg' or gt == 'ag' %}{% else %}#session_{{session.pk}}{% endif %}">Edit</a>
55
{% endwith %}

ietf/templates/meeting/group_materials.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
{% else %}
2323
<td>
2424
{% if session.agenda %}
25-
<a href="{{ session.agenda.href }}">Agenda</a>
25+
<a href="https://www.ietf.org/proceedings/{{ meeting_num }}/agenda/{{ session.agenda.external_url }}">Agenda</a>
2626
{% else %}
2727
{% if show_agenda == "True" %}
2828
<span class="label label-warning">No agenda</span>
@@ -31,7 +31,7 @@
3131
</td>
3232
<td>
3333
{% if session.minutes %}
34-
<a href="https://www.ietf.org/proceedings/{{ meeting_num }}/minutes/{{ session.minutes }}">Minutes</a>
34+
<a href="https://www.ietf.org/proceedings/{{ meeting_num }}/minutes/{{ session.minutes.external_url }}">Minutes</a>
3535
{% else %}
3636
{% if show_agenda == "True" %}
3737
<span class="label label-warning">No minutes</span>
@@ -41,7 +41,7 @@
4141
<td>
4242
{% with session.slides as slides %}
4343
{% for slide in slides %}
44-
<a href="https://www.ietf.org/proceedings/{{meeting_num}}/slides/{{ slide.external_url }}">{{ slide.title|clean_whitespace }}</a>
44+
<a href="https://www.ietf.org/proceedings/{{ meeting_num }}/slides/{{ slide.external_url }}">{{ slide.title|clean_whitespace }}</a>
4545
<br>
4646
{% empty %}
4747
<span class="label label-warning">No slides</span>
@@ -51,7 +51,7 @@
5151
<td>
5252
{% with session.all_meeting_drafts as drafts %}
5353
{% for draft in drafts %}
54-
<a href="{% url "doc_view" name=draft.canonical_name %}">{{ draft.canonical_name }}</a><br>
54+
<a href="{{ draft.href }}">{{ draft.name }}</a><br>
5555
{% empty %}
5656
<span class="label label-warning">No drafts</span>
5757
{% endfor %}

ietf/utils/test_data.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -375,17 +375,7 @@ def rfc_for_status_change_test_factory(name,rfc_num,std_level_id):
375375

376376
# Instances of the remaining document types
377377
# (Except liaison, liai-att, and recording which the code in ietf.doc does not use...)
378-
def other_doc_factory(type_id,name):
379-
doc = Document.objects.create(type_id=type_id,name=name,rev='00',group=mars_wg)
380-
DocAlias.objects.create(name=name,document=doc)
381-
doc.set_state(State.objects.get(type__slug=doc.type.slug,slug='active'))
382-
if type_id=='slides':
383-
doc.set_state(State.objects.get(type='reuse_policy',slug='single'))
384-
other_doc_factory('agenda','agenda-42-mars')
385-
other_doc_factory('minutes','minutes-42-mars')
386-
other_doc_factory('slides','slides-42-mars-1')
387-
# TODO: add
388-
#other_doc_factory('bluesheets','bluesheets-42-mars-1')
389-
#other_doc_factory('recording','recording-42-mars-1-00')
378+
# Meeting-related documents are created in make_meeting_test_data, and
379+
# associated with a session
390380

391381
return draft

0 commit comments

Comments
 (0)