class Contact(models.Model):
    """docstring for Contact"""
    contact_type = models.ForeignKey('ContactType')
    person = models.ForeignKey(Person)
    value = models.CharField(max_length=512)
    is_bounced = models.BooleanField(default=False)
    owner = models.ForeignKey(User, related_name="contacts", null=True)
    writeitinstance = models.ForeignKey('instance.WriteItInstance', related_name="contacts", null=True)
    popit_identifier = models.CharField(max_length=512, null=True)
    enabled = models.BooleanField(default=True)

    def __unicode__(self):
        return _('%(contact)s (%(type)s) for %(person)s') % {
            'contact': self.value,
            'type': self.contact_type.label_name,
            'person': self.person.name,
            }

    def set_outbound_messages_to_ready(self):
        for outbound_message in self.outboundmessage_set.all():
            outbound_message.status = 'ready'
            outbound_message.save()

    def resend_messages(self):
        self.is_bounced = False
        self.save()
        for outbound_message in self.outboundmessage_set.filter(status="error"):
            outbound_message.send()

    @classmethod
    def are_there_contacts_for(cls, person):
        if person.contact_set.exclude(is_bounced=True).exists():
            return True
        return False

    def save(self, *args, **kwargs):
        super(Contact, self).save(*args, **kwargs) class ContactType(models.Model):
    """This class contain all contact types"""
    name = models.CharField(max_length=255)
    label_name = models.CharField(max_length=255)

    def __unicode__(self):
        return self.label_name class WriteItInstance(models.Model):
    """WriteItInstance: Entity that groups messages and people
    for usability purposes. E.g. 'Candidates running for president'"""
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=512, blank=True)
    slug = AutoSlugField(populate_from='name', unique=True)
    persons = models.ManyToManyField(Person,
        related_name='writeit_instances',
        through='Membership')
    owner = models.ForeignKey(User, related_name="writeitinstances")

    def add_person(self, person):
        Membership.objects.get_or_create(writeitinstance=self, person=person)

    @property
    def persons_with_contacts(self):
        return self.persons.filter(contact__writeitinstance=self, contact__isnull=False).distinct()

    def relate_with_persons_from_popit_api_instance(self, popit_api_instance):
        try:
            popit_api_instance.fetch_all_from_api(writeitinstance=self)
        except ConnectionError, e:
            self.do_something_with_a_vanished_popit_api_instance(popit_api_instance)
            e.message = _('We could not connect with the URL')
            return (False, e)
        except Exception, e:
            self.do_something_with_a_vanished_popit_api_instance(popit_api_instance)
            return (False, e)
        persons = Person.objects.filter(api_instance=popit_api_instance)
        for person in persons:
            # There could be several memberships created.
            memberships = Membership.objects.filter(writeitinstance=self, person=person)
            if memberships.count() == 0:
                Membership.objects.create(writeitinstance=self, person=person)
            if memberships.count() > 1:
                membership = memberships[0]
                memberships.exclude(id=membership.id).delete()

        return (True, None)

    def do_something_with_a_vanished_popit_api_instance(self, popit_api_instance):
        pass

    def _load_persons_from_a_popit_api(self, popit_api_instance):
        success_relating_people, error = self.relate_with_persons_from_popit_api_instance(popit_api_instance)
        record = WriteitInstancePopitInstanceRecord.objects.get(
            writeitinstance=self,
            popitapiinstance=popit_api_instance
            )
        if success_relating_people:
            record.set_status('success')
        else:
            if isinstance(error, ConnectionError):
                record.set_status('error', _('We could not connect with the URL'))
            else:
                record.set_status('error', error.message)
        return (success_relating_people, error)

    def load_persons_from_a_popit_api(self, popit_url):
        '''This is an async wrapper for getting people from the api'''
        popit_api_instance, created = PopitApiInstance.objects.get_or_create(url=popit_url)
        record, created = WriteitInstancePopitInstanceRecord.objects.get_or_create(
            writeitinstance=self,
            popitapiinstance=popit_api_instance
            )
        if not created:
            record.updated = datetime.datetime.today()
            record.save()
        record.set_status('inprogress')
        from nuntium.tasks import pull_from_popit
        return pull_from_popit.delay(self, popit_api_instance)

    def get_absolute_url(self):
        return reverse('instance_detail', subdomain=self.slug)

    @property
    def pulling_from_popit_status(self):
        records = WriteitInstancePopitInstanceRecord.objects.filter(writeitinstance=self)
        result = {'nothing': 0, 'inprogress': 0, 'success': 0, 'error': 0}
        for record in records:
            result[record.status] += 1
        return result

    @property
    def can_create_messages(self):
        if self.config.allow_messages_using_form and \
                self.contacts.exists():
            return True

        return False

    def __unicode__(self):
        return self.name class Message(models.Model):
    """Message: Class that contain the info for a model, \
    despite of the input and the output channels. Subject \
    and content are mandatory fields"""
    author_name = models.CharField(max_length=512)
    author_email = models.EmailField()
    subject = models.CharField(max_length=255)
    content = models.TextField()
    writeitinstance = models.ForeignKey(WriteItInstance)
    confirmated = models.BooleanField(default=False)
    slug = models.SlugField(max_length=255, unique=True)
    public = models.BooleanField(default=True)
    moderated = models.NullBooleanField()
    created = models.DateTimeField(auto_now_add=True, null=True)
    updated = models.DateTimeField(auto_now=True, null=True)

    objects = MessagesManager()
    public_objects = PublicMessagesManager()
    moderation_required_objects = NonModeratedMessagesManager()

    class Meta:
        ordering = ["-created"]

    def __init__(self, *args, **kwargs):
        self.persons = None
        if 'persons' in kwargs:
            self.persons = kwargs.pop('persons')

        super(Message, self).__init__(*args, **kwargs)

    def clean(self):
        try:
            rate_limiter = RateLimiter.objects.get(
                writeitinstance=self.writeitinstance,
                email=self.author_email,
                day=datetime.date.today()
                )
            if self.writeitinstance.config.rate_limiter > 0 and \
                    rate_limiter.count >= self.writeitinstance.config.rate_limiter:
                raise ValidationError(_('You have reached '
                    + 'your limit for today please try again tomorrow'))
        except ObjectDoesNotExist:
            pass
        super(Message, self).clean()

    #TODO: only new outbound_messages
    def recently_confirmated(self):
        status = 'ready'
        if not self.public or \
                self.writeitinstance.config.moderation_needed_in_all_messages:
            moderation, created = Moderation.objects.get_or_create(message=self)
            self.send_moderation_mail()
            status = 'needmodera'
        for outbound_message in self.outbound_messages:
            outbound_message.status = status
            outbound_message.save()

        if self.author_email:
            Subscriber.objects.create(email=self.author_email, message=self)
        self.confirmated = True
        self.save()

    @property
    def people(self):
        people = Person.objects.filter(
            Q(contact__outboundmessage__message=self) |
            Q(nocontactom__message=self)
            ).distinct()

        return people

    @property
    def outbound_messages(self):
        no_contact_oms = NoContactOM.objects.filter(message=self)
        outbound_messages = OutboundMessage.objects.filter(message=self)

        return list(chain(no_contact_oms, outbound_messages))

    def get_absolute_url(self):
        return reverse(
            'thread_read',
            subdomain=self.writeitinstance.slug,
            kwargs={
                'slug': self.slug,
                },
            )

    def slugifyme(self):
        if not slugify(unidecode(unicode(self.subject))):
            self.subject = '-'

        self.slug = slugify(unidecode(unicode(self.subject)))
        #Previously created messages with the same slug

        regex = "^" + self.slug + "(-[0-9]*){0,1}$"
        previously = Message.objects.filter(slug__regex=regex)
        count = 1
        for message in previously:
            new_regex = "^" + self.slug + "-(\d+){0,1}$"
            if re.match(new_regex, message.slug) is not None:
                groups = re.match(new_regex, message.slug).groups()
                if len(groups) > 0:
                    if int(groups[0]) > count:
                        count = int(groups[0])

        previously = previously.count()
        if previously > 0:
            self.slug = self.slug + '-' + str(count + 1)

    def veryfy_people(self):
        if not self.persons:
            raise TypeError(_('A message needs persons to be sent'))

    def create_moderation(self):
        Moderation.objects.create(message=self)

    def create_outbound_messages(self):
        if not self.persons:
            return
        for person in self.persons:
            self.create_outbound_messages_to_person(person)

    def create_outbound_messages_to_person(self, person):
        if not person.contact_set.all():
            NoContactOM.objects.get_or_create(message=self, person=person, site=Site.objects.get_current())
            return
        for contact in person.contact_set.filter(writeitinstance=self.writeitinstance):
            if not contact.is_bounced:
                OutboundMessage.objects.get_or_create(
                    contact=contact, message=self, site=Site.objects.get_current())

    def save(self, *args, **kwargs):
        created = self.id is None
        if created and self.writeitinstance.config.moderation_needed_in_all_messages:
            self.moderated = False
        super(Message, self).save(*args, **kwargs)
        if created and not self.public:
            self.create_moderation()
        self.create_outbound_messages()

    def set_to_ready(self):
        for outbound_message in self.outbound_messages:
            outbound_message.status = 'ready'
            outbound_message.save()

    def send_moderation_mail(self):
        url_reject = reverse('moderation_rejected',
            subdomain=self.writeitinstance.slug,
            kwargs={
                'slug': self.moderation.key
            })

        url_accept = reverse('moderation_accept',
            subdomain=self.writeitinstance.slug,
            kwargs={
                'slug': self.moderation.key
            })

        context = {
            'owner_name': self.writeitinstance.owner.username,
            'author_name': self.author_name,
            'author_email': self.author_email,
            'recipients': u', '.join([x.name for x in self.people]),
            'subject': self.subject,
            'content': self.content,
            'site_name': self.writeitinstance.name,
            'url_reject': url_reject,
            'url_accept': url_accept,
            }

        if settings.SEND_ALL_EMAILS_FROM_DEFAULT_FROM_EMAIL:
            from_email = settings.DEFAULT_FROM_EMAIL
        else:
            from_domain = self.writeitinstance.config.custom_from_domain\
                or settings.DEFAULT_FROM_DOMAIN
            from_email = self.writeitinstance.slug + "@" + from_domain

        connection = self.writeitinstance.config.get_mail_connection()
        msg = EmailMultiAlternatives(
            moderation_subject.format(**context),
            template_with_wrap(moderation_content_txt, context),
            from_email,
            [self.writeitinstance.owner.email],
            connection=connection,
            )
        msg.send()

    def moderate(self):
        if not self.confirmated:
            raise ValidationError(_('The message needs '
                + 'to be confirmated first'))
        self.set_to_ready()
        self.moderation.success()

    def __unicode__(self):
        return _('%(subject)s at %(instance)s') % {
            'subject': self.subject,
            'instance': self.writeitinstance.name,
            } class OutboundMessage(AbstractOutboundMessage):
    """docstring for OutboundMessage: This class is \
    the message delivery unit. The OutboundMessage is \
    the one that will be tracked in order \
    to know the actual status of the message"""

    contact = models.ForeignKey(Contact)

    objects = OutboundMessageManager()

    def __unicode__(self):
        return _('%(subject)s sent to %(person)s '
                 + '(%(contact)s) at %(instance)s') % {
            'subject': self.message.subject,
            'person': self.contact.person.name,
            'contact': self.contact.value,
            'instance': self.message.writeitinstance.name,
        }

    def send(self):

        if self.status == "sent":
            return
        plugins = OutputPlugin.get_plugins()
        sent_completely = True

        #This is not the way it should be done
        #there should be some way to get the plugin from a contact_type
        outbound_message_plugin = None
        for plugin in plugins:
            if self.contact.contact_type == plugin.get_contact_type():
                outbound_message_plugin = plugin
                break
        outbound_record, created = OutboundMessagePluginRecord.objects.get_or_create(
            outbound_message=self,
            plugin=plugin.get_model(),
            )
        if not outbound_record.try_again:
            return

        if outbound_message_plugin is None:
            return

        successfully_sent, fatal_error = plugin.send(self)
        try_again = True
        if successfully_sent:
            try_again = False
        else:
            sent_completely = False
            if fatal_error:
                try_again = False
        outbound_record.sent = successfully_sent
        outbound_record.try_again = try_again
        outbound_record.number_of_attempts += 1
        outbound_record.save()
        # Also here comes what should be any priorization on the channels
        # that I'm not workin on right now and it should send to all of them
        # should I have another state "partly sent"? or
        # is it enough when I say "ready"?
        if sent_completely:
            self.status = "sent"
            self.save()
        MessageRecord.objects.create(content_object=self, status=self.status) class MessageRecord(models.Model):
    status = models.CharField(max_length=255)
    datetime = models.DateField(default=now)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

    def __unicode__(self):
        outbound_message = self.content_object
        return _('The message "%(subject)s" at %(instance)s'
           + ' turned %(status)s at %(date)s') % {
            'subject': outbound_message.message.subject,
            'instance': outbound_message.message.writeitinstance,
            'status': self.status,
            'date': str(self.datetime)
            } class OutboundMessagePluginRecord(models.Model):
    outbound_message = models.ForeignKey(OutboundMessage)
    plugin = models.ForeignKey(Plugin)
    sent = models.BooleanField(default=False)
    number_of_attempts = models.PositiveIntegerField(default=0)
    try_again = models.BooleanField(default=True) class OutboundMessageIdentifier(models.Model):
    outbound_message = models.OneToOneField(OutboundMessage)
    key = models.CharField(max_length=255)

    @classmethod
    def create_answer(cls, identifier_key, content, content_html=""):
        identifier = cls.objects.get(key=identifier_key)
        message = identifier.outbound_message.message
        person = identifier.outbound_message.contact.person
        the_created_answer = Answer.objects.create(message=message,
            person=person,
            content=content,
            content_html=content_html)
        return the_created_answer

    def save(self, *args, **kwargs):
        if not self.key:
            self.key = str(uuid.uuid1().hex)
        super(OutboundMessageIdentifier, self).save(*args, **kwargs) class Answer(models.Model):
    content = models.TextField()
    content_html = models.TextField()
    person = models.ForeignKey(Person)
    message = models.ForeignKey(Message, related_name='answers')
    created = models.DateTimeField(auto_now=True, null=True)

    def save(self, *args, **kwargs):
        memberships = self.message.writeitinstance.membership_set.filter(person=self.person)
        if memberships.count() == 0:
            raise AttributeError(_("This guy does not belong here"))
        super(Answer, self).save(*args, **kwargs)

    def __unicode__(self):
        return _("%(person)s said \"%(content)s\""
                 + " to the message %(message)s") % {
            'person': self.person.name,
            'content': self.content,
            'message': self.message.subject,
            } class NoContactOM(AbstractOutboundMessage):
    person = models.ForeignKey(Person) class AbstractOutboundMessage(models.Model):
    STATUS_CHOICES = (
        ("new", _("Newly created")),
        ("ready", _("Ready to send")),
        ("sent", _("Sent")),
        ("error", _("Error sending it")),
        ("needmodera", _("Needs moderation")),
        )

    message = models.ForeignKey(Message)
    status = models.CharField(
        max_length="10",
        choices=STATUS_CHOICES,
        default="new",
        )
    site = models.ForeignKey(Site)

    class Meta:
        abstract = True
