Пример #1
0
    def _assert_valid_duration(self, start_time, end_time):
        """
        Makes sure all durations are valid.

        :param datetime.datetime start_time: The date at which the schedule
        opens.
        :param datetime.datetime end_time: The date at which the schedule
        closes.
        :raises: DAOException
        :rtype: NoneType
        :returns: None
        """
        if start_time >= end_time:
            raise DAOException(
                'Invalid start time. Start must be before the end.')

        if end_time.day != start_time.day:
            raise DAOException(
                'A schedule must start and end in the same day.')

        min_diff = math.ceil((end_time - start_time).total_seconds() / 60)
        if min_diff not in [5, 15, 30, 60]:
            if min_diff > 60 and min_diff % 60 == 0:
                return

            raise DAOException(
                'Invalid duration. Attempted duration is not of valid length.')
    def put(self, user_id, **args):
        _update = {}

        current_user = None
        plaintext = args.get('plaintext_password')
        current_pass = args.get('current_password')
        if plaintext and plaintext != '':
            current_user = self.get(user_id)
            if current_user is None:
                logging.info(
                    'Attempted to update plaintext password for non-existent'
                    'account {0}'.format(user_id))
                raise DAOException('Requested user to update does not exist.')

        for k, v in args.items():
            if k == 'plaintext_password' and (v != '' or v is not None):
                if UserTable._bcrypt_compare(current_pass,
                                             current_user.password):
                    _update['password'] = UserTable._bcrypt_password(plaintext)
                    continue
                else:
                    raise DAOException('Invalid current password.')

            if k == 'current_password':
                continue

            _update[k] = v

        if len(_update.keys()) != 0:
            User.query.filter_by(public_id=str(user_id)).update(_update)
            db.session.commit()

        return self.get(user_id)
Пример #3
0
    def post(self, start_time, end_time, user_id):
        """
        Handles the creation of a new schedule.

        :param datetime.datetime start_time: The date at which the schedule
        opens.
        :param datetime.datetime end_time: The date at which the schedule
        closes.
        :param str user_id: The user to create the schedule for.
        :rtype: ScheduleTable
        :return: The newly scheduled table.
        """
        self._assert_valid_duration(start_time, end_time)
        self._assert_no_duration_overlap(start_time, end_time, user_id)
        self._assert_not_in_past(start_time, end_time)

        user_info = db.session.query(User).filter(
            User.public_id == user_id).first()

        if user_info is None:
            raise DAOException('Invalid user requested. Try again.')

        new_schedule = Schedule(start_time, end_time, user_id,
                                user_info.local_tz)

        exec_and_commit(db.session.add, new_schedule)

        return new_schedule
Пример #4
0
    def _assert_schedule_exists(self, start_time, end_time, scheduled_user_id):
        """
        Asserts that the scheduled user has a schedule open for the event.

        :param datetime.datetime start_time: The date at which the schedule
        opens.
        :param datetime.datetime end_time: The date at which the schedule
        closes.
        :param str scheduled_user_id: The user ID to retrieve schedules for.
        :raises: DAOException
        :rtype: NoneType
        :returns: None
        """
        if end_time.tzinfo is not None:
            end_time = end_time.replace(tzinfo=None)
        if start_time.tzinfo is not None:
            start_time = start_time.replace(tzinfo=None)

        schedule = db.session.query(Schedule).filter(
            Schedule.user_id == scheduled_user_id,
            func.lower(Schedule.utc_duration) <= start_time,
            func.upper(Schedule.utc_duration) >= end_time,
        )

        schedule = schedule.first()

        if schedule is None:
            raise DAOException(
                'Invalid event. User does not have an open schedule time slot '
                'for the requested booking.')
