Introduce generic states and reminders

- Legacy-Id: 3615
This commit is contained in:
Ole Laursen 2011-11-14 19:26:35 +00:00
parent 08ff44dd18
commit f6261a9a49
3 changed files with 153 additions and 27 deletions

View file

@ -1,22 +1,88 @@
from django.utils.safestring import mark_safe
from django.contrib import admin
from models import *
from person.models import *
from django import forms
class DocAliasAdmin(admin.ModelAdmin):
list_display = [ 'name', 'document_link', ]
search_fields = [ 'name', 'document__name', ]
raw_id_fields = ['document']
admin.site.register(DocAlias, DocAliasAdmin)
from models import *
from redesign.person.models import *
from redesign.doc.utils import get_state_types
class StateTypeAdmin(admin.ModelAdmin):
list_display = ["slug", "label"]
admin.site.register(StateType, StateTypeAdmin)
class StateAdmin(admin.ModelAdmin):
list_display = ["slug", "type", 'name', 'order', 'desc']
filter_horizontal = ["next_states"]
admin.site.register(State, StateAdmin)
class DocAliasInline(admin.TabularInline):
model = DocAlias
extra = 1
# document form for managing states in a less confusing way
class StatesWidget(forms.SelectMultiple):
"""Display all applicable states as separate select boxes,
requires 'instance' have been set on the widget."""
def render(self, name, value, attrs=None, choices=()):
types = StateType.objects.filter(slug__in=get_state_types(self.instance))
categorized_choices = []
for t in types:
states = State.objects.filter(type=t).select_related()
if states:
categorized_choices.append((t.label, states))
html = []
first = True
for label, states in categorized_choices:
htmlid = "id_%s_%s" % (name, label)
html.append('<div style="clear:both;padding-top:%s">' % ("1em" if first else "0.5em"))
html.append(u'<label for="%s">%s:</label>' % (htmlid, label))
html.append(u'<select name="%s" id="%s">' % (name, htmlid))
html.append(u'<option value="">-----------</option>')
for s in states:
html.append('<option %s value="%s">%s</option>' % ("selected" if s.pk in value else "", s.pk, s.name))
html.append(u'</select>')
html.append("</div>")
first = False
return mark_safe(u"".join(html))
class StatesField(forms.ModelMultipleChoiceField):
def __init__(self, *args, **kwargs):
# use widget with multiple select boxes
kwargs['widget'] = StatesWidget
super(StatesField, self).__init__(*args, **kwargs)
def clean(self, value):
if value and isinstance(value, (list, tuple)):
# remove "", in case a state is reset
value = [x for x in value if x]
return super(StatesField, self).clean(value)
class DocumentForm(forms.ModelForm):
states = StatesField(queryset=State.objects.all(), required=False)
def __init__(self, *args, **kwargs):
super(DocumentForm, self).__init__(*args, **kwargs)
# we don't normally have access to the instance in the widget
# so set it here
self.fields["states"].widget.instance = self.instance
class Meta:
model = Document
class DocumentAdmin(admin.ModelAdmin):
list_display = ['name', 'rev', 'state', 'group', 'pages', 'intended_std_level', 'author_list', 'time']
search_fields = ['name']
raw_id_fields = ['authors', 'related', 'group', 'shepherd', 'ad']
inlines = [DocAliasInline]
form = DocumentForm
admin.site.register(Document, DocumentAdmin)
@ -25,8 +91,15 @@ class DocHistoryAdmin(admin.ModelAdmin):
search_fields = ['doc__name']
ordering = ['time', 'doc', 'rev']
raw_id_fields = ['doc', 'authors', 'related', 'group', 'shepherd', 'ad']
admin.site.register(DocHistory, DocHistoryAdmin)
class DocAliasAdmin(admin.ModelAdmin):
list_display = [ 'name', 'document_link', ]
search_fields = [ 'name', 'document__name', ]
raw_id_fields = ['document']
admin.site.register(DocAlias, DocAliasAdmin)
# events

View file

