Merged in [8846] from rjsparks@nostrum.com:
Added tests for document urls that provide a revision for all the document types the view code currently handles. Refactored parts of Document and DocHistory into DocumentInfo to get the tests to pass. (but careful review is probably warranted). - Legacy-Id: 8880 Note: SVN reference [8846] has been migrated to Git commit ce5bda1835b32640d17136d09262f654fade94d4
This commit is contained in:
parent
a6e4618db6
commit
f15ba3346b
|
@ -168,6 +168,53 @@ class DocumentInfo(models.Model):
|
|||
else:
|
||||
return None
|
||||
|
||||
def friendly_state(self):
|
||||
""" Return a concise text description of the document's current state."""
|
||||
state = self.get_state()
|
||||
if not state:
|
||||
return "Unknown state"
|
||||
|
||||
if self.type_id == 'draft':
|
||||
iesg_state = self.get_state("draft-iesg")
|
||||
iesg_state_summary = None
|
||||
if iesg_state:
|
||||
iesg_substate = [t for t in self.tags.all() if t.slug in IESG_SUBSTATE_TAGS]
|
||||
# There really shouldn't be more than one tag in iesg_substate, but this will do something sort-of-sensible if there is
|
||||
iesg_state_summary = iesg_state.name
|
||||
if iesg_substate:
|
||||
iesg_state_summary = iesg_state_summary + "::"+"::".join(tag.name for tag in iesg_substate)
|
||||
|
||||
if state.slug == "rfc":
|
||||
return "RFC %s (%s)" % (self.rfc_number(), self.std_level)
|
||||
elif state.slug == "repl":
|
||||
rs = self.related_that("replaces")
|
||||
if rs:
|
||||
return mark_safe("Replaced by " + ", ".join("<a href=\"%s\">%s</a>" % (urlreverse('doc_view', kwargs=dict(name=alias.document)), alias.document) for alias in rs))
|
||||
else:
|
||||
return "Replaced"
|
||||
elif state.slug == "active":
|
||||
if iesg_state:
|
||||
if iesg_state.slug == "dead":
|
||||
# Many drafts in the draft-iesg "Dead" state are not dead
|
||||
# in other state machines; they're just not currently under
|
||||
# IESG processing. Show them as "I-D Exists (IESG: Dead)" instead...
|
||||
return "I-D Exists (IESG: %s)" % iesg_state_summary
|
||||
elif iesg_state.slug == "lc":
|
||||
e = self.latest_event(LastCallDocEvent, type="sent_last_call")
|
||||
if e:
|
||||
return iesg_state_summary + " (ends %s)" % e.expires.date().isoformat()
|
||||
|
||||
return iesg_state_summary
|
||||
else:
|
||||
return "I-D Exists"
|
||||
else:
|
||||
if iesg_state and iesg_state.slug == "dead":
|
||||
return state.name + " (IESG: %s)" % iesg_state_summary
|
||||
# Expired/Withdrawn by Submitter/IETF
|
||||
return state.name
|
||||
else:
|
||||
return state.name
|
||||
|
||||
def author_list(self):
|
||||
return ", ".join(email.address for email in self.authors.all())
|
||||
|
||||
|
@ -195,6 +242,69 @@ class DocumentInfo(models.Model):
|
|||
else:
|
||||
return False
|
||||
|
||||
def relations_that(self, relationship):
|
||||
"""Return the related-document objects that describe a given relationship targeting self."""
|
||||
if isinstance(relationship, str):
|
||||
relationship = [ relationship ]
|
||||
if isinstance(relationship, tuple):
|
||||
relationship = list(relationship)
|
||||
if not isinstance(relationship, list):
|
||||
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
|
||||
if isinstance(self, Document):
|
||||
return RelatedDocument.objects.filter(target__document=self, relationship__in=relationship).select_related('source')
|
||||
elif isinstance(self, DocHistory):
|
||||
return RelatedDocHistory.objects.filter(target__document=self, relationship__in=relationship).select_related('source')
|
||||
else:
|
||||
return RelatedDocument.objects.none()
|
||||
|
||||
def all_relations_that(self, relationship, related=None):
|
||||
if not related:
|
||||
related = []
|
||||
rels = self.relations_that(relationship)
|
||||
for r in rels:
|
||||
if not r in related:
|
||||
related += [ r ]
|
||||
related = r.source.all_relations_that(relationship, related)
|
||||
return related
|
||||
|
||||
def relations_that_doc(self, relationship):
|
||||
"""Return the related-document objects that describe a given relationship from self to other documents."""
|
||||
if isinstance(relationship, str):
|
||||
relationship = [ relationship ]
|
||||
if isinstance(relationship, tuple):
|
||||
relationship = list(relationship)
|
||||
if not isinstance(relationship, list):
|
||||
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
|
||||
if isinstance(self, Document):
|
||||
return RelatedDocument.objects.filter(source=self, relationship__in=relationship).select_related('target__document')
|
||||
elif isinstance(self, DocHistory):
|
||||
return RelatedDocHistory.objects.filter(source=self, relationship__in=relationship).select_related('target__document')
|
||||
else:
|
||||
return RelatedDocument.objects.none()
|
||||
|
||||
|
||||
def all_relations_that_doc(self, relationship, related=None):
|
||||
if not related:
|
||||
related = []
|
||||
rels = self.relations_that_doc(relationship)
|
||||
for r in rels:
|
||||
if not r in related:
|
||||
related += [ r ]
|
||||
related = r.target.document.all_relations_that_doc(relationship, related)
|
||||
return related
|
||||
|
||||
def related_that(self, relationship):
|
||||
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.relations_that(relationship)]))
|
||||
|
||||
def all_related_that(self, relationship, related=None):
|
||||
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.all_relations_that(relationship)]))
|
||||
|
||||
def related_that_doc(self, relationship):
|
||||
return list(set([x.target for x in self.relations_that_doc(relationship)]))
|
||||
|
||||
def all_related_that_doc(self, relationship, related=None):
|
||||
return list(set([x.target for x in self.all_relations_that_doc(relationship)]))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
@ -324,57 +434,6 @@ class Document(DocumentInfo):
|
|||
name = name.upper()
|
||||
return name
|
||||
|
||||
def relations_that(self, relationship):
|
||||
"""Return the related-document objects that describe a given relationship targeting self."""
|
||||
if isinstance(relationship, str):
|
||||
relationship = [ relationship ]
|
||||
if isinstance(relationship, tuple):
|
||||
relationship = list(relationship)
|
||||
if not isinstance(relationship, list):
|
||||
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
|
||||
return RelatedDocument.objects.filter(target__document=self, relationship__in=relationship).select_related('source')
|
||||
|
||||
def all_relations_that(self, relationship, related=None):
|
||||
if not related:
|
||||
related = []
|
||||
rels = self.relations_that(relationship)
|
||||
for r in rels:
|
||||
if not r in related:
|
||||
related += [ r ]
|
||||
related = r.source.all_relations_that(relationship, related)
|
||||
return related
|
||||
|
||||
def relations_that_doc(self, relationship):
|
||||
"""Return the related-document objects that describe a given relationship from self to other documents."""
|
||||
if isinstance(relationship, str):
|
||||
relationship = [ relationship ]
|
||||
if isinstance(relationship, tuple):
|
||||
relationship = list(relationship)
|
||||
if not isinstance(relationship, list):
|
||||
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
|
||||
return RelatedDocument.objects.filter(source=self, relationship__in=relationship).select_related('target__document')
|
||||
|
||||
def all_relations_that_doc(self, relationship, related=None):
|
||||
if not related:
|
||||
related = []
|
||||
rels = self.relations_that_doc(relationship)
|
||||
for r in rels:
|
||||
if not r in related:
|
||||
related += [ r ]
|
||||
related = r.target.document.all_relations_that_doc(relationship, related)
|
||||
return related
|
||||
|
||||
def related_that(self, relationship):
|
||||
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.relations_that(relationship)]))
|
||||
|
||||
def all_related_that(self, relationship, related=None):
|
||||
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.all_relations_that(relationship)]))
|
||||
|
||||
def related_that_doc(self, relationship):
|
||||
return list(set([x.target for x in self.relations_that_doc(relationship)]))
|
||||
|
||||
def all_related_that_doc(self, relationship, related=None):
|
||||
return list(set([x.target for x in self.all_relations_that_doc(relationship)]))
|
||||
|
||||
def telechat_date(self, e=None):
|
||||
if not e:
|
||||
|
@ -427,53 +486,6 @@ class Document(DocumentInfo):
|
|||
n = self.canonical_name()
|
||||
return n[3:] if n.startswith("rfc") else None
|
||||
|
||||
def friendly_state(self):
|
||||
""" Return a concise text description of the document's current state."""
|
||||
state = self.get_state()
|
||||
if not state:
|
||||
return "Unknown state"
|
||||
|
||||
if self.type_id == 'draft':
|
||||
iesg_state = self.get_state("draft-iesg")
|
||||
iesg_state_summary = None
|
||||
if iesg_state:
|
||||
iesg_substate = [t for t in self.tags.all() if t.slug in IESG_SUBSTATE_TAGS]
|
||||
# There really shouldn't be more than one tag in iesg_substate, but this will do something sort-of-sensible if there is
|
||||
iesg_state_summary = iesg_state.name
|
||||
if iesg_substate:
|
||||
iesg_state_summary = iesg_state_summary + "::"+"::".join(tag.name for tag in iesg_substate)
|
||||
|
||||
if state.slug == "rfc":
|
||||
return "RFC %s (%s)" % (self.rfc_number(), self.std_level)
|
||||
elif state.slug == "repl":
|
||||
rs = self.related_that("replaces")
|
||||
if rs:
|
||||
return mark_safe("Replaced by " + ", ".join("<a href=\"%s\">%s</a>" % (urlreverse('doc_view', kwargs=dict(name=alias.document)), alias.document) for alias in rs))
|
||||
else:
|
||||
return "Replaced"
|
||||
elif state.slug == "active":
|
||||
if iesg_state:
|
||||
if iesg_state.slug == "dead":
|
||||
# Many drafts in the draft-iesg "Dead" state are not dead
|
||||
# in other state machines; they're just not currently under
|
||||
# IESG processing. Show them as "I-D Exists (IESG: Dead)" instead...
|
||||
return "I-D Exists (IESG: %s)" % iesg_state_summary
|
||||
elif iesg_state.slug == "lc":
|
||||
e = self.latest_event(LastCallDocEvent, type="sent_last_call")
|
||||
if e:
|
||||
return iesg_state_summary + " (ends %s)" % e.expires.date().isoformat()
|
||||
|
||||
return iesg_state_summary
|
||||
else:
|
||||
return "I-D Exists"
|
||||
else:
|
||||
if iesg_state and iesg_state.slug == "dead":
|
||||
return state.name + " (IESG: %s)" % iesg_state_summary
|
||||
# Expired/Withdrawn by Submitter/IETF
|
||||
return state.name
|
||||
else:
|
||||
return state.name
|
||||
|
||||
def ipr(self,states=('posted','removed')):
|
||||
"""Returns the IPR disclosures against this document (as a queryset over IprDocRel)."""
|
||||
from ietf.ipr.models import IprDocRel
|
||||
|
@ -547,6 +559,10 @@ class DocHistory(DocumentInfo):
|
|||
def last_presented(self):
|
||||
return self.doc.last_presented()
|
||||
|
||||
@property
|
||||
def groupmilestone_set(self):
|
||||
return self.doc.groupmilestone_set
|
||||
|
||||
class Meta:
|
||||
verbose_name = "document history"
|
||||
verbose_name_plural = "document histories"
|
||||
|
@ -593,6 +609,8 @@ def save_document_in_history(doc):
|
|||
|
||||
return dochist
|
||||
|
||||
|
||||
|
||||
class DocAlias(models.Model):
|
||||
"""This is used for documents that may appear under multiple names,
|
||||
and in particular for RFCs, which for continuity still keep the
|
||||
|
|
|
@ -9,7 +9,7 @@ from pyquery import PyQuery
|
|||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
from ietf.doc.models import ( Document, DocAlias, DocRelationshipName, RelatedDocument, State,
|
||||
DocEvent, BallotPositionDocEvent, LastCallDocEvent, WriteupDocEvent )
|
||||
DocEvent, BallotPositionDocEvent, LastCallDocEvent, WriteupDocEvent, save_document_in_history )
|
||||
from ietf.group.models import Group
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.mail import outbox
|
||||
|
@ -205,6 +205,35 @@ class DocTestCase(TestCase):
|
|||
r = self.client.get(urlreverse("doc_view", kwargs=dict(name="draft-xyz123")))
|
||||
self.assertEqual(r.status_code, 404)
|
||||
|
||||
def test_document_primary_and_history_views(self):
|
||||
make_test_data()
|
||||
|
||||
# Ensure primary views of both current and historic versions of documents works
|
||||
for docname in ["draft-imaginary-independent-submission",
|
||||
"conflict-review-imaginary-irtf-submission",
|
||||
"status-change-imaginary-mid-review",
|
||||
"charter-ietf-mars",
|
||||
"agenda-42-mars",
|
||||
"minutes-42-mars",
|
||||
"slides-42-mars-1",
|
||||
]:
|
||||
doc = Document.objects.get(name=docname)
|
||||
# give it some history
|
||||
save_document_in_history(doc)
|
||||
doc.rev="01"
|
||||
doc.save()
|
||||
|
||||
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=doc.name)))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("%s-01"%docname in r.content)
|
||||
|
||||
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=doc.name,rev="01")))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
|
||||
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=doc.name,rev="00")))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("%s-00"%docname in r.content)
|
||||
|
||||
def test_document_charter(self):
|
||||
make_test_data()
|
||||
|
||||
|
|
|
@ -263,12 +263,12 @@ def make_test_data():
|
|||
)
|
||||
|
||||
# an independent submission before review
|
||||
doc = Document.objects.create(name='draft-imaginary-independent-submission',type_id='draft')
|
||||
doc = Document.objects.create(name='draft-imaginary-independent-submission',type_id='draft',rev='00')
|
||||
doc.set_state(State.objects.get(used=True, type="draft", slug="active"))
|
||||
DocAlias.objects.create(name=doc.name, document=doc)
|
||||
|
||||
# an irtf submission mid review
|
||||
doc = Document.objects.create(name='draft-imaginary-irtf-submission', type_id='draft')
|
||||
doc = Document.objects.create(name='draft-imaginary-irtf-submission', type_id='draft',rev='00')
|
||||
docalias = DocAlias.objects.create(name=doc.name, document=doc)
|
||||
doc.stream = StreamName.objects.get(slug='irtf')
|
||||
doc.save()
|
||||
|
@ -298,4 +298,13 @@ def make_test_data():
|
|||
rfc_for_status_change_test_factory('draft-ietf-random-otherthing',9998,'inf')
|
||||
rfc_for_status_change_test_factory('draft-was-never-issued',14,'unkn')
|
||||
|
||||
# Instances of the remaining document types
|
||||
# (Except liaison, liai-att, and recording which the code in ietf.doc does not use...)
|
||||
def other_doc_factory(type_id,name):
|
||||
doc = Document.objects.create(type_id=type_id,name=name,rev='00',group=mars_wg)
|
||||
DocAlias.objects.create(name=name,document=doc)
|
||||
other_doc_factory('agenda','agenda-42-mars')
|
||||
other_doc_factory('minutes','minutes-42-mars')
|
||||
other_doc_factory('slides','slides-42-mars-1')
|
||||
|
||||
return draft
|
||||
|
|
Loading…
Reference in a new issue