Exemple #1
0
class Event(Model):
    VERSION = 2
    MIGRATOR = EventMigrator

    # key is event_id
    event = VumiMessage(TransportEvent)
    message = ForeignKey(OutboundMessage)
    batches = ManyToMany(Batch)

    # Extra fields for compound indexes
    message_with_status = Unicode(index=True, null=True)
    batches_with_statuses_reverse = ListOf(Unicode(), index=True)

    def save(self):
        # We override this method to set our index fields before saving.
        timestamp = self.event['timestamp']
        if not isinstance(timestamp, basestring):
            timestamp = format_vumi_date(timestamp)
        status = self.event['event_type']
        if status == "delivery_report":
            status = "%s.%s" % (status, self.event['delivery_status'])
        self.message_with_status = u"%s$%s$%s" % (self.message.key, timestamp,
                                                  status)
        self.batches_with_statuses_reverse = []
        reverse_ts = to_reverse_timestamp(timestamp)
        for batch_id in self.batches.keys():
            self.batches_with_statuses_reverse.append(
                u"%s$%s$%s" % (batch_id, reverse_ts, status))
        return super(Event, self).save()
Exemple #2
0
class UserAccountV1(Model):
    """A user account."""

    bucket = "useraccount"
    VERSION = 1
    MIGRATOR = UserAccountMigrator

    # key is uuid
    username = Unicode(max_length=255)
    # TODO: tagpools can be made OneToMany once vumi.persist.fields
    #       gains a OneToMany field
    tagpools = ManyToMany(UserTagPermissionVNone)
    applications = ManyToMany(UserAppPermissionVNone)
    created_at = Timestamp(default=datetime.utcnow)
    event_handler_config = Json(default=list)
    msisdn = Unicode(max_length=255, null=True)
    confirm_start_conversation = Boolean(default=False)
    # `tags` is allowed to be null so that we can detect freshly-migrated
    # accounts and populate the tags from active conversations. A new account
    # has no legacy tags or conversations, so we start with an empty list and
    # skip the tag collection.
    tags = Json(default=[], null=True)

    @Manager.calls_manager
    def has_tagpool_permission(self, tagpool):
        for tp_bunch in self.tagpools.load_all_bunches():
            for tp in (yield tp_bunch):
                if tp.tagpool == tagpool:
                    returnValue(True)
        returnValue(False)
Exemple #3
0
class OutboundMessage(Model):
    VERSION = 5
    MIGRATOR = OutboundMessageMigrator

    # key is message_id
    msg = VumiMessage(TransportUserMessage)
    batches = ManyToMany(Batch)

    # Extra fields for compound indexes
    batches_with_addresses = ListOf(Unicode(), index=True)
    batches_with_addresses_reverse = ListOf(Unicode(), index=True)

    def save(self):
        # We override this method to set our index fields before saving.
        self.batches_with_addresses = []
        self.batches_with_addresses_reverse = []
        timestamp = self.msg['timestamp']
        if not isinstance(timestamp, basestring):
            timestamp = format_vumi_date(timestamp)
        reverse_ts = to_reverse_timestamp(timestamp)
        for batch_id in self.batches.keys():
            self.batches_with_addresses.append(
                u"%s$%s$%s" % (batch_id, timestamp, self.msg['to_addr']))
            self.batches_with_addresses_reverse.append(
                u"%s$%s$%s" % (batch_id, reverse_ts, self.msg['to_addr']))
        return super(OutboundMessage, self).save()
Exemple #4
0
class UserAccountV4(Model):
    """A user account."""

    VERSION = 4

    # key is uuid
    username = Unicode(max_length=255)
    # TODO: tagpools can be made OneToMany once vumi.persist.fields
    #       gains a OneToMany field
    tagpools = ManyToMany(UserTagPermissionVNone)
    applications = ManyToMany(UserAppPermissionVNone)
    created_at = Timestamp(default=datetime.utcnow)
    event_handler_config = Json(default=list)
    msisdn = Unicode(max_length=255, null=True)
    confirm_start_conversation = Boolean(default=False)
    can_manage_optouts = Boolean(default=False)
    email_summary = Unicode(max_length=255, null=True)
    tags = Json(default=[])
    routing_table = RoutingTableField(default=RoutingTable({}))

    @Manager.calls_manager
    def has_tagpool_permission(self, tagpool):
        for tp_bunch in self.tagpools.load_all_bunches():
            for tp in (yield tp_bunch):
                if tp.tagpool == tagpool:
                    returnValue(True)
        returnValue(False)