@ -11,6 +11,29 @@ from redesign.util import admin_link
import datetime, os
class StateType(models.Model):
slug = models.CharField(primary_key=True, max_length=30) # draft, draft_iesg, charter, ...
label = models.CharField(max_length=255) # State, IESG state, WG state, ...
def __unicode__(self):
return self.label
class State(models.Model):
type = models.ForeignKey(StateType)
slug = models.SlugField()
name = models.CharField(max_length=255)
used = models.BooleanField(default=True)
desc = models.TextField(blank=True)
order = models.IntegerField(default=0)
next_states = models.ManyToManyField('State', related_name="previous_states")
def __unicode__(self):
return self.name
class Meta:
ordering = ["type", "order"]
class DocumentInfo(models.Model):
"""Any kind of document. Draft, RFC, Charter, IPR Statement, Liaison Statement"""
time = models.DateTimeField(default=datetime.datetime.now) # should probably have auto_now=True
@ -18,12 +41,13 @@ class DocumentInfo(models.Model):
type = models.ForeignKey(DocTypeName, blank=True, null=True) # Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email, Review, Issue, Wiki, External ...
title = models.CharField(max_length=255)
# State
states = models.ManyToManyField(State, blank=True)
state = models.ForeignKey(DocStateName, blank=True, null=True) # Active/Expired/RFC/Replaced/Withdrawn
tags = models.ManyToManyField(DocInfoTagName, blank=True, null=True) # Revised ID Needed, ExternalParty, AD Followup, ...
tags = models.ManyToManyField(DocTagName, blank=True, null=True) # Revised ID Needed, ExternalParty, AD Followup, ...
stream = models.ForeignKey(DocStreamName, blank=True, null=True) # IETF, IAB, IRTF, Independent Submission
group = models.ForeignKey(Group, blank=True, null=True) # WG, RG, IAB, IESG, Edu, Tools
wg_state = models.ForeignKey(WgDocStateName, verbose_name="WG state", blank=True, null=True) # Not/Candidate/Active/Parked/LastCall/WriteUp/Submitted/Dead
iesg_state = models.ForeignKey(IesgDocStateName, verbose_name="IESG state", blank=True, null=True) #
iesg_state = models.ForeignKey(IesgDocStateName, verbose_name="IESG state", blank=True, null=True) #
iana_state = models.ForeignKey(IanaDocStateName, verbose_name="IANA state", blank=True, null=True)
rfc_state = models.ForeignKey(RfcDocStateName, verbose_name="RFC state", blank=True, null=True)
# Other
@ -48,7 +72,24 @@ class DocumentInfo(models.Model):
return os.path.join(settings.AGENDA_PATH, meeting, self.type_id) + "/"
else:
raise NotImplemented
def set_state(self, state):
already_set = self.states.filter(type=state.type)
others = [s for s in already_set if s != state]
if others:
self.states.remove(*others)
if state not in already_set:
self.states.add(state)
def unset_state(self, state):
self.states.remove(state)
def get_state(self, state_type):
try:
return self.states.get(type=state_type)
except State.DoesNotExist:
return None
class Meta:
abstract = True
def author_list(self):
@ -116,7 +157,6 @@ class Document(DocumentInfo):
if a:
name = a[0].name
return name
class RelatedDocHistory(models.Model):
source = models.ForeignKey('DocHistory')
@ -144,6 +184,9 @@ class DocHistory(DocumentInfo):
authors = models.ManyToManyField(Email, through=DocHistoryAuthor, blank=True)
def __unicode__(self):
return unicode(self.doc.name)
class Meta:
verbose_name = "document history"
verbose_name_plural = "document histories"
def save_document_in_history(doc):
def get_model_fields_as_dict(obj):
@ -198,6 +241,12 @@ class DocAlias(models.Model):
verbose_name = "document alias"
verbose_name_plural = "document aliases"
class DocReminder(models.Model):
event = models.ForeignKey('DocEvent')
type = models.ForeignKey(DocReminderTypeName)
due = models.DateTimeField()
active = models.BooleanField(default=True)
EVENT_TYPES = [
# core events
@ -205,6 +254,7 @@ EVENT_TYPES = [
("changed_document", "Changed document metadata"),
# misc document events
("changed_stream", "Changed document stream"),
("added_comment", "Added comment"),
("expired_document", "Expired document"),
("requested_resurrect", "Requested resurrect"),
@ -212,6 +262,7 @@ EVENT_TYPES = [
("published_rfc", "Published RFC"),
# WG events
("changed_group", "Changed group"),
("changed_protocol_writeup", "Changed protocol writeup"),
# IESG events

View file

@ -7,28 +7,30 @@ def get_state_types(doc):
return res
res.append(doc.type_id)
#if doc.type_id in ("agenda", "minutes", "slides", "liai-att"):
# res.append(doc.type_id)
if doc.type_id == "draft":
if doc.stream_id == "ietf":
wg_specific = doc.type_id + ":" + "wg" + ":" + doc.group.acronym
if State.objects.filter(type=wg_specific):
res.append(wg_specific)
else:
res.append(doc.type_id + ":" + "wg")
elif doc.stream_id == "irtf":
res.append(doc.type_id + ":" + "rg")
elif doc.stream_id == "iab":
res.append(doc.type_id + ":" + "iab")
elif doc.stream_id == "ise":
res.append(doc.type_id + ":" + "ise")
if doc.stream_id != "legacy":
res.append("draft-stream-%s" % doc.stream_id)
res.append(doc.type_id + ":" + "iesg")
res.append(doc.type_id + ":" + "iana")
res.append(doc.type_id + ":" + "rfc-editor")
res.append("draft-iesg")
res.append("draft-iana")
res.append("draft-rfceditor")
return res
def get_tags_for_stream_id(stream_id):
if stream_id == "ietf":
return ["w-expert", "w-extern", "w-merge", "need-aut", "w-refdoc", "w-refing", "rev-wglc", "rev-ad", "rev-iesg", "sheph-u", "other"]
elif stream_id == "iab":
return ["need-ed", "w-part", "w-review", "need-rev", "sh-f-up"]
elif stream_id == "irtf":
return ["need-ed", "need-sh", "w-dep", "need-rev", "iesg-com"]
elif stream_id == "ise":
return ["w-dep", "w-review", "need-rev", "iesg-com"]
else:
return []
def active_ballot_positions(doc):
"""Return dict mapping each active AD to a current ballot position (or None if they haven't voted)."""