class Mail(CRUDMixin, db.Model): __tablename__ = 'mails' id = db.Column(db.Integer, primary_key=True) mail = db.Column(db.String(200), unique=True, nullable=False) def __repr__(self): return "<Mail %s>" % self.mail
class Picture(db.Model): """ All pictures are stored in a html subpage inside the device_mocks folder. There is also a default one. """ __tablename__ = 'pictures' name = db.Column(db.String(64), unique=True, primary_key=True, nullable=False) filename = db.Column(db.String(128), unique=True, nullable=False) default = db.Column(db.Boolean, default=False, index=True) manufacturers = db.relationship('Manufacturer', backref='picture') devices = db.relationship('Device', backref='picture') def __repr__(self): return f"<Picture {self.name}@[{self.file}]" @classmethod def query_factory_all(cls): """ Query Factory for use in sqlalchemy.wtforms """ return cls.query.order_by(cls.name) @property def dir(self): return PICTURE_BASE_PATH @property def file(self): return f"{self.dir}{self.filename}" @classmethod def default_picture(cls): return cls.query.filter_by(default=True).first() @staticmethod def create_basic_pictures(): """ Insert default Pictures and set the default :return: """ basics = { 'htc': '_htc.html', 'ipad': '_ipad_mini.html', 'iphone_4s': '_iphone_4s.html', 'iphone_5s': '_iphone_5s.html', 'iphone_5c': '_iphone_5c.html', 'iphone_8': '_iphone_8.html', 'iphone_8_plus': '_iphone_8_plus.html', 'iphone_x': '_iphone_x.html', 'nexus': '_nexus_5.html', 'note': '_note_8.html', 's5': '_s5.html', } Picture.query.delete() # set default picture default = 'nexus' # bulk insert pictures pictures = [Picture(name=k, filename=v, default=k == default) for k, v in basics.items()] db.session.bulk_save_objects(pictures) db.session.commit()
class CouponCode(CRUDMixin, db.Model): VALUES = [5, 10] __tablename__ = 'coupons' id = db.Column(db.Integer, primary_key=True) code = db.Column(db.String(10), unique=True, nullable=False) value = db.Column(db.Integer) def __repr__(self): return "<Coupon %s>" % self.mail
class Repair(db.Model, CRUDMixin): """ Repair e.g. display """ __tablename__ = 'repair' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64)) price = db.Column(db.Integer, default=0) devices = relationship("Device", secondary=repair_association_table, back_populates="repairs", lazy='dynamic') def __repr__(self): return f"<{self.name} : {self.price}"
class Device(db.Model, CRUDMixin): """ Generic Device Can be a Smartphone, Tablet or anything else """ __tablename__ = 'devices' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, index=True) manufacturer_id = db.Column(db.Integer, db.ForeignKey('manufacturers.id')) manufacturer = relationship('Manufacturer', back_populates='devices') repairs = relationship('Repair', secondary=repair_association_table, back_populates='devices', lazy='dynamic') picture_id = db.Column(db.String, db.ForeignKey('pictures.name')) colors = relationship("Color", secondary=color_association_table) enquiries = relationship("Enquiry", back_populates="device") def __init__(self, **kwargs): super(Device, self).__init__(**kwargs) if len(self.colors) == 0: default_color = Color.query.filter_by(default=True).first() if default_color is not None: self.colors.append(default_color) def __repr__(self): return f"<Device: {self.manufacturer.name} - {self.name}>" @property def picture_file(self): """ Get the picture file or if none is provided use the manufacturers default and if no picture is defined at all it uses the default picture. :return: template path of associated html render """ if self.picture is None: return self.manufacturer.picture_file return self.picture.file @classmethod def query_factory_all(cls): """ Query Factory for use in sqlalchemy.wtforms """ return cls.query.order_by(cls.name) @classmethod def _check_if_paths_are_valid(cls): """ private function to ensure every device points to a html render :return: True if everything is valid else False """ for d in cls.query.all(): if d.picture is None or d.picture_file is None: return False return True
class Color(db.Model, CRUDMixin): """ Store colors and their associated color codes """ __tablename__ = 'color' name = db.Column(db.String(128), primary_key=True) color_code = db.Column(db.String(20)) default = db.Column(db.Boolean, default=False, index=True) @classmethod def query_factory_all(cls): """ Query Factory for use in sqlalchemy.wtforms """ return cls.query.order_by(cls.name) def __repr__(self): return f"<{self.name} : {self.color_code}>"
class User(UserMixin, CRUDMixin, db.Model): """Basic user model """ __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) password_hash = db.Column(db.String(255), nullable=False) def __repr__(self): return "<User %s>" % self.username @property def password(self): raise AttributeError('password is not a readable attribute') @password.setter def password(self, password): self.password_hash = generate_password_hash(password) def verify_password(self, password): return check_password_hash(self.password_hash, password)
class Manufacturer(db.Model, CRUDMixin): __tablename__ = 'manufacturers' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, index=True) devices = relationship('Device', back_populates='manufacturer', cascade="all, delete-orphan") picture_id = db.Column(db.String, db.ForeignKey('pictures.name')) def __init__(self, **kwargs): super(Manufacturer, self).__init__(**kwargs) if self.picture is None: self.picture = Picture.default_picture() @property def picture_file(self): if self.picture is None: return Picture.default_picture().file return self.picture.file @classmethod def query_factory_all(cls): """ Query Factory for use in sqlalchemy.wtforms """ return cls.query.order_by(cls.name)
class Shop(CRUDMixin, db.Model): """ Shops """ __tablename__ = 'shops' name = db.Column(db.String(128), primary_key=True, unique=True, default="Zentrale") @classmethod def query_factory_all(cls): # insert default if no shop exists if cls.query.first() is None: cls.create() return cls.query.order_by(cls.name) def __str__(self): return self.name __repr__ = __str__
class Preferences(db.Model, CRUDMixin): """ Store User Preferences here""" __tablename__ = 'preferences' id = db.Column(db.Integer, primary_key=True) imei_required = db.Column(db.Boolean, default=False) first_name_required = db.Column(db.Boolean, default=False) last_name_required = db.Column(db.Boolean, default=False) phone_required = db.Column(db.Boolean, default=False) mail_required = db.Column(db.Boolean, default=True) address_required = db.Column(db.Boolean, default=False) phone_number = db.Column(db.String(128)) mail = db.Column(db.String(128)) active_sale = db.Column(db.Boolean, default=False) sale_amount = db.Column(db.Integer, default=0) # Mail Settings mail_port = db.Column(db.Integer, default=587) mail_server = db.Column(db.String(128)) mail_encryption = db.Column(db.Integer, default=1) mail_username = db.Column(db.String(128)) mail_default_sender = db.Column(db.String(128)) mail_password_encrypted = db.Column(db.LargeBinary) order_copy_mail_address = db.Column(db.String(128)) @property def mail_password(self): raise AttributeError('password is not a readable attribute') def encrypt_mail_password(self, password): """ Accepts the clear text password and stores it encrypted with the app´s secret key. :param password: clear text password :return: """ if not has_app_context: raise ValueError("No App Context") secret_key = current_app.config['SECRET_KEY'] cryptor = rncryptor.RNCryptor() encrypted_password = cryptor.encrypt(password, secret_key) self.mail_password_encrypted = encrypted_password db.session.commit() current_app.logger.info("Successfully encrypted mail password.") def decrypt_mail_password(self): """ Decrypts the encrypted password with the app´s secret key. :return: decrypted password """ if not has_app_context: raise ValueError("No App Context") secret_key = current_app.config['SECRET_KEY'] cryptor = rncryptor.RNCryptor() decrypted_password = cryptor.decrypt(self.mail_password_encrypted, secret_key) current_app.logger.info("Successfully decrypted mail password.") return decrypted_password @classmethod def load_settings(cls): """ Load config into memory """ p = cls.query.first() if p is None: return current_app.config['IMEI_REQUIRED'] = p.imei_required current_app.config['FIRST_NAME_REQUIRED'] = p.first_name_required current_app.config['LAST_NAME_REQUIRED'] = p.last_name_required current_app.config['PHONE_REQUIRED'] = p.phone_required current_app.config['MAIL_REQUIRED'] = p.mail_required current_app.config['ADDRESS_REQUIRED'] = p.address_required current_app.config['USER_MAIL'] = p.mail current_app.config['USER_PHONE'] = p.phone_number current_app.config['ACTIVE_SALE'] = p.active_sale current_app.config['SALE_AMOUNT'] = p.sale_amount @property def mail_config(self): """ Load mail config settings """ config = { 'MAIL_USERNAME': self.mail_username, 'MAIL_PASSWORD': self.decrypt_mail_password(), 'MAIL_SERVER': self.mail_server, 'MAIL_USE_TLS': self.mail_encryption == Encryption.TLS, 'MAIL_USE_SSL': self.mail_encryption == Encryption.SSL, 'MAIL_DEFAULT_SENDER': self.mail_default_sender or self.mail_username, 'MAIL_PORT': self.mail_port } return config
@property def picture_file(self): if self.picture is None: return Picture.default_picture().file return self.picture.file @classmethod def query_factory_all(cls): """ Query Factory for use in sqlalchemy.wtforms """ return cls.query.order_by(cls.name) repair_association_table = db.Table('repair_association', db.Column('device_id', db.Integer, db.ForeignKey('devices.id', ondelete="cascade")), db.Column('repair_id', db.Integer, db.ForeignKey('repair.id', ondelete="cascade")) ) color_association_table = db.Table('color_association', db.Column('device_id', db.Integer, db.ForeignKey('devices.id', ondelete="cascade")), db.Column('color_name', db.String, db.ForeignKey('color.name', ondelete="cascade")) ) class Device(db.Model, CRUDMixin): """ Generic Device Can be a Smartphone, Tablet or anything else """ __tablename__ = 'devices'
class Enquiry(db.Model, CRUDMixin): __tablename__ = "enquiries" id = db.Column(db.Integer, primary_key=True) timestamp = db.Column(db.DateTime, default=dt.datetime.now) name = db.Column(db.String(128)) customer_first_name = db.Column(db.String(128)) customer_last_name = db.Column(db.String(128)) customer_email = db.Column(db.String(128)) customer_phone = db.Column(db.String(128)) customer_street = db.Column(db.String(128)) customer_city = db.Column(db.String(128)) customer_postal_code = db.Column(db.String(32)) sale = db.Column(db.Integer, default=0) imei = db.Column(db.String(64)) color = db.Column(db.String(64)) shop = db.Column(db.String(128)) device_id = db.Column(db.Integer, db.ForeignKey('devices.id')) device = relationship("Device", back_populates="enquiries") repairs = relationship("Repair", secondary=repairs) done = db.Column(db.Boolean, default=False) def __repr__(self): return f"<Enquiry ({self.name}) from {self.timestamp.strftime('%d.%m.%Y at %H:%M')}>" @property def address(self): return f"{self.customer_street or '-'}, {self.customer_postal_code or '-'}, {self.customer_street or '-'}"
from price_picker import db import datetime as dt from sqlalchemy.orm import relationship from price_picker.common.database import CRUDMixin repairs = db.Table( 'repairs', db.Column('repair_id', db.Integer, db.ForeignKey('repair.id', ondelete="cascade")), db.Column('enquiry_id', db.Integer, db.ForeignKey('enquiries.id', ondelete="cascade"))) class Enquiry(db.Model, CRUDMixin): __tablename__ = "enquiries" id = db.Column(db.Integer, primary_key=True) timestamp = db.Column(db.DateTime, default=dt.datetime.now) name = db.Column(db.String(128)) customer_first_name = db.Column(db.String(128)) customer_last_name = db.Column(db.String(128)) customer_email = db.Column(db.String(128)) customer_phone = db.Column(db.String(128)) customer_street = db.Column(db.String(128)) customer_city = db.Column(db.String(128)) customer_postal_code = db.Column(db.String(32)) sale = db.Column(db.Integer, default=0) imei = db.Column(db.String(64)) color = db.Column(db.String(64)) shop = db.Column(db.String(128)) device_id = db.Column(db.Integer, db.ForeignKey('devices.id')) device = relationship("Device", back_populates="enquiries")