コード例 #1
0
ファイル: Books.py プロジェクト: Ajay1290/booksite
class Comments(db.Model, ResourceMixin):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    book_id = db.Column(db.Integer, db.ForeignKey('books.id'), nullable=False)

    def __repr__(self):
        return f'<Comment {self.book_id} | {self.content}>'
コード例 #2
0
ファイル: Books.py プロジェクト: Ajay1290/booksite
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}>'
コード例 #3
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'))
コード例 #4
0
ファイル: machines.py プロジェクト: Ajay1290/agust-crm
class Machine(ResourceMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)

    # Info
    name = db.Column(db.String, unique=True, server_default='')
    description = db.Column(db.String, server_default='')
    product_code = db.Column(db.String, server_default='')
    category = db.Column(db.String, server_default='')

    model = db.Column(db.String, server_default='')
    serial = db.Column(db.String, server_default='')
    instdate = db.Column(db.Date, server_default='')

    Customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'))

    def __init__(self, **kwargs):
        super(Machine, self).__init__(**kwargs)
        self._create_transection('Purchased')

    def _create_transection(self, action):
        transection = Transection(
            name=f'{self.name} ({self.model} | {self.model})',
            category="Machine",
            action=action,
            unit='UNIT',
            qty='1')
        try:
            dbt.session.add(transection)
            dbt.session.commit()
        except Exception as e:
            print(e)
            dbt.session.rollback()
コード例 #5
0
ファイル: StaticFiles.py プロジェクト: Ajay1290/booksite
class CoverImage(db.Model, ResourceMixin):
    id = db.Column(db.Integer, primary_key=True)
    path = db.Column(db.String, nullable=False)
    book_id = db.Column(db.Integer,
                        db.ForeignKey('books.id'),
                        nullable=False,
                        default=1)

    def __init__(self, image=None, path=None, **kwargs):
        super(CoverImage, self).__init__(**kwargs)
        if image:
            self.path = CoverImage.save_book_cover(image)
        elif path:
            self.path = path
        else:
            raise ValueError('Enter Image or Path')

    @classmethod
    def save_book_cover(cls, image):
        random_hex = secrets.token_hex(8)
        _, f_ext = os.path.splitext(image.filename)
        picture_fn = random_hex + f_ext
        picture_path = current_app.root_path + f'\\static\\book_covers\\{picture_fn}'
        i = Image.open(image)
        output_size = (250, 380)
        i.thumbnail(output_size)
        i.save(picture_path)

        return picture_fn

    def __repr__(self):
        return '<CoverImage %d>' % self.book_id
コード例 #6
0
ファイル: StaticFiles.py プロジェクト: Ajay1290/booksite
class ProfileImage(db.Model, ResourceMixin):
    id = db.Column(db.Integer, primary_key=True)
    path = db.Column(db.String, nullable=False)
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id'),
                        nullable=False,
                        default=1)

    def __init__(self, image, **kwargs):
        super(ProfileImage, self).__init__(**kwargs)
        self.path = ProfileImage.save_user_file(image)

    @classmethod
    def save_user_file(cls, image):
        random_hex = secrets.token_hex(8)
        _, f_ext = os.path.splitext(image)
        picture_fn = random_hex + f_ext
        picture_path = current_app.root_path + f'\\static\\profile_pic\\{picture_fn}'
        i = Image.open(image)
        i.save(picture_path)

        return picture_path

    def __repr__(self):
        return '<ProfileImage %d>' % self.user_id
コード例 #7
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)
コード例 #8
0
ファイル: Books.py プロジェクト: Ajay1290/booksite
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}>"
コード例 #9
0
ファイル: Books.py プロジェクト: Ajay1290/booksite
class Downloads(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id'),
                        nullable=False,
                        default=1)
    book_id = db.Column(db.Integer,
                        db.ForeignKey('books.id'),
                        nullable=False,
                        default=1)

    def __init__(self, **kwargs):
        super(Downloads, self).__init__(**kwargs)
        Books.query.get(self.book_id).downloads += 1
        db.session.commit()

    def __repr__(self):
        return f'<Downloads {self.book_id} by {self.user_id}>'
コード例 #10
0
ファイル: contacts.py プロジェクト: Ajay1290/agust-crm
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)
コード例 #11
0
ファイル: StaticFiles.py プロジェクト: Ajay1290/booksite
class BookFile(db.Model, ResourceMixin):
    id = db.Column(db.Integer, primary_key=True)
    path = db.Column(db.String, nullable=False)
    book_id = db.Column(db.Integer, db.ForeignKey('books.id'), nullable=False)

    def __init__(self, file, scrape=False, **kwargs):
        super(BookFile, self).__init__(**kwargs)
        self.path = BookFile.save_book(file)
        if scrape:
            img_path = BookFile.scrape_cover_image(path=self.path)
            image = CoverImage(path=img_path, book_id=self.book_id)
            db.session.add(image)
            db.session.commit()

    @classmethod
    def save_book(cls, file):
        random_hex = secrets.token_hex(8)
        _, f_ext = os.path.splitext(file.filename)
        book_fn = random_hex + f_ext
        book_path = current_app.root_path + f'\\static\\books\\{book_fn}'
        file.save(book_path)

        return book_fn

    @classmethod
    def get_authors_from_string(cls, author_string):
        raw_authors = author_string.replace('and',
                                            ',').replace('&', ',').replace(
                                                '|', ',').split(',')
        author_names = [name.strip() for name in raw_authors if name.strip()]
        existing_authors = Authors.query.filter(Authors.name.in_(author_names))
        new_names = set(author_names) - set(
            [author.name for author in existing_authors])
        new_authors = [Authors(name=name) for name in new_names]
        return list(existing_authors) + new_authors

    @classmethod
    def scrape_cover_image(cls, path):
        doc = fitz.open(current_app.root_path + f'\\static\\books\\{path}')
        mat = fitz.Matrix(0.51, 0.58)
        pix = doc[0].getPixmap(alpha=False, matrix=mat)
        image_name = doc.name[len(doc.name[:-20]):]
        filename = image_name[:-4]
        img_path = current_app.root_path + '\\static\\book_covers\\' + filename + ".jpg"
        pix.writeImage(img_path)

        return filename + ".jpg"

    def __repr__(self):
        return '<File  %d>' % self.book_id
