Exemple #1
0
class Discount(BaseMixin, db.Model, ReprMixin):

    name = db.Column(db.String(55), nullable=True)
    value = db.Column(db.Float(precision=2), nullable=False)
    type = db.Column(db.Enum('PERCENTAGE', 'FIXED', name='varchar'), nullable=False, default='PERCENTAGE')

    orders = db.relationship('Order', secondary='order_discount')
Exemple #2
0
class HistorySyncDetailModel(ModelBase):
    __tablename__ = 'history_sync_details'
    id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
    type = db.Column(db.Enum(HistorySyncType),
                     nullable=False,
                     default=HistorySyncType.POSTGRES)
    details = db.Column(db.String(320), nullable=True)

    __table_args__ = (UniqueConstraint('type'), )

    def __repr__(self):
        return f"HistorySyncDetailModel(type = {self.type})"

    @classmethod
    def update_history_sync_details(cls, history_sync_detail: dict):
        detail: HistorySyncDetailModel = cls.query.filter_by(
            type=history_sync_detail.get('type')).first()
        if detail:
            detail.update(**history_sync_detail)
        else:
            detail = HistorySyncDetailModel(**history_sync_detail)
            detail.save_to_db()

    @classmethod
    def find_details_by_type(cls, _type: str) -> str:
        detail: HistorySyncDetailModel = cls.query.filter_by(
            type=_type).first()
        if detail:
            return detail.details
        return ''
class Transaction(db.Model):
    """
    Class for Transactions table
    """
    __tablename__ = 'transactions'

    id = db.Column(db.Integer, primary_key=True)
    amount = db.Column(db.Numeric(), nullable=False)
    paid_on = db.Column(db.DateTime)
    payment_status = db.Column(db.Enum(PaymentStatus))
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    expense_id = db.Column(db.Integer,
                           db.ForeignKey('expenses.id'),
                           nullable=False)
    updated_at = db.Column(db.DateTime)

    user = db.relationship('User', back_populates='transaction')
    expense = db.relationship('Expense', back_populates='transaction')

    def __init__(self, expense_id, user_id, amount):
        self.amount = Decimal(amount)
        self.user_id = user_id
        self.expense_id = expense_id
        self.payment_status = PaymentStatus.PENDING
        self.update_at = datetime.datetime.now()

    def __repr__(self):
        return f'<Transaction {self.id}>'
Exemple #4
0
class PrinterConfig(BaseMixin, db.Model, ReprMixin):
    header = db.Column(db.Text, nullable=True)
    footer = db.Column(db.Text, nullable=True)

    bill_template = db.Column(db.Text, nullable=True)
    receipt_template = db.Column(db.Text, nullable=True)

    bill_printer_type = db.Column(
        db.Enum('thermal', 'dot_matrix', 'laser', name='varchar'))
    receipt_printer_type = db.Column(
        db.Enum('thermal', 'dot_matrix', 'laser', name='varchar'))
    label_printer_type = db.Column(
        db.Enum('1x1', '2x1', '3x1', '4x1', name='varchar'))

    have_receipt_printer = db.Column(db.Boolean(), default=False)
    have_bill_printer = db.Column(db.Boolean(), default=False)

    store_id = db.Column(db.ForeignKey('store.id'))

    stores = db.relationship('Store',
                             back_populates='printer_config',
                             foreign_keys=[store_id])
Exemple #5
0
class ForwardEntity(db.Model):
    __tablename__ = 'forward'
    timestamp = db.Column(UtcDateTime,
                          nullable=False,
                          index=True,
                          primary_key=True)
    ticker = db.Column(db.String(10),
                       db.ForeignKey(StockEntity.ticker),
                       nullable=False,
                       primary_key=True)
    action = db.Column(db.Enum(ActionEnum))
    price = db.Column(db.DECIMAL)
    number = db.Column(db.DECIMAL)
    cash = db.Column(db.DECIMAL)
Exemple #6
0
class User(UserMixin, db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), nullable=False)
    login_method = db.Column(db.Enum('signup', 'google', 'facebook'),
                             default='signup')
    name = db.Column(db.String(100), nullable=True)
    avatar = db.Column(db.String(200))
    active = db.Column(db.Boolean, default=False)
    tokens = db.Column(db.Text)
    created_on = db.Column(db.DateTime, default=datetime.datetime.utcnow())
    posts = db.relationship('Post', backref='author', lazy='dynamic')

    def __repr__(self):
        return '<User %r>' % (self.email)
Exemple #7
0
class UserModel(ModelBase):
    __tablename__ = 'users'
    uuid = db.Column(db.String(80), primary_key=True, nullable=False)
    first_name = db.Column(db.String(80), nullable=False)
    last_name = db.Column(db.String(80), nullable=False)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(80), unique=True, nullable=False)
    state = db.Column(db.Enum(StateType),
                      nullable=False,
                      default=StateType.UNVERIFIED)
    devices = db.relationship('DeviceModel',
                              cascade="all,delete",
                              backref='user',
                              lazy=True)
    sites = db.relationship('UserSiteModel',
                            cascade="all,delete",
                            backref="user",
                            lazy=True)

    __table_args__ = (
        UniqueConstraint('username'),
        UniqueConstraint('email'),
    )

    @validates('email')
    def validate_email(self, _, value):
        if not re.search(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)",
                         value):
            raise ValueError("Invalid email")
        return value

    @validates('username')
    def validate_username(self, _, value):
        if not re.match("^([A-Za-z0-9_-])+$", value):
            raise ValueError(
                "username should be alphanumeric and can contain '_', '-'")
        return value

    @classmethod
    def find_by_username(cls, username: str):
        return cls.query.filter_by(username=username).first()

    @classmethod
    def find_by_email(cls, email: str):
        return cls.query.filter_by(email=email).first()
