diff --git a/pittgrub/app.py b/pittgrub/app.py index 86ec27a..7fbb26e 100644 --- a/pittgrub/app.py +++ b/pittgrub/app.py @@ -24,7 +24,8 @@ ) from handlers.user import ( UserHandler, UserVerificationHandler, UserPreferenceHandler, - UserPasswordHandler, UserPasswordResetHandler + UserPasswordHandler, UserPasswordResetHandler, + UserSettingsHandler ) from handlers.notifications import NotificationHandler from handlers.events import EventImageHandler, EventTestHandler @@ -63,10 +64,11 @@ def __init__(self, debug: bool, image_store: ImageStore, static_path: str=None, endpoints = [ (r"/(/*)", MainHandler), # index (r"/health(/*)", HealthHandler), # check status - (r'/users(/*)', UserHandler), # all users - (r'/users/(\d+/*)', UserHandler), # single user + # (r'/users(/*)', UserHandler), # all users + # (r'/users/(\d+/*)', UserHandler), # single user (r'/users/activate(/*)', UserVerificationHandler), # user activation (r'/users/preferences(/*)', UserPreferenceHandler), # user preferences (food, etc) + (r'/users/settings(/*)', UserSettingsHandler), # user settings (food prefs, pantry, etc) (r'/users/admin(/*)', AdminHandler), # make user admin (r'/notifications(/*)', NotificationHandler), # handle notifications (r'/token(/*)', NotificationTokenHandler), # add notification token diff --git a/pittgrub/db/__init__.py b/pittgrub/db/__init__.py index 95cdcf4..ede1e66 100644 --- a/pittgrub/db/__init__.py +++ b/pittgrub/db/__init__.py @@ -5,7 +5,6 @@ from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.pool import NullPool from .base import Entity, ReferralStatus, UserStatus, health_check from .default import DEFAULTS @@ -68,7 +67,7 @@ }) def __bulk_insert(engine, data: Dict[str, List[Tuple[Any]]]): - schema.Base.metadata.create_all(bind=engine) + schema.Base.metadata.create_all(bind=engine) for entity, values in data.items(): # get class of entity cls = getattr(sys.modules[__name__], entity) @@ -94,10 +93,10 @@ def init(username: str, password: str, url: str, database: str, engine = create_engine(f"mysql+pymysql://{username}:{password}" f"@{url}/{database}{params}", convert_unicode=True, echo=echo, - poolclass=NullPool) + pool_recycle=1800) session = scoped_session(sessionmaker(bind=engine)) print('Inserting default data') __bulk_insert(engine, DEFAULTS) # add default data - if generate: + if generate: print('Generating test data') __bulk_insert(engine, TEST_DATA) # add test data if generate flag is set to true diff --git a/pittgrub/db/schema.py b/pittgrub/db/schema.py index e5f94c8..4a5d869 100644 --- a/pittgrub/db/schema.py +++ b/pittgrub/db/schema.py @@ -33,6 +33,8 @@ class User(Base, Entity): admin = Column('admin', BOOLEAN, nullable=False, default=False) expo_token = Column('expo_token', VARCHAR(255), nullable=True) login_count = Column('login_count', INT, nullable=False) + pitt_pantry = Column('pitt_pantry', BOOLEAN, nullable=False, default=False) + eagerness = Column('eagerness', INT, nullable=False, default=3) # mappings food_preferences = association_proxy('_user_foodpreferences', 'food_preference') @@ -42,7 +44,8 @@ class User(Base, Entity): def __init__(self, id: int=None, email: str=None, password: str=None, status: UserStatus=None, active: bool=False, disabled: bool=False, - admin: bool=False, login_count: int=0, expo_token: str=None): + admin: bool=False, login_count: int=0, expo_token: str=None, + pitt_pantry: bool=False, eagerness: int=3): self.id = id self.created = datetime.datetime.utcnow() self.email = email @@ -53,6 +56,8 @@ def __init__(self, id: int=None, email: str=None, password: str=None, self.admin = admin self.login_count = login_count self.expo_token = expo_token + self.pitt_pantry = pitt_pantry + self.eagerness = eagerness @property def valid(self): @@ -137,13 +142,42 @@ def make_admin(self): db.session.commit() db.session.refresh(self) + def update_eagerness(self, value: int): + assert 0 < value + self.eagerness = value + db.session.commit() + db.session.refresh(self) + + def set_pitt_pantry(self, status: bool): + assert status is not None + self.pitt_pantry = status + db.session.commit() + db.session.refresh(self) + + def json_info(cls) -> Dict[str, Union[bool, int, str]]: + """Get json serializable representation of account related info""" + return dict( + id=cls.id, + active=cls.active, + admin=cls.admin, + status=cls.status.name) + + def json_settings(cls) -> Dict[str, Any]: + """Get json serializable representation of user settings""" + return dict( + eagerness=cls.eagerness, + pantry=cls.pitt_pantry, + food_preferences=[f.json() for f in cls.food_preferences]) + def json(cls, deep: bool=True) -> Dict[str, Any]: json = dict( id=cls.id, email=cls.email, active=cls.active, admin=cls.admin, - status=cls.status.name + status=cls.status.name, + eagerness=cls.eagerness, + pantry=cls.pitt_pantry ) if deep: json['food_preferences'] = [f.json() for f in cls.food_preferences] diff --git a/pittgrub/handlers/user.py b/pittgrub/handlers/user.py index 4a42247..d6cfe68 100644 --- a/pittgrub/handlers/user.py +++ b/pittgrub/handlers/user.py @@ -105,6 +105,39 @@ def post(self, path): self.write_error(400, 'Missing fields') +class UserSettingsHandler(SecureHandler): + def get(self, path): + # check token + user_id = self.get_user_id() + if user_id: + user = User.get_by_id(user_id) + settings = user.json_settings() + self.success(payload=Payload(settings)) + else: + self.write_error(403, 'Authentication is required') + + def post(self, path): + user_id = self.get_user_id() + user = User.get_by_id(user_id) + if user is not None: + # decode json + data = json_decode(self.request.body) + logging.info(f'Updating settings for user {user_id}, settings {data}') + if 'food_preferences' in data: + # ensure preference ids are legit + preference_ids = [pref.id for pref in FoodPreference.get_all()] + if all(pref in preference_ids for pref in data['food_preferences']): + UserFoodPreference.update(user_id, preference_ids) + else: + fields = ", ".join(set(data['food_preferences'])-preference_ids) + self.write_error(401, f'Food preferences not foudn: {fields}') + if 'pantry' in data: + user.set_pitt_pantry(data['pantry']) + if 'eagerness' in data: + user.update_eagerness(data['eagerness']) + self.success(status=204) + + class UserPreferenceHandler(SecureHandler): def get(self, path): # check token