class DateMixin(object): created = db.Column(db.TIMESTAMP, default=datetime.utcnow, server_default=text('CURRENT_TIMESTAMP')) updated = db.Column(db.TIMESTAMP, default=datetime.utcnow, onupdate=datetime.utcnow)
class Template(BaseModel, DateMixin): """ Template model """ id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) label = db.Column(db.String(128), nullable=False) text = db.Column(db.Text, nullable=False) def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'label': self.label, 'text': self.text, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties}
class Gammu(BaseModel): """ Gammu model """ id = db.Column(mysql.INTEGER(10), primary_key=True) version = db.Column('Version', mysql.INTEGER, nullable=False, server_default='0') info = db.Column('Info', mysql.TEXT) @classmethod def update_version(cls, version, library_version=None): """ Updating gammu version :param version: {str} gammu db version :param library_version: {str} gammu library version """ versions = cls.query.all() assert version is not None assert len(versions) <= 1, 'Gammu cannot have set multiple DB version' if not len(versions): gammu = Gammu() db.session.add(gammu) else: gammu = versions[0] gammu.version = version gammu.info = library_version
class Deamon(BaseModel): """ Deamon model """ __tablename__ = 'deamons' id = db.Column('ID', mysql.INTEGER(10), primary_key=True) start = db.Column('Start', mysql.TEXT, nullable=False) info = db.Column('Info', mysql.TEXT, nullable=False)
class UserToken(BaseModel, DateMixin): """ User token model """ token = db.Column(mysql.CHAR(64), nullable=False, default=generate_uuid, primary_key=True) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) info = db.Column(mysql.TEXT) agent = db.Column(mysql.VARCHAR(128))
class UserForgotPassword(BaseModel, DateMixin): """ User forgot passowrd model """ __tablename__ = "user_forgotPassword" # TODO(vojta) replace uuid by some generated hash token = db.Column(mysql.CHAR(64), nullable=False, default=generate_uuid, primary_key=True) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) expired = db.Column(db.TIMESTAMP)
class Application(BaseModel, DateMixin): """ Application model """ id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) label = db.Column(db.String(32), nullable=False) token = db.Column(db.String(32), unique=True, nullable=False) prefix = db.Column(db.String(5), unique=True) callbackUrl = db.Column(db.String(128)) note = db.Column(db.String(255)) outbox = relationship("Outbox", backref='application', lazy='dynamic') inbox = relationship("Inbox", backref='application', lazy='dynamic') sent_items = relationship("SentItem", backref='application', lazy='dynamic') def __init__(self, **kwargs): """ Create token on inicialization """ super(Application, self).__init__(**kwargs) # generate token self.regenerate_token() def regenerate_token(self): """ Regenerate token for API access """ self.token = os.urandom(16).encode('hex') def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'label': self.label, 'token': self.token, 'prefix': self.prefix, 'callbackUrl': self.callbackUrl, 'note': self.note, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties}
class Tag(BaseModel, DateMixin): """ Tag model """ id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) reference = db.Column(db.String(32), nullable=False) _label = db.Column("label", db.String(32), nullable=False) note = db.Column(db.String(255)) @property def label(self): """ Getting label :return: {str} label """ return self._label @label.setter def label(self, value): """ Setter for label which automatically set reference :param value: {str} label """ self._label = value self.reference = slugify(value, to_lower=True) def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'reference': self.reference, 'label': self.label, 'note': self.note, 'numberOfContacts': len(self.contacts), # TODO(vojta) lazy dynamic, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties}
class OutboxMultipart(BaseModel): """ Outbox multipart model """ __tablename__ = "outbox_multipart" id = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('outbox.id', onupdate="CASCADE", ondelete="CASCADE"), primary_key=True) sequencePosition = db.Column(mysql.INTEGER(11), primary_key=True) coding = db.Column(db.Enum('Default_No_Compression', 'Unicode_No_Compression', '8bit', 'Default_Compression', 'Unicode_Compression'), server_default='Default_No_Compression', nullable=False) text = db.Column(mysql.TEXT) textEncoded = db.Column(mysql.TEXT) udh = db.Column(mysql.TEXT) klass = db.Column('class', mysql.INTEGER, server_default='-1') def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'position': self.sequencePosition, 'text': self.text } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties}
class Outbox(BaseModel, DateMixin): """ Outbox model """ EIGHT_BIT = '8bit' DEFAULT_COMPRESSION = 'Default_Compression' DEFAULT_NO_COMPRESSION = 'Default_No_Compression' UNICODE_COMPRESSION = 'Unicode_Compression' UNICODE_NO_COMPRESSION = 'Unicode_No_Compression' id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) applicationId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('application.id')) group = db.Column(db.String(8)) creator = db.Column(mysql.TEXT, nullable=False) phone = db.Column(db.String(255)) destinationNumber = db.Column(db.String(20), nullable=False) coding = db.Column(db.Enum('Default_No_Compression', 'Unicode_No_Compression', '8bit', 'Default_Compression', 'Unicode_Compression'), server_default='Default_No_Compression', nullable=False) text = db.Column(mysql.TEXT, nullable=False) textEncoded = db.Column(mysql.TEXT) multipart = db.Column(db.Enum("false", "true"), server_default='false') udh = db.Column(mysql.TEXT) klass = db.Column('class', mysql.INTEGER, server_default='-1') deliveryReport = db.Column(db.Enum("default", "yes", "no"), server_default='default') relativeValidity = db.Column(mysql.INTEGER, server_default='-1') sent = db.Column("send", db.TIMESTAMP, default=datetime.utcnow) sendTimeout = db.Column(db.TIMESTAMP, default=datetime.utcnow) sendBefore = db.Column(db.TIME, nullable=False, server_default='23:59:59') sendAfter = db.Column(db.TIME, nullable=False, server_default='00:00:00') contact = db.relationship( 'Contact', primaryjoin="and_(Contact.phoneNumber==Outbox.destinationNumber," "Contact.userId==Outbox.userId)", foreign_keys=[destinationNumber], uselist=False) multiparts = db.relationship('OutboxMultipart', primaryjoin="OutboxMultipart.id==Outbox.id", foreign_keys=[id], uselist=True) def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'destinationNumber': self.destinationNumber, 'contact': self.contact.to_dict() if self.contact else None, 'application': self.application.to_dict() if self.application \ else None, 'text': self.text, 'multiparts': [multipart.to_dict() for multipart in self.multiparts], 'send': self.sent.isoformat(sep=' ') if self.sent else None, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties} @classmethod def get_all(cls, user_id, application_id=None): """ Get grouped messages :param user_id: {int} user identifier :param application_id: {int} application identifier :return: {list} """ groups = cls.query \ .with_entities( cls.id, cls.applicationId, cls.group, cls.text, cls.sent, cls.created, cls.updated, func.count(cls.id) ) \ .filter(cls.group != None) \ .filter(cls.userId == user_id) \ .filter(cls.applicationId == application_id) \ .order_by(cls.sent.desc()) \ .group_by(cls.group) \ .all() payload = [] for identifier, appId, group, message, send, created, updated, respondents in groups: multiparts = OutboxMultipart.query.filter_by(id=identifier).all() app = Application.get_one(id=appId) if appId else None message = "%s%s" % (message, "".join([m.text for m in multiparts])) payload.append({ 'id': group, 'text': message, 'multiparts': [multipart.to_dict() for multipart in multiparts], 'application': app.to_dict() if app else None, 'countOfRespondents': respondents, 'send': send.isoformat(sep=' ') if send else None, 'created': created.isoformat(sep=' ') if created else None, 'updated': updated.isoformat(sep=' ') if updated else None }) return payload @classmethod def get(cls, group, user_id, application_id=None): """ :param group: {str} group id :param user_id: {int} user identifier :param application_id: {int} application identifier :return: {list} """ items = cls.query \ .filter(cls.group == group) \ .filter(cls.userId == user_id) \ .filter(cls.applicationId == application_id) \ .order_by(cls.sent.desc()) \ .all() if not items: return None app = items[0].application send = items[0].sent created = items[0].created updated = items[0].updated contacts = [i.contact.to_dict() for i in items if i.contact] phone_numbers = [i.destinationNumber for i in items if not i.contact] multiparts = OutboxMultipart.query.filter_by(id=items[0].id).all() message = "%s%s" % (items[0].text, "".join( [m.text for m in multiparts])) return { 'id': group, 'application': app.to_dict() if app else None, 'contacts': contacts, 'phoneNumbers': phone_numbers, 'text': message, 'multiparts': [multipart.to_dict() for multipart in multiparts], 'send': send.isoformat(sep=' ') if send else None, 'countOfRespondents': len(contacts) + len(phone_numbers), 'created': created.isoformat(sep=' ') if created else None, 'updated': updated.isoformat(sep=' ') if updated else None } @classmethod def send(cls, destination_number, message, user=None, application_id=None, group=None, send=datetime.utcnow(), send_timeout=None, contact=None, send_before=None, send_after=None, flash=False, coding=DEFAULT_NO_COMPRESSION): """ Put to queue message to send :param destination_number: {str} phone number :param message: {str} body of text message :param user: {server.models.User} user instance :param application_id: {int} application identifier :param send: {datetime.datetime} when message should be dispatched :param send_timeout: {datetime.datetime} datetime for how long it should be timeouted :param send_before: {datetime.datetime} send before :param send_after: {datetime.datetime} send after :param flash: {boolean} if message is type flash or not :param coding: {str} coding of text message """ assert message is not None assert destination_number is not None assert type(message) == str or type(message) == unicode assert type(destination_number) == str or type( destination_number) == unicode # defining if message is type of flash or not, 0 flash, 1 not flash klass = str(int(not flash)) # get max message length depends by coding udh_length = 7 if coding == cls.DEFAULT_NO_COMPRESSION: max_msg_length = 160 elif coding == cls.UNICODE_NO_COMPRESSION: max_msg_length = 60 else: raise Exception('Not supported coding') # TODO(vojta) better exp # replacing first name and last name in message message = message \ .replace('{firstName}', contact.firstName if contact else '') \ .replace('{lastName}', contact.lastName if contact else '') # get message length depends by coding msg_length = cls.get_message_length(message, coding) # if message length is greater then max length, we need to split it # to multiparts # NOTICE(vojta) inspired by Kalkun # https://github.com/back2arie/Kalkun/blob/master/application/models/gateway/gammu_model.php if msg_length > max_msg_length: multipart_length = max_msg_length - udh_length udh = "050003{hex}".format( hex=str(hex(randint(0, 255)).split('x')[1]).ljust(2, "0")) multiparts = cls.get_message_multipart(message, multipart_length) part = str(len(multiparts)).rjust(2, "0") outbox = Outbox(userId=user.id if user else None, applicationId=application_id, group=group, coding=coding, text=multiparts[0], udh="{udh}{part}01".format(udh=udh, part=part), klass=klass, multipart="true", deliveryReport="no", destinationNumber=destination_number, relativeValidity=-1, creator=user.id if user is not None else '', sent=send, sendTimeout=send_timeout, sendBefore=send_before, sendAfter=send_after) # TODO(vojta) is there better way how to get ID of new inserted item ? db.session.add(outbox) db.session.commit() db.session.refresh(outbox) # add other parts of the message to database for index, message in enumerate(multiparts[1:]): position = index + 2 multipart = OutboxMultipart(id=outbox.id, sequencePosition=position, coding=coding, text=message, klass=klass, udh="{udh}{part}{position}".format( udh=udh, part=part, position=str(position).rjust( 2, "0"))) db.session.add(multipart) db.session.commit() else: # message with length lower or equal max length outbox = Outbox(userId=user.id if user else None, applicationId=application_id, group=group, coding=coding, text=message, klass=klass, deliveryReport="no", destinationNumber=destination_number, relativeValidity=-1, sent=send, creator=user.id if user is not None else '', sendTimeout=send_timeout, sendBefore=send_before, sendAfter=send_after) db.session.add(outbox) db.session.commit() db.session.refresh(outbox) return outbox @classmethod def get_message_multipart(cls, message, length, coding=DEFAULT_NO_COMPRESSION): """ :param message: {str} message text :param length: {int} length of multipart message :param coding: {str} coding of the message :return: {list} list of multipart messages """ multiparts = [] if coding == cls.DEFAULT_NO_COMPRESSION: multiparts = [u""] part = 0 char_left = length for char in message: value = 2 if is_special_char(char) else 1 if value <= char_left: multiparts[part] = u"{0}{1}".format(multiparts[part], char) char_left = char_left - value else: part = part + 1 multiparts.append(char) char_left = length - value else: # TODO(vojta) handle unicode pass return multiparts @classmethod def get_message_length(cls, message, coding=DEFAULT_NO_COMPRESSION): """ :param message: {str} message text :param coding: {str} coding of the message :return: {int} length of message depends by chars type """ length = 0 if coding == cls.DEFAULT_NO_COMPRESSION: for char in message: if is_special_char(char): length = length + 2 else: length = length + 1 else: # TODO(vojta) handle unicode pass return length
class Contact(BaseModel, DateMixin): """ Contact model """ id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) firstName = db.Column(db.String(16), nullable=False) lastName = db.Column(db.String(16), nullable=False) phoneNumber = db.Column(db.String(14)) email = db.Column(db.String(128)) note = db.Column(db.String(255)) _tags = db.relationship("Tag", secondary=relations.contact_on_tags, backref="contacts", lazy="dynamic") @property def tags(self): """ Return list of the tags :return: {list} """ return [tag.label for tag in self._tags.all()] @tags.setter def tags(self, items): """ Saves list of tags :param items: {list} list of tags """ if items is None: items = [] tags = [] for label in items: dummy = Tag(label=label) tag = Tag.get_or_create(reference=dummy.reference, userId=self.userId) tag.label = label tag.userId = self.userId tags.append(tag) self._tags = tags def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'firstName': self.firstName, 'lastName': self.lastName, 'phoneNumber': self.phoneNumber, 'email': self.email, 'note': self.note, 'tags': self.tags, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties}
class Inbox(BaseModel, DateMixin): """ Inbox model """ id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) userId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('user.id')) applicationId = db.Column(mysql.INTEGER(10, unsigned=True), ForeignKey('application.id')) recipient = db.Column(db.String(64)) senderNumber = db.Column(db.String(20)) smscNumber = db.Column(db.String(20)) processed = db.Column(db.Boolean, server_default='0') coding = db.Column(db.Enum('Default_No_Compression', 'Unicode_No_Compression', '8bit', 'Default_Compression', 'Unicode_Compression'), server_default='Default_No_Compression', nullable=False) text = db.Column(mysql.TEXT, nullable=False) textEncoded = db.Column(mysql.TEXT, nullable=False) udh = db.Column(mysql.TEXT, nullable=False) klass = db.Column('class', mysql.INTEGER, nullable=False, server_default='-1') received = db.Column(db.TIMESTAMP) contact = db.relationship( 'Contact', primaryjoin="and_(Contact.phoneNumber==Inbox.senderNumber, " "Contact.userId==Inbox.userId)", foreign_keys=[senderNumber]) def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'senderNumber': self.senderNumber, 'processed': bool(self.processed), 'contact': self.contact.to_dict() if self.contact else None, 'application': self.application.to_dict() if self.application \ else None, 'text': self.text, 'received': self.received.isoformat(sep=' ') if self.received else None, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties} @classmethod def convert_multipart_messages(cls): """ Converting multipart messages to single row in table """ # looking for distinct UDHs udhs = db.session.query(func.substr(cls.udh, 1, 10)) \ .filter(cls.udh.like("%01")) \ .filter(cls.processed==False) \ .all() udhs = [udh[0] for udh in udhs] # iterating over UDHs, looking for messages and merging that to single # one row for udh in udhs: messages = cls.query.filter(cls.udh.like("%s%%" % udh)) \ .filter(cls.processed==False) \ .order_by(cls.udh.asc()) \ .all() if not len(messages): break # creating new single message instead of multiple messages inbox = cls(text="".join([m.text for m in messages]), textEncoded="".join([m.textEncoded for m in messages]), recipient=messages[0].recipient, senderNumber=messages[0].senderNumber, smscNumber=messages[0].smscNumber, processed=False, udh="", klass=messages[0].klass, received=messages[0].received) db.session.add(inbox) # delete multiparts messages for message in messages: db.session.delete(message)
class Phone(BaseModel, DateMixin): """ Phone model """ id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) hostname = db.Column(db.String(64)) imei = db.Column(db.String(35), nullable=False) netCode = db.Column(db.String(10)) netName = db.Column(db.String(35)) battery = db.Column(mysql.INTEGER) signal = db.Column(mysql.INTEGER) client = db.Column(mysql.TEXT) sent = db.Column(mysql.INTEGER, nullable=False, server_default='0') received = db.Column(mysql.INTEGER, nullable=False, server_default='0') send = db.Column(db.Enum("yes", "no"), default='no', nullable=False) receive = db.Column(db.Enum("yes", "no"), default='no', nullable=False) timeout = db.Column(db.TIMESTAMP) def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'hostname': self.hostname, 'imei': self.imei, 'netCode': self.netCode, 'netName': self.netName, 'battery': self.battery, 'signal': self.signal, 'client': self.client, 'sent': self.sent, 'received': self.received, 'sendEnabled': True if self.send == 'yes' else False, 'receiveEnabled': True if self.receive == 'yes' else False, 'lastActivity': self.timeout.isoformat(sep=' ') if self.timeout \ else None, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties}
# -*- coding: utf-8 -*- # http://google-styleguide.googlecode.com/svn/trunk/pyguide.html from smsgw.core import db from sqlalchemy.dialects.mysql import INTEGER contact_on_tags = db.Table( 'contact_tag', db.Column('contactId', INTEGER(10, unsigned=True), db.ForeignKey('contact.id'), nullable=False, primary_key=True), db.Column('tagId', INTEGER(10, unsigned=True), db.ForeignKey('tag.id'), nullable=False, primary_key=True))
class User(BaseModel, DateMixin): """ User model """ ROLE_ADMIN = 'admin' ROLE_USER = '******' id = db.Column(mysql.INTEGER(10, unsigned=True), primary_key=True) uuid = db.Column(mysql.CHAR(36), unique=True, nullable=False, default=generate_uuid) email = db.Column(db.String(128), unique=True, nullable=False, server_default="") _password = db.Column('password', db.String(60), nullable=False) firstName = db.Column(db.String(16)) lastName = db.Column(db.String(16)) company = db.Column(db.String(32)) _timeZoneCode = db.Column("timeZoneCode", db.String(100), default='Europe/Prague') role = db.Column('role', db.Enum("user", "admin"), default='user', nullable=False) isActive = db.Column(db.Boolean, default=True) tokens = relationship("UserToken", backref='user', lazy='dynamic') templates = relationship("Template", backref='user', lazy='dynamic') contacts = relationship("Contact", backref='user', lazy='dynamic') tags = relationship("Tag", backref='user', lazy='dynamic') applications = relationship("Application", backref='user', lazy='dynamic') outbox = relationship("Outbox", backref='user', lazy='dynamic') inbox = relationship("Inbox", backref='user', lazy='dynamic') sent_items = relationship("SentItem", backref='user', lazy='dynamic') @property def password(self): """ Get user password :return: {str} hash """ return self._password @password.setter def password(self, password): """ Set user password as bcrypt hash :param password: {str} """ if password is not None: self._password = bcrypt.generate_password_hash(password, 12) @property def timeZoneCode(self): """ Get user timezone code :return: {str} Region/Location """ return self._timeZoneCode if self._timeZoneCode is not None else "UTC" @timeZoneCode.setter def timeZoneCode(self, value): """ Set user timezone code :param value: {str} Region/Location :raise: {UnknownTimeZoneError} when timezone code is invalid """ self._timeZoneCode = timezone(value).zone def compare_password(self, password): """ Comparing bcrypt hash with specified password :param password: {str} password :retur: {bool} """ return bcrypt.check_password_hash(self.password.encode('utf8'), password) def is_admin(self): """ Returning if user has administration role """ return (self.role == User.ROLE_ADMIN) @classmethod def is_exists_by_email(cls, email): """ Checking if user with specified emails is exists :param email: {str} user email """ return cls.query \ .with_entities(cls.id) \ .filter_by(email=email) \ .first() def to_dict(self, properties=None): """ To dictionary :param properties: {list} of required properties :return: {dict} """ dict = { 'id': self.id, 'uuid': self.uuid, 'email': self.email, 'firstName': self.firstName, 'lastName': self.lastName, 'company': self.company, 'role': self.role, 'isActive': self.isActive, 'numbers': { 'contacts': self.contacts.count(), 'sent': self.sent_items.count(), 'tags': self.tags.count(), 'outbox': self.outbox.count(), 'inbox': self.inbox.count(), 'templates': self.templates.count(), 'applications': self.applications.count() }, 'created': self.created.isoformat(sep=' ') if self.created \ else None, 'updated': self.updated.isoformat(sep=' ') if self.updated \ else None } if properties is None: properties = dict.keys() return {key: dict.get(key) for key in properties}