class Matter(ResourceMixin, db.Model):
    __tablename__ = 'matters'

    # Properties
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(1000))
    file_open = db.Column(AwareDateTime(),
                          default=tzaware_datetime)
    file_close = db.Column(AwareDateTime())
    costs_on_account = db.Column(db.Numeric(10, 2), server_default='0')
    active = db.Column(db.Boolean(), nullable=False,
                       server_default='1')

    def __init__(self, **kwargs):
        super(Matter, self).__init__(**kwargs)

    def to_json(self):
        """
        Convert Matter instance to json
        """
        clients = [client.id for client in self.clients]
        practice_areas = [practice_area.id for practice_area in self.practice_areas]
        staff = [staff_member.id for staff_member in self.staff]

        return {
            'id': self.id,
            'description': "[" + str(self.id) + "] " + (self.description if self.description else ''),
            'fileOpen': self.file_open,
            'fileClose': self.file_close,
            'active': self.active,
            'costsOnAccount': self.costs_on_account,
            'clients': clients,
            'practiceAreas': practice_areas,
            'staff': staff
        }
def upgrade():
    op.create_table(
        'foos', sa.Column('id', sa.Integer, primary_key=True),
        sa.Column('created_on', AwareDateTime(), default=tzware_datetime),
        sa.Column('updated_on',
                  AwareDateTime(),
                  default=tzware_datetime,
                  onupdate=tzware_datetime),
        sa.Column('bar', sa.String(128), index=True))
Example #3
0
class Tracker(db.Model):
    __tablename__ = 'tracker'
    id = db.Column(db.Integer, primary_key=True)

    tid = db.Column(db.Integer)

    datetime = db.Column(AwareDateTime())

    table = db.Column(db.String(255))

    inserted = db.Column(db.Boolean())

    deleted = db.Column(db.Boolean())

    data_before_change = db.Column(db.Integer)

    user = db.Column(db.String(255))

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Tracker, self).__init__(**kwargs)

    @classmethod
    def sort_by(cls, field, direction):
        """
        Validate the sort field and direction.

        :param field: Field name
        :type field: str
        :param direction: Direction
        :type direction: str
        :return: tuple
        """
        if field not in cls.__table__.columns:
            field = 'datetime'

        if direction not in ('asc', 'desc'):
            direction = 'desc'

        return field, direction

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return ''

        search_query = '%{0}%'.format(query)
        search_chain = (Tracker.datetime.ilike(search_query),
                        Tracker.user.ilike(search_query))

        return or_(*search_chain)
Example #4
0
class File(UserMixin, ResourceMixin, db.Model):

    __tablename__ = 'files'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    '''
    credit_card = db.relationship(CreditCard, uselist=False,
                                  backref='credit_card',
                                  passive_deletes=True)
    subscription = db.relationship(Subscription, uselist=False,
                                   backref='subscription',
                                   passive_deletes=True)
    invoices = db.relationship(Invoice, backref='invoices',
                               passive_deletes=True)
    bets = db.relationship(Bet, backref='bets', passive_deletes=True)
'''
    # Authentication.
    filename = db.Column(db.String(24), unique=True, index=True)

    # Activity tracking.
    created_on = db.Column(AwareDateTime())

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(User, self).__init__(**kwargs)
        #self.coins = 100

    @classmethod
    def file_search(cls, squery):
        """
        Search a resource by 1 or more fields.

        :param squery: Search query
        :type squery: str
        :return: SQLAlchemy filter
        """
        if squery == '':
            return text('')

        s_query = '%{0}%'.format(squery)
        s_chain = (File.filename.ilike(s_query))

        return s_chain

    def filename_update(self, flist):
        """
        Update filenames in the DB from filesystem.

        :param flist: FileNames
        :type flist: list
        :return: None
        """
        os.listdir('UWdataweb/dataweb/DataFiles')
Example #5
0
class Department(UserMixin, ResourceMixin, db.Model):

    __tablename__ = 'department'
    id = db.Column(db.Integer, primary_key=True)

    # Authentication.
    active = db.Column('is_active',
                       db.Boolean(),
                       nullable=False,
                       server_default='1')
    departmentname = db.Column(db.String(24), unique=True, index=True)
    deptowneremail = db.Column(db.String(255),
                               unique=True,
                               index=True,
                               nullable=False,
                               server_default='')
    parentid = db.Column(db.Integer, unique=True)
    whizcoin = db.Column(db.Integer, nullable=False, default=1000)
    # Activity tracking.
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Department, self).__init__(**kwargs)

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find a user by their e-mail or username.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        return Department.query.filter(
            (Department.departmentname == identity)).first()
Example #6
0
class Emotion(db.Model):
    __tablename__ = 'emotion'

    id = db.Column(db.Integer, primary_key=True)

    sense_id = db.Column('lexicalunit_id', db.Integer)

    emotions = db.Column(db.String(255))

    valuations = db.Column(db.String(255))

    markedness = db.Column(db.String(255))

    status = db.Column('unitStatus', db.Integer)

    example1 = db.Column(db.String(255))

    example2 = db.Column(db.String(255))

    owner = db.Column(db.String(255))

    creation_date = db.Column(AwareDateTime())

    super_annotation = db.Column('super_anotation', db.Integer)
