示例#1
0
class Books(db.Model, ResourceMixin):

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

    # Book Metadata
    title = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text)
    size = db.Column(db.String, default='0')
    pages = db.Column(db.Integer, default=0)
    isbn13 = db.Column(db.String(13), default='')
    isbn10 = db.Column(db.String(10), default='')
    language = db.Column(db.String(100), default='')

    downloads = db.Column(db.Integer, default=0)
    pg_views = db.Column(db.Integer, default=0)
    post_status = db.Column(db.String, default='0')

    # Book uploaded By
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    # Relation Ship
    image = db.relationship('CoverImage', backref='book', lazy=True)
    file = db.relationship('BookFile', backref='book', lazy=True)
    tags = db.relationship('Tags',
                           secondary=post_tags,
                           backref=db.backref('books', lazy='dynamic'))
    authors = db.relationship('Authors',
                              secondary=author_posts,
                              backref=db.backref('books', lazy='dynamic'))
    comments = db.relationship('Comments', backref='books', lazy='dynamic')
    reports = db.relationship('Reports', backref='problem_by', lazy=True)
    categories = db.relationship('Categories',
                                 secondary=categories_books,
                                 backref=db.backref('books', lazy='dynamic'))
    publishers = db.relationship('Publishers',
                                 secondary=publishers_books,
                                 backref=db.backref('books', lazy='dynamic'))

    @property
    def tag_list(self):
        return ', '.join(tag.name for tag in self.tags)

    # @classmethod
    def is_live(self):
        return self.post_status == POST_ACCESS['live']

    def is_draft(self):
        return self.post_status == POST_ACCESS['draft']

    def is_down(self):
        return self.post_status == POST_ACCESS['down']

    @property
    def contenty(self):
        return self.body[:100]

    def __repr__(self):
        return f"<Book {self.title}>"
示例#2
0
class Tags(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), unique=True)
    color = db.Column(db.String(100))
    categories = db.relationship('Categories',
                                 secondary=categories_tags,
                                 backref=db.backref('tags', lazy='dynamic'))

    def __repr__(self):
        return f'<Tag {self.name}>'
示例#3
0
class Contact(ResourceMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)

    name = db.Column(db.String(80), nullable=False)

    # Contact Info
    phone = db.Column(db.String(15), server_default='')
    email = db.Column(db.String(45), server_default='')
    website = db.Column(db.String(255), server_default='')

    Customer_id = db.Column(db.Integer,
                            db.ForeignKey('customer.id'),
                            nullable=False)
示例#4
0
class Complaint(ResourceMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)

    nature = db.Column(db.String(80), nullable=False)
    description = db.Column(db.String(80))
    contact_name = db.Column(db.String(80))
    contact_phone = db.Column(db.String(80))
    source = db.Column(db.String(80))

    completed_on = db.Column(AwareDateTime())

    Machine_id = db.Column(db.Integer, db.ForeignKey('machine.id'))
    Machine_Customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'))
    Employee_id = db.Column(db.Integer, db.ForeignKey('employee.id'))
示例#5
0
class Reports(db.Model, ResourceMixin):
    id = db.Column(db.Integer, primary_key=True)
    problem = db.Column(db.String(60), nullable=False)
    describe = db.Column(db.Text)
    book_id = db.Column(db.Integer, db.ForeignKey('books.id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    def __repr__(self):
        return f'<Report {self.book_id} | {self.problem}>'
示例#6
0
class Authors(db.Model, ResourceMixin):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), unique=True, nullable=False)
    content = db.Column(db.Text)
    image_file = db.relationship('AuthorProfileImage',
                                 backref='author_image',
                                 lazy=True)
    categories = db.relationship('Categories',
                                 secondary=author_categories,
                                 backref=db.backref('authors', lazy='dynamic'))
    pg_views = db.Column(db.Integer, default=0)

    def __repr__(self):
        return '<Auhtor  %s>' % self.name
