Ejemplo n.º 1
0
class Thread(UniqueMixin, Serializeable, TimestampMixin, db.Model):
    __table_args__ = (db.UniqueConstraint('account_id',
                                          'thread_id',
                                          name='tid_1'), )
    id = db.Column(db.Integer, primary_key=True)
    account_id = db.Column(db.Integer,
                           db.ForeignKey('account.id', ondelete='CASCADE'),
                           nullable=False)
    thread_id = db.Column(db.String, index=True, nullable=False)

    messages = db.relationship('Message',
                               cascade='all, delete',
                               backref=backref('thread'))
    senders = association_proxy('messages', 'sender')
    subjects = association_proxy('messages', 'subject')
    dates = association_proxy('messages', 'date')
    sizes = association_proxy('messages', 'size')
    labels = association_proxy('messages', 'labels')

    omit = ('account', 'messages')

    @classmethod
    def unique_hash(cls, account, thread_id):
        return hash((account, thread_id))

    @classmethod
    def unique_filter(cls, query, account, thread_id):
        return query.filter_by(thread_id=thread_id, account=account)

    def __repr__(self):
        return '%d: %s' % (self.id, self.thread_id)
Ejemplo n.º 2
0
class TimestampMixin(object):
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow)

    @declared_attr
    def __mapper_args__(cls):
        return {
            'properties': {
                'created': deferred(cls.__table__.c.created),
                'updated': deferred(cls.__table__.c.updated)
            }
        }
Ejemplo n.º 3
0
class Account(UniqueMixin, TimestampMixin, db.Model, Serializeable):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String, unique=True)
    nickname = db.Column(db.String(100), nullable=True)
    writable = db.Column(db.Boolean, default=False)
    can_send = db.Column(db.Boolean, default=False)

    omit = ('messages', 'threads', 'labels', 'key_value')

    key_value = db.relationship('KeyValue',
                                lazy='dynamic',
                                cascade='delete',
                                passive_deletes=True,
                                backref=backref('account'))
    labels = db.relationship('Label',
                             lazy='dynamic',
                             cascade='delete',
                             passive_deletes=True,
                             backref=backref('account'))
    threads = db.relationship('Thread',
                              lazy='dynamic',
                              cascade='all, delete',
                              passive_deletes=True,
                              backref=backref('account'))
    messages = db.relationship('Message',
                               lazy='dynamic',
                               cascade='delete',
                               passive_deletes=True,
                               backref=backref('account'))

    @db.validates('email')
    def validates_email(self, key, email):
        if '@' not in email:
            print("WARNING: '%s' in email field." % email)
        assert '@' in email
        return email

    @classmethod
    def unique_hash(cls, email, nickname, writable=None, can_send=None):
        return email

    @classmethod
    def unique_filter(cls,
                      query,
                      email,
                      nickname,
                      writable=None,
                      can_send=None):
        return query.filter(Account.email == email)

    def __repr__(self):
        return '%2d: %s <%s>' % (self.id, self.nickname, self.email)
Ejemplo n.º 4
0
class KeyValue(db.Model):
    #__table_args__ = (db.UniqueConstraint('account_id', 'key',
    #                                      name='key_1'), )
    id = db.Column(db.Integer, primary_key=True)
    key = db.Column(db.String, nullable=False)
    value = db.Column(db.String, nullable=False)
    account_id = db.Column(db.Integer,
                           db.ForeignKey('account.id', ondelete='CASCADE'),
                           index=True,
                           nullable=False)

    def __repr__(self):
        return '%d: %s => %s' % (self.id, self.key, self.value)