Example #7
0
class User(UserMixin, ResourceMixin, db.Model):
    ROLE = OrderedDict([
        ('member', 'Member'),
        ('admin', 'Admin')
    ])

    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    credit_card = db.relationship(CreditCard, uselist=False,
                                  backref='credit_card',
                                  passive_deletes=True)
    subscription = db.relationship(Subscription, uselist=False,
                                   backref='subscription',
                                   passive_deletes=True)
    invoices = db.relationship(Invoice, backref='invoices',
                               passive_deletes=True)
    descriptions = db.relationship(Create, backref='descriptions',
                                   passive_deletes=True)

    # Authentication.
    role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                     index=True, nullable=False, server_default='member')
    active = db.Column('is_active', db.Boolean(), nullable=False,
                       server_default='1')
    username = db.Column(db.String(24), unique=True, index=True)
    email = db.Column(db.String(255), unique=True, index=True, nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')

    # Billing.
    name = db.Column(db.String(128), index=True)
    payment_id = db.Column(db.String(128), index=True)
    cancelled_subscription_on = db.Column(AwareDateTime())
    previous_plan = db.Column(db.String(128))

    # Creating.
    credits = db.Column(db.Integer())
    last_created_on = db.Column(AwareDateTime())

    # Activity tracking.
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    # Additional settings.
    locale = db.Column(db.String(5), nullable=False, server_default='en')

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(User, self).__init__(**kwargs)

        self.password = User.encrypt_password(kwargs.get('password', ''))
        self.credits = 10

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find a user by their e-mail or username.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        return User.query.filter(
          (User.email == identity) | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        """
        Hash a plaintext string using PBKDF2. This is good enough according
        to the NIST (National Institute of Standards and Technology).

        In other words while bcrypt might be superior in practice, if you use
        PBKDF2 properly (which we are), then your passwords are safe.

        :param plaintext_password: Password in plain text
        :type plaintext_password: str
        :return: str
        """
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    @classmethod
    def deserialize_token(cls, token):
        """
        Obtain a user from de-serializing a signed token.

        :param token: Signed token.
        :type token: str
        :return: User instance or None
        """
        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)

            return User.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None

    @classmethod
    def initialize_password_reset(cls, identity):
        """
        Generate a token to reset the password for a specific user.

        :param identity: User e-mail address or username
        :type identity: str
        :return: User instance
        """
        u = User.find_by_identity(identity)
        reset_token = u.serialize_token()

        # This prevents circular imports.
        from perciapp.blueprints.user.tasks import (
            deliver_password_reset_email)
        deliver_password_reset_email(u.id, reset_token)

        return u

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if query == '':
            return text('')

        search_query = '%{0}%'.format(query)
        search_chain = (User.email.ilike(search_query),
                        User.username.ilike(search_query))

        return or_(*search_chain)

    @classmethod
    def is_last_admin(cls, user, new_role, new_active):
        """
        Determine whether or not this user is the last admin account.

        :param user: User being tested
        :type user: User
        :param new_role: New role being set
        :type new_role: str
        :param new_active: New active status being set
        :type new_active: bool
        :return: bool
        """
        is_demoting_admin = user.role == 'admin' and new_role != 'admin'
        is_changing_active = user.active is True and new_active is None
        admin_count = User.query.filter(User.role == 'admin').count()

        if is_demoting_admin and admin_count == 1:
            return True

        if is_changing_active and user.role == 'admin' and admin_count == 1:
            return True

        return False

    @classmethod
    def bulk_delete(cls, ids):
        """
        Override the general bulk_delete method because we need to delete them
        one at a time while also deleting them on Stripe.

        :param ids: List of ids to be deleted
        :type ids: list
        :return: int
        """
        delete_count = 0

        for id in ids:
            user = User.query.get(id)

            if user is None:
                continue

            if user.payment_id is None:
                user.delete()
            else:
                subscription = Subscription()
                cancelled = subscription.cancel(user=user)

                # If successful, delete it locally.
                if cancelled:
                    user.delete()

            delete_count += 1

        return delete_count

    def is_active(self):
        """
        Return whether or not the user account is active, this satisfies
        Flask-Login by overwriting the default value.

        :return: bool
        """
        return self.active

    def get_auth_token(self):
        """
        Return the user's auth token. Use their password as part of the token
        because if the user changes their password we will want to invalidate
        all of their logins across devices. It is completely fine to use
        md5 here as nothing leaks.

        This satisfies Flask-Login by providing a means to create a token.

        :return: str
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = URLSafeTimedSerializer(private_key)
        data = [str(self.id), md5(self.password.encode('utf-8')).hexdigest()]

        return serializer.dumps(data)

    def authenticated(self, with_password=True, password=''):
        """
        Ensure a user is authenticated, and optionally check their password.

        :param with_password: Optionally check their password
        :type with_password: bool
        :param password: Optionally verify this as their password
        :type password: str
        :return: bool
        """
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def serialize_token(self, expiration=3600):
        """
        Sign and create a token that can be used for things such as resetting
        a password or other tasks that involve a one off token.

        :param expiration: Seconds until it expires, defaults to 1 hour
        :type expiration: int
        :return: JSON
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)
        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    def update_activity_tracking(self, ip_address):
        """
        Update various fields on the user that's related to meta data on their
        account, such as the sign in count and ip address, etc..

        :param ip_address: IP address
        :type ip_address: str
        :return: SQLAlchemy commit results
        """
        self.sign_in_count += 1

        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip

        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()

    def add_credits(self, plan):
        """
        Add an amount of credits to an existing user.

        :param plan: Subscription plan
        :type plan: str
        :return: SQLAlchemy commit results
        """
        self.credits += plan['metadata']['credits']

        return self.save()
Example #8
0
class TrackerSenseHistory(db.Model):

    __tablename__ = 'view_tracker_sense_history'

    id = db.Column(db.Integer, primary_key=True)

    datetime = db.Column(AwareDateTime())

    user = db.Column(db.String(255))

    operation = db.Column(db.String(255))

    key_id = db.Column('k_id', db.Integer)

    key_lemma = db.Column('k_lemma', db.String(255))

    key_pos = db.Column('key_pos', db.Integer)

    key_status = db.Column('key_status', db.Integer)

    u1_lemma = db.Column('tu1_lemma', db.String(255))

    u1_variant = db.Column('tu1_variant', db.Integer)

    u1_domain = db.Column('tu1_domain', db.Integer)

    u1_pos = db.Column('tu1_pos', db.Integer)

    u1_status = db.Column('tu1_status', db.Integer)

    u1_comment = db.Column('tu1_comment', db.String(255))

    u1_owner = db.Column('tu1_owner', db.String(255))

    u2_lemma = db.Column('tu2_lemma', db.String(255))

    u2_variant = db.Column('tu2_variant', db.Integer)

    u2_domain = db.Column('tu2_domain', db.Integer)

    u2_pos = db.Column('tu2_pos', db.Integer)

    u2_status = db.Column('tu2_status', db.Integer)

    u2_comment = db.Column('tu2_comment', db.String(255))

    u2_owner = db.Column('tu2_owner', db.String(255))

    @classmethod
    def sort_by(cls, field, direction):
        """
        Validate the sort field and direction.

        :param field: Field name
        :type field: str
        :param direction: Direction
        :type direction: str
        :return: tuple
        """
        if field not in cls.__table__.columns:
            field = 'datetime'

        if direction not in ('asc', 'desc'):
            direction = 'desc'

        return field, direction

    @classmethod
    def search_by_form_filter(cls, date_from, date_to, sense_id, user, pos,
                              status):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        search_chain = list()

        if date_from is not '':
            search_chain.append(
                func.DATE(TrackerSenseHistory.datetime) >= date_from)
        if date_to is not '':
            search_chain.append(
                func.DATE(TrackerSenseHistory.datetime) <= date_to)
        if sense_id is not '':
            search_chain.append(TrackerSenseHistory.key_id == sense_id)
        if user is not '':
            search_chain.append(TrackerSenseHistory.user == user)
        if pos is not '':
            search_chain.append(TrackerSenseHistory.key_pos == pos)
        if status is not '':
            search_chain.append(TrackerSenseHistory.key_status == status)
        return and_(*search_chain)
Example #9
0
class TrackerSenseRelationsHistory(db.Model):
    __tablename__ = 'view_tracker_sense_relations_history'

    id = db.Column(db.Integer, primary_key=True)

    datetime = db.Column(AwareDateTime())

    user = db.Column(db.String(255))

    operation = db.Column(db.String(255))

    source_id = db.Column(db.Integer)

    source_unitstr = db.Column(db.Text(''))

    relation_id = db.Column(db.Integer)

    relation_name = db.Column(db.String(255))

    target_id = db.Column(db.Integer)

    target_unitstr = db.Column(db.Text(''))

    @classmethod
    def sort_by(cls, field, direction):
        """
        Validate the sort field and direction.

        :param field: Field name
        :type field: str
        :param direction: Direction
        :type direction: str
        :return: tuple
        """
        if field not in cls.__table__.columns:
            field = 'id'

        if direction not in ('asc', 'desc'):
            direction = 'desc'

        return field, direction

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return ''

        search_query = '%{0}%'.format(query)
        search_chain = (
            TrackerSenseRelationsHistory.id.ilike(search_query),
            TrackerSenseRelationsHistory.datetime.ilike(search_query),
            TrackerSenseRelationsHistory.user.ilike(search_query))

        return or_(*search_chain)

    @classmethod
    def search_by_form_filter(cls, date_from, date_to, sense_id, user,
                              relation_type):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        search_chain = list()

        if date_from is not '':
            search_chain.append(
                func.DATE(TrackerSenseRelationsHistory.datetime) >= date_from)
        if date_to is not '':
            search_chain.append(
                func.DATE(TrackerSenseRelationsHistory.datetime) <= date_to)
        if sense_id is not '':
            search_chain.append(
                or_(TrackerSenseRelationsHistory.source_id == sense_id))
        if user is not '':
            search_chain.append(TrackerSenseRelationsHistory.user == user)
        if relation_type is not '':
            search_chain.append(
                TrackerSenseRelationsHistory.relation_id == relation_type)
        return and_(*search_chain)
