Esempio n. 1
0
class AbstractModelBase(db.Model):
    """Abstract base class for SQLAlchemy models.

    Defines basic fields and generic create, update, delete, and aggregate table operations for application models.

    Defines the following fields:
        id (int): Auto-incrementing primary key id.
        date_created (datetime.datetime): Timestamp of model creation.
        date_modified (datetime.datetime): Timestamp of last model update.
    """
    __abstract__ = True

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
    date_modified = db.Column(db.DateTime,
                              default=db.func.current_timestamp(),
                              onupdate=db.func.current_timestamp())

    def create(self):
        """Add this model instance to the database."""
        db.session.add(self)
        db.session.commit()

    def update_instance(self, new_fields: dict):
        """Update this model instance in the database.

        Args:
            new_fields (dict): Dict containing new values for this `User`.
        """
        db.session.query(
            self.__class__).filter_by(id=self.id).update(new_fields)
        db.session.commit()

    def delete_instance(self):
        """Delete this model instance from the database."""
        db.session.query(self.__class__).filter_by(id=self.id).delete()
        db.session.commit()

    @classmethod
    def get_all(cls) -> List[T]:
        """Iterate through all model instances in the database.

        NOTE: This method can be very memory-intensive and should not be used in production.

        Yields:
            Next instance of this model in the database.
        """
        for model in db.session.query(cls):
            yield model

    @classmethod
    def delete_all(cls):
        """Delete all instances of this model in the database.

        NOTE: This method is incredibly desctructive and should not be used in production.
        """
        db.session.query(cls).delete()
        db.session.commit()
Esempio n. 2
0
class TimeRange(AbstractModelBase):
    """Data access object providing a static interface to a time range table."""
    __tablename__ = models.tables.TIME_RANGE

    description = db.Column(db.String(255))
    start_time = db.Column(db.Integer)
    end_time = db.Column(db.Integer)

    @staticmethod
    def find_by_id(time_range_id: int) -> TimeRangeType:
        """Look up a `TimeRange` by id.

        Args:
            time_range_id (int): id to match.

        Returns:
            TimeRange with the given id if found, None if not found.
        """
        return db.session.query(TimeRange).get(time_range_id)

    @staticmethod
    def find_by_start_time(start_time: int) -> TimeRangeType:
        """Look up a `TimeRange` by start time.

        Args:
            start_time (int): start time to match.

        Returns:
            TimeRange with the given start time if found, None if not found.
        """
        return db.session.query(TimeRange).filter(
            TimeRange.start_time == start_time).first()

    @staticmethod
    def find_by_end_time(end_time: int) -> TimeRangeType:
        """Look up a `Time Range` by end time.

        Args:
            end_time (int): end time to match.

        Returns:
            TimeRange with the given end time if found.
        """
        return db.session.query(TimeRange).filter(
            TimeRange.end_time == end_time).first()

    def __repr__(self) -> str:
        """Return a string representation of this `TimeRange`."""
        return f'TODO'
Esempio n. 3
0
class Location(AbstractModelBase):
    """Data access object providing a static interface to a location table."""
    __tablename__ = models.tables.LOCATION

    name = db.Column(db.String(128))
    db.UniqueConstraint('name')

    @staticmethod
    def find_by_id(location_id: int) -> LocationType:
        """Look up a `Location` by id.

        Args:
            location_id (int): id to match.

        Returns:
            `Location` with the given id if found, None if not found.
        """
        return db.session.query(Location).get(location_id)

    @staticmethod
    def find_by_name(name: str) -> LocationType:
        """Look up a `Location` by name.

        Args:
            name (str): name to match.

        Returns:
            `Location` with the given name if found, None if not found.
        """
        return db.session.query(Location).filter(Location.name == name).first()

    def __repr__(self) -> str:
        """Return a string representation of this `Location`."""
        return f"Location({self.id}, '{self.name}')"

    def __str__(self) -> str:
        """Return this `Location` as a friendly string."""
        return f"{self.id}. {self.name}"
Esempio n. 4
0
class Status(AbstractModelBase):
    """Data access object providing a static interface to a status table."""
    __tablename__ = models.tables.STATUS

    description = db.Column(db.String(64))

    @staticmethod
    def find_by_id(status_id: int) -> StatusType:
        """Look up a `Status` by id.

        Args:
            status_id (int): id to match.

        Returns:
            `Status` with the given id if found, None if not found.
        """
        return db.session.query(Status).get(status_id)

    @staticmethod
    def find_by_description(description: str) -> StatusType:
        """Look up a `Status` by description.

        Args:
            description: (str): description to match.

        Returns:
            `Status` with the given description if found, None if not found.
        """
        return db.session.query(Status).filter(Status.description == description).first()

    def __repr__(self) -> str:
        """Return a string representation of this `Status`."""
        return f"Status({self.id}, '{self.description}')"

    def __str__(self) -> str:
        """Return this `Status` as a friendly string."""
        return f"{self.id}. {self.description}"
