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