import datetime from django.conf import settings from django import forms from django.template.loader import render_to_string from workflows.models import State from workflows.utils import set_workflow_for_object from ietf.idtracker.models import PersonOrOrgInfo, IETFWG, InternetDraft from ietf.wgchairs.accounts import get_person_for_user from ietf.ietfworkflows.models import Stream, StreamDelegate from ietf.ietfworkflows.utils import (get_workflow_for_draft, get_workflow_for_wg, get_state_for_draft, get_state_by_name, update_state, FOLLOWUP_TAG, get_annotation_tags_for_draft, update_tags, update_stream) from ietf.ietfworkflows.accounts import is_secretariat from ietf.ietfworkflows.streams import (get_stream_from_draft, get_streamed_draft, get_stream_by_name, set_stream_for_draft) from ietf.ietfworkflows.constants import CALL_FOR_ADOPTION, IETF_STREAM from ietf.doc.utils import get_tags_for_stream_id from ietf.doc.models import save_document_in_history, DocEvent, Document from ietf.name.models import DocTagName, StreamName, RoleName from ietf.group.models import Group, GroupStateTransitions, Role from ietf.group.utils import save_group_in_history from ietf.person.models import Person, Email class StreamDraftForm(forms.Form): can_cancel = False template = None def __init__(self, *args, **kwargs): self.draft = kwargs.pop('draft', None) self.user = kwargs.pop('user', None) self.person = get_person_for_user(self.user) self.workflow = get_workflow_for_draft(self.draft) self.message = {} super(StreamDraftForm, self).__init__(*args, **kwargs) def get_message(self): return self.message def set_message(self, msg_type, msg_value): self.message = {'type': msg_type, 'value': msg_value, } def __unicode__(self): return render_to_string(self.template, {'form': self}) class NoWorkflowStateForm(StreamDraftForm): comment = forms.CharField(widget=forms.Textarea) weeks = forms.IntegerField(required=False) wg = forms.ChoiceField(required=False) template = 'ietfworkflows/noworkflow_state_form.html' def __init__(self, *args, **kwargs): super(NoWorkflowStateForm, self).__init__(*args, **kwargs) self.wgs = None if is_secretariat(self.user): wgs = IETFWG.objects.all().order_by('group_acronym__acronym') else: if settings.USE_DB_REDESIGN_PROXY_CLASSES: wgs = IETFWG.objects.filter(type="wg", state="active", role__name__in=("chair", "delegate"), role__person__user=self.user).order_by('acronym').distinct() else: wgs = set([i.group_acronym for i in self.person.wgchair_set.all()]).union(set([i.wg for i in self.person.wgdelegate_set.all()])) wgs = list(wgs) wgs.sort(lambda x, y: cmp(x.group_acronym.acronym, y.group_acronym.acronym)) self.wgs = wgs if settings.USE_DB_REDESIGN_PROXY_CLASSES: self.fields['wg'].choices = [(i.pk, '%s - %s' % (i.acronym, i.name)) for i in self.wgs] else: self.fields['wg'].choices = [(i.pk, '%s - %s' % (i.group_acronym.acronym, i.group_acronym.name)) for i in self.wgs] def save(self): comment = self.cleaned_data.get('comment').strip() weeks = self.cleaned_data.get('weeks') wg = IETFWG.objects.get(pk=self.cleaned_data.get('wg')) estimated_date = None if weeks: now = datetime.date.today() estimated_date = now + datetime.timedelta(weeks=weeks) if settings.USE_DB_REDESIGN_PROXY_CLASSES: # do changes on real Document object instead of proxy to avoid trouble doc = Document.objects.get(pk=self.draft.pk) save_document_in_history(doc) doc.time = datetime.datetime.now() new_stream = StreamName.objects.get(slug="ietf") if doc.stream != new_stream: e = DocEvent(type="changed_stream") e.time = doc.time e.by = self.user.get_profile() e.doc = doc e.desc = u"Changed to %s" % new_stream.name if doc.stream: e.desc += u" from %s" % doc.stream.name e.save() doc.stream = new_stream if doc.group.pk != wg.pk: e = DocEvent(type="changed_group") e.time = doc.time e.by = self.user.get_profile() e.doc = doc e.desc = u"Changed group to %s (%s)" % (wg.name, wg.acronym.upper()) if doc.group.type_id != "individ": e.desc += " from %s (%s)" % (doc.group.name, doc.group.acronym) e.save() doc.group_id = wg.pk doc.save() self.draft = InternetDraft.objects.get(pk=doc.pk) # make sure proxy object is updated else: workflow = get_workflow_for_wg(wg) set_workflow_for_object(self.draft, workflow) stream = get_stream_by_name(IETF_STREAM) streamed = get_streamed_draft(self.draft) if not streamed: set_stream_for_draft(self.draft, stream) streamed = get_streamed_draft(self.draft) streamed.stream = stream streamed.group = wg streamed.save() if settings.USE_DB_REDESIGN_PROXY_CLASSES: from ietf.doc.models import State to_state = State.objects.get(slug="c-adopt", type="draft-stream-%s" % self.draft.stream_id) else: to_state = get_state_by_name(CALL_FOR_ADOPTION) update_state(self.request, self.draft, comment=comment, person=self.person, to_state=to_state, estimated_date=estimated_date) if settings.USE_DB_REDESIGN_PROXY_CLASSES: if comment: e = DocEvent(type="added_comment") e.time = self.draft.time e.by = self.person e.doc_id = self.draft.pk e.desc = comment e.save() class DraftTagsStateForm(StreamDraftForm): comment = forms.CharField(widget=forms.Textarea) new_state = forms.ChoiceField() weeks = forms.IntegerField(required=False) tags = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False) template = 'ietfworkflows/state_form.html' def __init__(self, *args, **kwargs): super(DraftTagsStateForm, self).__init__(*args, **kwargs) self.state = get_state_for_draft(self.draft) self.fields['new_state'].choices = self.get_states() if self.is_bound: for key, value in self.data.items(): if key.startswith('transition_'): new_state = self.get_new_state(key) if new_state: self.data = self.data.copy() self.data.update({'new_state': new_state.id}) if key.startswith('new_state_'): # hack to get value from submit buttons self.data = self.data.copy() self.data['new_state'] = key.replace('new_state_', '') if settings.USE_DB_REDESIGN_PROXY_CLASSES: possible_tags = get_tags_for_stream_id(self.draft.stream_id) if self.draft.stream_id == "ietf" and self.draft.group: unused_tags = self.draft.group.unused_tags.values_list("slug", flat=True) possible_tags = [t for t in possible_tags if t not in unused_tags] self.available_tags = DocTagName.objects.filter(slug__in=possible_tags) self.tags = self.draft.tags.filter(slug__in=possible_tags) else: self.available_tags = self.workflow.get_tags() self.tags = [i.annotation_tag for i in get_annotation_tags_for_draft(self.draft)] self.fields['tags'].choices = [(i.pk, i.name) for i in self.available_tags] self.fields['tags'].initial = [i.pk for i in self.tags] def get_new_state(self, key): transition_id = key.replace('transition_', '') transition = self.get_transitions().filter(id=transition_id) if transition: return transition[0].destination return None def get_transitions(self): if settings.USE_DB_REDESIGN_PROXY_CLASSES: return [] return self.state.transitions.filter(workflow=self.workflow) def get_next_states(self): if settings.USE_DB_REDESIGN_PROXY_CLASSES: from ietf.doc.models import State state_type = "draft-stream-%s" % self.draft.stream_id s = self.draft.get_state(state_type) next_states = [] if s: next_states = s.next_states.all() if self.draft.stream_id == "ietf" and self.draft.group: transitions = self.draft.group.groupstatetransitions_set.filter(state=s) if transitions: next_states = transitions[0].next_states.all() else: # return the initial state states = State.objects.filter(type=state_type).order_by('order') if states: next_states = states[:1] unused = [] if self.draft.group: unused = self.draft.group.unused_states.values_list("pk", flat=True) return [n for n in next_states if n.pk not in unused] return [] def get_states(self): if settings.USE_DB_REDESIGN_PROXY_CLASSES: from ietf.doc.models import State states = State.objects.filter(type="draft-stream-%s" % self.draft.stream_id) if self.draft.stream_id == "ietf" and self.draft.group: unused_states = self.draft.group.unused_states.values_list("pk", flat=True) states = [s for s in states if s.pk not in unused_states] return [(i.pk, i.name) for i in states] return [(i.pk, i.name) for i in self.workflow.get_states()] def save_tags(self): comment = self.cleaned_data.get('comment') new_tags = self.cleaned_data.get('tags') set_tags = [tag for tag in self.available_tags if str(tag.pk) in new_tags and tag not in self.tags] reset_tags = [tag for tag in self.available_tags if str(tag.pk) not in new_tags and tag in self.tags] followup = bool([tag for tag in set_tags if tag.name == FOLLOWUP_TAG]) extra_notify = [] if followup: try: shepherd = self.draft.shepherd if shepherd: if settings.USE_DB_REDESIGN_PROXY_CLASSES: extra_notify = [shepherd.formatted_email()] else: extra_notify = ['%s <%s>' % shepherd.email()] except PersonOrOrgInfo.DoesNotExist: pass if not set_tags and not reset_tags: return update_tags(self.request, self.draft, comment=comment, person=self.person, set_tags=set_tags, reset_tags=reset_tags, extra_notify=extra_notify) def save_state(self): comment = self.cleaned_data.get('comment') if settings.USE_DB_REDESIGN_PROXY_CLASSES: from ietf.doc.models import State state = State.objects.get(pk=self.cleaned_data.get('new_state')) weeks = self.cleaned_data.get('weeks') estimated_date = None if weeks: now = datetime.date.today() estimated_date = now + datetime.timedelta(weeks=weeks) update_state(self.request, self.draft, comment=comment, person=self.person, to_state=state, estimated_date=estimated_date) def save(self): self.save_tags() if 'only_tags' not in self.data.keys(): self.save_state() if settings.USE_DB_REDESIGN_PROXY_CLASSES: comment = self.cleaned_data.get('comment').strip() if comment: e = DocEvent(type="added_comment") e.time = self.draft.time e.by = self.person e.doc_id = self.draft.pk e.desc = comment e.save() class DraftStreamForm(StreamDraftForm): comment = forms.CharField(widget=forms.Textarea) if settings.USE_DB_REDESIGN_PROXY_CLASSES: stream = forms.ModelChoiceField(StreamName.objects.exclude(slug="legacy")) else: stream = forms.ModelChoiceField(Stream.objects.all()) template = 'ietfworkflows/stream_form.html' def __init__(self, *args, **kwargs): super(DraftStreamForm, self).__init__(*args, **kwargs) self.stream = get_stream_from_draft(self.draft) self.tags = [i.annotation_tag for i in get_annotation_tags_for_draft(self.draft)] if self.stream: self.fields['stream'].initial = self.stream.pk def save(self): comment = self.cleaned_data.get('comment').strip() to_stream = self.cleaned_data.get('stream') update_stream(self.request, self.draft, comment=comment, person=self.person, to_stream=to_stream) if comment: e = DocEvent(type="added_comment") e.time = self.draft.time e.by = self.person e.doc_id = self.draft.pk e.desc = comment e.save() class StreamDelegatesForm(forms.Form): email = forms.EmailField() def __init__(self, *args, **kwargs): self.stream = kwargs.pop('stream') super(StreamDelegatesForm, self).__init__(*args, **kwargs) def get_person(self, email): if settings.USE_DB_REDESIGN_PROXY_CLASSES: persons = Person.objects.filter(email__address=email).distinct() else: persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).distinct() if not persons: return None return persons[0] def clean_email(self): email = self.cleaned_data.get('email') self.person = self.get_person(email) if not self.person: raise forms.ValidationError('There is no user with this email in the system') return email def save(self): if settings.USE_DB_REDESIGN_PROXY_CLASSES: stream_group = Group.objects.get(acronym=self.stream.slug) save_group_in_history(stream_group) Role.objects.get_or_create(person=self.person, group=stream_group, name=RoleName.objects.get(slug="delegate"), email=Email.objects.get(address=self.cleaned_data.get('email'))) return StreamDelegate.objects.get_or_create( person=self.person, stream=self.stream)