Exemple #8
0
class UserProfile(BaseMixin, db.Model):
    first_name = db.Column(db.String(255))
    last_name = db.Column(db.String(255))
    gender = db.Column(db.Enum('male', 'female', 'na', name='gender'),
                       default='na')
    dob = db.Column(db.DateTime,
                    default=db.func.current_timestamp(),
                    nullable=True)
    profile_picture = db.Column(db.Text(), nullable=True)

    user_id = db.Column(db.Integer,
                        db.ForeignKey('user.id', ondelete='CASCADE'),
                        unique=True)

    user = db.relationship('User',
                           back_populates="user_profile",
                           single_parent=True)
Exemple #9
0
class UserProfile(db.Model, BaseMixin):
    first_name = db.Column(db.String(255))
    last_name = db.Column(db.String(255))
    gender = db.Column(db.Enum('male', 'female', 'ns'), default='ns')
    dob = db.Column(db.DateTime, default=db.func.current_timestamp(), nullable=True)
    profile_picture = db.Column(db.String(512), nullable=True)
    address = db.Column(db.Integer)

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

    user = db.relationship('User', lazy='subquery', backref='user_profile')

    @hybrid_property
    def age(self):
        if self.dob:
            return datetime.now().year - self.dob.year
        else:
            return 0
Exemple #10
0
class DgfObject(db.Model):
    """ Data gouv fr objects Model """
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    suspicious = db.Column(db.Boolean())
    read = db.Column(db.Boolean())
    deleted = db.Column(db.Boolean())
    dgf_type = db.Column(
        db.Enum('user',
                'community_resource',
                'organization',
                'dataset',
                'reuse',
                name='dgf_type'))
    dgf_id = db.Column(db.String(255), nullable=False)
    comments = db.relationship('Comment', backref='dgf_object', lazy='dynamic')

    def __repr__(self):
        return f"<Dgf {self.dgf_type} with id {self.dgf_id}>"
Exemple #11
0
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    email = db.Column(db.String(120), unique=True, index=True)
    password = db.Column(db.String(255))
    account_type = db.Column(db.Enum(UserType))
    avatar_url = db.Column(db.String(255))
    html_url = db.Column(db.String(255))
    answers = db.relationship("Answer", backref="user", lazy="dynamic")
    answer_votes = db.relationship("AnswerVote", backref="user", lazy="dynamic")
    answer_flags = db.relationship("AnswerFlag", backref="user", lazy="dynamic")
    comments = db.relationship("Comment", backref="user", lazy="dynamic")
    comment_flags = db.relationship("CommentFlag", backref="user", lazy="dynamic")
    questions = db.relationship("Question", backref="user", lazy="dynamic")
    question_votes = db.relationship("QuestionVote", backref="user", lazy="dynamic")
    question_flags = db.relationship("QuestionFlag", backref="user", lazy="dynamic")

    @staticmethod
    def verify_token(token):
        id = jwt.decode(token, app.config.get("SECRET_KEY"), algorithms=["HS256"])[
            "reset_password"
        ]
        return User.query.get(id)

    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password, password)

    def get_token(self, expires=600):
        token = jwt.encode(
            {"reset_password": self.id, "exp": time() + expires},
            app.config.get("SECRET_KEY"),
            algorithm="HS256",
        ).decode("utf-8")
        return token

    def __repr__(self):
        return "User {}".format(self.username)
Exemple #12
0
class User(db.Model, BaseMixin, UserMixin, ReprMixin):
    email = db.Column(db.String(127), unique=True, nullable=False)
    password = db.Column(db.String(255), default='', nullable=False)
    username = db.Column(db.String(127), nullable=True)
    user_type = db.Column(db.Enum('student', 'counsellor'), default='counsellor')
    school_id = db.Column(db.Integer, db.ForeignKey('school.id'))

    active = db.Column(db.Boolean())
    confirmed_at = db.Column(db.DateTime())
    last_login_at = db.Column(db.DateTime())
    current_login_at = db.Column(db.DateTime())

    last_login_ip = db.Column(db.String(45))
    current_login_ip = db.Column(db.String(45))
    login_count = db.Column(db.Integer)
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))

    @staticmethod
    def hash_md5(data):
        return md5(data.encode('utf-8')).hexdigest()

    def get_auth_token(self):
        pass

    def generate_auth_token(self):
        token = serialize_data([str(self.id), self.hash_md5(self.password)])
        return token

    @hybrid_property
    def authentication_token(self):
        return self.generate_auth_token()

    @hybrid_property
    def name(self):
        if self.user_profile and self.user_profile.first_name:
            if self.user_profile.last_name:
                return self.user_profile.first_name + self.user_profile.last_name
            return self.user_profile.first_name