示例#7
0
class User(UserMixin, ResourceMixin, db.Model):

    ROLE = OrderedDict([('member', 'Member'), ('admin', 'Admin')])

    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)
    phone = db.Column(db.String(13))
    email = db.Column(db.String(255),
                      unique=True,
                      nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')
    phone_confirmed = db.Column(db.Boolean(),
                                nullable=False,
                                server_default='0')
    email_confirmed = db.Column(db.Boolean(),
                                nullable=False,
                                server_default='0')

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

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

    # 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')

    # RelationShips
    companies = db.relationship('Company', backref='owner', lazy=True)

    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):
        return User.query.filter((User.email == identity)
                                 | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    def authenticated(self, with_password=True, password=''):
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def is_active(self, act=False):
        if act:
            self.active = True
            return self.active
        return self.active

    def get_auth_token(self):
        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 serialize_token(self, expiration=3600):
        private_key = current_app.config['SECRET_KEY']
        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)

        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    @classmethod
    def confirm_email_token(cls, username, email, password, expiration=3600):
        private_key = current_app.config['SECRET_KEY']
        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)

        token = serializer.dumps({
            'user_username': username,
            'user_email': email,
            'user_password': password
        }).decode('utf-8')

        return token

    @classmethod
    def deserialize_token(cls, token):
        private_key = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)
            data = {
                'user_username': decoded_payload.get('user_username'),
                'user_email': decoded_payload.get('user_email'),
                'user_password': decoded_payload.get('user_password')
            }
            return data
        except Exception:
            return None

    def update_activity_tracking(self, ip_address):
        self.sign_in_count += 1
        self.active = True

        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()
示例#8
0
class Customers(ResourceMixin, db.Model):

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

    # Genral Info
    first_name = db.Column(db.String(50), nullable=False)
    last_name = db.Column(db.String(50))
    company = db.Column(db.String(50), index=True)
    display_name = db.Column(db.String(50), nullable=False)
    customer_type = db.Column(db.String(50))

    # Contant Info
    phone = db.Column(db.String(15), server_default='')
    email = db.Column(db.String(255), server_default='')
    website = db.Column(db.String(255), server_default='')

    # Billing Address
    address = db.Column(db.String(300), server_default='')
    pincode = db.Column(db.String(25), server_default='')
    city = db.Column(db.String(255), index=True, server_default='')
    state = db.Column(db.String(255), server_default='')
    country = db.Column(db.String(255), server_default='')

    # Shiping Address
    s_address = db.Column(db.String(300), server_default='')
    s_pincode = db.Column(db.String(25), server_default='')
    s_city = db.Column(db.String(255), server_default='')
    s_state = db.Column(db.String(255), server_default='')
    s_country = db.Column(db.String(255), server_default='')

    # Tax Info
    gst = db.Column(db.String(15), server_default='')
    pan = db.Column(db.String(10), server_default='')

    # Meta Info
    is_active = db.Column('is_active',
                          db.Boolean(),
                          nullable=False,
                          server_default='1')
