33import datetime
44import logging
55import os
6- #import re
76
87from django .db import models
98from django .core import checks
2221from ietf .person .models import Email , Person
2322from ietf .utils import log
2423from ietf .utils .admin import admin_link
25- # from ietf.utils.rfcmarkup import markup
24+ from ietf .utils .rfcmarkup import markup
2625from ietf .utils .validators import validate_no_control_chars
2726
2827logger = 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
663729class 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