Skip to content

Commit a67e9dd

Browse files
committed
Added a number of new methods on DocumentInfo/Document/DocHistory, to support the new htmlized document page. Added caching for some methods.
- Legacy-Id: 13039
1 parent 3f92aee commit a67e9dd

1 file changed

Lines changed: 103 additions & 31 deletions

File tree

ietf/doc/models.py

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import datetime
44
import logging
55
import os
6-
#import re
76

87
from django.db import models
98
from django.core import checks
@@ -22,7 +21,7 @@
2221
from ietf.person.models import Email, Person
2322
from ietf.utils import log
2423
from ietf.utils.admin import admin_link
25-
#from ietf.utils.rfcmarkup import markup
24+
from ietf.utils.rfcmarkup import markup
2625
from ietf.utils.validators import validate_no_control_chars
2726

2827
logger = logging.getLogger('django')
@@ -100,10 +99,13 @@ def file_extension(self):
10099
def get_file_path(self):
101100
if not hasattr(self, '_cached_file_path'):
102101
if self.type_id == "draft":
103-
if self.get_state_slug() == "rfc":
104-
self._cached_file_path = settings.RFC_PATH
105-
else:
102+
if self.is_dochistory():
106103
self._cached_file_path = settings.INTERNET_DRAFT_PATH
104+
else:
105+
if self.get_state_slug() == "rfc":
106+
self._cached_file_path = settings.RFC_PATH
107+
else:
108+
self._cached_file_path = settings.INTERNET_DRAFT_PATH
107109
elif self.type_id in ("agenda", "minutes", "slides", "bluesheets") and self.meeting_related():
108110
doc = self.doc if isinstance(self, DocHistory) else self
109111
if doc.session_set.exists():
@@ -124,16 +126,21 @@ def get_file_path(self):
124126
def get_base_name(self):
125127
if not hasattr(self, '_cached_base_name'):
126128
if self.type_id == 'draft':
127-
if self.get_state_slug() == 'rfc':
128-
self._cached_base_name = "%s.txt" % self.canonical_name()
129+
if self.is_dochistory():
130+
self._cached_base_name = "%s-%s.txt" % (self.doc.name, self.rev)
129131
else:
130-
self._cached_base_name = "%s-%s.txt" % (self.name, self.rev)
132+
if self.get_state_slug() == 'rfc':
133+
self._cached_base_name = "%s.txt" % self.canonical_name()
134+
else:
135+
self._cached_base_name = "%s-%s.txt" % (self.name, self.rev)
131136
elif self.type_id in ["slides", "agenda", "minutes", "bluesheets", ] and self.meeting_related():
132137
if self.external_url:
133138
# we need to remove the extension for the globbing below to work
134139
self._cached_base_name = self.external_url
135140
else:
136141
self._cached_base_name = "%s.txt" % self.canonical_name() # meeting materials are unversioned at the moment
142+
elif self.type_id == 'review':
143+
self._cached_base_name = "%s.txt" % self.name
137144
else:
138145
if self.rev:
139146
self._cached_base_name = "%s-%s.txt" % (self.canonical_name(), self.rev)
@@ -146,6 +153,16 @@ def get_file_name(self):
146153
self._cached_file_name = os.path.join(self.get_file_path(), self.get_base_name())
147154
return self._cached_file_name
148155