コード例 #12
0
ファイル: StaticFiles.py プロジェクト: Ajay1290/booksite
class AuthorProfileImage(db.Model, ResourceMixin):
    id = db.Column(db.Integer, primary_key=True)
    path = db.Column(db.String, nullable=False)
    author_id = db.Column(db.Integer,
                          db.ForeignKey('authors.id'),
                          nullable=False,
                          default=1)

    def __init__(self, image, **kwargs):
        super(AuthorProfileImage, self).__init__(**kwargs)
        self.path = AuthorProfileImage.save_author_file(image)

    @classmethod
    def save_author_file(cls, image):
        random_hex = secrets.token_hex(8)
        picture_fn = random_hex + '.jpg'
        picture_path = current_app.root_path + f'\\static\\author_files\\{picture_fn}'
        i = Image.open(image)
        i.save(picture_path)

        return picture_path

    def __repr__(self):
        return '<AuthorProfileImage %s>' % self.path
コード例 #13
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)
コード例 #14
0
ファイル: subscription.py プロジェクト: Ajay1290/agust-crm
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
コード例 #15
0
ファイル: Relations.py プロジェクト: Ajay1290/booksite
from manager.ext import db

post_tags = db.Table(
    'post_tags', db.Column('tag_id', db.Integer, db.ForeignKey('tags.id')),
    db.Column('book_id', db.Integer, db.ForeignKey('books.id')))

author_posts = db.Table(
    'author_posts',
    db.Column('author_id', db.Integer, db.ForeignKey('authors.id')),
    db.Column('book_id', db.Integer, db.ForeignKey('books.id')))

categories_books = db.Table(
    'categories_books',
    db.Column('categories_id', db.Integer, db.ForeignKey('categories.id')),
    db.Column('book_id', db.Integer, db.ForeignKey('books.id')))

publishers_books = db.Table(
    'publishers_books',
    db.Column('publishers_id', db.Integer, db.ForeignKey('publishers.id')),
    db.Column('book_id', db.Integer, db.ForeignKey('books.id')))

categories_tags = db.Table(
    'categories_tags',
    db.Column('categories_id', db.Integer, db.ForeignKey('categories.id')),
    db.Column('tag_id', db.Integer, db.ForeignKey('tags.id')))

author_categories = db.Table(
    'author_categories',
    db.Column('author_id', db.Integer, db.ForeignKey('authors.id')),
    db.Column('categories_id', db.Integer, db.ForeignKey('categories.id')))
コード例 #16
0
ファイル: contacts.py プロジェクト: Ajay1290/agust-crm
from manager.ext import db, dbt
from manager.libs.utils.util_sqlalchemy import ResourceMixin


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)


# -----Relations-------------------------------------------------------

customer_has_contact = db.Table(
    'customer_has_contact',
    db.Column('Customer_id', db.Integer, db.ForeignKey('customer.id')),
    db.Column('Contact_id', db.Integer, db.ForeignKey('contact.id')),
)
コード例 #17
0
    sales_price = db.Column(db.DECIMAL(precision=20, scale=2),
                            server_default='0')

    def __init__(self, **kwargs):
        super(Consumable, self).__init__(**kwargs)
        self._create_transection('Purchased', self.qty)

    def _create_transection(self, action, qty):
        transection = Transection(name=f'{self.name} ({self.model})',
                                  category="Consumable",
                                  action=action,
                                  unit=self.unit,
                                  qty=qty)
        try:
            dbt.session.add(transection)
            dbt.session.commit()
        except Exception as e:
            print(e)
            dbt.session.rollback()


# -----Relations-------------------------------------------------------

complaint_has_cosnumable = db.Table(
    'complaint_has_cosnumable',
    db.Column('Complaint_id', db.Integer, db.ForeignKey('complaint.id')),
    db.Column('Complaint_Machine_id', db.Integer, db.ForeignKey('machine.id')),
    db.Column('Complaint_Machine_Customer_id', db.Integer,
              db.ForeignKey('customer.id')),
    db.Column('Consumable_id', db.Integer, db.ForeignKey('consumable.id')),
)
コード例 #18
0
ファイル: credit_card.py プロジェクト: Ajay1290/agust-crm
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