Esempio n. 5
0
class User(AbstractModelBase):
    """Data access object providing a static interface to a user table."""
    __tablename__ = models.tables.USER

    first_name = db.Column(db.String(_MAX_LENGTH))
    last_name = db.Column(db.String(_MAX_LENGTH))
    email = db.Column(db.String(_MAX_LENGTH))
    password = db.Column(db.String(_MAX_LENGTH))

    @db.validates('first_name')
    def validate_first_name(self, key: str, first_name: str):
        """Check that a first name is valid.

        Args:
            key (str): Dict key corresponding to the field being validated.
            first_name (str): Value provided to field.

        Raises:
            'InvalidFirstNameError': If the length of given first name is longer than 64
                                     or contains an invalid character.

        """
        if len(first_name) > _MAX_LENGTH:
            raise InvalidFirstNameError(first_name)

        if re.compile(_INVALID_CHARS).search(first_name):
            raise InvalidFirstNameError(first_name)

        return first_name

    @db.validates('last_name')
    def validate_last_name(self, key: str, last_name: str):
        """Check that a first name is valid.

        Args:
            key (str): Dict key corresponding to the field being validated.
            last_name (str): Value provided to field.

        Raises:
            'InvalidLastNameError': If the length of given first name is longer than 64
                                     or contains an invalid character.
        """
        if len(last_name) > _MAX_LENGTH:
            raise InvalidLastNameError(last_name)

        if re.compile(_INVALID_CHARS).search(last_name):
            raise InvalidLastNameError(last_name)

        return last_name

    @db.validates('email')
    def validate_email(self, key: str, email: str):
        """Check that an email is unique seems valid.

        Args:
            key (str): Dict key corresponding to the field being validated.
            email (str): Value provided to field.

        Return:
            str: Email if valid.

        Raises:
            `InvalidEmailError`: If the given email does not have exactly one '@' and a '.' after the '@'.
            `DuplicateEmailError`: If a user with the given email already exists.
        """
        # REGEX notes:
        #
        # ^@ = any char except @
        # \ = inhibit the specialness of character (aka escape character)
        # https://developers.google.com/edu/python/regular-expressions

        if not re.compile(r'[^@]+@[^@]+\.[^@]+').match(email):
            raise InvalidEmailError(email)

        if self.find_by_email(email):
            raise DuplicateEmailError(email)

        return email

    @staticmethod
    def find_by_id(user_id: int) -> UserType:
        """Look up a `User` by id.

        Args:
            id (int): id to match.

        Returns:
            User with the given id if found, None if not found.

        See:
        https://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.get
        """
        return db.session.query(User).get(user_id)

    @staticmethod
    def find_by_email(email: str) -> UserType:
        """Look up a `User` by email.

        Args:
            email (str): email to match.

        Returns:
            `User` with the given email if found, None if not found.
        """
        return db.session.query(User).filter(User.email == email).first()

    def __repr__(self) -> str:
        """Return a string representation of this `User`."""
        return f'TODO'
Esempio n. 6
0
class Passenger(AbstractModelBase):
    """Data access object providing a static interface to a Passenger table."""
    __tablename__ = models.tables.PASSENGER

    # Column Attributes
    user_id = db.Column(
        db.Integer,
        db.ForeignKey(models.tables.USER + '.id', ondelete='CASCADE'))
    ride_id = db.Column(
        db.Integer,
        db.ForeignKey(models.tables.RIDE + '.id', ondelete='CASCADE'))
    status_id = db.Column(db.Integer,
                          db.ForeignKey(models.tables.STATUS + '.id'))

    # Relationship Attributes
    db.UniqueConstraint('user_id', 'ride_id', 'status_id')
    db.relationship('User',
                    uselist=False,
                    backref=db.backref('passenger', passive_deletes=True),
                    lazy='dynamic',
                    passive_deletes=True)
    db.relationship('Ride',
                    uselist=False,
                    backref=db.backref('passenger', passive_deletes=True),
                    lazy='dynamic',
                    passive_deletes=True)
    db.relationship('Status', uselist=False, lazy='dynamic')

    def update(self, new_status: int):
        """Update a passenger status by creating a new row.

        Args:
            new_status (int): id of new status.
        """
        # This should create a new row.
        pass

    @staticmethod
    def find_by_id(id: int) -> PassengerType:  # pylint: disable=C0103
        """Look up a `Passenger` by id.

        Args:
            id (int): id to match.

        Returns:
            `Passenger`s associated with the given id if found.
        """
        return db.session.query(Passenger).filter(Passenger.id == id).first()

    @staticmethod
    def find_by_ride_id(ride_id: int) -> List[PassengerType]:
        """Look up a `Passenger` by ride_id.

        Args:
            ride_id (int): id to match.

        Returns:
            `Passenger`s associated with the given ride_id if found.
        """
        return db.session.query(Passenger).filter(Passenger.ride_id == ride_id)

    @staticmethod
    def find_by_user_id(user_id: int) -> List[PassengerType]:
        """Look up a `Passenger` by user_id.

        Args:
            ride_id (int): id to match.

        Returns:
            `Passenger`s associated with the given user_id if found.
        """
        return db.session.query(Passenger).filter(Passenger.user_id == user_id)

    @staticmethod
    def find_by_status_id(status_id: int) -> List[PassengerType]:
        """Look up a `Passenger` by status_id.

        Args:
            status_id (int): id to match.

        Returns:
            `Passenger`s associated with the given status_id if found.
        """
        return db.session.query(Passenger).filter(
            Passenger.status_id == status_id)