Exemple #13
0
class HistorySyncLogModel(ModelBase):
    __tablename__ = 'history_sync_logs'
    id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
    type = db.Column(db.Enum(HistorySyncType),
                     nullable=False,
                     default=HistorySyncType.POSTGRES)
    point_uuid = db.Column(db.String,
                           db.ForeignKey('points.uuid'),
                           nullable=False)
    last_sync_id = db.Column(db.Integer(), nullable=False, default=0)

    __table_args__ = (UniqueConstraint('type', 'point_uuid'), )

    def __repr__(self):
        return f"HistorySyncLogModel(point_uuid = {self.point_uuid})"

    @classmethod
    def update_history_sync_logs(cls, history_sync_log_list: List[dict]):
        for history_sync_log in history_sync_log_list:
            log: HistorySyncLogModel = cls.query.filter_by(
                type=history_sync_log.get('type'),
                point_uuid=history_sync_log.get('point_uuid')).first()
            if log:
                log.update(**history_sync_log)
            else:
                log = HistorySyncLogModel(**history_sync_log)
                log.save_to_db()

    @classmethod
    def find_last_sync_id_by_type_point_uuid(cls, _type: str,
                                             point_uuid: str) -> int:
        log: HistorySyncLogModel = cls.query.filter_by(
            type=_type, point_uuid=point_uuid).first()
        if log:
            return log.last_sync_id
        return 0
Exemple #14
0
class User(db.Model):
    __tablename__ = "user"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(255),
                         unique=True,
                         nullable=False,
                         index=True)
    password = db.Column(db.String(255), nullable=False)
    role = db.Column(db.Enum(Roles))

    def __init__(self, username, password, role=Roles.pleb) -> None:
        self.username = username
        self.role = role
        self.set_password(password)

    def set_password(self, password) -> None:
        self.password = bcrypt.generate_password_hash(
            password=password).decode()

    def check_password(self, password) -> bool:
        return bcrypt.check_password_hash(self.password, password)

    def is_authorized(self, claims) -> bool:
        return self.is_admin() or claims['identity'] in self.id

    def is_admin(self) -> bool:
        return Roles.admin in self.role

    @classmethod
    def find_by_id(cls, user_id):
        return cls.query.filter_by(id=user_id).first()

    @classmethod
    def find_by_username(cls, username):
        return cls.query.filter_by(username=username).first()
Exemple #15
0
class LPGBPointMapping(ModelBase):
    """
    lora_point <> generic_point | bacnet_point
    """
    __tablename__ = 'mappings_lp_gbp'

    uuid = db.Column(db.String, primary_key=True)
    point_uuid = db.Column(db.String,
                           db.ForeignKey('points.uuid'),
                           nullable=False)
    mapped_point_uuid = db.Column(db.String(80), nullable=True, unique=True)
    point_name = db.Column(db.String(80), nullable=False)
    mapped_point_name = db.Column(db.String(80), nullable=True)
    type = db.Column(db.Enum(MapType), nullable=True)
    mapping_state = db.Column(db.Enum(MappingState),
                              default=MappingState.MAPPED)

    @validates('type')
    def validate_type(self, _, value):
        if not value:
            raise ValueError("type should not be null or blank")
        return value

    def check_self(self) -> (bool, any):
        super().check_self()
        if self.point_uuid:
            self.__set_point_name()
        if self.mapped_point_uuid:
            self.__set_mapped_point_name()
        if not self.point_uuid:
            self.__set_point_uuid()
        if not self.mapped_point_uuid:
            self.__set_mapped_point_uuid()

    def set_uuid_with_name(self):
        self.__set_point_uuid()
        self.__set_mapped_point_uuid()

    def __set_point_name(self):
        from src.models.model_point import PointModel
        if not self.point_uuid:
            raise ValueError(f"point_uuid should not be null or blank")
        point: PointModel = PointModel.find_by_uuid(self.point_uuid)
        if not point:
            raise ValueError(f"Does not exist point_uuid {self.point_uuid}")
        self.point_name = f"{point.device.name}:{point.name}"

    def __set_mapped_point_name(self):
        if not self.mapped_point_uuid:
            raise ValueError("mapped_point_uuid should not be null or blank")
        if self.type in (MapType.GENERIC.name, MapType.GENERIC):
            response: Response = gw_request(
                f'/ps/api/generic/points/get_name/uuid/{self.mapped_point_uuid}'
            )
            if response.status_code != 200:
                raise ValueError(
                    f"Does not exist mapped_point_uuid {self.mapped_point_uuid}"
                )
            self.mapped_point_name = json.loads(response.data).get('name')
        elif self.type in (MapType.BACNET.name, MapType.BACNET):
            response: Response = gw_request(
                f'/bacnet/api/bacnet/points/uuid/{self.mapped_point_uuid}')
            if response.status_code != 200:
                raise ValueError(
                    f"Does not exist mapped_point_uuid {self.mapped_point_uuid}"
                )
            self.mapped_point_name = json.loads(
                response.data).get('object_name')

    def __set_point_uuid(self):
        from src.models.model_point import PointModel
        point_names = self.point_name.split(":")
        if len(point_names) != 2:
            raise ValueError(
                "point_name should be colon (:) delimited device_name:point_name"
            )
        device_name, point_name = point_names
        point: PointModel = PointModel.find_by_name(device_name, point_name)
        if not point:
            raise ValueError(f"Does not exist point_name {self.point_name}")
        self.point_uuid = point.uuid

    def __set_mapped_point_uuid(self):
        if not self.mapped_point_name:
            raise ValueError("mapped_point_name should not be null or blank")
        if self.type in (MapType.GENERIC.name, MapType.GENERIC):
            mapped_point_names = self.mapped_point_name.split(":")
            if len(mapped_point_names) != 3:
                raise ValueError(
                    "mapped_point_names should be colon (:) delimited network_name:device_name:point_name"
                )
            network_name, device_name, point_name = mapped_point_names
            response: Response = gw_request(
                f'/ps/api/generic/points/name/{network_name}/{device_name}/{point_name}'
            )
            if response.status_code != 200:
                raise ValueError(
                    f"Does not exit mapped_point_name {self.mapped_point_name}"
                )
            self.mapped_point_uuid = json.loads(response.data).get('uuid')
        elif self.type in (MapType.BACNET.name, MapType.BACNET):
            response: Response = gw_request(
                api=f'/bacnet/api/bacnet/points/name/{self.mapped_point_name}')
            if response.status_code != 200:
                raise ValueError(
                    f"Does not exist mapped_point_name {self.mapped_point_name}"
                )
            self.mapped_point_uuid = json.loads(response.data).get('uuid')
        else:
            raise ValueError(f"Invalid type {self.type}")

    @classmethod
    def find_by_point_uuid(cls, point_uuid):
        return cls.query.filter_by(point_uuid=point_uuid).first()

    @classmethod
    def find_mapped_point_uuid_type(cls, mapped_point_uuid, map_type):
        return cls.query.filter_by(mapped_point_uuid=mapped_point_uuid,
                                   type=map_type).first()