class InboundMessageV3(Model):
    bucket = 'inboundmessage'

    VERSION = 3
    MIGRATOR = InboundMessageMigrator

    # key is message_id
    msg = VumiMessage(TransportUserMessage)
    batches = ManyToMany(BatchVNone)

    # Extra fields for compound indexes
    batches_with_timestamps = ListOf(Unicode(), index=True)
    batches_with_addresses = ListOf(Unicode(), index=True)

    def save(self):
        # We override this method to set our index fields before saving.
        batches_with_timestamps = []
        batches_with_addresses = []
        timestamp = self.msg['timestamp']
        for batch_id in self.batches.keys():
            batches_with_timestamps.append(u"%s$%s" % (batch_id, timestamp))
            batches_with_addresses.append(
                u"%s$%s$%s" % (batch_id, timestamp, self.msg['from_addr']))
        self.batches_with_timestamps = batches_with_timestamps
        self.batches_with_addresses = batches_with_addresses
        return super(InboundMessageV3, self).save()
Exemple #6
0
class ContactV1(Model):
    """A contact"""

    bucket = "contact"

    VERSION = 1
    MIGRATOR = ContactMigrator

    # key is UUID
    user_account = ForeignKey(UserAccount)
    name = Unicode(max_length=255, null=True)
    surname = Unicode(max_length=255, null=True)
    email_address = Unicode(null=True)  # EmailField?
    msisdn = Unicode(max_length=255)
    dob = Timestamp(null=True)
    twitter_handle = Unicode(max_length=100, null=True)
    facebook_id = Unicode(max_length=100, null=True)
    bbm_pin = Unicode(max_length=100, null=True)
    gtalk_id = Unicode(null=True)
    mxit_id = Unicode(null=True)
    wechat_id = Unicode(null=True)
    created_at = Timestamp(default=datetime.utcnow)
    groups = ManyToMany(ContactGroupVNone)
    extra = Dynamic(prefix='extras-')
    subscription = Dynamic(prefix='subscription-')

    def add_to_group(self, group):
        if isinstance(group, ContactGroupVNone):
            self.groups.add(group)
        else:
            self.groups.add_key(group)

    def addr_for(self, delivery_class):
        if delivery_class is None:
            # FIXME: Find a better way to do get delivery_class and get rid of
            #        this hack.
            return self.msisdn
        # TODO: delivery classes need to be defined somewhere
        if delivery_class in ('sms', 'ussd'):
            return self.msisdn
        elif delivery_class == 'gtalk':
            return self.gtalk_id
        elif delivery_class == 'twitter':
            return self.twitter_handle
        elif delivery_class == 'mxit':
            return self.mxit_id
        elif delivery_class == 'wechat':
            return self.wechat_id
        else:
            return None

    def __unicode__(self):
        if self.name and self.surname:
            return u' '.join([self.name, self.surname])
        else:
            return (self.surname or self.name or
                    self.gtalk_id or self.twitter_handle or self.msisdn or
                    self.mxit_id or self.wechat_id or
                    'Unknown User')
Exemple #7
0
 class ComputedValueModel(Model):
     """
     Toy model for ComputedValue tests.
     """
     a = Integer()
     b = Unicode()
     c = ListOf(Unicode())
     a_with_b = ComputedValue(lambda m: u"%s::%s" % (m.a, m.b),
                              Unicode(index=True))
     b_with_a = ComputedValue(lambda m: u"%s::%s" % (m.b, m.a), Unicode())
     a_with_c = ComputedValue(lambda m: [u"%s::%s" % (m.a, c) for c in m.c],
                              ListOf(Unicode(), index=True))
