def post(self, user_id):
        user = UserData.get_user_from_id(user_id)
        if not user:
            raise Exception("I don't know that person")

        #TODO (phillip): The flow of this code looks more complicated and confusing than it
        # needs to be. Try to clean it up
        data = request.form
        p = None
        for exc in user.point_exceptions:
            if exc.point_category == data['point_category']:
                p = exc
        if not p:
            p = PointException()
            p.point_category = data.get('point_category', type=str)
            p.points_needed = data.get('points_needed', type=int)
            user.point_exceptions.append(p)
        else:
            p.points_needed = data.get('points_needed', type=int)
        user.put()

        response = jsonify()
        response.status_code = 201
        response.headers['location'] = "/api/users/" + user.user_id + \
                                       "/point-exceptions/" + \
                                       str(user.point_exceptions.index(p))
        return response
    def put(self, user_id):
        """ Updates the user profile information
        This method can only be called by the user being updated or an officer.
        We do not need to protect against the user changing their point
        exceptions in this method because the method does not update the point
        exceptions.
        """
        user = UserData.get_user_from_id(user_id)
        if not user:
            raise Exception("I don't know that person")

        data = request.form
        user.first_name = data["fname"]
        user.last_name = data["lname"]
        if data["active"] == "true":
            user.active = True
        else:
            user.active = False
        user.classification = data["classification"]
        user.graduation_semester = data["grad_semester"]
        user.graduation_year = data.get("grad_year", type=int)
        user.put()

        # TODO (phillip): A put request (and really any other request that creates or
        # updates an object) should return the new representation of that
        # object so clients don't need to make more API calls

        response = jsonify()
        response.status_code = 204
        # TODO (phillip): I believe only POST requests need to return this header
        response.headers["location"] = "/api/users/" + str(user.user_id)
        return response
    def get(self, user_id):
        user = UserData.get_user_from_id(user_id)
        data = {
            u'permissions': user.user_permissions,
        }

        return jsonify(**data)
    def delete(self, user_id, perm):
        user = UserData.get_user_from_id(user_id)
        if perm not in user.user_permissions:
            response = jsonify(message="Resource does not exist")
            response.status_code = 404
            return response

        user.user_permissions.remove(perm)
        user.put()
    def get(self, user_id):
        user = UserData.get_user_from_id(user_id)
        data = {
            "point_exceptions": [{
                "point_category": exc.point_category,
                "points_needed": exc.points_needed,
            } for exc in user.point_exceptions],
        }

        return jsonify(**data)
    def delete(self, user_id, index):
        user = UserData.get_user_from_id(user_id)
        if not user:
            raise Exception("I don't know that person")

        if index > len(user.point_exceptions)-1:
            response = jsonify(message="Resource does not exist")
            response.status_code = 404
            return response

        del user.point_exceptions[index]
        user.put()
    def post(self, user_id):
        user = UserData.get_user_from_id(user_id)
        data = request.form
        perm = data['permission']
        if perm not in user.user_permissions:
            # TODO (phillip): an officer could theoretically give themselves 'admin'
            # privileges using this function. They wouldn't get actual google
            # admin privileges but it would fool my permissions system
            user.user_permissions.append(perm)
        user.put()

        response = jsonify()
        response.status_code = 201
        response.headers['location'] = "/api/users/" + user.user_id + \
                                       "/permissions/" + perm
        return response
    def get(self, user_id, index):
        user = UserData.get_user_from_id(user_id)
        if not user:
            raise Exception("I don't know that person")

        if index > len(user.point_exceptions)-1:
            response = jsonify(message="Resource does not exist")
            response.status_code = 404
            return response

        exc = user.point_exceptions[index]
        data = {
            "point_category": exc.point_category,
            "points_needed": exc.points_needed,
        }

        return jsonify(**data)
    def get(self, user_id):
        user = UserData.get_user_from_id(user_id)
        data = {
            "active": user.active,
            "classification": user.classification,
            "grad_year": user.graduation_year,
            "grad_semester": user.graduation_semester,
            "fname": user.first_name,
            "lname": user.last_name,
            "permissions": user.user_permissions,
            "user_id": user.user_id,
            "point_exceptions": [
                {"point_category": exc.point_category, "points_needed": exc.points_needed}
                for exc in user.point_exceptions
            ],
        }

        return jsonify(**data)
    def get(self, user_id):
        output = {u"categories": {}}

        user = UserData.get_user_from_id(user_id)

        point_exceptions = {exc.point_category: exc.points_needed for exc in user.point_exceptions}
        categories = PointCategory.query(ancestor=PointCategory.root_key())
        for cat in categories:
            # TODO (phillip): Write tests to test the behavior when calculating requirement for
            # a category with children having a higher requirement. (i.e. max(self, children))

            # TODO (phillip): remove some of the ugly repeated code below
            # Add each category and the required number of points
            if user.is_baby():
                requirement = cat.baby_requirement
                if requirement is None:
                    requirement = 0

                sub_req_list = []
                for sub in cat.sub_categories:
                    req = sub.get().member_requirement
                    if req is None:
                        sub_req_list.append(0)
                    else:
                        sub_req_list.append(req)
                sub_req = sum(sub_req_list)
                requirement = max(requirement, sub_req)
            else:
                requirement = cat.member_requirement
                if requirement is None:
                    requirement = 0

                # TODO (phillip): add test when one of the requirements is None
                sub_req_list = []
                for sub in cat.sub_categories:
                    req = sub.get().member_requirement
                    if req is None:
                        sub_req_list.append(0)
                    else:
                        sub_req_list.append(req)
                sub_req = sum(sub_req_list)
                requirement = max(requirement, sub_req)

            if cat.name in point_exceptions:
                requirement = point_exceptions[cat.name]

            output["categories"][cat.name] = {u"required": requirement}

            output["categories"][cat.name]["received"] = 0
            if cat.parent is not None:
                output["categories"][cat.name]["level"] = 2
            else:
                output["categories"][cat.name]["level"] = 1

        # NOTE: At this point I am assuming each category has been added to
        # the output. If this is not true, the following code will fail
        # miserably.
        records = PointRecord.query()
        # username = user.username
        records = records.filter(PointRecord.username == user.username)
        for record in records:
            event = Event.get_from_name(record.event_name)
            if event is None:
                logging.error("Uknown event " + record.event_name + " requested for point record: " + str(record))
                record.key.delete()
                continue

            category = event.point_category.get()
            if record.points_earned is None:
                record.points_earned = 0
                record.put()
            output["categories"][category.name]["received"] += record.points_earned

            # Make sure to also count the points for the parent category
            if category.parent is not None:
                output["categories"][category.parent.name]["received"] += record.points_earned
                output["categories"][category.name]["level"] = 2

        output = self.reshape_categories(output)

        return jsonify(**output)