Esempio n. 7
0
class Ride(AbstractModelBase):
    """Data access object providing a static interface to a Ride table."""
    __tablename__ = models.tables.RIDE

    # Column Attributes
    #actual_departure_time and departure_date were originally db.DateTime
    #but changed to db.String for the purpose of just getting this f*****g thing to work
    #
    actual_departure_time = db.Column(db.DateTime)
    departure_date = db.Column(db.DateTime)
    capacity = db.Column(db.Integer)
    time_range_id = db.Column(db.Integer,
                              db.ForeignKey(models.tables.TIME_RANGE + '.id'),
                              nullable=False)
    driver_id = db.Column(db.Integer,
                          db.ForeignKey(models.tables.USER + '.id'),
                          nullable=False)
    start_location_id = db.Column(db.Integer,
                                  db.ForeignKey(models.tables.LOCATION +
                                                '.id'),
                                  nullable=False)
    destination_id = db.Column(db.Integer,
                               db.ForeignKey(models.tables.LOCATION + '.id'),
                               nullable=False)

    # Relationship Attributes
    time_range = db.relationship(TimeRange)
    driver = db.relationship(User, backref='drives', lazy=True)
    passengers = db.relationship(Passenger, cascade="all, delete-orphan")
    start_location = db.relationship(Location,
                                     foreign_keys=[start_location_id])
    destination = db.relationship(Location, foreign_keys=[destination_id])

    @db.validates('capacity')
    def validate_capacity(self, key: str, capacity: str):
        """Check that capacity is valid.

        Args:
            capacity (str): Value provided to field.

        Return:
            int: capacity if valid.

        Raises:
            `InvalidCapacityError`: If the given capacity is not an int or greater than max.
        """
        if int(capacity) > _MAX_CAPACITY:
            raise InvalidCapacityError(capacity)

        if not str(capacity).isdigit():
            raise InvalidCapacityError(capacity)

        return int(capacity)

    # @db.validates('time_range')
    # TODO

    # @db.validates('driver')
    # TODO

    # I actually don't know if this is supposed to be validating 'start_location' or 'start_location_id'.
    @db.validates('start_location')
    def validate_start_location(self, key: str, start_location: str):
        """Check that a start location is valid.

        Args:
            start_location (str): Value provided to field.

        Return:
            int: start location id if valid.
        """
        # TODO
        return start_location

    # See start_location
    # @db.validates('destination')
    # TODO

    @staticmethod
    def find_by_id(ride_id: int) -> RideType:
        """Look up a `Ride` by id.

        Args:
            id (int): id to match.

        Returns:
            Ride with the given id if found.
        """
        return db.session.query(Ride).filter(Ride.id == ride_id).first()

    @staticmethod
    def find_by_departure_date(departure_date: datetime) -> List[RideType]:
        """Look up a `Ride` by departure date.

        Args:
            departure_date (datetime): id to match.

        Returns:
            `Ride`s associated with the given departure date if found.
        """
        return db.session.query(Ride).filter(
            Ride.departure_date == departure_date)

    @staticmethod
    def find_by_actual_departure_time(
            actual_departure_time: datetime) -> List[RideType]:
        """Look up a `Ride` by actual departure time.

        Args:
            actual_departure_time (datetime): id to match.

        Returns:
            `Ride`s associated with the given actual departure time if found.
        """
        return db.session.query(Ride).filter(
            Ride.actual_departure_time == actual_departure_time)

    @staticmethod
    def find_by_time_range_id(time_range_id: int) -> List['Ride']:
        """Look up a `Ride` by time_range_id.

        Args:
            time_range_id (int): id to match.

        Returns:
            `Ride`s associated with the given time_range_id if found.
        """
        return db.session.query(Ride).filter(
            Ride.time_range_id == time_range_id)

    @staticmethod
    def find_by_driver_id(driver_id: int) -> List['Ride']:
        """Look up a `Ride` by driver_id.

        Args:
            driver_id (int): id to match.

        Returns:
            `Ride`s associated with the given driver_id if found.
        """
        return db.session.query(Ride).filter(Ride.driver_id == driver_id)

    @staticmethod
    def find_by_start_location_id(start_location_id: int) -> List[RideType]:
        """Look up a `Ride` by start_location_id.

        Args:
            start_location_id (int): id to match.

        Returns:
            `Ride`s associated with the given start_location_ idif found.
        """
        return db.session.query(Ride).filter(
            Ride.start_location_id == start_location_id)

    @staticmethod
    def find_by_destination_id(destination_id: int) -> List[RideType]:
        """Look up a `Ride` by destination_id.

        Args:
            destination (int): id to match.

        Returns:
            `Ride`s associated with the given destination_id if found.
        """
        return db.session.query(Ride).filter(
            Ride.destination_id == destination_id)

    def __repr__(self) -> str:
        """Return a string representation of this `Ride`."""
        return f'TODO'