Exemple #8
0
class Contact(Model):
    """A contact"""

    VERSION = 2
    MIGRATOR = ContactMigrator

    # key is UUID
    user_account = ForeignKey(UserAccount)
    name = Unicode(max_length=255, null=True)
    surname = Unicode(max_length=255, null=True)
    email_address = Unicode(null=True)  # EmailField?
    dob = Timestamp(null=True)
    created_at = Timestamp(default=datetime.utcnow)
    groups = ManyToMany(ContactGroup)
    extra = Dynamic(prefix='extras-')
    subscription = Dynamic(prefix='subscription-')

    # Address fields
    msisdn = Unicode(max_length=255, index=True)
    twitter_handle = Unicode(max_length=100, null=True, index=True)
    facebook_id = Unicode(max_length=100, null=True, index=True)
    bbm_pin = Unicode(max_length=100, null=True, index=True)
    gtalk_id = Unicode(null=True, index=True)
    mxit_id = Unicode(null=True, index=True)
    wechat_id = Unicode(null=True, index=True)

    def add_to_group(self, group):
        if isinstance(group, ContactGroup):
            self.groups.add(group)
        else:
            self.groups.add_key(group)

    def addr_for(self, delivery_class):
        if delivery_class is None:
            # FIXME: Find a better way to do get delivery_class and get rid of
            #        this hack.
            return self.msisdn

        delivery_class = DELIVERY_CLASSES.get(delivery_class)
        if delivery_class is not None:
            return getattr(self, delivery_class['field'])

        return None

    def __unicode__(self):
        if self.name and self.surname:
            return u' '.join([self.name, self.surname])
        else:
            return (self.surname or self.name or
                    self.gtalk_id or self.twitter_handle or self.msisdn or
                    self.mxit_id or self.wechat_id or
                    'Unknown User')
Exemple #9
0
class UserAccountVNone(Model):
    """A user account."""
    bucket = "useraccount"

    # key is uuid
    username = Unicode(max_length=255)
    # TODO: tagpools can be made OneToMany once vumi.persist.fields
    #       gains a OneToMany field
    tagpools = ManyToMany(UserTagPermissionVNone)
    applications = ManyToMany(UserAppPermissionVNone)
    created_at = Timestamp(default=datetime.utcnow)
    event_handler_config = Json(null=True)
    msisdn = Unicode(max_length=255, null=True)
    confirm_start_conversation = Boolean(default=False)
Exemple #10
0
class UserTagPermissionVNone(Model):
    """A description of a tag a user account is allowed access to."""
    bucket = "usertagpermission"

    # key is uuid
    tagpool = Unicode(max_length=255)
    max_keys = Integer(null=True)
Exemple #11
0
class CurrentTag(Model):
    # key is flattened tag
    current_batch = ForeignKey(Batch, null=True)
    tag = Tag()
    metadata = Dynamic(Unicode())

    @staticmethod
    def _flatten_tag(tag):
        return "%s:%s" % tag

    @staticmethod
    def _split_key(key):
        return tuple(key.split(':', 1))

    @classmethod
    def _tag_and_key(cls, tag_or_key):
        if isinstance(tag_or_key, tuple):
            # key looks like a tag
            tag, key = tag_or_key, cls._flatten_tag(tag_or_key)
        else:
            tag, key = cls._split_key(tag_or_key), tag_or_key
        return tag, key

    def __init__(self, manager, key, _riak_object=None, **kw):
        tag, key = self._tag_and_key(key)
        if _riak_object is None:
            kw['tag'] = tag
        super(CurrentTag, self).__init__(manager, key,
                                         _riak_object=_riak_object, **kw)

    @classmethod
    def load(cls, manager, key, result=None):
        _tag, key = cls._tag_and_key(key)
        return super(CurrentTag, cls).load(manager, key, result)
Exemple #12
0
 def test_validate(self):
     u = Unicode()
     u.validate(u"")
     u.validate(u"a")
     u.validate(u"æ")
     u.validate(u"foé")
     self.assertRaises(ValidationError, u.validate, "")
     self.assertRaises(ValidationError, u.validate, "foo")
     self.assertRaises(ValidationError, u.validate, 3)
Exemple #13
0
class ContactGroup(Model):
    """A group of contacts"""
    # key is UUID
    name = Unicode()
    query = Unicode(null=True)
    user_account = ForeignKey(UserAccount)
    created_at = Timestamp(default=datetime.utcnow)

    @Manager.calls_manager
    def add_contacts(self, contacts, save=True):
        for contact in contacts:
            contact.groups.add(self)
            yield contact.save()

    def is_smart_group(self):
        return self.query is not None

    def __unicode__(self):
        return self.name
Exemple #14
0
 def test_validate(self):
     u = Unicode()
     u.validate(u"")
     u.validate(u"a")
     u.validate(u"æ")
     u.validate(u"foé")
     self.assertRaises(ValidationError, u.validate, "")
     self.assertRaises(ValidationError, u.validate, "foo")
     self.assertRaises(ValidationError, u.validate, 3)