Example #10
0
class User(UserMixin, ResourceMixin, db.Model):
    ROLE = OrderedDict([('member', 'Member'), ('admin', 'Admin'),
                        ('bnf', 'Budget and Finance')])

    ACCT_TYPE = OrderedDict([('none', 'None'), ('ASG of IVC', 'ASG of IVC'),
                             ('Club', 'Club'),
                             ('Co-Curricular', 'Co-Curricular'),
                             ('Department', 'Department'),
                             ('Student Life', 'Student Life')])

    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)

    budget = db.relationship(Budget, backref='users', passive_deletes=True)

    # Authentication.
    role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                     index=True,
                     nullable=False,
                     server_default='member')
    active = db.Column('is_active',
                       db.Boolean(),
                       nullable=False,
                       server_default='1')
    username = db.Column(db.String(24), unique=True, index=True)
    email = db.Column(db.String(255),
                      unique=True,
                      index=True,
                      nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')

    # Activity tracking.
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    # Budgeting
    acct_type = db.Column(db.Enum(*ACCT_TYPE,
                                  name='account_types',
                                  native_enum=False),
                          nullable=False,
                          server_default='none')
    allocated_to_date = db.Column(db.Float())
    expenses_to_date = db.Column(db.Float())
    income_to_date = db.Column(db.Float())
    total_to_date = db.Column(db.Float())

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(User, self).__init__(**kwargs)
        self.allocated_to_date = 0.00
        self.expenses_to_date = 0.00
        self.income_to_date = 0.00
        self.total_to_date = 0.00

        self.password = User.encrypt_password(kwargs.get('password', ''))

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find a user by their e-mail or username.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        return User.query.filter((User.email == identity)
                                 | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        """
        Hash a plaintext string using PBKDF2. This is good enough according
        to the NIST (National Institute of Standards and Technology).

        In other words while bcrypt might be superior in practice, if you use
        PBKDF2 properly (which we are), then your passwords are safe.

        :param plaintext_password: Password in plain text
        :type plaintext_password: str
        :return: str
        """
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    @classmethod
    def deserialize_token(cls, token):
        """
        Obtain a user from de-serializing a signed token.

        :param token: Signed token.
        :type token: str
        :return: User instance or None
        """
        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)

            return User.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None

    @classmethod
    def initialize_password_reset(cls, identity):
        """
        Generate a token to reset the password for a specific user.

        :param identity: User e-mail address or username
        :type identity: str
        :return: User instance
        """
        u = User.find_by_identity(identity)
        reset_token = u.serialize_token()

        # This prevents circular imports.
        from track.blueprints.user.tasks import (deliver_password_reset_email)
        deliver_password_reset_email.delay(u.id, reset_token)

        return u

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return ''

        search_query = '%{0}%'.format(query)
        search_chain = (User.email.ilike(search_query),
                        User.username.ilike(search_query))

        return or_(*search_chain)

    @classmethod
    def is_last_admin(cls, user, new_role, new_active):
        """
        Determine whether or not this user is the last admin account.

        :param user: User being tested
        :type user: User
        :param new_role: New role being set
        :type new_role: str
        :param new_active: New active status being set
        :type new_active: bool
        :return: bool
        """
        is_changing_roles = user.role == 'admin' and new_role != 'admin'
        is_changing_active = user.active is True and new_active is None

        if is_changing_roles or is_changing_active:
            admin_count = User.query.filter(User.role == 'admin').count()
            active_count = User.query.filter(User.is_active is True).count()

            if admin_count == 1 or active_count == 1:
                return True

        return False

    def is_active(self):
        """
        Return whether or not the user account is active, this satisfies
        Flask-Login by overwriting the default value.

        :return: bool
        """
        return self.active

    def get_auth_token(self):
        """
        Return the user's auth token. Use their password as part of the token
        because if the user changes their password we will want to invalidate
        all of their logins across devices. It is completely fine to use
        md5 here as nothing leaks.

        This satisfies Flask-Login by providing a means to create a token.

        :return: str
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = URLSafeTimedSerializer(private_key)
        data = [str(self.id), md5(self.password.encode('utf-8')).hexdigest()]

        return serializer.dumps(data)

    def authenticated(self, with_password=True, password=''):
        """
        Ensure a user is authenticated, and optionally check their password.

        :param with_password: Optionally check their password
        :type with_password: bool
        :param password: Optionally verify this as their password
        :type password: str
        :return: bool
        """
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def serialize_token(self, expiration=3600):
        """
        Sign and create a token that can be used for things such as resetting
        a password or other tasks that involve a one off token.

        :param expiration: Seconds until it expires, defaults to 1 hour
        :type expiration: int
        :return: JSON
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)
        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    def update_activity_tracking(self, ip_address):
        """
        Update various fields on the user that's related to meta data on their
        account, such as the sign in count and ip address, etc..

        :param ip_address: IP address
        :type ip_address: str
        :return: SQLAlchemy commit results
        """
        self.sign_in_count += 1

        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip

        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()

    def update_allocated_total(self, field):
        self.allocated_to_date += float(field)
        round(self.allocated_to_date, 2)
        '{:.2f}'.format(self.allocated_to_date)
        self.total_to_date += float(field)
        self.total_to_date = self.total_to_date * 100
        self.total_to_date = round(self.total_to_date)
        self.total_to_date = self.total_to_date / 100
        '{:.2f}'.format(self.total_to_date)
        return self.save()

    def update_expenses_total(self, field):
        self.expenses_to_date += float(field)
        round(self.expenses_to_date, 2)
        '{:.2f}'.format(self.expenses_to_date)
        self.total_to_date -= float(field)
        self.total_to_date = self.total_to_date * 100
        self.total_to_date = round(self.total_to_date)
        self.total_to_date = self.total_to_date / 100
        '{:.2f}'.format(self.total_to_date)
        return self.save()

    def update_income_total(self, field):
        self.income_to_date += float(field)
        round(self.income_to_date, 2)
        '{:.2f}'.format(self.income_to_date)
        self.total_to_date += float(field)
        self.total_to_date = self.total_to_date * 100
        self.total_to_date = round(self.total_to_date)
        self.total_to_date = self.total_to_date / 100
        '{:.2f}'.format(self.total_to_date)
        return self.save()
Example #11
0
def upgrade():
        op.add_column('foos', sa.Column('hello_on', AwareDateTime(),
                                default=tzware_datetime))