示例#9
0
class Users(UserMixin, ResourceMixin, db.Model):
    
    id = db.Column(db.Integer, primary_key=True)

    username = db.Column(db.String(24), unique=True, index=True)
    phone = db.Column(db.String(13))
    email = db.Column(db.String(255), unique=True, nullable=False, server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')
    
    phone_confirmed = db.Column(db.Boolean(), nullable=False, server_default='0')
    email_confirmed = db.Column(db.Boolean(), nullable=False, server_default='0')
    
    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')
    otp = db.Column(db.String(10), nullable=True)

    # 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):
        super(Users, self).__init__(**kwargs)
        self.password = Users.encrypt_password(kwargs.get('password', ''))

    @classmethod
    def find_by_identity(cls, identity):
        return Users.query.filter((Users.email == identity) | (Users.username == identity)).first()
    
    @classmethod
    def encrypt_password(cls, plaintext_password):
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None
    
    def authenticated(self, with_password=True, password=''):        
        if with_password:
            return check_password_hash(self.password, password)

        return True
    
    def is_active(self, act=False):
        if act:
            self.active = True
            return self.active
        return self.active

    def update_activity_tracking(self, ip_address):
        self.sign_in_count += 1
        self.active = True

        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 get_auth_token(self):        
        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 serialize_token(self, expiration=3600):        
        private_key = current_app.config['SECRET_KEY']
        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)

        return serializer.dumps({'user_email': self.email}).decode('utf-8')

    @classmethod
    def confirm_email_token(cls, username, email, password, expiration=3600):
        private_key = current_app.config['SECRET_KEY']
        serializer = TimedJSONWebSignatureSerializer(private_key, expiration)

        token = serializer.dumps({'user_username': username,'user_email': email,'user_password': password}).decode('utf-8')

        return token

    @classmethod
    def deserialize_token(cls, token):        
        private_key = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'])
        try:
            decoded_payload = private_key.loads(token)
            data = {'user_username':decoded_payload.get('user_username'),
                    'user_email':decoded_payload.get('user_email'),
                    'user_password':decoded_payload.get('user_password')}
            return data
        except Exception:
            return None


    def genrate_otp(self, digit=4):
        f = int('1'+'0'*(digit - 1))
        l = int('9'*digit)
        otp = random.randint(f,l)

        return otp
    


    def verify_otp(self, otp):
        if self.otp:
            if self.otp == otp:
                return True
        
        return False
示例#10
0
class Customer(ResourceMixin, db.Model):

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

    # Genral Info
    first_name = db.Column(db.String(80), nullable=False)
    last_name = db.Column(db.String(65))

    display_name = db.Column(db.String(145), nullable=False)
    company = db.Column(db.String(125))
    customer_type = db.Column(db.String(45))

    # Contant Info
    phone = db.Column(db.String(15), server_default='')
    email = db.Column(db.String(45), server_default='')
    website = db.Column(db.String(255), server_default='')

    # Billing Address
    address = db.Column(db.String(200), server_default='')
    pincode = db.Column(db.String(25), server_default='')
    city = db.Column(db.String(45), server_default='')
    state = db.Column(db.String(45), server_default='')
    country = db.Column(db.String(45), server_default='')

    # Shiping Address
    s_address = db.Column(db.String(200), server_default='')
    s_pincode = db.Column(db.String(25), server_default='')
    s_city = db.Column(db.String(45), server_default='')
    s_state = db.Column(db.String(45), server_default='')
    s_country = db.Column(db.String(45), server_default='')

    # Tax Info
    gst = db.Column(db.String(15), server_default='')
    pan = db.Column(db.String(10), server_default='')

    # Meta Info
    is_active = db.Column('is_active',
                          db.Boolean(),
                          nullable=False,
                          server_default='1')

    # Relationships
    machines = db.relationship('Machine', backref='owner', lazy=True)
    contacts = db.relationship('Contact', backref='in_reach', lazy=True)