Ejemplo n.º 5
0
class Label(UniqueMixin, db.Model, Serializeable, TimestampMixin):
    __table_args__ = (db.UniqueConstraint('account_id', 'gid', name='gid_1'), )
    id = db.Column(db.Integer, primary_key=True)
    account_id = db.Column(db.Integer,
                           db.ForeignKey('account.id', ondelete='CASCADE'),
                           nullable=False)
    name = db.Column(db.String)
    gid = db.Column(db.String)

    messages = db.relationship('Message',
                               secondary=label_association,
                               lazy='dynamic',
                               passive_deletes=True,
                               backref='labels')
    nickname = association_proxy('account', 'nickname')

    omit = ('account', 'messages', 'nickname')

    @classmethod
    def unique_hash(cls, account, gid, name=None):
        return gid

    @classmethod
    def unique_filter(cls, query, account, gid, name=None):
        return query.filter(and_(Label.gid == gid, Label.account == account))

    def __repr__(self):
        return '%s' % self.name

    @staticmethod
    def info(account_id, skip_categories=True):
        query = Message.query.filter_by(account_id=account_id)
        query = query.join(label_association).join(Label)
        if skip_categories:
            query = query.filter(~Label.name.like('CATEGORY_%'))
        query = query.with_entities(
            Label.id, Label.name, Label.gid,
            db.func.min(Message.article_id).label('min_id'),
            db.func.max(Message.article_id).label('max_id'),
            db.func.count(Message.article_id).label('count'))
        query = query.group_by(label_association.c.label_id)
        return query
Ejemplo n.º 6
0
class Addressee(db.Model, Serializeable):
    id = db.Column(db.Integer, primary_key=True)
    contact_id = db.Column(db.Integer,
                           db.ForeignKey('contact.id'),
                           nullable=False)
    message_id = db.Column(db.Integer,
                           db.ForeignKey('message.id', ondelete='CASCADE'),
                           nullable=False)
    type_ = db.Column('type_', db.Enum(AddresseeEnum))

    contact = db.relationship('Contact')
    message = db.relationship('Message', backref='addressees')

    name = association_proxy('contact', 'name')
    email = association_proxy('contact', 'email')

    __mapper_args__ = {'polymorphic_on': type_}

    def serialize(self):
        return self.contact.serialize()
Ejemplo n.º 7
0
class Contact(UniqueMixin, db.Model, Serializeable):
    __table_args__ = (db.UniqueConstraint('name', 'email', name='unique_1'), )
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    email = db.Column(db.String, index=True)

    _received = db.relationship('ToAddressee')
    _cced = db.relationship('CcAddressee')
    _bcced = db.relationship('BccAddressee')

    received = association_proxy('_received', 'message')
    cced = association_proxy('_cced', 'message')
    bcced = association_proxy('_bcced', 'message')

    omit = ('sent', '_received', '_cced', '_bcced')

    @db.validates('email')
    def validates_email(self, key, email):
        if '@' not in email:
            print("WARNING: '%s' in email field." % email)
        assert '@' in email
        return email

    @classmethod
    def unique_hash(cls, email, name=None):
        return email.lower()

    @classmethod
    def unique_filter(cls, query, email, name=None):
        return query.filter(
            db.func.lower(Contact.email) == db.func.lower(email))

    def __repr__(self):
        if self.name:
            return '%s <%s>' % (self.name, self.email)
        return '<%s>' % self.email