Example #12
0
class User(UserMixin, ResourceMixin, db.Model):
    ROLE = OrderedDict([('partner', 'Partner'), ('admin', 'Admin')])
    TYPE = OrderedDict([('Wedding Services', _('Wedding service')),
                        ('Restaurants', _('Restaurant')),
                        ('Singers', _('Singer')), ('DJ', _('DJ')),
                        ('Musicians', _('Musician')), ('Tamada', _('Tamada')),
                        ('Dancers', _('Dancer')),
                        ('Decorations', _('Decorations')),
                        ('Wedding Cars', _('Wedding Car')),
                        ('Beauty Salons', _('Beauty Salon')),
                        ('Hair Stylists', _('Hair Stylist')),
                        ('Wedding Dresses', _('Wedding House')),
                        ('Florists', _('Florists')),
                        ('Mens Wear', _('Mens Wear')),
                        ('Photographers', _('Photographer')),
                        ('Videographers', _('Videographer')),
                        ('Jewelers', _('Jeweler')), ('Artists', _('Artist')),
                        ('Cooks', _('Cook')), ('Kebab Chefs', _('Kebab Chef')),
                        ('Bridal Gifts', _('Bridal Gifts')),
                        ('Small Presents', _('Small Presents')),
                        ('Beverages', _('Wedding Beverages')),
                        ('Comedians', _('Comedian'))])

    REGION = OrderedDict([('Ashgabat', _('Ashgabat')),
                          ('Dashoguz', _('Dashoguz')),
                          ('Balkanabat', _('Balkanabat'))])

    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)

    #Relationship
    services = db.relationship(Services, backref='users', passive_deletes=True)
    payments = db.relationship(Payments, backref='users', passive_deletes=True)
    #Many-to-Many Relationship
    clients = db.relationship(Client,
                              secondary=client_user,
                              backref=db.backref('assign'),
                              cascade='all,delete',
                              lazy='joined')

    # Authentication.
    role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                     index=True,
                     nullable=False,
                     server_default='partner')
    category = db.Column(
        db.Enum(*TYPE, name='category_types', native_enum=False))
    region = db.Column(db.Enum(*REGION, name='region_types',
                               native_enum=False),
                       index=True,
                       nullable=False,
                       server_default='Ashgabat')
    active = db.Column('is_active',
                       db.Boolean(),
                       nullable=False,
                       server_default='1')
    confirmed = db.Column('is_confirmed',
                          db.Boolean(),
                          nullable=False,
                          server_default='0')
    username = db.Column(db.String(24), unique=True, index=True)
    company_name = db.Column(db.String(100), nullable=False)
    company_name_ru = db.Column(db.String(100))
    wedding_count = db.Column(db.Integer(), nullable=False, server_default='0')
    telephone = db.Column(db.String(50))
    telephone_2 = db.Column(db.String(50))
    telephone_3 = db.Column(db.String(50))
    image_name = db.Column(db.String(50))
    image_count = db.Column(db.Integer)
    payment = db.Column(db.Float)
    price = db.Column(db.Float)
    description = db.Column(db.Text)
    description_ru = db.Column(db.Text)
    email = db.Column(db.String(255),
                      unique=True,
                      index=True,
                      nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')
    profile_pic = db.Column(db.Text())

    # Additional settings.
    locale = db.Column(db.String(5), nullable=False, server_default='en')

    # Activity tracking.
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(User, self).__init__(**kwargs)

        self.password = User.encrypt_password(kwargs.get('password', ''))

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find a user by their e-mail or username.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        #current_app.logger.debug('{0} has tried to login'.format(identity))

        return User.query.filter((User.email == identity)
                                 | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        """
        Hash a plaintext string using PBKDF2. This is good enough according
        to the NIST (National Institute of Standards and Technology).

        In other words while bcrypt might be superior in practice, if you use
        PBKDF2 properly (which we are), then your passwords are safe.

        :param plaintext_password: Password in plain text
        :type plaintext_password: str
        :return: str
        """
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    @classmethod
    def deserialize_token(cls, token):
        """
        Obtain a user from de-serializing a signed token.

        :param token: Signed token.
        :type token: str
        :return: User instance or None
        """
        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)

            return User.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None

    @classmethod
    def initialize_password_reset(cls, identity):
        """
        Generate a token to reset the password for a specific user.

        :param identity: User e-mail address or username
        :type identity: str
        :return: User instance
        """
        u = User.find_by_identity(identity)
        reset_token = u.serialize_token()

        # This prevents circular imports.
        from toyetjek.blueprints.user.tasks import (
            deliver_password_reset_email)
        deliver_password_reset_email.delay(u.id, reset_token)

        return u

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return ''

        search_query = '%{0}%'.format(query)
        search_chain = (User.email.ilike(search_query),
                        User.username.ilike(search_query))

        return or_(*search_chain)

    @classmethod
    def is_last_admin(cls, user, new_role, new_active):
        """
        Determine whether or not this user is the last admin account.

        :param user: User being tested
        :type user: User
        :param new_role: New role being set
        :type new_role: str
        :param new_active: New active status being set
        :type new_active: bool
        :return: bool
        """
        is_demoting_admin = user.role == 'admin' and new_role != 'admin'
        is_changing_active = user.active is True and new_active is None
        admin_count = User.query.filter(User.role == 'admin').count()

        if is_demoting_admin and admin_count == 1:
            return True

        if is_changing_active and user.role == 'admin' and admin_count == 1:
            return True

        return False

    def is_active(self):
        """
        Return whether or not the user account is active, this satisfies
        Flask-Login by overwriting the default value.

        :return: bool
        """
        return self.active

    def is_confirmed(self):
        """
        Return whether or not the user account is confirmed, this satisfies
        Flask-Login by overwriting the default value.

        :return: bool
        """
        return self.confirmed

    def get_auth_token(self):
        """
        Return the user's auth token. Use their password as part of the token
        because if the user changes their password we will want to invalidate
        all of their logins across devices. It is completely fine to use
        md5 here as nothing leaks.

        This satisfies Flask-Login by providing a means to create a token.

        :return: str
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = URLSafeTimedSerializer(private_key)
        data = [str(self.id), md5(self.password.encode('utf-8')).hexdigest()]

        return serializer.dumps(data)

    def authenticated(self, with_password=True, password=''):
        """
        Ensure a user is authenticated, and optionally check their password.

        :param with_password: Optionally check their password
        :type with_password: bool
        :param password: Optionally verify this as their password
        :type password: str
        :return: bool
        """
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def serialize_token(self, expiration=3600):
        """
        Sign and create a token that can be used for things such as resetting
        a password or other tasks that involve a one off token.

        :param expiration: Seconds until it expires, defaults to 1 hour
        :type expiration: int
        :return: JSON
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)
        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    def update_activity_tracking(self, ip_address):
        """
        Update various fields on the user that's related to meta data on their
        account, such as the sign in count and ip address, etc..

        :param ip_address: IP address
        :type ip_address: str
        :return: SQLAlchemy commit results
        """
        self.sign_in_count += 1

        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip

        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()