示例#11
0
class CreditCard(ResourceMixin, db.Model):
    IS_EXPIRING_THRESHOLD_MONTHS = 2

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

    # Relationships.
    user_id = db.Column(db.Integer,
                        db.ForeignKey('user.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=False)

    # Card details.
    brand = db.Column(db.String(32))
    last4 = db.Column(db.Integer)
    exp_date = db.Column(db.Date, index=True)
    is_expiring = db.Column(db.Boolean(), nullable=False, server_default='0')

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

    @classmethod
    def is_expiring_soon(cls, compare_date=None, exp_date=None):
        """Determine whether or not this credit card is expiring soon.

        :param compare_date: Date to compare at
        :type compare_date: date
        :param exp_date: Expiration date
        :type exp_date: date
        :return: bool
        """
        return exp_date <= timedelta_months(
            CreditCard.IS_EXPIRING_THRESHOLD_MONTHS, compare_date=compare_date)

    @classmethod
    def mark_old_credit_cards(cls, compare_date=None):
        """
        Mark credit cards that are going to expire soon or have expired.

        :param compare_date: Date to compare at
        :type compare_date: date
        :return: Result of updating the records
        """
        today_with_delta = timedelta_months(
            CreditCard.IS_EXPIRING_THRESHOLD_MONTHS, compare_date)

        CreditCard.query.filter(
            CreditCard.exp_date <= today_with_delta).update(
                {CreditCard.is_expiring: True})

        return db.session.commit()

    @classmethod
    def extract_card_params(cls, customer):
        """
        Extract the credit card info from a payment customer object.

        :param customer: Payment customer
        :type customer: Payment customer
        :return: dict
        """
        card_data = customer.sources.data[0]
        exp_date = datetime.date(card_data.exp_year, card_data.exp_month, 1)

        card = {
            'brand': card_data.brand,
            'last4': card_data.last4,
            'exp_date': exp_date,
            'is_expiring': CreditCard.is_expiring_soon(exp_date=exp_date)
        }

        return card
示例#12
0
class Users(db.Model,ResourceMixin, UserMixin):    
    
    ROLE = OrderedDict([
        ('member', 'Member'),
        ('admin', 'Admin')
    ])    

    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')

    # Meta Info
    username = db.Column(db.String(24), unique=True, index=True)
    phone = db.Column(db.String(13))
    email = db.Column(db.String(255), unique=True, nullable=False, server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')
    
    pg_views = db.Column(db.Integer, default=0)

    # 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')    

    # RelationShips
    image = db.relationship('ProfileImage', backref='user', lazy=True)    
    books = db.relationship('Books', backref='user', lazy=True)
    comments = db.relationship('Comments', backref='user', lazy='dynamic')
    downloads = db.relationship('Downloads', backref='user', lazy=True)

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Users, self).__init__(**kwargs)
        self.password = Users.encrypt_password(kwargs.get('password', ''))        

    @classmethod
    def find_by_identity(cls, identity):        
        return Users.query.filter(
          (Users.email == identity) | (Users.username == 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 Users.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None


    @classmethod
    def search(cls, query):
        if not query:
            return ''

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

        return or_(*search_chain)

    @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 = Users.find_by_identity(identity)
        reset_token = u.serialize_token()

        # This prevents circular imports.
        from manager.apps.users.tasks import deliver_password_reset_email
        deliver_password_reset_email.delay(u.id, reset_token)

        return u

    @classmethod
    def is_last_admin(cls, user, new_role, new_active):
        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 = Users.query.filter(Users.role == 'admin').count()
            active_count = Users.query.filter(Users.is_active is True).count()

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

        return False

    def is_active(self, act):
        if act:
            self.active = act
            return self.active
        return self.active

    def get_auth_token(self):        
        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=''):        
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def serialize_token(self, expiration=3600):        
        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):        
        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 = tzware_datetime()
        self.current_sign_in_ip = ip_address

        return self.save()
示例#13
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'])
        from manager.apps.billing.gateways.stripecom import Coupon as PaymentCoupon
        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
            from manager.apps.billing.gateways.stripecom import Coupon as PaymentCoupon
            # 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 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
示例#14
0
class Employee(UserMixin, ResourceMixin, db.Model):
    ROLE = OrderedDict([('engineer', 'Engineer'), ('manager', 'Manager'),
                        ('admin', 'Admin')])

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

    # Authentication.
    role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
                     nullable=False,
                     server_default='engineer')
    active = db.Column('is_active',
                       db.Boolean(),
                       nullable=False,
                       server_default='1')
    name = db.Column(db.String(24), unique=True)
    phone = db.Column(db.String(13))
    email = db.Column(db.String(255),
                      unique=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):
        super(Employee, self).__init__(**kwargs)
        self.password = Employee.encrypt_password(kwargs.get('password', ''))

    @classmethod
    def find_by_identity(cls, identity):
        return User.query.filter((User.email == identity)
                                 | (User.username == identity)).first()

    @classmethod
    def encrypt_password(cls, plaintext_password):
        if plaintext_password:
            return generate_password_hash(plaintext_password)

        return None

    def authenticated(self, with_password=True, password=''):
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def is_active(self, act=False):
        if act:
            self.active = True
            return self.active
        return self.active

    def update_activity_tracking(self, ip_address):
        self.sign_in_count += 1
        self.active = True

        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()
示例#15
0
class Subscription(ResourceMixin, db.Model):
    __tablename__ = 'subscriptions'
    
    id = db.Column(db.Integer, primary_key=True)
    
    # Subscription details.
    plan = db.Column(db.String(128))
    coupon = db.Column(db.String(128))
    
    # Relationships.
    user_id = db.Column(db.Integer, db.ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE'), index=True, nullable=False)
    
    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Subscription, self).__init__(**kwargs)

    @classmethod
    def get_plan_by_id(cls, plan):
        """Pick the plan based on the plan identifier.
        
        :param |plan (str): Plan identifier
        :return: dict or None
        """
        for key, value in settings.STRIPE_PLANS.items():
            if value.get('id') == plan:
                return settings.STRIPE_PLANS[key]

        return None
    
    def create(self, user=None, name=None, plan=None, coupon=None, token=None):
        """Create a recurring subscription.

        :param |user (~User): User to apply the subscription to
        :param |name (str): User's billing name
        :param |plan (str): Plan identifier
        :param |coupon (str): Coupon code to apply
        :param |token (str): Token returned by JavaScript
        :return: bool
        """
        if token is None:
            return False

        if coupon:
            self.coupon = coupon.upper()
        
        from manager.apps.billing.gateways.stripecom import Subscription as PaymentSubscription
        customer = PaymentSubscription.create(token=token, email=user.email, plan=plan, coupon=self.coupon)
        
        # Update the user account.
        user.payment_id = customer.id
        user.name = name
        user.cancelled_subscription_on = None

        # Set the subscription details.
        self.user_id = user.id
        self.plan = plan
        
        # Redeem the coupon.
        if coupon:
            coupon = Coupon.query.filter(Coupon.code == self.coupon).first()
            coupon.redeem()

        # Create the credit card.
        credit_card = CreditCard(user_id=user.id,
                                 **CreditCard.extract_card_params(customer))

        db.session.add(user)
        db.session.add(credit_card)
        db.session.add(self)

        db.session.commit()

        return True
    
    @classmethod
    def get_new_plan(cls, keys):
        """Pick the plan based on the plan identifier.

        :param | keys (list): Keys to look through
        :return: str or None
        """
        for key in keys:
            split_key = key.split('submit_')

            if isinstance(split_key, list) and len(split_key) == 2:
                if Subscription.get_plan_by_id(split_key[1]):
                    return split_key[1]

        return None
    
    def update(self, user=None, coupon=None, plan=None):
        """
        Update an existing subscription.

        :param user: User to apply the subscription to
        :type user: User instance
        :param coupon: Coupon code to apply
        :type coupon: str
        :param plan: Plan identifier
        :type plan: str
        :return: bool
        """
        from manager.apps.billing.gateways.stripecom import Subscription as PaymentSubscription
        PaymentSubscription.update(user.payment_id, coupon, plan)

        user.subscription.plan = plan
        if coupon:
            user.subscription.coupon = coupon
            coupon = Coupon.query.filter(Coupon.code == coupon).first()

            if coupon:
                coupon.redeem()

        db.session.add(user.subscription)
        db.session.commit()

        return True

    def cancel(self, user=None, discard_credit_card=True):
        """
        Cancel an existing subscription.

        :param user: User to apply the subscription to
        :type user: User instance
        :param discard_credit_card: Delete the user's credit card
        :type discard_credit_card: bool
        :return: bool
        """
        from manager.apps.billing.gateways.stripecom import Subscription as PaymentSubscription
        PaymentSubscription.cancel(user.payment_id)

        user.payment_id = None
        user.cancelled_subscription_on = datetime.datetime.now(pytz.utc)

        db.session.add(user)
        db.session.delete(user.subscription)

        # Explicitly delete the credit card because the FK is on the
        # user, not subscription so we can't depend on cascading deletes.
        # This is for cases where you may want to keep a user's card
        # on file even if they cancelled.
        if discard_credit_card:
            db.session.delete(user.credit_card)

        db.session.commit()

        return True

    def update_payment_method(self, user=None, credit_card=None,
                              name=None, token=None):
        """
        Update the subscription.

        :param user: User to modify
        :type user: User instance
        :param credit_card: Card to modify
        :type credit_card: Credit Card instance
        :param name: User's billing name
        :type name: str
        :param token: Token returned by JavaScript
        :type token: str
        :return: bool
        """
        if token is None:
            return False
        from manager.apps.billing.gateways.stripecom import Card as PaymentCard
        customer = PaymentCard.update(user.payment_id, token)
        user.name = name

        # Update the credit card.
        new_card = CreditCard.extract_card_params(customer)
        credit_card.brand = new_card.get('brand')
        credit_card.last4 = new_card.get('last4')
        credit_card.exp_date = new_card.get('exp_date')
        credit_card.is_expiring = new_card.get('is_expiring')

        db.session.add(user)
        db.session.add(credit_card)

        db.session.commit()

        return True
示例#16
0
class Company(ResourceMixin, db.Model):

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

    name = db.Column(db.String(24), unique=True, index=True)

    # Contant Info
    phone = db.Column(db.String(15), server_default='')
    email = db.Column(db.String(255), server_default='')
    website = db.Column(db.String(255), server_default='')

    # Address
    address = db.Column(db.String(300), server_default='')
    pincode = db.Column(db.String(25), server_default='')
    city = db.Column(db.String(255), server_default='')
    state = db.Column(db.String(255), server_default='')
    country = db.Column(db.String(255), server_default='')

    # Tax Info
    gst = db.Column(db.String(15), server_default='')
    pan = db.Column(db.String(10), server_default='')

    # Meta Info
    is_active = db.Column('is_active',
                          db.Boolean(),
                          nullable=False,
                          server_default='1')

    # RelationShips
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
示例#17
0
class Users(UserMixin, ResourceMixin, db.Model):

    ROLE = OrderedDict([('member', 'Member'), ('admin', 'Admin')])

    id = db.Column(db.Integer, primary_key=True)
    # Relationships.
    # credit_card = db.relationship(CreditCard, uselist=False, backref='users',
    #                               passive_deletes=True)
    # subscription = db.relationship(Subscription, uselist=False,
    #                                backref='users', passive_deletes=True)
    # invoices = db.relationship(Invoice, 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)
    phone = db.Column(db.String(13))
    email = db.Column(db.String(255),
                      unique=True,
                      nullable=False,
                      server_default='')
    password = db.Column(db.String(128), nullable=False, server_default='')

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

    # 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')

    # RelationShips
    companies = db.relationship('Company', backref='owner', lazy=True)

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Users, self).__init__(**kwargs)
        self.password = Users.encrypt_password(kwargs.get('password', ''))

    @classmethod
    def find_by_identity(cls, identity):
        return Users.query.filter((Users.email == identity)
                                  | (Users.username == 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 Users.find_by_identity(decoded_payload.get('user_email'))
        except Exception:
            return None

    # @classmethod
    # def initialize_password_reset(cls, identity):
    #     u = Users.find_by_identity(identity)
    #     reset_token = u.serialize_token()

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

    #     return u

    @classmethod
    def search(cls, query):
        if not query:
            return ''

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

        return or_(*search_chain)

    @classmethod
    def is_last_admin(cls, user, new_role, new_active):
        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 = Users.query.filter(Users.role == 'admin').count()
            active_count = Users.query.filter(Users.is_active is True).count()

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

        return False

    # @classmethod
    # def bulk_delete(cls, ids):
    #     delete_count = 0

    #     for id in ids:
    #         user = Users.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 self.active

    def get_auth_token(self):
        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=''):
        if with_password:
            return check_password_hash(self.password, password)

        return True

    def serialize_token(self, expiration=3600):
        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):
        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()