Exemple #15
0
class OutboundMessage(Model):
    VERSION = 3
    MIGRATOR = OutboundMessageMigrator

    # key is message_id
    msg = VumiMessage(TransportUserMessage)
    batches = ManyToMany(Batch)

    # Extra fields for compound indexes
    batches_with_timestamps = ListOf(Unicode(), index=True)
    batches_with_addresses = ListOf(Unicode(), index=True)

    def save(self):
        # We override this method to set our index fields before saving.
        batches_with_timestamps = []
        batches_with_addresses = []
        timestamp = format_vumi_date(self.msg['timestamp'])
        for batch_id in self.batches.keys():
            batches_with_timestamps.append(u"%s$%s" % (batch_id, timestamp))
            batches_with_addresses.append(
                u"%s$%s$%s" % (batch_id, timestamp, self.msg['to_addr']))
        self.batches_with_timestamps = batches_with_timestamps
        self.batches_with_addresses = batches_with_addresses
        return super(OutboundMessage, self).save()
Exemple #16
0
    def test_validate_with_subtype(self):
        """
        If an explicit subtype is provided, its validation is used.
        """
        listof_unicode = ListOf(Unicode())
        listof_unicode.validate([u"a", u"b"])
        self.assertRaises(ValidationError, listof_unicode.validate, [1, 2])

        listof_int = ListOf(Integer())
        listof_int.validate([1, 2])
        self.assertRaises(ValidationError, listof_int.validate, [u"a", u"b"])

        listof_smallint = ListOf(Integer(max=10))
        listof_smallint.validate([1, 2])
        self.assertRaises(ValidationError, listof_smallint.validate, [1, 100])
Exemple #17
0
class Event(Model):
    VERSION = 1
    MIGRATOR = EventMigrator

    # key is event_id
    event = VumiMessage(TransportEvent)
    message = ForeignKey(OutboundMessage)

    # Extra fields for compound indexes
    message_with_status = Unicode(index=True, null=True)

    def save(self):
        # We override this method to set our index fields before saving.
        timestamp = self.event['timestamp']
        status = self.event['event_type']
        if status == "delivery_report":
            status = "%s.%s" % (status, self.event['delivery_status'])
        self.message_with_status = u"%s$%s$%s" % (self.message.key, timestamp,
                                                  status)
        return super(Event, self).save()
Exemple #18
0
 def test_validate_max_length(self):
     u = Unicode(max_length=5)
     u.validate(u"12345")
     u.validate(u"1234")
     self.assertRaises(ValidationError, u.validate, u"123456")
class SimpleModel(model.Model):
    VERSION = 2
    MIGRATOR = SimpleModelMigrator
    a = Unicode()
class SimpleModelOld(model.Model):
    VERSION = 1
    bucket = 'simplemodel'
    a = Unicode()
Exemple #21
0
class DynamicModel(Model):
    a = Unicode()
    contact_info = Dynamic()
Exemple #22
0
class IndexedModel(Model):
    a = Integer(index=True)
    b = Unicode(index=True, null=True)
Exemple #23
0
class SimpleModel(Model):
    a = Integer()
    b = Unicode()
Exemple #24
0
class ConversationV1(Model):
    """A conversation with an audience"""

    VERSION = 1
    MIGRATOR = ConversationMigrator

    bucket = 'conversation'

    user_account = ForeignKey(UserAccount)
    name = Unicode(max_length=255)
    description = Unicode(default=u'')
    conversation_type = Unicode(index=True)
    config = Json(default=dict)

    created_at = Timestamp(default=datetime.utcnow, index=True)
    start_timestamp = Timestamp(index=True)
    end_timestamp = Timestamp(null=True, index=True)
    status = Unicode(default=CONVERSATION_DRAFT, index=True)

    groups = ManyToMany(ContactGroup)
    batches = ManyToMany(Batch)

    delivery_class = Unicode(null=True)
    delivery_tag_pool = Unicode(null=True)
    delivery_tag = Unicode(null=True)

    def started(self):
        return self.running() or self.ended()

    def ended(self):
        return self.status == CONVERSATION_FINISHED

    def running(self):
        return self.status == CONVERSATION_RUNNING

    def get_status(self):
        """
        Get the status of this conversation

        :rtype: str, (CONVERSATION_FINISHED, CONVERSATION_RUNNING, or
            CONVERSATION_DRAFT)

        """
        return self.status

    # The following are to keep the implementation of this stuff in the model
    # rather than potentially multiple external places.
    def set_status_started(self):
        self.status = CONVERSATION_RUNNING

    def set_status_finished(self):
        self.status = CONVERSATION_FINISHED

    def add_group(self, group):
        if isinstance(group, ContactGroup):
            self.groups.add(group)
        else:
            self.groups.add_key(group)

    def __unicode__(self):
        return self.name

    def get_contacts_addresses(self, contacts):
        """
        Get the contacts assigned to this group with an address attribute
        that is appropriate for this conversation's delivery_class
        """
        addrs = [contact.addr_for(self.delivery_class) for contact in contacts]
        return [addr for addr in addrs if addr]

    def get_routing_name(self):
        return ':'.join((self.conversation_type, self.key))
