class CustomerModel(UserMixin, db.Model):
    __tablename__ = "customers"

    payment_gateway_id = db.Column(db.String(75), nullable=True)
    bookings = db.relationship(lambda: BookingModel, uselist=True, lazy="dynamic", passive_deletes=True)
    payments = db.relationship(lambda: PaymentModel, lazy="dynamic", uselist=True, passive_deletes=True, )
    refunds = db.relationship(lambda: BookingRefundModel, lazy="dynamic", uselist=True, passive_deletes=True, )
    role_id = db.Column(db.String(36), db.ForeignKey("roles.id", onupdate="CASCADE", ondelete="CASCADE"),
                        nullable=False,
                        index=True, )
    role = db.relationship("RoleModel", viewonly=True)
    activity_monitoring = db.relationship(lambda: CustomerActivityModel, lazy="dynamic", uselist=True,
                                          passive_deletes=True)

    def __init__(self, **kwargs):
        super(CustomerModel, self).__init__(**kwargs)
        self.password = CustomerModel.encrypt_password(kwargs["password"])

    def initialize_refund_message(self, status: bool):
        """
        Send email concerning refund claim
        """

        from app.blueprints.customer.tasks import deliver_refund_mail

        deliver_refund_mail.delay(self.email, status)
        return None
Beispiel #2
0
class BookingContactModel(ResourceMixin, db.Model):
    __tablename__ = "booking_contacts"

    first_name = db.Column(db.String(25), nullable=True, index=False)
    last_name = db.Column(db.String(25), nullable=True, index=False)
    telephone = db.Column(db.String(15),
                          nullable=False,
                          index=True, )
    email = db.Column(db.String(128),
                      nullable=False,
                      index=True, )
    note = db.Column(db.String(250), nullable=True, index=False)
    event_address = db.Column(db.String(150), nullable=False, index=False)
    event_gps = db.Column(db.String(150), nullable=True, index=False)
    booking_id = db.Column(db.String(36), db.ForeignKey("bookings.id", onupdate="CASCADE", ondelete="CASCADE"),
                           nullable=False,
                           index=True, )
    booking = db.relationship("BookingModel", viewonly=True)

    def __init__(self, **kwargs):
        super(BookingContactModel, self).__init__(**kwargs)

    @classmethod
    def get_booking_contact_by_booking_id(cls, booking_id) -> 'BookingContactModel':
        """:return: BookingContactModel
            :param booking_id:str
        """
        return cls.query.filter_by(booking_id=booking_id).first()
Beispiel #3
0
class ReviewModel(ResourceMixin, db.Model):
    __tablename__ = "reviews"

    def __init__(self, **kwargs):
        super(ReviewModel, self).__init__(**kwargs)

    rating = db.Column(db.Integer(), nullable=False, default=0, index=False)
    review_body = db.Column(db.String(125), nullable=True, index=False)
    booking_id = db.Column(db.String(36),
                           db.ForeignKey("bookings.id",
                                         onupdate="CASCADE",
                                         ondelete="CASCADE"),
                           nullable=False,
                           index=True)
    booking = db.relationship("BookingModel", viewonly=True)
Beispiel #4
0
class CustomerActivityModel(ResourceMixin, db.Model):
    __tablename__ = "customer_activity_monitoring"

    def __init__(self, **kwargs):
        super(CustomerActivityModel, self).__init__(**kwargs)

    sign_in_ip = db.Column(db.String(45), nullable=True, index=False, )
    customer_id = db.Column(db.String(36), db.ForeignKey("customers.id", onupdate="CASCADE", ondelete="CASCADE"),
                            nullable=False,
                            index=True, )
    customer = db.relationship("CustomerModel", viewonly=True)

    @classmethod
    def update_activity_tracking(cls, customer_id: str, ip_address: str):
        """
        Update client's activity monitoring
        """

        monitor = cls(customer_id=customer_id, sign_in_ip=ip_address)
        return monitor.save()