Exemple #16
0
class Product(BaseMixin, db.Model, ReprMixin):

    name = db.Column(db.String(127), unique=False, nullable=False, index=True)
    min_stock = db.Column(db.SmallInteger, nullable=False)
    auto_discount = db.Column(db.FLOAT(precision=2), default=0, nullable=False)
    description = db.Column(db.JSON(), nullable=True)
    sub_description = db.Column(db.Text(), nullable=True)
    is_disabled = db.Column(db.Boolean(), default=False)
    default_quantity = db.Column(db.Float(precision=2), default=1)
    quantity_label = db.Column(db.Enum('KG', 'GM', 'MG', 'L', 'ML', 'TAB', 'SYRUP', 'OTH', 'TAB', 'ML', 'CAP', 'INJ',
                                       'BOTTLE', 'VAIL', 'KIT', 'STRIP', 'OTHER', 'PACK', 'SET', 'LTR', 'SACHET',
                                       'PILLS', 'SYRINGE', 'SYRUP', 'ROLL', name='varchar'),
                               default='OTH', nullable=True)
    is_loose = db.Column(db.Boolean(), default=False)
    barcode = db.Column(db.String(13), nullable=True)

    retail_shop_id = db.Column(UUID, db.ForeignKey('retail_shop.id', ondelete='CASCADE'), index=True, nullable=False)
    brand_id = db.Column(UUID, db.ForeignKey('brand.id'), index=True, nullable=False)

    retail_shop = db.relationship('RetailShop', foreign_keys=[retail_shop_id], uselist=False, back_populates='products')
    taxes = db.relationship('Tax', back_populates='products', secondary='product_tax')
    tags = db.relationship('Tag', back_populates='products', secondary='product_tag')
    brand = db.relationship('Brand', foreign_keys=[brand_id], uselist=False, back_populates='products')

    stocks = db.relationship('Stock', uselist=True, cascade="all, delete-orphan", lazy='dynamic')
    # distributors = db.relationship('Distributor', back_populates='products', secondary='product_distributor')
    combos = db.relationship('Combo', back_populates='products', secondary='combo_product')
    salts = db.relationship('Salt', back_populates='products', secondary='product_salt')
    add_ons = db.relationship('AddOn', back_populates='products', secondary='product_add_on')

    UniqueConstraint('barcode', 'retail_shop_id', 'bar_retail_un')

    @hybrid_property
    def available_stock(self):
        return self.stocks.filter(Stock.is_sold != True, Stock.expired == False)\
            .with_entities(func.coalesce(func.Sum(Stock.units_purchased), 0)-func.coalesce(func.Sum(Stock.units_sold),
                                                                                           0)).scalar()

    @hybrid_property
    def available_stocks(self):
        return self.stocks.filter(and_(or_(Stock.is_sold != True), Stock.expired == False)).all()

    @available_stock.expression
    def available_stock(cls):
        return select([func.coalesce(func.Sum(Stock.units_purchased), 0)-func.coalesce(func.Sum(Stock.units_sold), 0)])\
            .where(and_(or_(Stock.is_sold != True), Stock.product_id == cls.id)).as_scalar()

    @hybrid_property
    def mrp(self):
        mrp = self.stocks.filter(or_(Stock.is_sold != True))\
            .with_entities(Stock.selling_amount).order_by(Stock.id).first()
        return mrp[0] if mrp else 0

    @hybrid_property
    def similar_products(self):
        if len(self.salts):
            return [i[0] for i in Product.query.with_entities(Product.id)
                    .join(ProductSalt, and_(ProductSalt.product_id == Product.id))
                    .filter(ProductSalt.salt_id.in_([i.id for i in self.salts])).group_by(Product.id)
                    .having(func.Count(func.Distinct(ProductSalt.salt_id)) == len(self.salts)).all()]
        return []

    @hybrid_property
    def last_purchase_amount(self):
        return self.stocks.order_by(desc(Stock.purchase_date)).first().purchase_amount

    @hybrid_property
    def last_selling_amount(self):
        return self.stocks.order_by(desc(Stock.purchase_date)).first().selling_amount

    @hybrid_property
    def stock_required(self):
        return abs(self.min_stock - self.available_stock)

    @stock_required.expression
    def stock_required(self):
        return self.min_stock - self.available_stock

    @hybrid_property
    def is_short(self):
        return self.min_stock >= self.available_stock

    @hybrid_property
    def product_name(self):
        return self.name

    @hybrid_property
    def distributors(self):
        return self.brand.distributors.all()

    @hybrid_property
    def brand_name(self):
        return self.brand.name

    @brand_name.expression
    def brand_name(self):
        return select([Brand.name]).where(Brand.id == self.brand_id).as_scalar()