from global_test_case import GlobalTestCase as TestCase
from django.db import IntegrityError
from django.db import models
from django.utils.translation import ugettext as _
from contactos.models import Contact, ContactType
from instance.models import WriteItInstance
from ..models import Message, OutboundMessage, \
    MessageRecord, OutboundMessagePluginRecord, \
    OutboundMessageIdentifier, Answer, \
    NoContactOM, AbstractOutboundMessage
from django.contrib.sites.models import Site
from popit.models import Person, ApiInstance
from mock import patch
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from plugin_mock.mental_message_plugin import MentalMessage, FatalException, TryAgainException
            message=self.message, contact=self.contact1, site=Site.objects.get_current())
        expected_unicode = _('%(subject)s sent to %(person)s (%(contact)s) at %(instance)s') % {
            'subject': self.message.subject,
            'person': self.contact1.person.name,
            'contact': self.contact1.value,
            'instance': self.message.writeitinstance.name,
            }
        self.assertEquals(outbound_message.__unicode__(), expected_unicode)

    def test_outbound_messsages_creation_on_message_save(self):
        """Creates an outbound message when a message is created"""
        message = Message.objects.create(
            content='Content 1',
            author_name='Felipe',
            author_email="falvarez@votainteligente.cl",
            subject='Fiera es una perra feroz',
            writeitinstance=self.message.writeitinstance,
            persons=[self.contact1.person],
            )

        new_outbound_messages = OutboundMessage.objects.filter(message=message)

        self.assertEquals(new_outbound_messages.count(), 1)
        outbound_message = new_outbound_messages[0]
        self.assertEquals(outbound_message.contact, self.contact1)
        self.assertEquals(outbound_message.message, message)

    def test_successful_send(self):
        outbound_message = OutboundMessage.objects.create(
            message=self.message,
            contact=self.contact1,
            site=Site.objects.get_current(),
            )
        outbound_message.send()

        outbound_message = OutboundMessage.objects.get(id=outbound_message.id)
        self.assertEquals(outbound_message.status, "sent")

    def test_there_is_a_manager_that_retrieves_all_the_available_messages(self):
        outbound_message = OutboundMessage.objects.create(
            message=self.message,
            contact=self.contact1,
            status="ready",
            site=Site.objects.get_current(),
            )

        self.assertEquals(OutboundMessage.objects.to_send().filter(id=outbound_message.id).count(), 1)

    def test_create_a_new_outbound_message_identifier_on_creation(self):
        outbound_message = OutboundMessage.objects.create(
            message=self.message,
            contact=self.contact1,
            status="ready",
            site=Site.objects.get_current(),
            )
        identifier = OutboundMessageIdentifier.objects.get(outbound_message=outbound_message)

        self.assertTrue(identifier)

    def test_statuses(self):
        self.assertIn(("new", _("Newly created")), OutboundMessage.STATUS_CHOICES)
        self.assertIn(("ready", _("Ready to send")), OutboundMessage.STATUS_CHOICES)
        self.assertIn(("sent", _("Sent")), OutboundMessage.STATUS_CHOICES)
        self.assertIn(("error", _("Error sending it")), OutboundMessage.STATUS_CHOICES)
        self.assertIn(("needmodera", _("Needs moderation")), OutboundMessage.STATUS_CHOICES)