156+
def revisions(self):
157+
revisions = []
158+
doc = self.doc if isinstance(self, DocHistory) else self
159+
for e in doc.docevent_set.filter(type='new_revision').distinct().order_by("time", "id"):
160+
if e.rev and not e.rev in revisions:
161+
revisions.append(e.rev)
162+
if not doc.rev in revisions:
163+
revisions.append(doc.rev)
164+
return revisions
165+
149166
def href(self, meeting=None):
150167
"""
151168
Returns an url to the document text. This differs from .get_absolute_url(),
@@ -290,6 +307,11 @@ def friendly_state(self):
290307
else:
291308
return state.name
292309

310+
def is_rfc(self):
311+
if not hasattr(self, '_cached_is_rfc'):
312+
self._cached_is_rfc = self.pk and self.type_id == 'draft' and self.states.filter(type='draft',slug='rfc').exists()
313+
return self._cached_is_rfc
314+
293315
def author_list(self):
294316
return ", ".join(email.address for email in self.authors.all())
295317

@@ -383,8 +405,35 @@ def related_that_doc(self, relationship):
383405
def all_related_that_doc(self, relationship, related=None):
384406
return list(set([x.target for x in self.all_relations_that_doc(relationship)]))
385407

408+
def replaces(self):
409+
return set([ r.document for r in self.related_that_doc("replaces")])
410+
386411
def replaced_by(self):
387-
return [ r.document for r in self.related_that("replaces") ]
412+
return set([ r.document for r in self.related_that("replaces") ])
413+
414+
def text(self):
415+
path = self.get_file_name()
416+
try:
417+
with open(path, 'rb') as file:
418+
raw = file.read()
419+
except IOError as e:
420+
logger.error("IOError for %s: %s", path, e, exc_info=e)
421+
return None
422+
try:
423+
text = raw.decode('utf-8')
424+
except UnicodeDecodeError as e:
425+
text = raw.decode('latin-1')
426+
#
427+
return text
428+
429+
def htmlized(self):
430+
text = self.text()
431+
html = None
432+
if text:
433+
# The path here has to match the urlpattern for htmlized documents
434+
html = markup(text, path=settings.HTMLIZER_URL_PREFIX)
435+
#html = re.sub(r'<hr[^>]*/>','', html)
436+
return html
388437

389438
class Meta:
390439
abstract = True
@@ -477,7 +526,6 @@ def get_absolute_url(self):
477526
return url
478527
return urlreverse('ietf.doc.views_doc.document_main', kwargs={ 'name': name }, urlconf="ietf.urls")
479528

480-
481529
def file_tag(self):
482530
return u"<%s>" % self.filename_with_rev()
483531

@@ -497,9 +545,9 @@ def canonical_name(self):
497545
if not hasattr(self, '_canonical_name'):
498546
name = self.name
499547
if self.type_id == "draft" and self.get_state_slug() == "rfc":
500-
a = self.docalias_set.filter(name__startswith="rfc")
548+
a = self.docalias_set.filter(name__startswith="rfc").first()
501549
if a:
502-
name = a[0].name
550+
name = a.name
503551
elif self.type_id == "charter":
504552
from ietf.doc.utils_charter import charter_name_for_group # Imported locally to avoid circular imports
505553
try:
@@ -593,8 +641,15 @@ def displayname_with_link(self):
593641
return mark_safe('<a href="%s">%s-%s</a>' % (self.get_absolute_url(), self.name , self.rev))
594642

595643
def rfc_number(self):
596-
n = self.canonical_name()
597-
return n[3:] if n.startswith("rfc") else None
644+
if not hasattr(self, '_cached_rfc_number'):
645+
self._cached_rfc_number = None
646+
if self.is_rfc():
647+
n = self.canonical_name()
648+
if n.startswith("rfc"):
649+
self._cached_rfc_number = n[3:]
650+
else:
651+
logger.error("Document self.is_rfc() is True but self.canonical_name() is %s" % n)
652+
return self._cached_rfc_number
598653

599654
def ipr(self,states=('posted','removed')):
600655
"""Returns the IPR disclosures against this document (as a queryset over IprDocRel)."""
@@ -640,25 +695,36 @@ def pub_date(self):
640695
event = self.latest_event(type='new_revision')
641696
return event.time
642697

643-
def text(self):
644-
path = self.get_file_name()
645-
try:
646-
with open(path, 'rb') as file:
647-
text = file.read().decode('utf-8')
648-
except (IOError, UnicodeDecodeError) as e:
649-
text = None
650-
logger.error("Failure to read document text for %s", self.name, exc_info=e)
651-
#
652-
return text
698+
def is_dochistory(self):
699+
return False
653700

701+
def fake_history_obj(self, rev):
702+
"""
703+
Mock up a fake DocHistory object with the given revision, for
704+
situations where we need an entry but there is none in the DocHistory
705+
table.
706+
XXX TODO: Add missing objects to DocHistory instead
707+
"""
708+
history = DocHistory.objects.filter(doc=self, rev=rev).order_by("time")
709+
if history.exists():
710+
return history.first()
711+
else:
712+
# fake one
713+
events = self.docevent_set.order_by("time", "id")
714+
rev_events = events.filter(rev=rev)
715+
new_rev_events = rev_events.filter(type='new_revision')
716+
if new_rev_events.exists():
717+
time = new_rev_events.first().time
718+
elif rev_events.exists():
719+
time = rev_events.first().time
720+
else:
721+
time = datetime.datetime.fromtimestamp(0)
722+
dh = DocHistory(name=self.name, rev=rev, doc=self, time=time, type=self.type, title=self.title,
723+
stream=self.stream, group=self.group)
724+
725+
return dh
726+
654727

655-
# def htmlized(self):
656-
# text = self.text()
657-
# html = None
658-
# if text:
659-
# html = markup(text, path="/doc")
660-
# html = re.sub(r'<hr[^>]*/>','', html)
661-
# return html
662728

663729
class RelatedDocHistory(models.Model):
664730
source = models.ForeignKey('DocHistory')
@@ -712,6 +778,12 @@ def groupmilestone_set(self):
712778
def docalias_set(self):
713779
return self.doc.docalias_set
714780

781+
def is_dochistory(self):
782+
return True
783+
784+
def related_ipr(self):
785+
return self.doc.related_ipr()
786+
715787
class Meta:
716788
verbose_name = "document history"
717789
verbose_name_plural = "document histories"

0 commit comments

Comments
 (0)