Example #13
0
class User(ResourceMixin, db.Model):
    ROLE = OrderedDict([('member', 'Member'), ('admin', 'Admin')])

    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)

    # relationships
    credit_card = db.relationship(CreditCard,
                                  uselist=False,
                                  backref='credit_card',
                                  passive_deletes=True)
    subscription = db.relationship(Subscription,
                                   uselist=False,
                                   backref='subscription',
                                   passive_deletes=True)
    invoices = db.relationship(Invoice,
                               backref='invoices',
                               passive_deletes=True)

    # Auth
    role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                     index=True,
                     nullable=False,
                     server_default='member')
    username = db.Column(db.String(24), unique=True, index=True)
    email = db.Column(db.String(255),
                      unique=True,
                      index=True,
                      nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')
    active = db.Column('is_active',
                       db.Boolean(),
                       nullable=False,
                       server_default='0')

    # Billing
    name = db.Column(db.String(255), index=True)
    payment_id = db.Column(db.String(128), index=True)
    cancelled_subscription_on = db.Column(AwareDateTime())

    # Activity tracking
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    def __init__(self, **kwargs):
        # Call Flask_SQLAlchemy's constructor
        super(User, self).__init__(**kwargs)

        self.password = User.encrypt_password(kwargs.get('password', ''))

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find a user by their e-mail or username.

        :param identity: Email or username
        :type identity" str

        :return: User instance
        """
        return User.query.filter((User.email == identity)
                                 | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        """
        Hash a plaintext string using PBKDF2.

        :param plaintext_password: Password in plain text
        :type plaintext_password: str

        :return: str
        """
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    @classmethod
    def is_last_admin(cls, user, new_role):
        """
        Determine if this user is the last admin in the system. This is to
        ensure that there is always at least one admin.

        :param user: User being modified
        :type user: User

        :param new_role: Role the user is being changed to
        :type new_role: str

        :return: bool
        """
        is_changing_roles = user.role == 'admin' and new_role != 'admin'

        if is_changing_roles:
            admin_count = User.query.filter(User.role == 'admin').count()
            print('ADMIN COUNT')
            print(admin_count)
            if admin_count == 1:
                return True

        return False

    @classmethod
    def search(cls, query):
        """
        Search a resource by one or more fields.

        :param query: Query to search by
        :type query: str
        :return: SQLAlchemy filter
        """
        if query == '':
            return text('')

        search_query = '%{0}%'.format(query)  # %% partial words
        search_chain = (User.email.ilike(search_query),
                        User.username.ilike(search_query))

        # or_ allows a match on the email OR username
        return or_(*search_chain)

    @classmethod
    def bulk_delete(cls, ids):
        """
        Override the bulk_deleted method on ResourceMixin because users need
        to be deleted one at a time while also being deleted on stripe.

        :param ids: List of ids to be deleted
        :types ids: list
        :return: int
        """
        delete_count = 0

        for id in ids:
            user = User.query.get(id)

            if user is None:
                continue
            # see if user is currently subbed
            if user.payment_id is None:
                user.delete()
            else:
                subscription = Subscription()
                cancelled = subscription.cancel(user=user)
                # if cancelled sub on stripe successfully, delete locally
                if cancelled:
                    user.delete()

            delete_count += 1

        return delete_count

    @classmethod
    def init_verify_email(cls, identity):
        """
        Generate a one time use token to be sent to a User's email. This will
        verify their email address and activate their account so that they can
        authenticate themselves.

        :param identity: User e-mail address or username
        :type identity: str
        :return: User instance
        """
        u = User.find_by_identity(identity)
        activation_token = u.serialize_token(expiration=86400)

        # send the email as a background task with celery
        from vidme.blueprints.user.tasks import (deliver_verification_email)
        deliver_verification_email.delay(u.id, activation_token)

    @classmethod
    def deserialize_token(cls, token):
        """
        Obtain a user from de-serializing a signed token.

        :param token: Signed token
        :type token: str
        :return: User instance or None
        """
        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)

            return User.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None

    def serialize_token(self, expiration=3600):
        """
        Create a one time use token for things such as sending a confirmation
        email.

        :param expiration: Seconds until it expires, defaults to 1 hour
        :type expiration: int
        :return: JSON
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)
        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    def authenticated(self, with_password=True, password=''):
        """
        Ensure a user is authenticated, and optionally check their password

        :param with_password: Optionally check user's password
        :type with_password: bool

        :param password: Optionally verify this as their password
        :type password: str

        :return: bool
        """
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def update_activity_tracking(self, ip_address):
        """
        Update various fields on the user that's related to meta data on their
        account, such as sign_in_count and ip_address, etc...

        :param ip_address: IP address
        :type ip_address: str

        :return: SQLAlchemy commit results
        """
        self.sign_in_count += 1

        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip

        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()

    def is_active(self):
        """
        Return whether or not the user account is active

        :return: bool
        """
        return self.active
Example #14
0
class User(UserMixin, ResourceMixin, db.Model):
    ROLE = OrderedDict([('member', 'Member'), ('admin', 'Admin')])

    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)

    # Authentication.
    role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                     index=True,
                     nullable=False,
                     server_default='member')
    active = db.Column('is_active',
                       db.Boolean(),
                       nullable=False,
                       server_default='1')
    username = db.Column(db.String(24), unique=True, index=True)
    email = db.Column(db.String(255),
                      unique=True,
                      index=True,
                      nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')

    # Activity tracking.
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(User, self).__init__(**kwargs)

        self.password = User.encrypt_password(kwargs.get('password', ''))

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find a user by their e-mail or username.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        return User.query.filter((User.email == identity)
                                 | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        """
        Hash a plaintext string using PBKDF2. This is good enough according
        to the NIST (National Institute of Standards and Technology).

        In other words while bcrypt might be superior in practice, if you use
        PBKDF2 properly (which we are), then your passwords are safe.

        :param plaintext_password: Password in plain text
        :type plaintext_password: str
        :return: str
        """
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    @classmethod
    def deserialize_token(cls, token):
        """
        Obtain a user from de-serializing a signed token.

        :param token: Signed token.
        :type token: str
        :return: User instance or None
        """
        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)

            return User.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None

    @classmethod
    def initialize_password_reset(cls, identity):
        """
        Generate a token to reset the password for a specific user.

        :param identity: User e-mail address or username
        :type identity: str
        :return: User instance
        """
        u = User.find_by_identity(identity)
        reset_token = u.serialize_token()

        # This prevents circular imports.
        from socialtrack.blueprints.user.tasks import (
            deliver_password_reset_email)
        deliver_password_reset_email.delay(u.id, reset_token)

        return u

    def is_active(self):
        """
        Return whether or not the user account is active, this satisfies
        Flask-Login by overwriting the default value.

        :return: bool
        """
        return self.active

    def get_auth_token(self):
        """
        Return the user's auth token. Use their password as part of the token
        because if the user changes their password we will want to invalidate
        all of their logins across devices. It is completely fine to use
        md5 here as nothing leaks.

        This satisfies Flask-Login by providing a means to create a token.

        :return: str
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = URLSafeTimedSerializer(private_key)
        data = [str(self.id), md5(self.password.encode('utf-8')).hexdigest()]

        return serializer.dumps(data)

    def authenticated(self, with_password=True, password=''):
        """
        Ensure a user is authenticated, and optionally check their password.

        :param with_password: Optionally check their password
        :type with_password: bool
        :param password: Optionally verify this as their password
        :type password: str
        :return: bool
        """
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def serialize_token(self, expiration=3600):
        """
        Sign and create a token that can be used for things such as resetting
        a password or other tasks that involve a one off token.

        :param expiration: Seconds until it expires, defaults to 1 hour
        :type expiration: int
        :return: JSON
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)
        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    def update_activity_tracking(self, ip_address):
        """
        Update various fields on the user that's related to meta data on their
        account, such as the sign in count and ip address, etc..

        :param ip_address: IP address
        :type ip_address: str
        :return: SQLAlchemy commit results
        """
        self.sign_in_count += 1

        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip

        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()