示例#18
0
class Invoice(ResourceMixin, db.Model):
    __tablename__ = 'invoices'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    user_id = db.Column(db.Integer,
                        db.ForeignKey('user.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=False)

    # Invoice details.
    plan = db.Column(db.String(128), index=True)
    receipt_number = db.Column(db.String(128), index=True)
    description = db.Column(db.String(128))
    period_start_on = db.Column(db.Date)
    period_end_on = db.Column(db.Date)
    currency = db.Column(db.String(8))
    tax = db.Column(db.Integer())
    tax_percent = db.Column(db.Float())
    total = db.Column(db.Integer())

    # De-normalize the card details so we can render a user's history properly
    # even if they have no active subscription or changed cards at some point.
    brand = db.Column(db.String(32))
    last4 = db.Column(db.Integer)
    exp_date = db.Column(db.Date, index=True)

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

    @classmethod
    def billing_history(cls, user=None):
        """
        Return the billing history for a specific user.

        :param user: Users whose billing history will be retrieved
        :type user: Users instance

        :return: Invoices
        """
        invoices = Invoice.query.filter(Invoice.user_id == user.id) \
            .order_by(Invoice.created_on.desc()).limit(12)

        return invoices

    @classmethod
    def parse_from_event(cls, payload):
        """
        Parse and return the invoice information that will get saved locally.

        :return: dict
        """
        data = payload['data']['object']
        plan_info = data['lines']['data'][0]['plan']

        period_start_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['start']).date()
        period_end_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['end']).date()

        invoice = {
            'payment_id': data['customer'],
            'plan': plan_info['name'],
            'receipt_number': data['receipt_number'],
            'description': plan_info['statement_descriptor'],
            'period_start_on': period_start_on,
            'period_end_on': period_end_on,
            'currency': data['currency'],
            'tax': data['tax'],
            'tax_percent': data['tax_percent'],
            'total': data['total']
        }

        return invoice

    @classmethod
    def parse_from_api(cls, payload):
        """
        Parse and return the invoice information we are interested in.

        :return: dict
        """
        plan_info = payload['lines']['data'][0]['plan']
        date = datetime.datetime.utcfromtimestamp(payload['date'])

        invoice = {
            'plan': plan_info['name'],
            'description': plan_info['statement_descriptor'],
            'next_bill_on': date,
            'amount_due': payload['amount_due'],
            'interval': plan_info['interval']
        }

        return invoice

    @classmethod
    def prepare_and_save(cls, parsed_event):
        """
        Potentially save the invoice after argument the event fields.

        :param parsed_event: Event params to be saved
        :type parsed_event: dict
        :return: Users instance
        """
        # Avoid circular imports.
        from manager.models.Users import Users

        # Only save the invoice if the user is valid at this point.
        id = parsed_event.get('payment_id')
        user = Users.query.filter((Users.payment_id == id)).first()

        if user and user.credit_card:
            parsed_event['user_id'] = user.id
            parsed_event['brand'] = user.credit_card.brand
            parsed_event['last4'] = user.credit_card.last4
            parsed_event['exp_date'] = user.credit_card.exp_date

            del parsed_event['payment_id']

            invoice = Invoice(**parsed_event)
            invoice.save()

        return user

    @classmethod
    def upcoming(cls, customer_id):
        """
        Return the upcoming invoice item.

        :param customer_id: Stripe customer id
        :type customer_id: int
        :return: Stripe invoice object
        """
        from manager.apps.billing.gateways.stripecom import Invoice as PaymentInvoice
        invoice = PaymentInvoice.upcoming(customer_id)

        return Invoice.parse_from_api(invoice)