datatracker/ietf/ietfworkflows/utils.py
2012-01-24 17:17:24 +00:00

431 lines
15 KiB
Python

import copy
import datetime
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from django.template.defaultfilters import pluralize
from workflows.models import State, StateObjectRelation
from workflows.utils import (get_workflow_for_object, set_workflow_for_object,
get_state, set_state)
from ietf.ietfworkflows.streams import (get_streamed_draft, get_stream_from_draft,
set_stream_for_draft)
from ietf.ietfworkflows.models import (WGWorkflow, AnnotationTagObjectRelation,
AnnotationTag, ObjectAnnotationTagHistoryEntry,
ObjectHistoryEntry, StateObjectRelationMetadata,
ObjectWorkflowHistoryEntry, ObjectStreamHistoryEntry)
from ietf.idtracker.models import InternetDraft
from ietf.utils.mail import send_mail
from ietf.doc.models import Document, DocEvent, save_document_in_history, DocReminder, DocReminderTypeName
from ietf.group.models import Role
WAITING_WRITEUP = 'WG Consensus: Waiting for Write-Up'
FOLLOWUP_TAG = 'Doc Shepherd Follow-up Underway'
def get_default_workflow_for_wg():
try:
workflow = WGWorkflow.objects.get(name='Default WG Workflow')
return workflow
except WGWorkflow.DoesNotExist:
return None
def clone_transition(transition):
new = copy.copy(transition)
new.pk = None
new.save()
# Reference original initial states
for state in transition.states.all():
new.states.add(state)
return new
def clone_workflow(workflow, name):
new = WGWorkflow.objects.create(name=name, initial_state=workflow.initial_state)
# Reference default states
for state in workflow.states.all():
new.selected_states.add(state)
# Reference default annotation tags
for tag in workflow.annotation_tags.all():
new.selected_tags.add(tag)
# Reference cloned transitions
for transition in workflow.transitions.all():
new.transitions.add(clone_transition(transition))
return new
def get_workflow_for_wg(wg, default=None):
workflow = get_workflow_for_object(wg)
try:
workflow = workflow and workflow.wgworkflow
except WGWorkflow.DoesNotExist:
workflow = None
if not workflow:
if default:
workflow = default
else:
workflow = get_default_workflow_for_wg()
if not workflow:
return None
workflow = clone_workflow(workflow, name='%s workflow' % wg)
set_workflow_for_object(wg, workflow)
return workflow
def get_workflow_for_draft(draft):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return True if get_streamed_draft(draft) else None
workflow = get_workflow_for_object(draft)
try:
workflow = workflow and workflow.wgworkflow
except WGWorkflow.DoesNotExist:
workflow = None
if not workflow:
streamed_draft = get_streamed_draft(draft)
if not streamed_draft or not streamed_draft.stream:
return None
stream = streamed_draft.stream
if stream.document_group_attribute:
group = streamed_draft.get_group()
if not group:
return None
else:
workflow = get_workflow_for_wg(group, streamed_draft.stream.workflow)
else:
workflow = stream.workflow
set_workflow_for_object(draft, workflow)
return workflow
def get_workflow_history_for_draft(draft, entry_type=None):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.doc.proxy import ObjectHistoryEntryProxy
return ObjectHistoryEntryProxy.objects.filter(doc=draft).order_by('-time', '-id').select_related('by')
ctype = ContentType.objects.get_for_model(draft)
filter_param = {'content_type': ctype,
'content_id': draft.pk}
if entry_type:
filter_param.update({'%s__isnull' % entry_type: False})
history = ObjectHistoryEntry.objects.filter(**filter_param).\
select_related('objectworkflowhistoryentry', 'objectannotationtaghistoryentry',
'objectstreamhistoryentry')
return history
def get_annotation_tags_for_draft(draft):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.name.proxy import AnnotationTagObjectRelationProxy
from ietf.doc.utils import get_tags_for_stream_id
return AnnotationTagObjectRelationProxy.objects.filter(document=draft.pk, slug__in=get_tags_for_stream_id(draft.stream_id))
ctype = ContentType.objects.get_for_model(draft)
tags = AnnotationTagObjectRelation.objects.filter(content_type=ctype, content_id=draft.pk)
return tags
def get_state_for_draft(draft):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return draft.get_state("draft-stream-%s" % draft.stream_id)
return get_state(draft)
def get_state_by_name(state_name):
try:
return State.objects.get(name=state_name)
except State.DoesNotExist:
return None
def get_annotation_tag_by_name(tag_name):
try:
return AnnotationTag.objects.get(name=tag_name)
except AnnotationTag.DoesNotExist:
return None
def set_tag(obj, tag):
ctype = ContentType.objects.get_for_model(obj)
(relation, created) = AnnotationTagObjectRelation.objects.get_or_create(
content_type=ctype,
content_id=obj.pk,
annotation_tag=tag)
return relation
def set_tag_by_name(obj, tag_name):
try:
tag = AnnotationTag.objects.get(name=tag_name)
return set_tag(obj, tag)
except AnnotationTag.DoesNotExist:
return None
def reset_tag(obj, tag):
ctype = ContentType.objects.get_for_model(obj)
try:
tag_relation = AnnotationTagObjectRelation.objects.get(
content_type=ctype,
content_id=obj.pk,
annotation_tag=tag)
tag_relation.delete()
return True
except AnnotationTagObjectRelation.DoesNotExist:
return False
def reset_tag_by_name(obj, tag_name):
try:
tag = AnnotationTag.objects.get(name=tag_name)
return reset_tag(obj, tag)
except AnnotationTag.DoesNotExist:
return False
def set_state_for_draft(draft, state, estimated_date=None):
workflow = get_workflow_for_draft(draft)
if state in workflow.get_states():
set_state(draft, state)
relation = StateObjectRelation.objects.get(
content_type=ContentType.objects.get_for_model(draft),
content_id=draft.pk)
metadata = StateObjectRelationMetadata.objects.get_or_create(relation=relation)[0]
metadata.from_date = datetime.date.today()
metadata.to_date = estimated_date
metadata.save()
return state
return False
def notify_entry(entry, template, extra_notify=[]):
doc = entry.content
wg = doc.group.ietfwg
mail_list = set(['%s <%s>' % i.person.email() for i in wg.wgchair_set.all() if i.person.email()])
mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in wg.wgdelegate_set.all() if i.person.email()])
mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in doc.authors.all() if i.person.email()])
mail_list = mail_list.union(extra_notify)
mail_list = list(mail_list)
subject = 'Annotation tags have changed for draft %s' % doc
body = render_to_string(template, {'doc': doc,
'entry': entry,
})
mail = EmailMessage(subject=subject,
body=body,
to=mail_list,
from_email=settings.DEFAULT_FROM_EMAIL)
# Only send emails if we are not debug mode
if not settings.DEBUG:
mail.send()
return mail
def notify_tag_entry(entry, extra_notify=[]):
return notify_entry(entry, 'ietfworkflows/annotation_tags_updated_mail.txt', extra_notify)
def notify_state_entry(entry, extra_notify=[]):
return notify_entry(entry, 'ietfworkflows/state_updated_mail.txt', extra_notify)
def notify_stream_entry(entry, extra_notify=[]):
return notify_entry(entry, 'ietfworkflows/stream_updated_mail.txt', extra_notify)
def get_notification_receivers(doc, extra_notify):
persons = set()
res = []
for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")):
res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address))
persons.add(r.person)
for email in doc.authors.all():
if email.person not in persons:
res.append(email.formatted_email())
persons.add(email.person)
for x in extra_notify:
if not x in res:
res.append(x)
return res
def update_tags(request, obj, comment, person, set_tags=[], reset_tags=[], extra_notify=[]):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
doc = Document.objects.get(pk=obj.pk)
save_document_in_history(doc)
obj.tags.remove(*reset_tags)
obj.tags.add(*set_tags)
doc.time = datetime.datetime.now()
e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc)
l = []
if set_tags:
l.append(u"Annotation tag%s %s set." % (pluralize(set_tags), ", ".join(x.name for x in set_tags)))
if reset_tags:
l.append(u"Annotation tag%s %s cleared." % (pluralize(reset_tags), ", ".join(x.name for x in reset_tags)))
e.desc = " ".join(l)
e.save()
receivers = get_notification_receivers(doc, extra_notify)
send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL,
u"Annotations tags changed for draft %s" % doc.name,
'ietfworkflows/annotation_tags_updated_mail.txt',
dict(doc=doc,
entry=dict(setted=", ".join(x.name for x in set_tags),
unsetted=", ".join(x.name for x in reset_tags),
change_date=doc.time,
person=person,
comment=comment)))
return
ctype = ContentType.objects.get_for_model(obj)
setted = []
resetted = []
for tag in set_tags:
if isinstance(tag, basestring):
if set_tag_by_name(obj, tag):
setted.append(tag)
else:
if set_tag(obj, tag):
setted.append(tag.name)
for tag in reset_tags:
if isinstance(tag, basestring):
if reset_tag_by_name(obj, tag):
resetted.append(tag)
else:
if reset_tag(obj, tag):
resetted.append(tag.name)
entry = ObjectAnnotationTagHistoryEntry.objects.create(
content_type=ctype,
content_id=obj.pk,
setted=','.join(setted),
unsetted=','.join(resetted),
date=datetime.datetime.now(),
comment=comment,
person=person)
notify_tag_entry(entry, extra_notify)
def update_state(request, doc, comment, person, to_state, estimated_date=None, extra_notify=[]):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
doc = Document.objects.get(pk=doc.pk)
save_document_in_history(doc)
doc.time = datetime.datetime.now()
from_state = doc.get_state("draft-stream-%s" % doc.stream_id)
doc.set_state(to_state)
e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc)
e.desc = u"%s changed to <b>%s</b> from %s" % (to_state.type.label, to_state, from_state)
e.save()
# reminder
reminder_type = DocReminderTypeName.objects.get(slug="stream-s")
try:
reminder = DocReminder.objects.get(event__doc=doc, type=reminder_type,
active=True)
except DocReminder.DoesNotExist:
reminder = None
if estimated_date:
if not reminder:
reminder = DocReminder(type=reminder_type)
reminder.event = e
reminder.due = estimated_date
reminder.active = True
reminder.save()
elif reminder:
reminder.active = False
reminder.save()
receivers = get_notification_receivers(doc, extra_notify)
send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL,
u"State changed for draft %s" % doc.name,
'ietfworkflows/state_updated_mail.txt',
dict(doc=doc,
entry=dict(from_state=from_state,
to_state=to_state,
transition_date=doc.time,
person=person,
comment=comment)))
return
ctype = ContentType.objects.get_for_model(doc)
from_state = get_state_for_draft(doc)
to_state = set_state_for_draft(doc, to_state, estimated_date)
if not to_state:
return False
entry = ObjectWorkflowHistoryEntry.objects.create(
content_type=ctype,
content_id=doc.pk,
from_state=from_state and from_state.name or '',
to_state=to_state and to_state.name or '',
date=datetime.datetime.now(),
comment=comment,
person=person)
notify_state_entry(entry, extra_notify)
def update_stream(request, doc, comment, person, to_stream, extra_notify=[]):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
doc = Document.objects.get(pk=doc.pk)
save_document_in_history(doc)
doc.time = datetime.datetime.now()
from_stream = doc.stream
doc.stream = to_stream
doc.save()
e = DocEvent(type="changed_stream", time=doc.time, by=person, doc=doc)
e.desc = u"Stream changed to <b>%s</b> from %s" % (to_stream.name, from_stream.name)
e.save()
receivers = get_notification_receivers(doc, extra_notify)
send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL,
u"Stream changed for draft %s" % doc.name,
'ietfworkflows/stream_updated_mail.txt',
dict(doc=doc,
entry=dict(from_stream=from_stream,
to_stream=to_stream,
transition_date=doc.time,
person=person,
comment=comment)))
return
ctype = ContentType.objects.get_for_model(doc)
from_stream = get_stream_from_draft(doc)
to_stream = set_stream_for_draft(doc, to_stream)
entry = ObjectStreamHistoryEntry.objects.create(
content_type=ctype,
content_id=doc.pk,
from_stream=from_stream and from_stream.name or '',
to_stream=to_stream and to_stream.name or '',
date=datetime.datetime.now(),
comment=comment,
person=person)
notify_stream_entry(entry, extra_notify)
def get_full_info_for_draft(draft):
return dict(
streamed=get_streamed_draft(draft),
stream=get_stream_from_draft(draft),
workflow=get_workflow_for_draft(draft),
tags=[i.annotation_tag for i in get_annotation_tags_for_draft(draft)],
state=get_state_for_draft(draft),
shepherd=draft.shepherd if draft.shepherd_id else None,
)