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}>"
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}>'
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)
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'))
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}>'
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
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()
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')
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
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)
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
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()
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
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()
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
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)
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()
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)