Ejemplo n.º 8
0
class Message(TimestampMixin, Serializeable, db.Model):
    __table_args__ = (db.UniqueConstraint('account_id',
                                          'google_id',
                                          name='gid_1'),
                      db.UniqueConstraint('account_id',
                                          'article_id',
                                          name='aid_1'))

    id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
    account_id = db.Column(db.Integer,
                           db.ForeignKey('account.id', ondelete='CASCADE'),
                           index=True,
                           nullable=False)
    article_id = db.Column(db.Integer, nullable=False)
    google_id = db.Column(HexInt, index=True, nullable=False)
    message_id = db.Column(db.String(100), index=True, nullable=False)
    thread_id = db.Column(db.Integer,
                          db.ForeignKey('thread.id', ondelete='CASCADE'),
                          index=True)
    from_id = db.Column(db.Integer,
                        db.ForeignKey('contact.id'),
                        nullable=False)
    date = db.Column(db.DateTime)
    subject = db.Column(db.String)
    references = db.Column(db.String)
    snippet = db.Column(db.String(200))
    size = db.Column(db.Integer, default=0)
    _raw = db.deferred(db.Column(db.BLOB))
    modified = db.deferred(db.Column(db.DateTime))

    sender = db.relationship(Contact,
                             foreign_keys=[from_id],
                             backref='sent',
                             innerjoin=True)

    to_ = db.relationship('ToAddressee', cascade='all,delete')
    cc = db.relationship('CcAddressee', cascade='all,delete')
    bcc = db.relationship('BccAddressee', cascade='all,delete')

    label_names = association_proxy('labels', 'name')
    tos = association_proxy('to_',
                            'contact',
                            creator=lambda c: ToAddressee(contact=c))
    ccs = association_proxy('cc',
                            'contact',
                            creator=lambda c: CcAddressee(contact=c))
    bccs = association_proxy('bcc',
                             'contact',
                             creator=lambda c: BccAddressee(contact=c))

    omit = ('_raw', 'raw', 'account', 'thread', 'addressees', 'created',
            'modified', 'updated')

    @property
    def __raw(self):
        if self._raw is None:
            return None
        if 'raw2' in dir(self):
            return self.raw2
        self.raw2 = zlib.decompress(self._raw)
        return self.raw2

    @__raw.setter
    def __raw(self, raw):
        if raw is None:
            del self.raw2
            self._raw = None
            self.modified = None
            return
        self.raw2 = raw
        self.update = datetime.now()
        self._raw = zlib.compress(raw)

    raw = db.synonym("_raw", descriptor=__raw)

    @staticmethod
    def find_labels(session, account, gid):
        # Use non-ORM (i.e. sql) syntax to bypass reading in the Message
        # table itself since updating labels only requires reading the
        # association table.
        return db.session.query(label_association).\
            filter_by(message_gid=gid).\
            join(Label).\
            filter(Label.account==account).all()

    @staticmethod
    def rem_labels(session, gid, label_ids):
        if not label_ids:
            return
        q = label_association.delete()
        q = q.where(
            and_(label_association.c.label_id.in_(label_ids),
                 label_association.c.message_gid == gid))
        res = session.execute(q)
        res.close()
        return

    @staticmethod
    def add_labels(session, gid, label_ids):
        if not label_ids:
            return
        values = [(lid, gid) for lid in label_ids]
        q = label_association.insert()
        res = session.execute(q.values(values))
        res.close()
        return

    @staticmethod
    def by_label(account_id, label_name='INBOX'):
        query = Message.query.join(label_association).join(Label)
        if account_id:
            query = query.filter(Message.account_id == account_id)
        query = query.filter(Label.name == label_name)
        return query

    @staticmethod
    def unread(account_id=None):
        return Message.by_label(account_id, 'UNREAD')
Ejemplo n.º 9
0
class ToAddressee(Addressee):
    __mapper_args__ = {'polymorphic_identity': AddresseeEnum.to}


class CcAddressee(Addressee):
    __mapper_args__ = {'polymorphic_identity': AddresseeEnum.cc}


class BccAddressee(Addressee):
    __mapper_args__ = {'polymorphic_identity': AddresseeEnum.bcc}


label_association = db.Table(
    'label_association',
    db.Column('label_id',
              db.Integer,
              db.ForeignKey('label.id', ondelete='CASCADE'),
              nullable=False),
    db.Column('message_id',
              db.Integer,
              db.ForeignKey('message.id', ondelete='CASCADE'),
              index=True,
              nullable=False),
    UniqueConstraint('label_id', 'message_id', name='mid_1'))
ix_lid_mid = db.Index('ix_lid_mid', label_association.c.label_id,
                      label_association.c.message_id)


class Label(UniqueMixin, db.Model, Serializeable, TimestampMixin):
    __table_args__ = (db.UniqueConstraint('account_id', 'gid', name='gid_1'), )
    id = db.Column(db.Integer, primary_key=True)
    account_id = db.Column(db.Integer,