Exemple #17
0
class DeviceModel(ModelBase):
    __tablename__ = 'devices'

    uuid = db.Column(db.String(80), primary_key=True, nullable=False)
    name = db.Column(db.String(80), nullable=False, unique=True)
    device_id = db.Column(db.String(8), nullable=False, unique=True)
    enable = db.Column(db.Boolean, nullable=False, default=True)
    device_type = db.Column(db.Enum(DeviceTypes), nullable=False)
    device_model = db.Column(db.Enum(DeviceModels), nullable=False)
    description = db.Column(db.String(120), nullable=True)
    ai_1_config = db.Column(db.Enum(MicroEdgeConfig), nullable=True)
    ai_2_config = db.Column(db.Enum(MicroEdgeConfig), nullable=True)
    ai_3_config = db.Column(db.Enum(MicroEdgeConfig), nullable=True)
    fault = db.Column(db.Integer, nullable=True)
    points = db.relationship('PointModel',
                             cascade="all,delete",
                             backref='device',
                             lazy=True)

    def __repr__(self):
        return "DeviceModel({})".format(self.uuid)

    @validates('name')
    def validate_name(self, _, value):
        if not re.match("^([A-Za-z0-9_-])+$", value):
            raise ValueError(
                "name should be alphanumeric and can contain '_', '-'")
        return value

    @validates('device_id')
    def validate_device_id(self, _, value):
        if value != value.upper():
            raise ValueError("device_id characters should be in upper case")
        if len(value) != 8:
            raise ValueError("device_id should be of 8 characters")
        if not all(c in string.hexdigits for c in value):
            raise ValueError("device_id is not hex characters")
        return value

    @classmethod
    def find_by_name(cls, name: str):
        return cls.query.filter_by(name=name).first()

    @classmethod
    def find_by_id(cls, device_id: str):
        return cls.query.filter_by(device_id=device_id).first()

    def save_to_db(self):
        self.save_to_db_no_commit()
        super().save_to_db()

    def save_to_db_no_commit(self):
        if not self.points or not len(self.points):
            from src.models.device_point_presets import get_device_points
            device_points = get_device_points(self.device_model)
            for point in device_points:
                point.uuid = shortuuid.uuid()
                point.device_point_name = point.name  # to match decoder key
                point.device_uuid = self.uuid
                point.save_to_db_no_commit()
        super().save_to_db_no_commit()

    def delete_from_db(self):
        super().delete_from_db()

    @validates('device_model', 'device_type')
    def validate_device_model(self, key, value):
        if key == 'device_type':
            if isinstance(value, DeviceTypes):
                return value
            if not value or value not in DeviceTypes.__members__:
                raise ValueError("Invalid Device Type")
            value = DeviceTypes[value]
        else:
            if not isinstance(value, DeviceModels):
                if not value or value not in DeviceModels.__members__:
                    raise ValueError("Invalid Device Model")
                value = DeviceModels[value]

        # handle for both cases depending on which field is validated first
        if key == 'device_model' and self.device_type is not None:
            if not verify_device_model(self.device_type, value):
                raise ValueError('Invalid device model for device type')
        elif key == 'device_type' and self.device_model is not None:
            if not verify_device_model(value, self.device_model):
                raise ValueError('Invalid device model for device type')
        return value

    def update_mqtt(self):
        output: dict = {}
        for point in self.points:
            output[point.device_point_name] = point.point_store.value
        logger.debug(f'Publish payload: {json.dumps(output)}')
        MqttClient().publish_value((self.device_id, self.name),
                                   json.dumps(output))
