def post(self, org_id, location_id, role_id, schedule_id): parser = restful.reqparse.RequestParser() parser.add_argument("user_id", type=int, required=True) parser.add_argument("preference", type=str, required=True) parameters = parser.parse_args(strict=True) # Check and see if there is existing preference p = Preference.query.filter( Preference.user_id == parameters.get("user_id"), Preference.schedule_id == schedule_id).first() if p is not None: return { "message": "User already has preference - use a patch method to modify it." }, 400 try: preference = json.loads(parameters.get("preference")) except: return {"message": "Unable to parse preference json body"}, 400 if preference is None: return {"message": "Unable to parse preference json body"}, 400 if not verify_days_of_week_struct(preference, True): return {"message": "preference is improperly formatted"}, 400 user_id = parameters.get("user_id") # verify user_id is valid to add assocs = Role.query.get(role_id).members ok = False for assoc in assocs: if user_id == assoc.user_id: ok = True if not ok: return {"message": "User not in role"}, 400 p = Preference( preference=json.dumps(preference), user_id=user_id, schedule_id=schedule_id, ) db.session.add(p) try: db.session.commit() g.current_user.track_event("created_preference") return marshal(p, preference_fields), 201 except: abort(500)
def patch(self, org_id, location_id, role_id, schedule_id, user_id): parser = reqparse.RequestParser() parser.add_argument("preference", type=str, required=True) changes = parser.parse_args(strict=True) # Filter out null values changes = dict((k, v) for k, v in changes.iteritems() if v is not None) p = Preference.query.filter( Preference.user_id == user_id, Preference.schedule_id == schedule_id).first() if p is None: abort(404) if changes.get("preference") is not None: try: preference = json.loads(changes.get("preference")) except: return {"message": "Unable to parse preference json body"}, 400 if preference is None: return {"message": "Unable to parse preference json body"}, 400 if not verify_days_of_week_struct(preference, True): return {"message": "preference is improperly formatted"}, 400 changes["preference"] = preference for change, value in changes.iteritems(): if change == "preference": value = json.dumps(value) if value is not None: try: setattr(p, change, value) db.session.commit() except Exception as exception: db.session.rollback() current_app.logger.exception(str(exception)) abort(400) g.current_user.track_event("updated_preference") return changes
def post(self, org_id, location_id, role_id): """ Add a user as a worker either by user id or email """ role = Role.query.get_or_404(role_id) org = Organization.query.get_or_404(org_id) parser = reqparse.RequestParser() parser.add_argument("email", type=str) parser.add_argument("id", type=int) parser.add_argument("min_hours_per_workweek", type=int, required=True) parser.add_argument("max_hours_per_workweek", type=int, required=True) parser.add_argument("name", type=str) parser.add_argument("internal_id", type=str) parser.add_argument("working_hours", type=str) parameters = parser.parse_args(strict=True) if parameters.get("id") is not None: user = User.query.get_or_404(parameters.get("id")) elif parameters.get("email") is not None: email = parameters.get("email") # Check if valid email if "@" not in email: abort(400) # Check if user has email user = User.query.filter_by(email=email.lower().strip()).first() # otherwise invite by email if user is None: user = User.create_and_invite(email, parameters.get("name"), org.name) else: return {"message": "unable to identify user"}, 400 # get min/max workweek values min_half_hours_per_workweek = parameters.get( "min_hours_per_workweek") * 2 max_half_hours_per_workweek = parameters.get( "max_hours_per_workweek") * 2 if min_half_hours_per_workweek > max_half_hours_per_workweek: return { "message": "min_hours_per_workweek cannot be greater than max_hours_per_workweek" }, 400 if not (0 <= min_half_hours_per_workweek <= 336): return { "message": "min_hours_per_workweek cannot be less than 0" }, 400 if not (0 <= max_half_hours_per_workweek <= 336): return { "message": "max_hours_per_workweek cannot be greater than 168" }, 400 # check if the user already is in the role membership = RoleToUser.query.filter_by(role_id=role_id).filter_by( user_id=user.id).first() if membership: if membership.archived == False: return {"message": "user already in role"}, 400 membership.archived = False else: membership = RoleToUser() membership.user = user membership.role = role membership.min_half_hours_per_workweek = min_half_hours_per_workweek membership.max_half_hours_per_workweek = max_half_hours_per_workweek # internal_id in post? I'll allow it if parameters.get("internal_id") is not None: membership.internal_id = parameters["internal_id"] if parameters.get("working_hours") is not None: try: working_hours = json.loads(parameters.get("working_hours")) except: return { "message": "Unable to parse working hours json body" }, 400 if working_hours is None: return { "message": "Unable to parse working hours json body" }, 400 if not verify_days_of_week_struct(working_hours, True): return { "message": "working hours is improperly formatted" }, 400 membership.working_hours = json.dumps(working_hours) try: db.session.commit() except Exception as exception: db.session.rollback() current_app.logger.exception(str(exception)) abort(400) location = Location.query.get(location_id) organization = Organization.query.get(org_id) alert_email( user, "You have been added to %s on Staffjoy" % organization.name, "%s is using Staffjoy to manage its workforce, and you have been added to the team <b>%s</b> at the <b>%s</b> location." % ( organization.name, role.name, location.name, )) data = {} data.update(marshal(user, user_fields)) data.update(marshal(membership, role_to_user_fields)) g.current_user.track_event("added_role_member") return data, 201
def patch(self, org_id, location_id, role_id, schedule_id): schedule = Schedule2.query.get_or_404(schedule_id) org = Organization.query.get_or_404(org_id) parser = reqparse.RequestParser() parser.add_argument("demand", type=str) parser.add_argument("state", type=str) parser.add_argument("min_shift_length_hour", type=int) parser.add_argument("max_shift_length_hour", type=int) changes = parser.parse_args(strict=True) # Filter out null values changes = dict((k, v) for k, v in changes.iteritems() if v is not None) original_state = schedule.state if len(changes) == 0: return {"message": "No valid changes detected"}, 400 # schedule can only be modified from initial or unpublished state if not sudo if not g.current_user.is_sudo(): if original_state not in ["initial", "unpublished"]: return { "message": "You are not able to modify a schedule from its current state." }, 400 if "min_shift_length_hour" in changes: min_shift_length_half_hour = changes["min_shift_length_hour"] * 2 else: min_shift_length_half_hour = schedule.min_shift_length_half_hour if "max_shift_length_hour" in changes: max_shift_length_half_hour = changes["max_shift_length_hour"] * 2 else: max_shift_length_half_hour = schedule.max_shift_length_half_hour # now verification # NOTE that if we choose to support lengths of 0, these 1st two checks will break # because None and 0 get evalulated as the same if bool(min_shift_length_half_hour) != bool( max_shift_length_half_hour): return { "message": "min_shift_length_hour and max_shift_length_hour most both be defined" }, 400 if min_shift_length_half_hour and max_shift_length_half_hour: if min_shift_length_half_hour > max_shift_length_half_hour: return { "message": "min_shift_length_hour cannot be greater than max_shift_length_hour" }, 400 if min_shift_length_half_hour: if not (1 <= min_shift_length_half_hour <= 46): return { "message": "min_shift_length_hour must be between 1 and 24" }, 400 if max_shift_length_half_hour: if not (1 <= max_shift_length_half_hour <= 46): return { "message": "max_shift_length_hour must be between 1 and 24" }, 400 if "min_shift_length_hour" in changes: del changes["min_shift_length_hour"] changes["min_shift_length_half_hour"] = min_shift_length_half_hour if "max_shift_length_hour" in changes: del changes["max_shift_length_hour"] changes["max_shift_length_half_hour"] = max_shift_length_half_hour if "demand" in changes: # admins can only modify demand in the unpublished state if not g.current_user.is_sudo(): if changes.get("state", schedule.state) not in [ "unpublished", "chomp-queue" ]: return { "message": "Admins can only modify demand when the schedule is in the unpublished state." }, 400 # demand can be set to None when it is sent down without a value in the request # (not "") will resolve to True, which we consider None - assume json for all other cases if not changes["demand"]: changes["demand"] = None else: try: demand = json.loads(changes.get("demand")) except: return {"message": "Unable to parse demand json body"}, 400 if demand is None or not isinstance(demand, dict): return {"message": "Unable to parse demand json body"}, 400 # Check that days of week are right if not verify_days_of_week_struct(demand): return {"message": "demand is improperly formatted"}, 400 try: changes["demand"] = json.dumps(demand) except Exception as exception: return {"message": "Unable to parse demand json body"}, 400 g.current_user.track_event("updated_demand") if "state" in changes: state = changes.get("state") if state == original_state: return { "message": "Schedule is already in state %s." % state }, 400 if state not in [ "unpublished", "chomp-queue", "mobius-queue", "published" ]: return { "message": "State can only be updated to 'unpublished', 'chomp-queue', 'mobius-queue' or 'done'." }, 400 if not org.active: return { "message": "This organization must be active for a state change" }, 400 if state == "chomp-queue": if not changes.get("min_shift_length_half_hour", schedule.min_shift_length_half_hour): return { "message": "min_shift_length_hour must be set for chomp queue" }, 400 if not changes.get("max_shift_length_half_hour", schedule.max_shift_length_half_hour): return { "message": "max_shift_length_hour must be set for chomp queue" }, 400 if original_state not in ["unpublished", "chomp-processing"]: return {"message": "This state change is not allowed"}, 400 # reset timing measurements - although they will soon be reset, the monitoring timing # may be inaccurate for the duration of calculation (e.g. a requeue) changes["chomp_start"] = None changes["chomp_end"] = None if not g.current_user.is_sudo(): g.current_user.track_event("chomp_schedule_calculation") schedule.transition_to_chomp_queue() elif state == "published": if original_state not in ["unpublished", "mobius-processing"]: return {"message": "This state change is not allowed"}, 400 schedule.transition_to_published() if not g.current_user.is_sudo(): g.current_user.track_event("published_schedule") elif state == "mobius-queue": if original_state not in ["unpublished", "mobius-processing"]: return {"message": "This state change is not allowed"}, 400 # reset timing measurements - although they will soon be reset, the monitoring timing # may be inaccurate for the duration of calculation (e.g. a requeue) changes["mobius_start"] = None changes["mobius_end"] = None schedule.transition_to_mobius_queue() elif state == "unpublished": if original_state not in ["initial", "chomp-processing"]: return { "message": "Schedule cannot be set to unpublished from its current state" } schedule.transition_to_unpublished() for change, value in changes.iteritems(): try: setattr(schedule, change, value) db.session.commit() except Exception as exception: db.session.rollback() current_app.logger.exception(str(exception)) abort(400) Schedules2Cache.delete(role_id) return changes
def patch(self, org_id, location_id, role_id, user_id): parser = reqparse.RequestParser() parser.add_argument("min_hours_per_workweek", type=int) parser.add_argument("max_hours_per_workweek", type=int) parser.add_argument("internal_id", type=str) parser.add_argument("archived", type=inputs.boolean) parser.add_argument("activateReminder", type=inputs.boolean) parser.add_argument("working_hours", type=str) # Filter out null values changes = parser.parse_args(strict=True) changes = dict((k, v) for k, v in changes.iteritems() if v is not None) rtu = RoleToUser.query.filter_by(user_id=user_id, role_id=role_id).first() if rtu is None: abort(404) if "archived" in changes: if not g.current_user.is_sudo(): return { "message": "You do not have permission to modify 'archived'." }, 401 role = Role.query.get_or_404(role_id) if role.archived: return {"message": "The parent role is archived."}, 400 elif rtu.archived: abort(400) # activation email reminder - it can't be committed to this RTU model though if "activateReminder" in changes: user = User.query.get_or_404(user_id) org = Organization.query.get_or_404(org_id) if user.active: return {"message": "This user is already active"}, 400 user.send_activation_reminder(user, org.name) del changes["activateReminder"] # extract workweek limits and convert into half hour if "min_hours_per_workweek" in changes: min_half_hours_per_workweek = changes["min_hours_per_workweek"] * 2 else: min_half_hours_per_workweek = rtu.min_half_hours_per_workweek if "max_hours_per_workweek" in changes: max_half_hours_per_workweek = changes["max_hours_per_workweek"] * 2 else: max_half_hours_per_workweek = rtu.max_half_hours_per_workweek # some verification if min_half_hours_per_workweek > max_half_hours_per_workweek: return { "message": "min_hours_per_workweek cannot be greater than max_hours_per_workweek" }, 400 if not (0 <= min_half_hours_per_workweek <= 336): return { "message": "min_hours_per_workweek cannot be less than 0" }, 400 if not (0 <= max_half_hours_per_workweek <= 336): return { "message": "max_hours_per_workweek cannot be greater than 168" }, 400 # the proper db term must be submitted if it is intended to be changed if "min_hours_per_workweek" in changes: del changes["min_hours_per_workweek"] changes[ "min_half_hours_per_workweek"] = min_half_hours_per_workweek if "max_hours_per_workweek" in changes: del changes["max_hours_per_workweek"] changes[ "max_half_hours_per_workweek"] = max_half_hours_per_workweek if changes.get("working_hours") is not None: try: working_hours = json.loads(changes.get("working_hours")) except: return { "message": "Unable to parse working hours json body" }, 400 if working_hours is None: return { "message": "Unable to parse working hours json body" }, 400 if not verify_days_of_week_struct(working_hours, True): return { "message": "working hours is improperly formatted" }, 400 g.current_user.track_event("modified_working_hours") for change, value in changes.iteritems(): if value is not None: try: setattr(rtu, change, value) db.session.add(rtu) db.session.commit() except Exception as exception: db.session.rollback() current_app.logger.exception(str(exception)) abort(400) g.current_user.track_event("modified_role_member") return changes