Example #15
0
class Usuario(UserMixin, ResourceMixin, db.Model):
    ROLE = OrderedDict([('basico', 'Basico'), ('admin', 'Administrador')])

    __tablename__ = 'usuarios'
    id = db.Column(db.Integer, primary_key=True)

    # Authentication.
    perfil = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                       index=True,
                       nullable=False,
                       server_default='basico')
    ativo = db.Column(db.Boolean(), nullable=False, server_default='1')
    nome = db.Column(db.String(24), unique=True, index=True)
    email = db.Column(db.String(255),
                      unique=True,
                      index=True,
                      nullable=False,
                      server_default='')
    senha = db.Column(db.String(128), nullable=False, server_default='')

    # Activity tracking.
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Usuario, self).__init__(**kwargs)

        self.senha = Usuario.encrypt_password(kwargs.get('senha', ''))

    @classmethod
    def find_by_identity(cls, identity):

        return Usuario.query.filter((Usuario.email == identity)
                                    | (Usuario.nome == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):

        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    @classmethod
    def deserialize_token(cls, token):

        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])

        try:
            decoded_payload = private_key.loads(token)
            return Usuario.find_by_identity(
                decoded_payload.get('usuario_email'))

        except Exception:
            return None

    @classmethod
    def initialize_password_reset(cls, identity):

        u = Usuario.find_by_identity(identity)
        reset_token = u.serialize_token()

        # This prevents circular imports.
        from sca.blueprints.usuario.tasks import deliver_password_reset_email
        deliver_password_reset_email.delay(u.id, reset_token)

        return u

    def esta_ativo(self):

        return self.ativo

    def get_auth_token(self):

        private_key = current_app.config['SECRET_KEY']

        serializer = URLSafeTimedSerializer(private_key)
        data = [str(self.id), md5(self.senha.encode('utf-8')).hexdigest()]

        return serializer.dumps(data)

    def authenticated(self, with_password=True, senha=''):

        if with_password:
            return check_password_hash(self.senha, senha)

        return True

    def serialize_token(self, expiration=3600):

        private_key = current_app.config['SECRET_KEY']

        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)
        return serializer.dumps({'usuario_email': self.email}).decode('utf-8')

    def update_activity_tracking(self, ip_address):

        self.sign_in_count += 1

        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip

        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()
class User(UserMixin, ResourceMixin, db.Model):
    ROLE = OrderedDict([('admin', 'Admin'), ('client', 'Client'),
                        ('staff', 'Staff'), ('public', 'Public')])

    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships
    client = db.relationship(Client,
                             backref=db.backref('user', uselist=False),
                             uselist=False,
                             cascade="all, delete-orphan")
    staff = db.relationship(Staff,
                            backref=db.backref('user', uselist=False),
                            uselist=False,
                            cascade="all, delete-orphan")
    comments = db.relationship(Comment,
                               backref=db.backref('author', uselist=False),
                               lazy='dynamic')
    appointments = db.relationship(Appointment, backref='user', lazy='dynamic')

    # Properties.
    role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                     index=True,
                     nullable=False,
                     server_default='public')
    active = db.Column('is_active',
                       db.Boolean(),
                       nullable=False,
                       server_default='1')
    username = db.Column(db.String(24),
                         unique=True,
                         index=True,
                         nullable=False)
    email = db.Column(db.String(255),
                      unique=True,
                      index=True,
                      nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')
    first_name = db.Column(db.String(256), nullable=False)
    middle_name = db.Column(db.String(256))
    last_name = db.Column(db.String(256), index=True, nullable=False)
    unit_number = db.Column(db.String(128))
    street_address = db.Column(db.String(128))
    suburb = db.Column(db.String(128))
    postcode = db.Column(db.String(4))
    state = db.Column(db.String(128))
    country = db.Column(db.String(128))
    phone_number = db.Column(db.String(128), nullable=False)
    photo = db.Column(db.String(256))

    # Activity tracking.
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    # Additional settings.
    locale = db.Column(db.String(5), nullable=False, server_default='en')

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(User, self).__init__(**kwargs)

        self.password = User.encrypt_password(kwargs.get('password', ''))

    @hybrid_property
    def full_name(self):
        if self.middle_name:
            return self.last_name.upper(
            ) + ', ' + self.first_name + ' ' + self.middle_name
        else:
            return self.last_name.upper() + ', ' + self.first_name

    @hybrid_property
    def first_last_name(self):
        return self.first_name + ' ' + self.last_name

    @hybrid_property
    def full_address(self):
        street_address = self.unit_number + "/" + self.street_address if self.unit_number and self.street_address else self.street_address
        address_elements = [
            street_address, self.suburb, self.postcode, self.state,
            self.country
        ]
        return ", ".join(
            address_elements) if None not in address_elements else None

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find user by e-mail or username.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        return User.query.filter((User.email == identity)
                                 | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        """
        Hash password using Bcrypt.

        :param plaintext_password: Password in plain text
        :type plaintext_password: str
        :return: str
        """
        if plaintext_password:
            return hashpw(plaintext_password, gensalt())

        return None

    @classmethod
    def deserialize_token(cls, token):
        """
        Obtain user from de-serializing token.

        :param token: Signed token.
        :type token: str
        :return: User instance or None
        """
        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)

            return User.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None

    @classmethod
    def initialize_password_reset(cls, identity):
        """
        Generate token to reset the password for a specific user.

        :param identity: User e-mail address or username
        :type identity: str
        :return: User instance
        """
        u = User.find_by_identity(identity)
        reset_token = u.serialize_token()

        # This prevents circular imports.
        from server.blueprints.user.tasks import (deliver_password_reset_email)
        deliver_password_reset_email.delay(u.id, reset_token)

        return u

    @classmethod
    def search(cls, query):
        """
        Search using ILIKE (case-insensitive) expression.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return ''

        search_query = '%{0}%'.format(query)
        search_chain = (User.email.ilike(search_query),
                        User.username.ilike(search_query))

        return or_(*search_chain)

    @classmethod
    def is_last_admin(cls, user, new_role, new_active):
        """
        Return whether user is last admin account.

        :param user: User being tested
        :type user: User
        :param new_role: New role being set
        :type new_role: str
        :param new_active: New active status being set
        :type new_active: bool
        :return: bool
        """
        is_changing_roles = user.role == 'admin' and new_role != 'admin'
        is_changing_active = user.active is True and new_active is None

        if is_changing_roles or is_changing_active:
            admin_count = User.query.filter(User.role == 'admin').count()
            active_count = User.query.filter(User.is_active is True).count()

            if admin_count == 1 or active_count == 1:
                return True

        return False

    def is_active(self):
        """
        Return whether user account is active (overrides Flask-Login default).

        :return: bool
        """
        return self.active

    def get_auth_token(self):
        """
        Return user's auth token.

        :return: str
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = URLSafeTimedSerializer(private_key)
        data = [str(self.id), md5(self.password.encode('utf-8')).hexdigest()]

        return serializer.dumps(data)

    def authenticated(self, with_password=True, password=''):
        """
        Ensure user authenticated.

        :param with_password: Optionally check password
        :type with_password: bool
        :param password: Password to verify
        :type password: str
        :return: bool
        """
        if with_password:
            return checkpw(password.encode('utf-8'),
                           self.password.encode('utf-8'))

        return True

    def serialize_token(self, expiration=3600):
        """
        Serialize token for resetting passwords, etc.

        :param expiration: Seconds until it expires, defaults to 1 hour
        :type expiration: int
        :return: JSON
        """
        private_key = current_app.config['SECRET_KEY']

        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)
        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    def update_activity_tracking(self, ip_address):
        """
        Update user's meta data.

        :param ip_address: IP address
        :type ip_address: str
        :return: SQLAlchemy commit results
        """
        self.sign_in_count += 1
        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip
        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()

    def to_json(self):
        active = "Active" if self.active == True else "Disabled"
        json_data = {
            'id': self.id,
            'created': self.created_on,
            'updated': self.updated_on,
            'active': active,
            'firstLastName': self.first_last_name,
            'firstName': self.first_name,
            'middleName': self.middle_name,
            'lastName': self.last_name.upper(),
            'fullName': self.full_name,
            'username': self.username,
            'email': self.email,
            'phoneNumber': self.phone_number,
            'unitNumber': self.unit_number,
            'streetAddress': self.street_address,
            'suburb': self.suburb,
            'postcode': self.postcode,
            'state': self.state,
            'country': self.country,
            'fullAddress': self.full_address,
            'role': self.role,
            'photo': self.photo,
        }

        return json_data
