class Message(db.Model): """An individual message ("warble").""" __tablename__ = 'messages' id = db.Column( db.Integer, primary_key=True, ) text = db.Column( db.String(140), nullable=False, ) timestamp = db.Column( db.DateTime, nullable=False, default=datetime.utcnow(), ) user_id = db.Column( db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, ) user = db.relationship('User') def __repr__(self): return f"<Message #{self.id}: u_id={self.user_id}>"
class Delivery(db.Model): """A single delivery""" __tablename__ = "deliveries" id = db.Column(db.Integer, autoincrement=True, primary_key=True) driver_id = db.Column(db.Integer, db.ForeignKey( 'users.id', ondelete='CASCADE')) driver = db.relationship('User', backref='deliveries') orders = db.relationship('Order', backref='delivery') @classmethod def save_delivery(cls, delivery, driver_id, api_id): date = delivery['date'] store = delivery['store'] orders = delivery['orders'] old_deliveries = set() delivery = Delivery(driver_id=driver_id) db.session.add(delivery) db.session.commit() ###### # A little wasteful, good optimization possibility, I think for order in orders: # maybe change this to a hash of the phone and address customer_id = Customer.make_id_from_phone(order['phone']) customer = Customer.create_or_get( id=customer_id) # name = order['name'].split(' ')[0] db_order = Order.get(order['num'], date, store, api_id) if db_order: old_deliveries.add(db_order.del_id) db_order.del_id = delivery.id db_order.driver_id = driver_id db_order.cust_id = customer_id else: db_order = Order( num=order['num'], date=date, store=store, api_id=api_id, del_id=delivery.id, cust_id=customer_id, driver_id=driver_id) db.session.add(db_order) db.session.commit() Delivery.dispose_of_empty_dels(old_deliveries) return delivery @classmethod def dispose_of_empty_dels(cls, del_ids): for del_id in del_ids: d = cls.query.get(del_id) if len(d.orders) == 0: db.session.delete(d) db.session.commit()
class Like(db.Model): """Mapping user likes to warbles.""" __tablename__ = 'likes' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='cascade')) message_id = db.Column(db.Integer, db.ForeignKey('messages.id', ondelete='cascade'), unique=True)
class Note(db.Model): """A single note""" __tablename__ = "notes" cust_id = db.Column(db.String(32), db.ForeignKey('customers.id', ondelete='CASCADE'), primary_key=True) driver_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True) note = db.Column(db.Text, nullable=False) driver = db.relationship('User', backref='notes') customer = db.relationship('Customer', backref='notes') def serialize(self): return { "cust_id": self.cust_id, "driver_id": self.driver_id, "note": self.note } @classmethod def get(cls, cust_id, driver_id): return cls.query.get((cust_id, driver_id)) @classmethod def delete(cls, cust_id, driver_id): note = cls.get(cust_id=cust_id, driver_id=driver_id) if note: db.session.delete(note) db.session.commit() return True else: return False @classmethod def create_or_update_note(cls, cust_id, driver_id, new_note): try: note = cls.get(cust_id=cust_id, driver_id=driver_id) if note: note.note = new_note else: note = cls(cust_id=cust_id, driver_id=driver_id, note=new_note) db.session.add(note) db.session.commit() return note except: db.session.rollback() return False
class Follow(db.Model): """Connection of a follower <-> followed_user.""" __tablename__ = 'follows' user_being_followed_id = db.Column( db.Integer, db.ForeignKey('users.id', ondelete="cascade"), primary_key=True, ) user_following_id = db.Column( db.Integer, db.ForeignKey('users.id', ondelete="cascade"), primary_key=True, )
class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True) def __init__(self, username): self.username = username def __repr__(self): return "<User %r>" % self.username def json(self): return {"username": self.username} # if __name__ == "__main__": # db.create_all() # db.session.commit()
class Customer(db.Model): """A single customer""" __tablename__ = "customers" id = db.Column(db.String(32), primary_key=True) name = db.Column(db.Text) phone = db.Column(db.Text) address = db.Column(db.Text) def serialize(self): return { "id": self.id, "name": self.name, "phone": self.phone, "address": self.address, "notes": [n.serialize() for n in self.notes] } def has_note_from(self, user_id): for note in self.notes: if note.driver_id == user_id: return True return False @classmethod def make_id_from_phone(cls, phone): return hashlib.md5(phone.encode()).hexdigest() @classmethod def create_or_get(cls, id, name=None, address=None, phone=None): customer = cls.query.get(id) if customer: # update customer if name: customer.name = name if address: customer.address = address if phone: customer.phone = phone else: customer = cls(id=id, name=name, address=address, phone=phone) db.session.add(customer) db.session.commit() return customer
class WeekCode(db.Model): """represets one week of data(so we don't write the same week twice)""" __tablename__ = "week_codes" code = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey( 'users.id', ondelete='CASCADE'), primary_key=True) @classmethod def add(cls, code, user_id): """add a code to the db""" db_entry = cls(code=code, user_id=user_id) db.session.add(db_entry) try: db.session.commit() return True except: return False @classmethod def get_codes_for_user(cls, user_id, limit = 8): """gets the codes for a specified user. defaults to limiting to 3""" week_codes = cls.query.filter_by(user_id=user_id).order_by( cls.code.desc()).limit(limit).all() return [w.code for w in week_codes]
class User(db.Model): """User in the system.""" __tablename__ = 'users' id = db.Column( db.Integer, primary_key=True, ) email = db.Column( db.Text, nullable=False, unique=True, ) username = db.Column( db.Text, nullable=False, unique=True, ) image_url = db.Column( db.Text, default="/static/images/default-pic.png", ) header_image_url = db.Column(db.Text, default="/static/images/warbler-hero.jpg") bio = db.Column(db.Text, ) location = db.Column(db.Text, ) password = db.Column( db.Text, nullable=False, ) messages = db.relationship('Message') followers = db.relationship( "User", secondary="follows", primaryjoin=(Follow.user_being_followed_id == id), secondaryjoin=(Follow.user_following_id == id)) following = db.relationship( "User", secondary="follows", primaryjoin=(Follow.user_following_id == id), secondaryjoin=(Follow.user_being_followed_id == id)) likes = db.relationship('Message', secondary="likes") def __repr__(self): return f"<User #{self.id}: {self.username}, {self.email}>" def is_followed_by(self, other_user): """Is this user followed by `other_user`?""" found_user_list = [ user for user in self.followers if user == other_user ] return len(found_user_list) == 1 def is_following(self, other_user): """Is this user following `other_use`?""" found_user_list = [ user for user in self.following if user == other_user ] return len(found_user_list) == 1 def update_from_serial(self, d): username = d.get('username') email = d.get('email') image_url = d.get('image_url') if image_url == '': image_url = User.image_url.default.arg header_image_url = d.get('header_image_url') if header_image_url == '': header_image_url = User.header_image_url.default.arg bio = d.get('bio') location = d.get('location') if username: self.username = username if email: self.email = email if image_url: self.image_url = image_url if header_image_url: self.header_image_url = header_image_url if bio: self.bio = bio if location: self.location = location @classmethod def signup(cls, username, email, password, image_url): """Sign up user. Hashes password and adds user to system. """ hashed_pwd = bcrypt.generate_password_hash(password).decode('UTF-8') user = User( username=username, email=email, password=hashed_pwd, image_url=image_url, ) db.session.add(user) return user @classmethod def authenticate(cls, username, password): """Find user with `username` and `password`. This is a class method (call it on the class, not an individual user.) It searches for a user whose password hash matches this password and, if it finds such a user, returns that user object. If can't find matching user (or if password is wrong), returns False. """ user = cls.query.filter_by(username=username).first() if user: is_auth = bcrypt.check_password_hash(user.password, password) if is_auth: return user return False
class Order(db.Model): """A single order""" __tablename__ = "orders" id = db.Column(db.Integer, primary_key=True) num = db.Column(db.Integer) date = db.Column(db.Date) store = db.Column(db.Text) api_id = db.Column(db.Text) tip = db.Column(db.Float, server_default='0') del_id = db.Column(db.Integer, db.ForeignKey( 'deliveries.id', ondelete='CASCADE')) cust_id = db.Column(db.String(32), db.ForeignKey( 'customers.id', ondelete='SET NULL')) driver_id = db.Column(db.Integer, db.ForeignKey( 'users.id', ondelete='CASCADE')) driver = db.relationship('User', backref='orders') customer = db.relationship('Customer', backref='orders') def serialize(self): return {"id": self.id, "num": self.num, "tip": self.tip, "date": self.date, "del_id": self.del_id, "cust_id": self.cust_id, "driver_id": self.driver_id, "store": self.store, "api_id": self.api_id } def update_db(self): db.session.add(self) db.session.commit() @classmethod def get(cls, num, date, store, api_id): return cls.query.filter_by(num=num, date=date, store=store, api_id=api_id).first() @classmethod def get_orders_for_day(cls, driver_id, date=date_class.today()): """ Returns {num, tip, date, del_id, cust_id, driver_id, customer:{id, name, phone, address} """ result = db.session.query( Order, Customer).join( Customer, Customer.id == Order.cust_id).filter( Order.driver_id == driver_id, Order.date == date).all() return Order.compile_orders_and_customers(result) @classmethod def get_orders_for(cls, driver_id): """ Returns 213, 04-19-21, 8425-881-7843 """ result = db.session.query( Order, Customer).join( Customer, Customer.id == Order.cust_id).filter( Order.driver_id == driver_id).order_by(Order.date.desc(), Order.num.desc()).all() orders = Order.compile_orders_and_customers(result) orders_list = {} for order in orders: orders_for_date = orders_list.get(order['date'], []) orders_for_date.append(order) orders_list[order['date']] = orders_for_date return orders_list @classmethod def make_id_from_date_and_num(cls, date, num): return f'{date}|{num}' @ classmethod def compile_orders_and_customers(cls, order_array): return [{**order.serialize(), "customer": {**customer.serialize()}} for (order, customer) in order_array]
class Schedule(db.Model): """A single shift""" __tablename__ = "schedules" id = db.Column( db.Integer, autoincrement=True, primary_key=True) start = db.Column(db.DateTime, nullable=False) end = db.Column(db.DateTime, nullable=False) shift_type = db.Column(db.String(20)) user_id = db.Column(db.Integer, db.ForeignKey( 'users.id', ondelete='CASCADE')) def get_shift_length(self): """gets the length of the shift returns a datetime.timedelta object """ return self.end-self.start def get_shift_hours(self): """gets the length of the shift in hours """ delta = self.get_shift_length() # 60 seconds in a min, 60 mins in a hour return delta.total_seconds() / 60 / 60 @classmethod def get_future_shifts(cls, user_id): """returns a list of shifts for user_id that end on or after today""" #get a timestamp for the beginning of the day today = datetime.date.today() return cls.query.filter( Schedule.user_id == user_id, Schedule.end >= today ).order_by(Schedule.start).all() @classmethod def get_last(cls, user_id, delta=None): """returns a list of shifts for user_id that start on or before today optionally the delta param further limits the results to shits that end on or after today - delta""" #get a timestamp at the end of the day today = datetime.datetime.today() + datetime.timedelta(hours=11, minutes=59) if delta: return cls.query.filter( Schedule.user_id == user_id, Schedule.start <= today, Schedule.end >= (datetime.date.today() - delta) ).all() else: return cls.query.filter( Schedule.user_id == user_id, Schedule.start <= today ).all() @classmethod def add_from_pag(cls, schedules, user_id): """adds a list of schedules from the pag api""" #weeks that are already in the DB used_codes = WeekCode.get_codes_for_user(user_id, limit=10) for week in schedules: code = week['pag_code'] if int(code) not in used_codes: # for each week in schedules, # add the pag_code to WeekCode WeekCode.add(code, user_id) for shift in week['schedule']: # for every shift, make a new schedule db_shift = Schedule( start=shift['start'], end=shift['end'], shift_type=shift['shift_type'], user_id=user_id) db.session.add(db_shift) db.session.commit() @classmethod def add_from_demo(cls, schedules, user_id): """adds a list of schedules from the demo api""" used_codes = WeekCode.get_codes_for_user(user_id, limit=10) for week in schedules: # for each week in schedules, # add the week_code to WeekCode code = week['week_code'] if int(code) not in used_codes: WeekCode.add(code, user_id) for shift in week['schedule']: # for every shift, make a new schedule db_shift = Schedule( start=shift['start'], end=shift['end'], shift_type=shift['shift_type'], user_id=user_id) db.session.add(db_shift) db.session.commit()
class User(db.Model): """A single user""" __tablename__ = "users" id = db.Column(db.Integer, autoincrement=True, primary_key=True) name = db.Column(db.Text) email = db.Column(db.Text, nullable=False) token = db.Column(db.Text, nullable=False, unique=True) token_expiration = db.Column(db.DateTime, nullable=True) accessor = db.Column(db.Text, nullable=True) accessor_expiration = db.Column(db.DateTime, nullable=True) api_id = db.Column(db.Text, nullable=False) shifts = db.relationship('Schedule', backref='user') def make_jwt(self): #JWT payload looks like {ACCESSOR_SESSION_KEY: accessor, USER_SESSION_KEY: u.id} return jwt.encode({ ACCESSOR_SESSION_KEY: self.accessor, USER_SESSION_KEY: self.id }, SECRET_KEY, algorithm="HS256") def update_token(self, token, token_expiration): """updates a users token""" self.token = token self.token_expiration = token_expiration db.session.add(self) db.session.commit() def update_accessor(self): """updates a users accessor""" ACCESSOR_TIMEOUT_DAYS = 4 accessor = User.create_unique_accessor() self.accessor = accessor expiration = make_date_time_from_now(days=ACCESSOR_TIMEOUT_DAYS) self.accessor_expiration = expiration db.session.add(self) db.session.commit() # unneccesary # #use bcrypt to hash the accessor # hashed = bcrypt.generate_password_hash(user.accessor) # # turn bytestring into normal (unicode utf8) string # hashed_utf8 = hashed.decode("utf8") return self.accessor @classmethod def get(cls, pk): """a shortcut for the cls.query.get() function""" return cls.query.get(pk) @classmethod def authenticate(cls, user_jwt): """gets a user by its accessor""" user_data = jwt.decode(user_jwt, SECRET_KEY, algorithms="HS256") accessor = user_data[ACCESSOR_SESSION_KEY] u_id = user_data[USER_SESSION_KEY] user = cls.get(u_id) if user: if user.accessor == accessor: return user else: return False else: return False @classmethod def get_by_accessor(cls, accessor): """gets a user by its accessor""" user = cls.query.filter_by(accessor=accessor).first() return user if user else False @classmethod def delete_by_email(cls, email): """A quick way of deleting a user by email""" cls.query.filter_by(email=email).delete() db.session.commit() @classmethod def create(cls, email, token, api_id, token_expiration=None, name=None): user = User(email=email, token=token, token_expiration=token_expiration, name=email, api_id=api_id) if name: user.name = name db.session.add(user) db.session.commit() user.update_accessor() return user @classmethod def create_or_update(cls, email, token, api_id, token_expiration=None, name=None): """If a user with this email already exists, update the token (and name if given) and return the id to the caller otherwise, create the user and return the id to the caller""" user = cls.query.filter_by(email=email).first() if user: # user already exists, just update token user.token = token else: user = User(email=email, token=token, name=email, api_id=api_id) if name: user.name = name if token_expiration: user.token_expiration = token_expiration db.session.add(user) db.session.commit() user.update_accessor() return user @classmethod def create_unique_accessor(cls): accessor = secrets.token_urlsafe() while cls.get_by_accessor(accessor): accessor = secrets.token_urlsafe() return accessor