Exemple #25
0
class Conversation(Model):
    """A conversation with an audience"""

    VERSION = 3
    MIGRATOR = ConversationMigrator

    user_account = ForeignKey(UserAccount)
    name = Unicode(max_length=255)
    description = Unicode(default=u'')
    conversation_type = Unicode(index=True)
    config = Json(default=dict)
    extra_endpoints = ListOf(Unicode())

    created_at = Timestamp(default=datetime.utcnow, index=True)
    archived_at = Timestamp(null=True, index=True)

    archive_status = Unicode(default=CONVERSATION_ACTIVE, index=True)
    status = Unicode(default=CONVERSATION_STOPPED, index=True)

    groups = ManyToMany(ContactGroup)
    batch = ForeignKey(Batch)

    delivery_class = Unicode(null=True)

    def active(self):
        return self.archive_status == CONVERSATION_ACTIVE

    def archived(self):
        return self.archive_status == CONVERSATION_ARCHIVED

    def ended(self):
        # TODO: Get rid of this once the old UI finally goes away.
        return self.archived()

    def starting(self):
        return self.status == CONVERSATION_STARTING

    def running(self):
        return self.status == CONVERSATION_RUNNING

    def stopping(self):
        return self.status == CONVERSATION_STOPPING

    def stopped(self):
        return self.status == CONVERSATION_STOPPED

    def is_draft(self):
        # TODO: Get rid of this once the old UI finally goes away.
        return self.active() and self.status == CONVERSATION_STOPPED

    def get_status(self):
        """Get the status of this conversation.

        Possible values are:

          * CONVERSATION_STARTING
          * CONVERSATION_RUNNING
          * CONVERSATION_STOPPING
          * CONVERSATION_STOPPED

        :rtype: str

        """
        return self.status

    # The following are to keep the implementation of this stuff in the model
    # rather than potentially multiple external places.
    def set_status_starting(self):
        self.status = CONVERSATION_STARTING

    def set_status_started(self):
        self.status = CONVERSATION_RUNNING

    def set_status_stopping(self):
        self.status = CONVERSATION_STOPPING

    def set_status_stopped(self):
        self.status = CONVERSATION_STOPPED

    def set_status_finished(self):
        self.archive_status = CONVERSATION_ARCHIVED

    def add_group(self, group):
        if isinstance(group, ContactGroup):
            self.groups.add(group)
        else:
            self.groups.add_key(group)

    def __unicode__(self):
        return self.name

    def get_contacts_addresses(self, contacts):
        """
        Get the contacts assigned to this group with an address attribute
        that is appropriate for this conversation's delivery_class
        """
        addrs = [contact.addr_for(self.delivery_class) for contact in contacts]
        return [addr for addr in addrs if addr]

    def get_connector(self):
        return GoConnector.for_conversation(self.conversation_type, self.key)
Exemple #26
0
 def test_validate_max_length(self):
     u = Unicode(max_length=5)
     u.validate(u"12345")
     u.validate(u"1234")
     self.assertRaises(ValidationError, u.validate, u"123456")
class BatchVNone(Model):
    bucket = 'batch'

    # key is batch_id
    tags = ListOf(Tag())
    metadata = Dynamic(Unicode())
Exemple #28
0
class SimpleModel(Model):
    VERSION = 1
    a = Unicode(index=True)
    even_odd = Unicode(index=True)
Exemple #29
0
class Batch(Model):
    # key is batch_id
    tags = ListOf(Tag())
    metadata = Dynamic(Unicode())
Exemple #30
0
class VersionedModel(Model):
    VERSION = 2
    MIGRATOR = VersionedModelMigrator
    c = Integer()
    text = Unicode(null=True)
class SimpleModel(Model):
    a = Unicode()
Exemple #32
0
class IndexedVersionedModel(Model):
    VERSION = 3
    MIGRATOR = VersionedModelMigrator
    bucket = 'versionedmodel'
    c = Integer()
    text = Unicode(null=True, index=True)