Example #17
0
class Projects(UserMixin, ResourceMixin, db.Model):
    #    ROLE = OrderedDict([
    #        ('member', 'Member'),
    #        ('admin', 'Admin')
    #    ])

    __tablename__ = 'projects'
    id = db.Column(db.Integer, primary_key=True)

    # Authentication.
    projectid = db.Column(db.String(24), unique=True, index=True)
    description = db.Column(db.String(4100), nullable=False, server_default='')
    skills = db.Column(db.String(4100), unique=False, server_default='')
    email = db.Column(db.String(255),
                      unique=False,
                      index=False,
                      nullable=False,
                      server_default='')
    resource_email = db.Column(db.String(255),
                               unique=False,
                               index=False,
                               nullable=False,
                               server_default='')
    whizcoin = db.Column(db.Integer, nullable=False, default=100)
    department = db.Column(db.String(255),
                           unique=False,
                           index=False,
                           nullable=False,
                           server_default='')
    protype = db.Column(db.String(255),
                        unique=False,
                        index=False,
                        nullable=False,
                        server_default='')
    startdate = db.Column(db.String(255),
                          unique=False,
                          index=False,
                          nullable=False,
                          server_default='')
    enddate = db.Column(db.String(255),
                        unique=False,
                        index=False,
                        nullable=False,
                        server_default='')
    interesting_participants = db.Column(db.String(4100),
                                         unique=False,
                                         server_default='')
    project_status = db.Column(db.String(255),
                               unique=False,
                               index=False,
                               nullable=False,
                               server_default='')

    # Activity tracking.
    sign_in_count = db.Column(db.Integer, nullable=False, default=0)
    current_sign_in_on = db.Column(AwareDateTime())
    current_sign_in_ip = db.Column(db.String(45))
    last_sign_in_on = db.Column(AwareDateTime())
    last_sign_in_ip = db.Column(db.String(45))

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Projects, self).__init__(**kwargs)

    @classmethod
    def find_by_identity(cls, identity):
        """
        Find a user by their e-mail or username.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        return Projects.query.filter((Projects.projectid == identity)).first()

    def update_activity_tracking(self, ip_address):
        """
        Update various fields on the user that's related to meta data on their
        account, such as the sign in count and ip address, etc..

        :param ip_address: IP address
        :type ip_address: str
        :return: SQLAlchemy commit results
        """
        self.sign_in_count += 1

        self.last_sign_in_on = self.current_sign_in_on
        self.last_sign_in_ip = self.current_sign_in_ip

        self.current_sign_in_on = datetime.datetime.now(pytz.utc)
        self.current_sign_in_ip = ip_address

        return self.save()
class Staff(ResourceMixin, db.Model):
    __tablename__ = 'staff'

    # Relationships
    posts_authored = db.relationship(Post,
                                     backref=db.backref('author',
                                                        lazy="joined"),
                                     lazy='dynamic')
    matters_handled = db.relationship(Matter,
                                      backref='staff',
                                      secondary=staff_matter)
    appointments = db.relationship(Appointment,
                                   backref='staff',
                                   secondary=staff_appointment)
    practice_areas = db.relationship(PracticeArea,
                                     backref='staff',
                                     secondary=staff_practice_area)

    id = db.Column(db.Integer, primary_key=True)
    dated_joined = db.Column(AwareDateTime(), default=tzaware_datetime)
    position = db.Column(db.String(128))
    description = db.Column(db.ARRAY(db.String))

    # Foreign Keys
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id'),
                        index=True,
                        unique=True,
                        nullable=False)

    @classmethod
    def find_by_user_id(cls, user_id):
        """
        Find staff by user ID.

        :param user_id: user ID
        :type title: str
        :return: Staff instance
        """
        return cls.query.filter(cls.user_id == user_id).first()

    def to_json(self):
        matters_handled = [matter.id for matter in self.matters_handled]
        posts_authored = [post.id for post in self.posts_authored]
        practice_areas = [
            practice_area.id for practice_area in self.practice_areas
        ]

        return {
            'id': self.id,
            'name': self.user.first_last_name,
            'dateJoined': self.dated_joined,
            'position': self.position,
            'photo': self.user.photo,
            'posts': self.posts_authored.count(),
            'description': self.description,
            'userId': self.user_id,
            'mattersHandled': matters_handled,
            'postsAuthored': posts_authored,
            'practiceAreas': practice_areas
        }
Example #19
0
class Coupon(ResourceMixin, db.Model):
    DURATION = OrderedDict([
        ('forever', 'Forever'),
        ('once', 'Once'),
        ('repeating', 'Repeating')
    ])

    __tablename__ = 'coupons'
    id = db.Column(db.Integer, primary_key=True)

    # Coupon details.
    code = db.Column(db.String(128), index=True, unique=True)
    duration = db.Column(db.Enum(*DURATION, name='duration_types'),
                         index=True, nullable=False, server_default='forever')
    amount_off = db.Column(db.Integer())
    percent_off = db.Column(db.Integer())
    currency = db.Column(db.String(8))
    duration_in_months = db.Column(db.Integer())
    max_redemptions = db.Column(db.Integer(), index=True)
    redeem_by = db.Column(AwareDateTime(), index=True)
    times_redeemed = db.Column(db.Integer(), index=True,
                               nullable=False, default=0)
    valid = db.Column(db.Boolean(), nullable=False, server_default='1')

    def __init__(self, **kwargs):
        if self.code:
            self.code = self.code.upper()
        else:
            self.code = Coupon.random_coupon_code()

        # Call Flask-SQLAlchemy's constructor.
        super(Coupon, self).__init__(**kwargs)

    @hybrid_property
    def redeemable(self):
        """
        Return coupons that are still redeemable. Coupons will become invalid
        once they run out on save. We want to explicitly do a date check to
        avoid having to hit Stripe's API to get back potentially valid codes.

        :return: SQLAlchemy query object
        """
        is_redeemable = or_(self.redeem_by.is_(None),
                            self.redeem_by >= datetime.datetime.now(pytz.utc))

        return and_(self.valid, is_redeemable)

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return ''

        search_query = '%{0}%'.format(query)

        return or_(Coupon.code.ilike(search_query))

    @classmethod
    def random_coupon_code(cls):
        """
        Create a human readable random coupon code.

        :return: str
        """
        charset = string.digits + string.ascii_uppercase
        charset = charset.replace('B', '').replace('I', '')
        charset = charset.replace('O', '').replace('S', '')
        charset = charset.replace('0', '').replace('1', '')

        random_chars = ''.join(choice(charset) for _ in range(14))

        coupon_code = '{0}-{1}-{2}'.format(random_chars[0:4],
                                           random_chars[5:9],
                                           random_chars[10:14])

        return coupon_code

    @classmethod
    def expire_old_coupons(cls, compare_datetime=None):
        """
        Invalidate coupons that are past their redeem date.

        :param compare_datetime: Time to compare at
        :type compare_datetime: date
        :return: The result of updating the records
        """
        if compare_datetime is None:
            compare_datetime = datetime.datetime.now(pytz.utc)

        Coupon.query.filter(Coupon.redeem_by <= compare_datetime) \
            .update({Coupon.valid: not Coupon.valid})

        return db.session.commit()

    @classmethod
    def create(cls, params):
        """
        Return whether or not the coupon was created successfully.

        :return: bool
        """
        payment_params = params

        payment_params['code'] = payment_params['code'].upper()

        if payment_params.get('amount_off'):
            payment_params['amount_off'] = \
                dollars_to_cents(payment_params['amount_off'])

        PaymentCoupon.create(**payment_params)

        if 'id' in payment_params:
            payment_params['code'] = payment_params['id']
            del payment_params['id']

        if 'redeem_by' in payment_params:
            if payment_params.get('redeem_by') is not None:
                params['redeem_by'] = payment_params.get('redeem_by').replace(
                    tzinfo=pytz.UTC)

        coupon = Coupon(**payment_params)

        db.session.add(coupon)
        db.session.commit()

        return True

    @classmethod
    def bulk_delete(cls, ids):
        """
        Override the general bulk_delete method because we need to delete them
        one at a time while also deleting them on Stripe.

        :param ids: List of ids to be deleted
        :type ids: list
        :return: int
        """
        delete_count = 0

        for id in ids:
            coupon = Coupon.query.get(id)

            if coupon is None:
                continue

            # Delete on Stripe.
            stripe_response = PaymentCoupon.delete(coupon.code)

            # If successful, delete it locally.
            if stripe_response.get('deleted'):
                coupon.delete()
                delete_count += 1

        return delete_count

    @classmethod
    def find_by_code(cls, code):
        """
        Find a coupon by its code.

        :param code: Coupon code to find
        :type code: str
        :return: Coupon instance
        """
        formatted_code = code.upper()
        coupon = Coupon.query.filter(Coupon.redeemable,
                                     Coupon.code == formatted_code).first()

        return coupon

    def redeem(self):
        """
        Update the redeem stats for this coupon.

        :return: Result of saving the record
        """
        self.times_redeemed += 1

        if self.max_redemptions:
            if self.times_redeemed >= self.max_redemptions:
                self.valid = False

        return db.session.commit()

    def apply_discount_to(self, amount):
        """
        Apply the discount to an amount.

        :param amount: Amount in cents
        :type amount: int
        :return: int
        """
        if self.amount_off:
            amount -= self.amount_off
        elif self.percent_off:
            amount *= (1 - (self.percent_off * 0.01))

        return int(amount)

    def to_json(self):
        """
        Return JSON fields to represent a coupon.

        :return: dict
        """
        params = {
            'duration': self.duration,
            'duration_in_months': self.duration_in_months,
        }

        if self.amount_off:
            params['amount_off'] = cents_to_dollars(self.amount_off)

        if self.percent_off:
            params['percent_off'] = self.percent_off,

        return params
Example #20
0
class Event(ResourceMixin, db.Model):
    __tablename__ = 'events'

    # Objects.
    id = db.Column(db.Integer, primary_key=True)
    event_id = db.Column(db.BigInteger,
                         unique=True,
                         index=True,
                         nullable=False)
    requester_name = db.Column(db.String(255), unique=False, index=True)
    requester_email = db.Column(db.String(255), unique=False, index=True)
    notes = db.Column(db.String, unique=False, index=True)
    zoom = db.Column(db.String, unique=False, index=True)
    event_datetime = db.Column(AwareDateTime())
    date = db.Column(db.String(255))
    start_time = db.Column(db.Time)
    tz_offset = db.Column(db.String(255))
    duration_minutes = db.Column(db.Integer)

    # Relationships.
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=True,
                        primary_key=False,
                        unique=False)
    account_id = db.Column(db.BigInteger,
                           db.ForeignKey(Account.account_id,
                                         onupdate='CASCADE',
                                         ondelete='CASCADE'),
                           index=True,
                           nullable=True,
                           primary_key=False,
                           unique=False)
    event_type_id = db.Column(db.BigInteger,
                              db.ForeignKey('event_types.event_type_id',
                                            onupdate='CASCADE',
                                            ondelete='CASCADE'),
                              index=True,
                              nullable=True,
                              primary_key=False,
                              unique=False)

    def __init__(self, user_id, calendar_id=None, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Event, self).__init__(**kwargs)
        self.event_id = Event.generate_id()
        self.user_id = user_id

        if calendar_id is not None:
            self.calendar_id = calendar_id

    def as_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    @classmethod
    def generate_id(cls, size=8):
        # Generate a random 8-character id
        chars = string.digits
        result = int(''.join(random.choice(chars) for _ in range(size)))

        # Check to make sure there isn't already that id in the database
        if not db.session.query(
                exists().where(cls.event_id == result)).scalar():
            return result
        else:
            Event.generate_id()

    @classmethod
    def find_by_id(cls, identity):
        """
        Find an email by its message id.

        :param identity: Email or username
        :type identity: str
        :return: User instance
        """
        return Event.query.filter((Event.id == identity).first())

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return ''

        search_query = '%{0}%'.format(query)
        search_chain = (Event.id.ilike(search_query))

        return or_(*search_chain)

    @classmethod
    def bulk_delete(cls, ids):
        """
        Override the general bulk_delete method because we need to delete them
        one at a time while also deleting them on Stripe.

        :param ids: Event of ids to be deleted
        :type ids: event
        :return: int
        """
        delete_count = 0

        for id in ids:
            event = Event.query.get(id)

            if event is None:
                continue

            event.delete()

            delete_count += 1

        return delete_count


# class Event:
#
#     tags = ''
#
#     def __init__(self, event):
#         self.description = event['body_html']
#         self.title = event['title']
#         self.created = event['created_at']
#         self.event_id = event['id']
#         self.images = event['images']
#         self.options = event['options']
#         self.variants = event['variants']