Пример #5
0
    def _assert_no_duration_overlap(self, start_time, end_time, user_id):
        """
        Retrieves all of a users schedules and asserts that there is no
        schedule overlap.

        :param datetime.datetime start_time: The date at which the schedule
        opens.
        :param datetime.datetime end_time: The date at which the schedule
        closes.
        :param str user_id: The user ID to filter by.
        :raises: DAOException
        :rtype: NoneType
        :returns: Nothing
        """
        schedules = db.session.query(Schedule).filter(
            func.lower(Schedule.utc_duration) <= start_time,
            func.upper(Schedule.utc_duration) >= end_time,
            Schedule.user_id == user_id,
            Schedule.day_number == end_time.day,
        ).all()

        if not schedules:
            return

        raise DAOException(
            "Invalid schedule. This overlaps with a previous schedule.")
    def test_rollback_on_failure(self, transaction_mock):
        with app.app_context():
            with self.assertRaises(FacadeException) as e:
                new_schedule = Schedule(
                    datetime.utcnow().replace(hour=1) + timedelta(days=1),
                    datetime.utcnow().replace(hour=5) + timedelta(days=1),
                    self.scheduled_user,
                    'UTC',
                )

                db.session.add(new_schedule)
                db.session.commit()

                transaction_mock.side_effect = DAOException('test')

                new_event = EventFacade().create_new_event(
                    self.scheduling_user,
                    self.scheduled_user,
                    (datetime.utcnow().replace(hour=2) +
                     timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'),
                    (datetime.utcnow().replace(hour=3) +
                     timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'),
                    'UTC',
                    'test3-1234ko1234',
                    'fake-nonce',
                )

                self.assertEqual(e.msg, 'test')

                found_event = db.session.query(Event).filter_by(
                    notes='test3-1234ko1234', ).first()

                self.assertIsNone(found_event)
    def login(self, user_challenge, plaintext_password):
        """
        Handles logging in a user.

        :param str user_challenge: The email/username to get the user by
        :param str plaintext_password: The password to compare against.
        :raises: BadPasswordException
        :rtype: UserTable
        :return: The logged in user.
        """
        user_dao = UserDAO()
        user_info = user_dao.get_by_email_or_username(user_challenge)

        if user_info is None:
            raise DAOException("Invalid email. User does not exist.")

        if user_info.compare_password(plaintext_password):
            return user_info

        raise DAOException("Invalid credentials. Try again.")
Пример #8
0
    def lookup_enum_type(enum_type):
        """
        Returns a time period value based

        :param str enum_type: A time period type like "month", "week".
        :rtype: TimePeriodEnum
        :return: The enum value corresponding to the input.
        """
        try:
            return TimePeriodEnum.__value_lookup[enum_type.lower()]
        except KeyError:
            raise DAOException("Invalid time period type.")
Пример #9
0
    def put(self, schedule_id, start_time, end_time, user_id):
        """
        Updates a schedule to new duration.

        :param int schedule_id: The schedule to update.
        :param datetime.datetime start_time: The date at which the schedule
        opens.
        :param datetime.datetime end_time: The date at which the schedule
        closes.
        :param str user_id: The user to create the schedule for.
        :rtype: ScheduleTable
        :return: The newly scheduled table.
        """
        self._assert_valid_duration(start_time, end_time)

        schedule = self.get_by_schedule_id(schedule_id)

        if schedule is None:
            raise DAOException('Requested schedule is invalid. Try again.')

        if start_time < schedule.utc_open.replace(
                tzinfo=None) or end_time > schedule.utc_end.replace(
                    tzinfo=None):
            self._assert_no_duration_overlap(start_time, end_time, user_id)

        rows_affected = Schedule.query.filter(
            Schedule.user_id == user_id,
            Schedule.public_id == schedule_id).update({
                'utc_duration':
                DateTimeRange(start_time, end_time),
            })

        db.session.commit()

        if rows_affected == 0:
            raise DAOException(
                'Failed to update schedule. Schedule not found.')

        return self.get_by_schedule_id(schedule_id)
Пример #10
0
    def regenerate_token(self, email, token_choice):
        """
        Handles regenerating a  token for a user.

        :param str email: The user's email to regenerate for.
        :param str token_choice: `verify_token` or `reset_token`.
        :rtype: str
        :returns: The newly generated token.
        """
        found_user = self.get_by_email(email)

        if found_user is None:
            logging.error('Failed to regenerate token for email {0}. '
                          'Email not exists.'.format(email))
            raise DAOException('Failed to find requested user account.',
                               HTTPStatus.NOT_FOUND)

        if found_user.is_validated and token_choice == 'verify_token':
            logging.error('Requested user {0} is already activated. Skipping.')
            raise DAOException('Requested account is already activated.')

        return self._regen_token(email, token_choice), found_user
    def cancel_subscription(self, user_id):
        """
        Handles cancelling a subscription for the user.

        :param str user_id: The customer whom we want to cancel the
        subscription for.
        :rtype: SubscriptionTable
        :return: The recently cancelled subscription
        """
        found_subscription = self.get_subscription_by_user_id(user_id)

        if found_subscription is None:
            raise DAOException(
                'Subscription not found.',
                status_code=HTTPStatus.NOT_FOUND
            )

        db.session.query(
            Subscription
        ).filter_by(
            public_id=found_subscription.public_id,
        ).update({
            'is_deleted': True,
            'date_ended': datetime.utcnow(),
        })

        try:
            db.session.commit()
        except sqlalchemy.exc.IntegrityError as e:
            logging.error(
                'Failed to remove subscription for user {0} with '
                'exc: {1}'.format(
                    user_id,
                    e,
                )
            )
            raise DAOException('Failed to cancel subscription.')

        return found_subscription
Пример #12
0
    def create_customer(self,
                        bt_customer_id,
                        cc_token,
                        first_name,
                        last_name,
                        user_id,
                        *,
                        is_default=False,
                        skip_commit=False):
        """
        Handles creating a new mirrored customer in our database which
        represents the necessary information in our system. Note: these values
        also persist in Braintree.

        :param str bt_customer_id: The unique identifier generated by
        Braintree.
        :param str cc_token: A uniquely generated (by us) value for the payment
        method.
        :param str first_name: The customer's first name.
        :param str last_name: The customer's last name.
        :param str user_id: The customer's UUID.
        :param bool is_default: If we want this new payment method to be the
        default payment method.
        :rtype: CustomerTable
        :return: The newly created customer.
        """
        new_customer = Customer(bt_customer_id,
                                cc_token,
                                first_name,
                                last_name,
                                user_id,
                                is_default=is_default)

        try:
            exec_and_commit(
                db.session.add,
                new_customer,
                skip_commit=skip_commit,
            )
        except Exception as e:
            logging.critical(
                'Failed to create a new customer in our database: {0} with'
                'exception of: {1}'.format(
                    new_customer,
                    e,
                ))
            raise DAOException('Failed to create new customer.')

        return new_customer
Пример #13
0
    def get_master_merchant(self):
        """
        Handles retrieving the master merchant account.

        :rtype: MasterMerchantTable
        :return: The master merchant info.
        """
        retval = db.session.query(MasterMerchant).filter_by(
            environment=current_app.config.get('ENVIRONMENT',
                                               'dev').upper()).first()

        if retval is None:
            raise DAOException('Requested merchant account does not exist.')

        return retval
Пример #14
0
    def _assert_valid_duration(self, start_time, end_time):
        """
        Makes sure all durations are valid.

        :param datetime.datetime start_time: The date at which the schedule
        opens.
        :param datetime.datetime end_time: The date at which the schedule
        closes.
        :raises: DAOException
        :rtype: NoneType
        :returns: Nothing
        """
        if start_time >= end_time:
            raise DAOException(
                "Invalid start time. Start must be before the end.")
Пример #15
0
    def get_submerchant_by_id(self, submerchant_public_id):
        """
        Retrieves the submerchant from the DB by it's public ID.

        :param str submerchant_public_id: The submerchan'ts public UUID
        :rtype: SubmerchantTable
        :return: The submerchant.
        """
        try:
            return db.session.query(SubMerchant).filter_by(
                user_id=submerchant_public_id, ).first()
        except Exception as e:
            logging.error('Failed to query submerchant w/ public ID {0} '
                          'w/ exc of {1}'.format(submerchant_public_id, e))
            raise DAOException('No submerchant found with that id.')
Пример #16
0
    def _assert_not_in_past(self, start_time, end_time):
        """
        Asserts the new schedule is not in the past.

        :param datetime.datetime start_time: The date at which the schedule
        opens.
        :param datetime.datetime end_time: The date at which the schedule
        closes.
        :raises: DAOException
        :rtype: NoneType
        :returns: Nothing
        """
        current_time = datetime.now(start_time.tzinfo)

        if start_time < current_time:
            raise DAOException('Cannot create schedules in the past.')
Пример #17
0
    def get_customer_by_user_id(self, user_id):
        """
        Handles retrieving a customer for the user.
        :param str user_id: The customer's UUID.
        :rtype: CustomerTable
        :return: The requested customer.
        """
        found_customer = db.session.query(Customer, ).filter(
            Customer.user_id == user_id, ).first()

        if found_customer is None:
            logging.error('Failed to retrieve customer by User ID {0}'.format(
                user_id, ))
            raise DAOException('Failed to retrieve selected customer.')

        return found_customer
Пример #18
0
    def get_customer_by_public_id(self, customer_public_id):
        """
        Handles retrieving a customer by it's public ID.

        :param str customer_public_id: The public ID for the customer
        :rtype: CustomerTable
        :return: The requested customer.
        """
        found_customer = db.session.query(Customer).filter_by(
            public_id=customer_public_id).first()

        if found_customer is None:
            logging.error('Failed to retrieve customer by ID {0}'.format(
                customer_public_id, ))
            raise DAOException('Failed to retrieve selected customer.')

        return found_customer
Пример #19
0
    def eradicate_event(self, event_public_id):
        """
        Handles event rollbacks in case the payment fails.

        :param str event_public_id: The event we're wanting to delete
        :rtype: EventTable
        :return: The event which was eradicated.
        """
        found_event = db.session.query(Event).filter_by(
            public_id=event_public_id).first()

        if found_event is None:
            raise DAOException('Failed to delete event. Event does not exist.')

        exec_and_commit(db.session.delete, found_event)

        return found_event
Пример #20
0
    def delete(self, user_id, schedule_id):
        """
        Deletes a schedule by its id.

        :param str user_id: The user to delete the schedule for.
        :param int schedule_id: The schedule to delete.
        """
        rows_affected = Schedule.query.filter(
            Schedule.user_id == user_id,
            Schedule.public_id == schedule_id,
        ).delete()

        db.session.commit()

        if rows_affected == 0:
            raise DAOException(
                'Failed to delete schedule. Schedule not found.')
    def get_default_for_user(self, user_public_id):
        """
        Retrieves the default address for a user.

        :param str user_public_id: The user's public ID.
        :rtype: Address
        :return: The default address associated with a user.
        """
        address = db.session.query(Address).filter_by(
            is_deleted=False,
            is_default=True,
        ).join(User).filter_by(public_id=user_public_id).first()

        if address is None:
            logging.error('Failed to find default address for user {0}'.format(
                user_public_id))
            raise DAOException('Failed to find default address.',
                               HTTPStatus.NOT_FOUND)

        return address
Пример #22
0
    def get_by_email(self, email):
        """
        Retrieves user info by the user's email.

        :param str email: The user to retrieve by email. Emails are unique so
        this is a safe retrieval.
        :rtype: UserTable
        :return: The UserTable row or None
        """
        found_user = db.session.query(User).filter_by(email=email).first()

        if found_user is None:
            logging.error(
                'Failed to find requested user by email {0}.'.format(email))
            raise DAOException(
                'Failed to find requested user.',
                HTTPStatus.NOT_FOUND,
            )

        return found_user
    def create_subscription(self, bt_sub_id, user_id, *, plan_id='Basic0x01',
                            skip_commit=False):
        """
        Handles creating a subscription record in the db.


        :param str bt_sub_id: The unique identifier generated by braintree
        when subscriptions are created.
        :param str user_id: The customer's UUID.
        :param str plan_id: The plan we want to subscribe to.
        :param bool skip_commit: Handles skipping the commit to create a pseudo
        transaction.
        :rtype: SubscriptionTable
        :return: The newly created subscription
        """
        new_subscription = Subscription(
            self.customer_dao.get_customer_by_user_id(user_id).public_id,
            user_id,
            bt_sub_id,
            plan_id=plan_id,
        )

        try:
            exec_and_commit(
                db.session.add,
                new_subscription,
                skip_commit=skip_commit,
            )
        except Exception as e:
            logging.critical(
                'Failed to create a new subscription in our database: {0} with'
                'exception of: {1}'.format(
                    new_subscription,
                    e,
                )
            )
            raise DAOException(
                'Failed to create new subscription.'
            )

        return new_subscription
Пример #24
0
    def get(self, user_id):
        """
        Retrieves a single user by its user id.

        :param str user_id: The User ID to look up.
        :rtype: UserTable
        :return: The UserTable row or None.
        """
        found_user = User.query.filter(
            User.public_id == str(user_id),
            User.is_deleted == False).limit(1).first()

        if found_user is None:
            logging.error(
                'Failed to find requested user by id {0}.'.format(user_id))
            raise DAOException(
                'Failed to find requested user.',
                HTTPStatus.NOT_FOUND,
            )

        return found_user
Пример #25
0
    def get_by_event_id(self, user_id, event_id):
        """
        Returns the information for an event.

        :param str user_id: Either the scheduled or scheduling user.
        :param str event_id: The event to retrieve
        :rtype: EventTable
        :return: The requested event.
        """
        scheduled = aliased(User)
        scheduling = aliased(User)

        event = db.session.query(
            Event,
            scheduled,
            scheduling,
        ).filter(
            or_(
                Event.scheduled_user_id == user_id,
                Event.scheduling_user_id == user_id,
            ),
            Event.public_id == event_id,
        ).join(
            scheduled,
            Event.scheduled_user_id == scheduled.public_id,
        ).join(
            scheduling,
            Event.scheduling_user_id == scheduling.public_id,
        ).first()

        if event is None:
            logging.error(
                'Failed to retrieve event ({0}) for user {1}.'.format(
                    event_id,
                    user_id,
                ))
            raise DAOException(
                'Requested event not found. Please refresh and try again.')

        return event
    def get_by_public_id(self, public_id, user_public_id):
        """
        Retrieves a single address by its public ID.

        :param str public_id: The UUID associated with an address.
        :param str user_public_id: The user's public ID.
        :rtype: AddressTable
        :return: A single address object.
        """
        address = db.session.query(Address).filter_by(
            public_id=public_id,
            is_deleted=False,
        ).join(User).filter_by(public_id=user_public_id).first()

        if address is None:
            logging.error(
                'Failed to find requested address {0} for user {1}'.format(
                    public_id, user_public_id))
            raise DAOException('Failed to find requested address.',
                               HTTPStatus.NOT_FOUND)

        return address
Пример #27
0
    def get_by_email_or_username(self, user_challenge):
        """
        Handles retrieving a user by either username or email.

        :param str user_challenge: Email or username.
        :rtype: UserTable
        :return: The found user/email.
        """
        found_user = db.session.query(User).filter(
            or_(User.email == user_challenge,
                User.username == user_challenge)).first()

        if found_user is None:
            logging.error(
                'Failed to find requested user by email/username {0}.'.format(
                    user_challenge, ))
            raise DAOException(
                'Failed to find requested user.',
                HTTPStatus.NOT_FOUND,
            )

        return found_user
    def get_subscription_by_public_id(self, subscription_public_id):
        """
        Handles retrieving a subscription by it's public ID.

        :param str subscription_public_id: The public ID for the customer
        :rtype: SubscriptionTable
        :return: The requested subscription.
        """
        found_subscription = db.session.query(
            Subscription
        ).filter_by(
            public_id=subscription_public_id
        ).first()

        if found_subscription is None:
            logging.error(
                'Failed to retrieve subscription by ID {0}'.format(
                    subscription_public_id,
                )
            )
            raise DAOException('Failed to retrieve selected subscription.')

        return found_subscription
    def get_subscription_by_user_id(self, user_id):
        """
        Handles retrieving a subscription for the user.

        :param str user_id: The customer's UUID.
        :rtype: SubscriptionTable
        :return: The requested subscription.
        """
        found_subscription = db.session.query(
            Subscription,
        ).filter(
            Subscription.user_id == user_id,
        ).first()

        if found_subscription is None:
            logging.error(
                'Failed to retrieve subscription by User ID {0}'.format(
                    user_id,
                )
            )
            raise DAOException('Failed to retrieve subscription info.')

        return found_subscription
Пример #30
0
    def delete_customer(self, customer_public_id):
        """
        Handles setting a customer account as deleted. (Soft delete)

        :param str customer_public_id: The UUID identifying the customer
        account in the database.

        :rtype: CustomerTable
        :return: The recently deleted customer.
        """
        found_customer = self.get_customer_by_public_id(customer_public_id)

        db.session.query(Customer).filter_by(
            public_id=customer_public_id, ).update({"is_deleted": True})

        try:
            db.session.commit()
        except sqlalchemy.exc.IntegrityError as e:
            logging.error('Failed to remove customer account {0}'.format(
                customer_public_id, ))
            raise DAOException('Failed to remove selected customer.')

        return found_customer