text: # Copyright The IETF Trust 2016-2020, All Rights Reserved
  # -*- coding: utf-8 -*-
  
  
  import debug    # pyflakes:ignore
  import factory
  import factory.fuzzy
  import datetime
  
  from typing import Optional         # pyflakes:ignore
  
  from django.conf import settings
  
  from ietf.doc.models import ( Document, DocEvent, NewRevisionDocEvent, DocAlias, State, DocumentAuthor,
      StateDocEvent, BallotPositionDocEvent, BallotDocEvent, BallotType, IRSGBallotDocEvent, TelechatDocEvent,
      DocumentActionHolder, BofreqEditorDocEvent, BofreqResponsibleDocEvent )
  from ietf.group.models import Group
  from ietf.person.factories import PersonFactory
  from ietf.group.factories import RoleFactory
  from ietf.utils.text import xslugify
  
  
  def draft_name_generator(type_id,group,n):
          return '%s-%s-%s-%s%d'%( 
                type_id,
                'bogusperson',
                group.acronym if group else 'netherwhere',
                'musings',
                n,
              )
  
  class BaseDocumentFactory(factory.django.DjangoModelFactory):
      class Meta:
          model = Document
  
      title = factory.Faker('sentence',nb_words=5)
      abstract = factory.Faker('paragraph', nb_sentences=5)
      rev = '00'
      std_level_id = None                 # type: Optional[str]
      intended_std_level_id = None
      time = datetime.datetime.now()
      expires = factory.LazyAttribute(lambda o: o.time+datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE))
      pages = factory.fuzzy.FuzzyInteger(2,400)
  
  
      @factory.lazy_attribute_sequence
      def name(self, n):
          return draft_name_generator(self.type_id,self.group,n)
  
      newrevisiondocevent = factory.RelatedFactory('ietf.doc.factories.NewRevisionDocEventFactory','doc')
  
      @factory.post_generation
      def other_aliases(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
          alias = DocAliasFactory(name=obj.name)
          alias.docs.add(obj)
          if create and extracted:
              for name in extracted:
                  alias = DocAliasFactory(name=name)
                  alias.docs.add(obj)
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
          if create and extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
              if obj.type_id == 'draft':
                  if not obj.states.filter(type_id='draft-iesg').exists():
                      obj.set_state(State.objects.get(type_id='draft-iesg', slug='idexists'))
  
      @factory.post_generation
      def authors(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
          if create and extracted:
              order = 0
              for person in extracted:
                  DocumentAuthor.objects.create(document=obj, person=person, email=person.email(), order=order)
                  order += 1
  
      @factory.post_generation
      def relations(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
          if create and extracted:
              for (rel_id, doc) in extracted:
                  if isinstance(doc, Document):
                      docalias = doc.docalias.first()
                  elif isinstance(doc, DocAlias):
                      docalias = doc
                  else:
                      continue
                  obj.relateddocument_set.create(relationship_id=rel_id, target=docalias)
  
      @factory.post_generation
      def create_revisions(obj, create, extracted, **kwargs):  # pylint: disable=no-self-argument
          """Create additional revisions of the document
  
          Argument should be an iterable of revisions. Remember that range() is exclusive on the end
          index, so range(1, 10) stops at 9.
          """
          if create and extracted:
              for rev in extracted:
                  e = NewRevisionDocEventFactory(doc=obj, rev=f'{rev:02d}')
                  obj.rev = f'{rev:02d}'
                  obj.save_with_history([e])
  
      @classmethod
      def _after_postgeneration(cls, obj, create, results=None):
          """Save again the instance if creating and at least one hook ran."""
          if create and results:
              # Some post-generation hooks ran, and may have modified us.
              obj._has_an_event_so_saving_is_allowed = True
              obj.save()
  
  #TODO remove this - rename BaseDocumentFactory to DocumentFactory
  class DocumentFactory(BaseDocumentFactory):
  
      type_id = 'draft'
      group = factory.SubFactory('ietf.group.factories.GroupFactory',acronym='none')
  
  
  class IndividualDraftFactory(BaseDocumentFactory):
  
      type_id = 'draft'
      group = factory.SubFactory('ietf.group.factories.GroupFactory',acronym='none')
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
              if not obj.get_state('draft-iesg'):
                  obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
          else:
              obj.set_state(State.objects.get(type_id='draft',slug='active'))
              obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
  
  class IndividualRfcFactory(IndividualDraftFactory):
  
      alias2 = factory.RelatedFactory('ietf.doc.factories.DocAliasFactory','document',name=factory.Sequence(lambda n: 'rfc%04d'%(n+1000)))
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
          else:
              obj.set_state(State.objects.get(type_id='draft',slug='rfc'))
  
      @factory.post_generation
      def reset_canonical_name(obj, create, extracted, **kwargs): 
          if hasattr(obj, '_canonical_name'):
              del obj._canonical_name
          return None
  
  class WgDraftFactory(BaseDocumentFactory):
  
      type_id = 'draft'
      group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='wg')
      stream_id = 'ietf'
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
              if not obj.get_state('draft-iesg'):
                  obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
          else:
              obj.set_state(State.objects.get(type_id='draft',slug='active'))
              obj.set_state(State.objects.get(type_id='draft-stream-ietf',slug='wg-doc'))
              obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
  
  class WgRfcFactory(WgDraftFactory):
  
      alias2 = factory.RelatedFactory('ietf.doc.factories.DocAliasFactory','document',name=factory.Sequence(lambda n: 'rfc%04d'%(n+1000)))
  
      std_level_id = 'ps'
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
              if not obj.get_state('draft-iesg'):
                  obj.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
          else:
              obj.set_state(State.objects.get(type_id='draft',slug='rfc'))
              obj.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
  
      @factory.post_generation
      def reset_canonical_name(obj, create, extracted, **kwargs): 
          if hasattr(obj, '_canonical_name'):
              del obj._canonical_name
          return None
  
  class RgDraftFactory(BaseDocumentFactory):
  
      type_id = 'draft'
      group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='rg')
      stream_id = 'irtf'
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
              if not obj.get_state('draft-iesg'):
                  obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
          else:
              obj.set_state(State.objects.get(type_id='draft',slug='active'))
              obj.set_state(State.objects.get(type_id='draft-stream-irtf',slug='active'))
              obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
  
  
  class RgRfcFactory(RgDraftFactory):
  
      alias2 = factory.RelatedFactory('ietf.doc.factories.DocAliasFactory','document',name=factory.Sequence(lambda n: 'rfc%04d'%(n+1000)))
  
      std_level_id = 'inf'
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
              if not obj.get_state('draft-stream-irtf'):
                  obj.set_state(State.objects.get(type_id='draft-stream-irtf', slug='pub'))
              if not obj.get_state('draft-iesg'):
                  obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
          else:
              obj.set_state(State.objects.get(type_id='draft',slug='rfc'))
              obj.set_state(State.objects.get(type_id='draft-stream-irtf', slug='pub'))
              obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
  
      @factory.post_generation
      def reset_canonical_name(obj, create, extracted, **kwargs): 
          if hasattr(obj, '_canonical_name'):
              del obj._canonical_name
          return None          
  
  
  class CharterFactory(BaseDocumentFactory):
  
      type_id = 'charter'
      group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='wg')
      name = factory.LazyAttribute(lambda o: 'charter-ietf-%s'%o.group.acronym)
  
      @factory.post_generation
      def set_group_charter_document(obj, create, extracted, **kwargs):
          if not create:
              return
          obj.group.charter = extracted or obj
          obj.group.save()
  
  class StatusChangeFactory(BaseDocumentFactory):
      type_id='statchg'
  
      group = factory.SubFactory('ietf.group.factories.GroupFactory',acronym='iesg',type_id='ietf')
      name = factory.Sequence(lambda n: f'status-change-{n}-factoried')
  
      @factory.post_generation
      def changes_status_of(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (rel, target) in extracted:
                  obj.relateddocument_set.create(relationship_id=rel,target=extracted)
          else:
              obj.relateddocument_set.create(relationship_id='tobcp', target=WgRfcFactory().docalias.first())
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for state in extracted:
                  obj.set_state(state)
          else:
              obj.set_state(State.objects.get(type_id='statchg',slug='appr-sent'))
  
  
  class ConflictReviewFactory(BaseDocumentFactory):
      type_id='conflrev'
      
      @factory.post_generation
      def review_of(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              obj.relateddocument_set.create(relationship_id='conflrev',target=extracted.docalias.first())
          else:
              obj.relateddocument_set.create(relationship_id='conflrev',target=DocumentFactory(type_id='draft',group=Group.objects.get(type_id='individ')).docalias.first())
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for state in extracted:
                  obj.set_state(state)
          else:
              obj.set_state(State.objects.get(type_id='conflrev',slug='iesgeval'))
  
  # This is very skeletal. It is enough for the tests that use it now, but when it's needed, it will need to be improved with, at least, a group generator that backs the object with a review team.
  class ReviewFactory(BaseDocumentFactory):
      type_id = 'review'
      name = factory.LazyAttribute(lambda o: 'review-doesnotexist-00-%s-%s'%(o.group.acronym,datetime.date.today().isoformat()))
      group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='review')
  
  class DocAliasFactory(factory.django.DjangoModelFactory):
      class Meta:
          model = DocAlias
  
      @factory.post_generation
      def document(self, create, extracted, **kwargs):
          if create and extracted:
              self.docs.add(extracted)
  
      @factory.post_generation
      def docs(self, create, extracted, **kwargs):
          if create and extracted:
              for doc in extracted:
                  if not doc in self.docs.all():
                      self.docs.add(doc)
  
  
  class DocEventFactory(factory.django.DjangoModelFactory):
      class Meta:
          model = DocEvent
  
      type = 'added_comment'
      by = factory.SubFactory('ietf.person.factories.PersonFactory')
      doc = factory.SubFactory(DocumentFactory)
      desc = factory.Faker('sentence',nb_words=6)
  
      @factory.lazy_attribute
      def rev(self):
          return self.doc.rev
  
  class TelechatDocEventFactory(DocEventFactory):
      class Meta:
          model = TelechatDocEvent
  
      telechat_date = datetime.datetime.today()+datetime.timedelta(days=14)
      type = 'scheduled_for_telechat'
  
  class NewRevisionDocEventFactory(DocEventFactory):
      class Meta:
          model = NewRevisionDocEvent
  
      type = 'new_revision'
      rev = '00'
  
      @factory.lazy_attribute
      def desc(self):
           return 'New version available %s-%s'%(self.doc.name,self.rev)
  
  class StateDocEventFactory(DocEventFactory):
      class Meta:
          model = StateDocEvent
  
      type = 'changed_state'
      state_type_id = 'draft-iesg'
  
      @factory.post_generation
      def state(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              (state_type_id, state_slug) = extracted
              obj.state = State.objects.get(type_id=state_type_id,slug=state_slug)
          else:
              obj.state = State.objects.get(type_id='draft-iesg',slug='ad-eval')
          obj.save()
  
  # All of these Ballot* factories are extremely skeletal. Flesh them out as needed by tests.
  class BallotTypeFactory(factory.django.DjangoModelFactory):
      class Meta:
          model = BallotType
          django_get_or_create = ('slug','doc_type_id')
  
      doc_type_id = 'draft'
      slug = 'approve'
  
  
  class BallotDocEventFactory(DocEventFactory):
      class Meta:
          model = BallotDocEvent
  
      ballot_type = factory.SubFactory(BallotTypeFactory)
      type = 'created_ballot'
  
  class IRSGBallotDocEventFactory(BallotDocEventFactory):
      class Meta:
          model = IRSGBallotDocEvent
  
      duedate = datetime.datetime.now() + datetime.timedelta(days=14)
      ballot_type = factory.SubFactory(BallotTypeFactory, slug='irsg-approve')
  
  class BallotPositionDocEventFactory(DocEventFactory):
      class Meta:
          model = BallotPositionDocEvent
  
      type = 'changed_ballot_position'
      ballot = factory.SubFactory(BallotDocEventFactory)
      doc = factory.SelfAttribute('ballot.doc')  # point to same doc as the ballot
      balloter = factory.SubFactory('ietf.person.factories.PersonFactory')
      pos_id = 'discuss'
  
  class DocumentActionHolderFactory(factory.django.DjangoModelFactory):
      class Meta:
          model = DocumentActionHolder
          
      document = factory.SubFactory(WgDraftFactory)
      person = factory.SubFactory('ietf.person.factories.PersonFactory')
  
  class DocumentAuthorFactory(factory.django.DjangoModelFactory):
      class Meta:
          model = DocumentAuthor
  
      document = factory.SubFactory(DocumentFactory)
      person = factory.SubFactory('ietf.person.factories.PersonFactory')
      email = factory.LazyAttribute(lambda obj: obj.person.email())
      affiliation = factory.Faker('company')
      country = factory.Faker('country')
      order = factory.LazyAttribute(lambda o: o.document.documentauthor_set.count() + 1)
  
  class WgDocumentAuthorFactory(DocumentAuthorFactory):
      document = factory.SubFactory(WgDraftFactory)
  
  class BofreqEditorDocEventFactory(DocEventFactory):
      class Meta:
          model = BofreqEditorDocEvent
  
      type = "changed_editors"
      doc = factory.SubFactory('ietf.doc.factories.BofreqFactory')
  
  
      @factory.post_generation
      def editors(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              obj.editors.set(extracted)
          else:
              obj.editors.set(PersonFactory.create_batch(3))
          obj.desc = f'Changed editors to {", ".join(obj.editors.values_list("name",flat=True)) or "(None)"}'
  
  class BofreqResponsibleDocEventFactory(DocEventFactory):
      class Meta:
          model = BofreqResponsibleDocEvent
  
      type = "changed_responsible"
      doc = factory.SubFactory('ietf.doc.factories.BofreqFactory')
  
  
      @factory.post_generation
      def responsible(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              obj.responsible.set(extracted)
          else:
              ad = RoleFactory(group__type_id='area',name_id='ad').person
              obj.responsible.set([ad])
          obj.desc = f'Changed responsible leadership to {", ".join(obj.responsible.values_list("name",flat=True)) or "(None)"}'        
  
  class BofreqFactory(BaseDocumentFactory):
      type_id = 'bofreq'
      title = factory.Faker('sentence')
      name = factory.LazyAttribute(lambda o: 'bofreq-%s-%s'%(xslugify(o.requester_lastname), xslugify(o.title)))
  
      bofreqeditordocevent = factory.RelatedFactory('ietf.doc.factories.BofreqEditorDocEventFactory','doc')
      bofreqresponsibledocevent = factory.RelatedFactory('ietf.doc.factories.BofreqResponsibleDocEventFactory','doc')
  
      class Params:
          requester_lastname = factory.Faker('last_name')
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
          else:
              obj.set_state(State.objects.get(type_id='bofreq',slug='proposed'))
  
  
  class ProceedingsMaterialDocFactory(BaseDocumentFactory):
      type_id = 'procmaterials'
      abstract = ''
      expires = None
  
      @factory.post_generation
      def states(obj, create, extracted, **kwargs):
          if not create:
              return
          if extracted:
              for (state_type_id,state_slug) in extracted:
                  obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
          else:
              obj.set_state(State.objects.get(type_id='procmaterials', slug='active'))
  