Exemple #18
0
class QuestionFlag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    question_id = db.Column(db.Integer, db.ForeignKey("question.id"))
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    flag = db.Column(db.Enum(Flag))
Exemple #19
0
class NetworkModel(ModelBase):
    __tablename__ = 'network'

    uuid = db.Column(db.String(80), primary_key=True, nullable=False)
    name = db.Column(db.String(80), nullable=False)
    port = db.Column(db.String(40), nullable=False, unique=True)
    baud_rate = db.Column(db.Integer, default=9600)
    stop_bits = db.Column(db.Integer, default=1)
    parity = db.Column(db.Enum(SerialParity), default=SerialParity.N)
    byte_size = db.Column(db.Integer, default=8)
    timeout = db.Column(db.Integer, default=5)
    firmware_version = db.Column(db.Enum(SupportedFirmwareVersion),
                                 nullable=False)
    encryption_key = db.Column(db.String(32))

    @validates('name')
    def validate_name(self, _, value):
        if not re.match("^([A-Za-z0-9_-])+$", value):
            raise ValueError(
                "name should be alphanumeric and can contain '_', '-'")
        return value

    @classmethod
    def filter_one(cls):
        return cls.query.filter()

    @classmethod
    def find_one(cls):
        driver = cls.query.first()
        if driver:
            db.session.refresh(driver)
        return driver

    def save_to_db(self):
        db.session.add(self)
        db.session.commit()

    @classmethod
    def create_network(cls, config: SerialSetting):
        serial_driver = NetworkModel.find_one()
        if not serial_driver:
            serial_driver = NetworkModel(
                uuid=shortuuid.uuid(),
                name=config.port,
                port=config.port,
                baud_rate=config.baud_rate,
                stop_bits=config.stop_bits,
                parity=config.parity,
                byte_size=config.byte_size,
                timeout=config.timeout,
                firmware_version=config.firmware_version,
                encryption_key=config.encryption_key)
            serial_driver.save_to_db()
        else:
            db.session.refresh(serial_driver)

        return serial_driver

    @validates('port')
    def validate_name(self, _, value):
        self.name = value
        return value

    @validates('firmware_version')
    def validate_data_endian(self, _, value):
        if isinstance(value, SupportedFirmwareVersion):
            return value
        if not value or value not in SupportedFirmwareVersion.__members__:
            raise ValueError("Invalid Firmware Version")
        return SupportedFirmwareVersion[value]

    @validates('encryption_key')
    def validate_encryption_key(self, _, value):
        # TODO: handle self.firmware_version is None when this is validated first
        if self.firmware_version == SupportedFirmwareVersion.v1 or \
                self.firmware_version == SupportedFirmwareVersion.v2_encryption:
            return None
        else:
            assert len(value) == 32
            return value