Beispiel #5
0
class RoleModel(ResourceMixin, db.Model):
    __tablename__ = "roles"

    name = db.Column(db.String(10), index=True, unique=True, nullable=False)

    def __init__(self, **kwargs):
        super(RoleModel, self).__init__(**kwargs)

    @classmethod
    def find_by_identity(cls, identity):
        search_chain = ((cls.name == identity), (cls.id == identity))
        return cls.query.filter(or_(*search_chain)).first()
class EmployeeModel(UserMixin, db.Model):
    __tablename__ = "employees"

    services = db.relationship(lambda: ServiceModel,
                               secondary=lambda: employees_services,
                               lazy="dynamic",
                               backref=db.backref("employees", lazy="dynamic"))
    role_id = db.Column(
        db.String(36),
        db.ForeignKey("roles.id", onupdate="CASCADE", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    role = db.relationship("RoleModel", viewonly=True)
    time_sheets = db.relationship(lambda: TimeSheetModel,
                                  lazy="dynamic",
                                  uselist=True,
                                  passive_deletes=True)

    def __init__(self, **kwargs):
        super(EmployeeModel, self).__init__(**kwargs)
        self.password = EmployeeModel.encrypt_password(kwargs["password"])

    @classmethod
    def get_employee_detail(cls,
                            employee_id: str) -> Optional["EmployeeModel"]:
        employee = cls.query.get(employee_id)
        if employee is None:
            return None
        return employee.services.all()

    @classmethod
    def get_employees(cls) -> List["EmployeeModel"]:
        return cls.query.all()

    @classmethod
    def get_employees_by_service_rendered(
            cls, service_id: str) -> List["EmployeeModel"]:
        return cls.query.join(
            cls.services).filter(ServiceModel.id == service_id).all()

    @classmethod
    def get_active_employees_by_service_rendered(
            cls, service_id: str) -> List["EmployeeModel"]:
        data = cls.query.join(cls.services).filter(
            ServiceModel.id == service_id, cls.is_active).all()
        return data
class ServiceMonitoringModel(ResourceMixin, db.Model):
    __tablename__ = "service_monitoring"

    def __init__(self, **kwargs):
        super(ServiceMonitoringModel, self).__init__(**kwargs)

    service_id = db.Column(
        db.String(36),
        db.ForeignKey("services.id", onupdate="CASCADE", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    service = db.relationship("ServiceModel", viewonly=True)
    total_seconds = db.Column(db.Integer(),
                              nullable=False,
                              index=False,
                              default=0)

    @classmethod
    def update_activity_tracking(cls, service_id: str, total_seconds: int):
        """
        Update service's monitoring
        """

        monitor = cls(service_id=service_id, total_seconds=total_seconds)
        return monitor.save()

    @classmethod
    def get_service_activities(
        cls,
        order_values: str,
        page: int,
    ):
        """Returns Paginated ServiceMonitoringModel"""
        paginated_monitor = cls.query.order_by(text(order_values)).paginate(
            page, 50, False)
        return paginated_monitor
class BookingRefundModel(ResourceMixin, db.Model):
    BOOKING_REFUND_REASON = OrderedDict([("duplicate", "Duplicate"),
                                         ("fraudulent", "Fraudulent"),
                                         ("requested_by_customer",
                                          "Requested by customer")])
    BOOKING_REFUND_STATUS = OrderedDict([('failed', 'Failed'),
                                         ('canceled', 'Canceled'),
                                         ('succeeded', 'Succeeded')])

    __tablename__ = "booking_refunds"

    refund_id = db.Column(
        db.String(75),
        nullable=False,
        index=True,
    )
    amount = db.Column(db.Float(precision=2), nullable=False)
    currency = db.Column(db.String(3), nullable=False)

    payment_id = db.Column(
        db.String(75),
        db.ForeignKey("payments.stripe_id",
                      onupdate="CASCADE",
                      ondelete="CASCADE"),
        nullable=False,
    )
    payment = db.relationship("PaymentModel")

    customer_id = db.Column(
        db.String(75),
        db.ForeignKey("customers.id", onupdate="CASCADE", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    customer = db.relationship("CustomerModel", viewonly=True)

    reason = db.Column(db.Enum(*BOOKING_REFUND_REASON, native_enum=False),
                       nullable=False)
    status = db.Column(db.Enum(*BOOKING_REFUND_STATUS, native_enum=False),
                       nullable=False)
    receipt_number = db.Column(db.String(50), nullable=True)

    def __init__(self, **kwargs):
        super(BookingRefundModel, self).__init__(**kwargs)

    @classmethod
    def find_by_identity(cls, identity: str) -> Optional["BookingRefundModel"]:
        """:return: RefundModel
            :param identity:str
        """
        search_chain = ((cls.payment_id == identity), (cls.id == identity))
        return cls.query.filter(or_(*search_chain)).first()

    @classmethod
    def construct_refund_body(cls, reason: str, payment_id: str, amount: float,
                              meta: Dict[str, Any]) -> Dict[str, Any]:
        """:param payment_id:str
            :param amount: float
            :param meta:Dict[str, Any]
            :param reason:str
            :return:Dict[str, Any]

            amount is in lowest denomination of currency. eg Dollar will be cents
        """
        return {
            "charge": payment_id,
            "amount": amount,
            "meta": meta,
            "reason": reason
        }
Beispiel #9
0
class ResourceMixin(object):
    id = db.Column(db.String(36),
                   primary_key=True,
                   unique=True,
                   nullable=False,
                   default=id_generator)
    created_at = db.Column(db.TIMESTAMP(timezone=True),
                           nullable=False,
                           default=tz_aware_datetime)
    updated_at = db.Column(db.TIMESTAMP(timezone=True),
                           nullable=False,
                           default=tz_aware_datetime,
                           onupdate=tz_aware_datetime)

    @classmethod
    def sort_by(cls, field: str, direction: str) -> Tuple[str, str]:
        """
        Validates the sort field and direction
        """
        if field not in cls.__table__.columns:
            field = "created_at"
        if direction not in ("asc", "desc"):
            direction = "asc"
        return field, direction

    @classmethod
    def get_bulk_ids(cls, scope, ids, omit_ids=None, query=""):
        if omit_ids is None:
            omit_ids = []
        omit__map_ids = map(str, omit_ids)
        if scope == "all_search_results":
            ids = cls.query.with_entites(cls.id).filter(cls.search(query))
            ids = [str(item[0] for item in ids)]

        if omit__map_ids:
            ids = [_id for _id in ids if _id not in omit__map_ids]
        return ids

    @classmethod
    def get_by_id(cls, _id):
        """
            Get Model by ID
        """
        return cls.query.get(_id)

    @classmethod
    def bulk_delete(cls, ids):
        delete_count = cls.query.filter(
            cls.id.in_(ids)).delete(synchronize_session=False)
        db.session.commit()
        return delete_count

    def save(self):
        db.session.add(self)
        db.session.commit()
        return None

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

    @classmethod
    def for_update(cls, _id, kwargs):
        cls.query.filter(cls.id == _id).update({**kwargs},
                                               synchronize_session='evaluate')
        return None

    def __str__(self):
        """
        Create a human readable version of class instance
        :return self
        """
        obj_id = hex(id(self))
        columns = self.__table__.c.keys()
        values = ", ".join("%s=%r" % (n, getattr(self, n)) for n in columns)
        return '<%s %s(%s)>' % (obj_id, self.__class__.__name__, values)
Beispiel #10
0
class PaymentModel(ResourceMixin, db.Model):
    PAYMENT_STATUS = OrderedDict([("paid", "Paid"), ("unpaid", "Unpaid"),
                                  ("processing", "Processing")])

    __tablename__ = "payments"

    stripe_id = db.Column(
        db.String(75),
        nullable=False,
        unique=True,
        index=True,
    )
    currency = db.Column(
        db.String(3),
        nullable=False,
        index=False,
    )
    amount_subtotal = db.Column(db.Float(precision=2), nullable=False)
    payment_method = db.Column(db.String(15), nullable=False)
    amount_total = db.Column(db.Float(precision=2), nullable=False)
    booking_id = db.Column(
        db.String(36),
        db.ForeignKey("bookings.id", onupdate="CASCADE", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    coupon_code = db.Column(db.String(16), nullable=True)
    booking = db.relationship("BookingModel")
    customer_id = db.Column(
        db.String(36),
        db.ForeignKey("customers.id", onupdate="CASCADE", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    customer = db.relationship("CustomerModel", viewonly=True)
    status = db.Column("status",
                       db.Enum(*PAYMENT_STATUS, native_enum=False),
                       nullable=False,
                       index=False,
                       default="processing")
    booking_refund = db.relationship(
        lambda: BookingRefundModel,
        lazy="dynamic",
        uselist=True,
        passive_deletes=True,
    )

    def __init__(self, **kwargs):
        super(PaymentModel, self).__init__(**kwargs)

    @classmethod
    def paginate_payments(cls,
                          page: int,
                          order_values: str,
                          search_query=None,
                          customer_id: str = None,
                          is_admin: bool = False):
        """Returns Paginated PaymentModel"""

        paginated_payments = None

        if search_query is None:
            if not is_admin:
                paginated_payments = cls.query.filter_by(
                    customer_id=customer_id).order_by(
                        text(order_values)).paginate(page, 50, False)
            else:
                paginated_payments = cls.query.order_by(
                    text(order_values)).paginate(page, 50, False)
        else:

            if not is_admin:
                paginated_payments = cls.query.filter(
                    cls.customer_id == customer_id, search_query).order_by(
                        text(order_values)).paginate(page, 50, False)
            else:
                paginated_payments = cls.query.filter(search_query).order_by(
                    text(order_values)).paginate(page, 50, False)
        return paginated_payments

    @classmethod
    def find_by_identity(cls,
                         stripe_id: str,
                         customer_id: str,
                         is_admin: bool = False) -> "PaymentModel":
        if not is_admin:
            return cls.query.filter_by(client_id=customer_id,
                                       stripe_id=stripe_id).first()
        return cls.query.filter_by(stripe_id=stripe_id).first()

    @classmethod
    def get_payment(cls, stripe_id: str) -> "PaymentModel":
        return cls.query.filter_by(stripe_id=stripe_id).first()

    @classmethod
    def search(cls, search_query: str = None):
        """
        Returns a search query based on search term.
        """
        if not search_query:
            return None
        fmt_query = f"%{search_query}%"
        search_chain = (cls.status.ilike(fmt_query))

        return or_(*search_chain)
Beispiel #11
0
class ServiceModel(ResourceMixin, db.Model):
    __tablename__ = "services"

    def __init__(self, **kwargs):
        super(ServiceModel, self).__init__(**kwargs)

    name = db.Column(db.String(75), unique=True, nullable=False, index=True)
    description = db.Column(db.String(125), nullable=False, index=False)
    price = db.Column(db.Float(precision=2), nullable=False, index=False)
    is_available = db.Column(db.Boolean,
                             nullable=False,
                             index=False,
                             default=True)
    activity_monitoring = db.relationship(lambda: ServiceMonitoringModel,
                                          lazy="dynamic",
                                          uselist=True,
                                          passive_deletes=True)
    bookings = db.relationship("BookingModel",
                               lazy="dynamic",
                               uselist=True,
                               passive_deletes=True)

    @classmethod
    def get_services(cls, include_outdated=False, is_admin=False):
        if is_admin:
            if include_outdated:
                return cls.query.all()
        return cls.query.filter_by(is_available=True).all()

    @classmethod
    def get_service(
        cls,
        service_name: str,
        service_id: str,
    ):
        query = ((cls.name == service_name), (cls.id == service_id))
        return cls.query.filter(or_(*query)).first()

    @classmethod
    def get_service_by_identity(cls, identity: str):
        query = ((cls.name == identity), (cls.id == identity))
        return cls.query.filter(or_(*query)).first()

    @classmethod
    def get_service_and_status(cls, identity: str):
        query = ((cls.name == identity), (cls.id == identity))
        return cls.query.filter(or_(*query), cls.is_available).first()

    @classmethod
    def search(cls, search_query: str = None):
        """
        Returns a search query based on search term.
        """

        if not search_query:
            return None
        fmt_query = f"%{search_query}%"
        search_chain = [(cls.name.ilike(fmt_query))]
        return or_(*search_chain)

    @classmethod
    def paginate_services(cls,
                          page: int,
                          order_values: str,
                          search_query=None,
                          is_admin=False):
        """Returns Paginated BookingModel"""
        paginated_services = None
        if search_query is None:
            if not is_admin:
                paginated_services = cls.query.filter_by(
                    is_available=True).order_by(text(order_values)).paginate(
                        page, 50, False)
            else:
                paginated_services = cls.query.order_by(
                    text(order_values)).paginate(page, 50, False)
        else:

            if not is_admin:
                paginated_services = cls.query.filter(
                    cls.is_available, search_query).order_by(
                        text(order_values)).paginate(page, 50, False)
            else:
                paginated_services = cls.query.filter(search_query).order_by(
                    text(order_values)).paginate(page, 50, False)
        return paginated_services
from app.extensions.db_ext import db

employees_services = db.Table(
    "employees_services", db.Model.metadata,
    db.Column("employee_id",
              db.String(),
              db.ForeignKey("employees.id"),
              primary_key=True),
    db.Column("service_id",
              db.String(),
              db.ForeignKey("services.id"),
              primary_key=True))
Beispiel #13
0
class UserMixin(ResourceMixin):
    GENDER = OrderedDict([("male", "Male"), ("female", "Female"), ("private", "Private")])

    password = db.Column(db.String(128), nullable=False, index=False)
    gender = db.Column(db.Enum(*GENDER, native_enum=False), nullable=False, index=True, default="private")
    is_active = db.Column(db.Boolean, nullable=False, default=True)
    email = db.Column(db.String(128), nullable=False, index=True, unique=True)
    first_name = db.Column(db.String(45), nullable=False, index=False, )
    last_name = db.Column(db.String(45), nullable=False, index=False)
    telephone = db.Column(db.String(15), nullable=False, index=True, unique=True)
    # ! Address
    country = db.Column(db.String(25), nullable=False, index=False)
    city = db.Column(db.String(50), nullable=False, index=False)
    street = db.Column(db.String(50), nullable=True, index=False, )

    @classmethod
    def deserialize_token(cls, reset_token):
        """
            Deserialize password reset token
        """
        private_key = current_app.config["SECRET_KEY"]
        serializer = TimedJSONWebSignatureSerializer(private_key)

        try:
            payload = serializer.loads(reset_token)
            return cls.find_by_identity(payload["email"])
        except Exception as e:
            return None

    def initialize_password_reset(self):
        """
        generate token to reset password for a user
        """
        reset_token = self.serialize_token()

        # TODO: Password reset email

        from app.blueprints.auth.tasks import deliver_password_reset_mail

        deliver_password_reset_mail.delay(self.email, reset_token)
        return None

    def serialize_token(self, expiration=3600):
        private_key = current_app.config["SECRET_KEY"]
        serializer = TimedJSONWebSignatureSerializer(private_key, expires_in=expiration)
        return serializer.dumps({"email": self.email}).decode("utf-8")

    def authenticate(self, plain_password, with_password=True):
        """
            Check password
        """
        if with_password:
            return check_password_hash(self.password, plain_password)
        return True

    @classmethod
    def search(cls, search_query):
        """
        Returns a search query based on search term.
        """
        if not search_query:
            return None
        fmt_query = f"%{search_query}%"
        search_chain = (cls.last_name.ilike(fmt_query), cls.first_name.ilike(fmt_query), cls.city.ilike(fmt_query),
                        cls.country.ilike(fmt_query))

        return or_(*search_chain)

    @classmethod
    def find_by_identity(cls, identity):
        search_chain = ((cls.email == identity), (cls.telephone == identity), (cls.id == identity))
        return cls.query.filter(or_(*search_chain)).first()

    @classmethod
    def verify_for_signup(cls, email: str, telephone: str) -> bool:
        search_chain = ((cls.email == email), (cls.telephone == telephone))
        user = cls.query.filter(or_(*search_chain)).first()
        return user is not None

    @classmethod
    def encrypt_password(cls, plain_password: str) -> str:
        """
         Hash Password
        """
        return generate_password_hash(plain_password)

    @classmethod
    def paginate_account(cls, page: int, order_values: str, search_query=None):
        """Returns Paginated UserModel"""

        paginated_account = None
        if search_query is None:
            paginated_account = cls.query.order_by(cls.role_id,
                                                   text(order_values)).paginate(
                page, 50, False)
        else:
            paginated_account = cls.query.filter(search_query).order_by(cls.role_id,
                                                                        text(order_values)).paginate(
                page, 50, False)
        return paginated_account
Beispiel #14
0
class TimeSheetModel(ResourceMixin, db.Model):
    AVAILABILITY_STATUS = OrderedDict([('pending', 'Pending'),
                                       ('completed', 'Completed'),
                                       ('cancelled', 'Cancelled'),
                                       ('ongoing', 'Ongoing')])
    __tablename__ = "time_sheets"

    def __init__(self, **kwargs):
        super(TimeSheetModel, self).__init__(**kwargs)

    booking_id = db.Column(db.String(36),
                           db.ForeignKey("bookings.id",
                                         onupdate="CASCADE",
                                         ondelete="CASCADE"),
                           index=True)
    booking = db.relationship("BookingModel", viewonly=True)

    service_id = db.Column(db.String(36),
                           db.ForeignKey("services.id",
                                         onupdate="CASCADE",
                                         ondelete="CASCADE"),
                           index=True)
    service = db.relationship("ServiceModel", viewonly=True)

    employee_id = db.Column(db.String(36),
                            db.ForeignKey("employees.id",
                                          onupdate="CASCADE",
                                          ondelete="CASCADE"),
                            index=True)
    employee = db.relationship("EmployeeModel", viewonly=True)

    status = db.Column(db.Enum(*AVAILABILITY_STATUS, native_enum=False),
                       nullable=True,
                       index=True,
                       default="pending")

    start_date = db.Column(db.TIMESTAMP(timezone=True), nullable=False)
    end_date = db.Column(db.TIMESTAMP(timezone=True), nullable=False)

    @classmethod
    def get_schedules(cls, ) -> List["TimeSheetModel"]:
        return cls.query.all()

    @classmethod
    def get_staff_schedules_for_booking(cls, employee_id,
                                        start_date) -> List["TimeSheetModel"]:
        return cls.query.filter(cls.employee_id == employee_id,
                                cls.start_date >= start_date,
                                cls.status != "cancelled").all()

    @classmethod
    def populate_schedules(cls, schedules: List, booking_id: str,
                           employee_id: str, service_id: str):
        instances = [
            TimeSheetModel(booking_id=booking_id,
                           employee_id=employee_id,
                           start_date=schedule.get("start_date"),
                           end_date=schedule.get("end_date"),
                           service_id=service_id) for schedule in schedules
        ]
        db.session.add_all(instances)
        db.session.commit()
        return None

    @classmethod
    def update_populate_schedules(cls, booking_id: str, status: str):
        cls.query.filter(cls.booking_id == booking_id).update(
            {cls.status: status}, synchronize_session="evaluate")
        return None