class OutboundMessageIdentifierTestCase(TestCase):
    def setUp(self):
        super(OutboundMessageIdentifierTestCase, self).setUp()
        self.outbound_message = OutboundMessage.objects.get(id=1)
        self.message = Message.objects.get(id=1)
        self.contact1 = Contact.objects.get(id=1)

    def test_create_an_outbound_message_identifier_when_creating_(self):
        with patch('uuid.uuid1') as string:
            string.return_value.hex = 'oliwi'
            outbound_message = OutboundMessage.objects.create(message=self.message, contact=self.contact1, site=Site.objects.get_current())
            identifier = OutboundMessageIdentifier.objects.get(outbound_message=outbound_message)
            string.assert_called()
            # the key is created when saved
            self.assertEquals(identifier.key, 'oliwi')

    def test_create_only_one_identifier_per_outbound_message(self):
        with self.assertRaises(IntegrityError):
            OutboundMessageIdentifier.objects.create(outbound_message=self.outbound_message)

    def test_the_key_does_not_change_on_save_twice(self):
        identifier = OutboundMessageIdentifier.objects.get(outbound_message=self.outbound_message)
        expected_key = identifier.key

        identifier.save()

        self.assertEquals(expected_key, identifier.key)

    def test_create_an_answer_only_having_an_identifier(self):
        identifier = OutboundMessageIdentifier.objects.get(outbound_message=self.outbound_message)
        answer_content = "La fiera no tiene pulgas."
        OutboundMessageIdentifier.create_answer(identifier.key, answer_content)
        the_person = self.outbound_message.contact.person
        the_answer = Answer.objects.get(message=self.outbound_message.message, person=the_person, content=answer_content)

        self.assertEquals(the_answer.content, answer_content)

    def test_create_an_answer_returns_the_answer_created(self):
        '''OutboundMessageIdentifier.create_answer returns an answer'''
        identifier = OutboundMessageIdentifier.objects.get(outbound_message=self.outbound_message)
        answer_content = "La fiera no tiene pulgas."
        returned_answer = OutboundMessageIdentifier.create_answer(identifier.key, answer_content)
        the_person = self.outbound_message.contact.person
        the_answer = Answer.objects.get(message=self.outbound_message.message, person=the_person, content=answer_content)

        self.assertEquals(returned_answer, the_answer)

    def test_create_an_answer_with_content_html(self):
        '''OutboundMessageIdentifier.create_answer can receive a content_html field'''
        identifier = OutboundMessageIdentifier.objects.get(outbound_message=self.outbound_message)
        answer_content = u"La fiera no tiene pulgas."
        answer_content_html = u'<p>La fiera no tiene pulgas.</p>'


        returned_answer = OutboundMessageIdentifier.create_answer(identifier.key,