Exemple #20
0
class PointModel(ModelBase):
    __tablename__ = 'points'
    uuid = db.Column(db.String(80), primary_key=True, nullable=False)
    name = db.Column(db.String(80), nullable=False)
    device_uuid = db.Column(db.String,
                            db.ForeignKey('devices.uuid'),
                            nullable=False)
    enable = db.Column(db.Boolean(), nullable=False, default=True)
    history_enable = db.Column(db.Boolean(), nullable=False, default=True)
    history_type = db.Column(db.Enum(HistoryType),
                             nullable=False,
                             default=HistoryType.INTERVAL)
    history_interval = db.Column(db.Integer, nullable=False, default=15)
    writable = db.Column(db.Boolean, nullable=False, default=True)
    priority_array_write = db.relationship('PriorityArrayModel',
                                           backref='point',
                                           lazy=True,
                                           uselist=False,
                                           cascade="all,delete")
    cov_threshold = db.Column(db.Float, nullable=False, default=0)
    value_round = db.Column(db.Integer(), nullable=False, default=2)
    value_operation = db.Column(db.String, nullable=True, default="x + 0")
    input_min = db.Column(db.Float())
    input_max = db.Column(db.Float())
    scale_min = db.Column(db.Float())
    scale_max = db.Column(db.Float())
    tags = db.Column(db.String(320), nullable=True)
    point_store = db.relationship('PointStoreModel',
                                  backref='point',
                                  lazy=True,
                                  uselist=False,
                                  cascade="all,delete")
    point_store_history = db.relationship('PointStoreHistoryModel',
                                          backref='point',
                                          lazy=True,
                                          cascade="all,delete")
    history_sync_log = db.relationship('HistorySyncLogModel',
                                       backref='hsl',
                                       lazy=True,
                                       cascade="all,delete")
    fallback_value = db.Column(db.Float(), nullable=True)
    disable_mqtt = db.Column(db.Boolean, nullable=False, default=True)
    type = db.Column(db.Enum(GenericPointType),
                     nullable=False,
                     default=GenericPointType.FLOAT)
    unit = db.Column(db.String, nullable=True)

    __table_args__ = (UniqueConstraint('name', 'device_uuid'), )

    def __repr__(self):
        return f"Point(uuid = {self.uuid})"

    @validates('name')
    def validate_name(self, _, value):
        if not re.match("^([A-Za-z0-9_-])+$", value):
            raise ValueError(
                "name should be alphanumeric and can contain '_', '-'")
        return value

    @validates('value_operation')
    def validate_value_operation(self, _, value):
        try:
            if value and value.strip():
                eval_arithmetic_expression(value.lower().replace(
                    'x', str(random.randint(1, 9))))
        except Exception:
            raise ValueError(
                "Invalid value_operation, must be a valid arithmetic expression"
            )
        return value

    @classmethod
    def find_by_name(cls, network_name: str, device_name: str,
                     point_name: str):
        results = cls.query.filter_by(name=point_name) \
            .join(DeviceModel).filter_by(name=device_name) \
            .join(NetworkModel).filter_by(name=network_name) \
            .first()
        return results

    def save_to_db(self):
        self.point_store = PointStoreModel.create_new_point_store_model(
            self.uuid)
        super().save_to_db()

    def update_point_value(
            self,
            point_store: PointStoreModel,
            cov_threshold: float = None,
            priority_array_write_obj: PriorityArrayModel = None) -> bool:
        if not point_store.fault:
            if cov_threshold is None:
                cov_threshold = self.cov_threshold

            value = point_store.value_original
            if value is not None:
                value = self.apply_scale(value, self.input_min, self.input_max,
                                         self.scale_min, self.scale_max)
                value = self.apply_value_operation(value, self.value_operation)
                value = round(value, self.value_round)
            point_store.value = self.apply_point_type(value)
        return point_store.update(cov_threshold, priority_array_write_obj)

    @validates('tags')
    def validate_tags(self, _, value):
        """
        Rules for tags:
        - force all tags to be lower case
        - if there is a gap add an underscore
        - no special characters
        """
        if value is not None:
            try:
                return validate_json(value)
            except ValueError:
                raise ValueError('tags needs to be a valid JSON')
        return value

    @validates('history_interval')
    def validate_history_interval(self, _, value):
        if self.history_type == HistoryType.INTERVAL and value is not None and value < 1:
            raise ValueError(
                "history_interval needs to be at least 1, default is 15 (in minutes)"
            )
        return value

    @validates('input_min')
    def validate_input_min(self, _, value):
        if value is not None and self.input_max is not None and value > self.input_max:
            raise ValueError("input_min cannot be greater than input_max")
        return value

    @validates('input_max')
    def validate_input_max(self, _, value):
        if self.input_min is not None and value is not None and self.input_min > value:
            raise ValueError("input_min cannot be greater than input_max")
        return value

    @validates('scale_min')
    def validate_scale_min(self, _, value):
        if value is not None and self.scale_max is not None and value > self.scale_max:
            raise ValueError("scale_min cannot be greater than scale_max")
        return value

    @validates('scale_max')
    def validate_scale_max(self, _, value):
        if self.scale_min is not None and value is not None and self.scale_min > value:
            raise ValueError("scale_min cannot be greater than scale_max")
        return value

    def update(self, **kwargs) -> bool:
        publish_cov: bool = self.disable_mqtt != kwargs.get('disable_mqtt')
        changed: bool = super().update(**kwargs)
        updated: bool = self.update_point_value(self.point_store, 0)
        if updated or publish_cov:
            self.publish_cov(self.point_store)

        return changed

    def update_point_store(self, value: float, priority: int,
                           priority_array_write: dict):
        priority_array_write_obj, highest_priority_value = self.update_priority_value_without_commit(
            value, priority, priority_array_write)
        point_store = PointStoreModel(point_uuid=self.uuid,
                                      value_original=highest_priority_value)
        updated = self.update_point_value(
            point_store, priority_array_write_obj=priority_array_write_obj)
        if updated:
            self.publish_cov(point_store)

    def update_priority_value_without_commit(self, value: float, priority: int, priority_array_write: dict) -> \
            (Union[PriorityArrayModel, None], Union[float, None]):
        if priority_array_write:
            priority_array: PriorityArrayModel = PriorityArrayModel.find_by_point_uuid(
                self.uuid)
            if priority_array:
                highest_priority_value: float = priority_array.update_with_no_commit(
                    **priority_array_write)
                return priority_array, highest_priority_value
            return None, None
        if not priority:
            priority = 16
        if priority not in range(1, 17):
            raise ValueError('priority should be in range(1, 17)')
        if priority:
            priority_array: PriorityArrayModel = PriorityArrayModel.find_by_point_uuid(
                self.uuid)
            if priority_array:
                highest_priority_value: float = priority_array.update_with_no_commit(
                    **{f"_{priority}": value})
                return priority_array, highest_priority_value
        return None, None

    def update_priority_value(self, value: float, priority: int,
                              priority_array_write: dict):
        self.update_priority_value_without_commit(value, priority,
                                                  priority_array_write)
        db.session.commit()

    @classmethod
    def apply_value_operation(cls, original_value,
                              value_operation: str) -> float or None:
        """Do calculations on original value with the help of point details"""
        if original_value is None or value_operation is None or not value_operation.strip(
        ):
            return original_value
        return eval_arithmetic_expression(value_operation.lower().replace(
            'x', str(original_value)))

    @classmethod
    def apply_scale(cls, value: float, input_min: float, input_max: float, output_min: float, output_max: float) \
            -> float or None:
        if value is None or input_min is None or input_max is None or output_min is None or output_max is None:
            return value
        if input_min == input_max or output_min == output_max:
            return value
        scaled = (
            (value - input_min) /
            (input_max - input_min)) * (output_max - output_min) + output_min
        if scaled > max(output_max, output_min):
            return max(output_max, output_min)
        elif scaled < min(output_max, output_min):
            return min(output_max, output_min)
        else:
            return scaled

    def apply_point_type(self, value: float):
        if value is not None:
            if self.type == GenericPointType.STRING:
                value = None
            elif self.type == GenericPointType.INT:
                value = round(value, 0)
            elif self.type == GenericPointType.BOOL:
                value = float(bool(value))
        return value

    def publish_cov(self,
                    point_store: PointStoreModel,
                    device: DeviceModel = None,
                    network: NetworkModel = None,
                    force_clear: bool = False):
        if point_store is None:
            raise Exception('Point.publish_cov point_store cannot be None')
        if device is None:
            device = DeviceModel.find_by_uuid(self.device_uuid)
        if network is None:
            network = NetworkModel.find_by_uuid(device.network_uuid)
        if device is None or network is None:
            raise Exception(
                f'Cannot find network or device for point {self.uuid}')
        priority = self._get_highest_priority_field()

        if self.history_enable \
                and (self.history_type == HistoryType.COV or self.history_type == HistoryType.COV_AND_INTERVAL) \
                and network.history_enable \
                and device.history_enable:
            PointStoreHistoryModel.create_history(point_store)
            db.session.commit()
        if not self.disable_mqtt:
            from src.services.mqtt_client import MqttClient
            MqttClient.publish_point_cov(Drivers.GENERIC.name, network, device,
                                         self, point_store, force_clear,
                                         priority)

    def _get_highest_priority_field(self):
        for i in range(1, 17):
            value = getattr(self.priority_array_write, f'_{i}', None)
            if value is not None:
                return i
        return 16
Exemple #21
0
class CommentFlag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    comment_id = db.Column(db.Integer, db.ForeignKey("comment.id"))
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    flag = db.Column(db.Enum(Flag))
Exemple #22
0
class AnswerFlag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    answer_id = db.Column(db.Integer, db.ForeignKey("answer.id"))
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    flag = db.Column(db.Enum(Flag))
Exemple #23
0
class BACnetPointModel(db.Model):
    __tablename__ = 'bac_points'
    uuid = db.Column(db.String(80), primary_key=True, nullable=False)
    object_type = db.Column(db.Enum(PointType), nullable=False)
    object_name = db.Column(db.String(80), nullable=False, unique=True)
    use_next_available_address = db.Column(db.Boolean(),
                                           nullable=False,
                                           default=False)
    address = db.Column(db.Integer(), nullable=True, unique=False)
    relinquish_default = db.Column(db.Float(), nullable=False)
    priority_array_write = db.relationship('PriorityArrayModel',
                                           backref='point',
                                           lazy=False,
                                           uselist=False,
                                           cascade="all,delete")
    event_state = db.Column(db.Enum(BACnetEventState), nullable=False)
    units = db.Column(db.Enum(Units), nullable=False)
    description = db.Column(db.String(120), nullable=False)
    enable = db.Column(db.Boolean(), nullable=False)
    fault = db.Column(db.Boolean(), nullable=True)
    data_round = db.Column(db.Integer(), nullable=True)
    data_offset = db.Column(db.Float(), nullable=True)
    cov = db.Column(db.Float(), nullable=True)
    source = db.Column(db.Enum(Sources), default=Sources.OWN)
    point_store = db.relationship('BACnetPointStoreModel',
                                  backref='point',
                                  lazy=False,
                                  uselist=False,
                                  cascade="all,delete")
    bp_gp_mapping = db.relationship('BPGPointMapping',
                                    backref='point',
                                    lazy=True,
                                    uselist=False,
                                    cascade="all,delete")
    created_on = db.Column(db.DateTime, server_default=db.func.now())
    updated_on = db.Column(db.DateTime,
                           server_default=db.func.now(),
                           onupdate=db.func.now())

    def __repr__(self):
        return f"BACnetPointModel({self.uuid})"

    @validates('object_name')
    def validate_object_name(self, _, value):
        if not re.match("^([A-Za-z0-9_-])+$", value):
            raise ValueError(
                "object_name should be alphanumeric and can contain '_', '-'")
        return value

    @classmethod
    def find_all(cls, *args, **kwargs):
        if 'source' in kwargs:
            return cls.query.filter_by(source=kwargs['source']).all()
        return cls.query.all()

    @classmethod
    def find_by_uuid(cls, uuid):
        return cls.query.filter_by(uuid=uuid).first()

    @classmethod
    def filter_by_uuid(cls, uuid):
        return cls.query.filter_by(uuid=uuid)

    @classmethod
    def find_by_object_id(cls, object_type, address):
        return cls.query.filter((BACnetPointModel.object_type == object_type) &
                                (BACnetPointModel.address == address)).first()

    @classmethod
    def find_by_object_name(cls, object_name):
        return cls.query.filter(
            BACnetPointModel.object_name == object_name).first()

    @classmethod
    def get_next_available_address(cls, current_address):
        addresses = cls.query.filter().with_entities(
            BACnetPointModel.address).all()
        sorted_addresses = sorted({address[0]
                                   for address in addresses} -
                                  {current_address})
        available_address = 1
        for address in sorted_addresses:
            if address == available_address:
                available_address += 1
            else:
                break
        return available_address

    @classmethod
    def delete_all_from_db(cls):
        cls.query.delete()
        db.session.commit()

    def save_to_db(self, priority_array_write: dict):
        self.priority_array_write = PriorityArrayModel(point_uuid=self.uuid,
                                                       **priority_array_write)
        self.point_store = BACnetPointStoreModel.create_new_point_store_model(
            self.uuid)
        db.session.add(self)
        db.session.commit()

    def delete_from_db(self):
        db.session.delete(self)
        db.session.commit()