def get(self):
        point_categories = PointCategory.query(ancestor=PointCategory.root_key())
        out = {}
        for p in point_categories:
            if p.parent is None:
                out[p.name] = {
                    "name": p.name,
                    "baby_requirement": p.baby_requirement,
                    "member_requirement": p.member_requirement,
                    "sub_categories": [],
                }
                invalid_keys = []
                for key in p.sub_categories:
                    sub = key.get()

                    # The key in sub_categories is no longer valid
                    if sub is None:
                        invalid_keys.append(key)
                    else:
                        sub_cat = {
                            "name": sub.name,
                            "baby_requirement": sub.baby_requirement,
                            "member_requirement": sub.member_requirement,
                        }
                        out[p.name]['sub_categories'].append(sub_cat)

                for key in invalid_keys:
                    p.sub_categories.remove(key)
                if invalid_keys:
                    p.put()

        return jsonify(**out)
    def get(self):
        """ Gets all events that have been created.

        URL Args:
            category (str): The category you want the events for. If category
                is 'all', then this will return all events. Any sub_categories
                will also be queried for events.
        """
        category = request.args.get("category", "all")
        if category == 'all':
            events = Event.query(ancestor=Event.root_key())
        else:
            category = PointCategory.get_from_name(category)
            if category is None:
                response = jsonify(message="Category does not exist")
                response.status_code = 404
                return response

            keys = []
            keys.append(category.key)
            keys.extend(category.sub_categories)

            events = Event.query(Event.point_category.IN(keys), ancestor=Event.root_key())

        events = events.order(Event.date)

        out = {'events': []}
        for event in events:
            out['events'].append({
                "name": event.name,
                "date": event.date.strftime('%m/%d/%Y'),
                "point-category": event.point_category.get().name,
            })

        return jsonify(**out)
    def post(self):
        """ Creates a new event. """
        data = request.form

        # Don't allow duplicate events
        event = Event.get_from_name(data['name'])
        if event is not None:
            response = jsonify(message="Duplicate resource")
            response.status_code = 409
            return response

        # Get the point category by name
        point_category = PointCategory.get_from_name(data['point-category'])
        if point_category is None:
            raise Exception("Unknonwn point category: " + data['point-category'])

        new_event = Event(parent=Event.root_key())
        new_event.date = datetime.strptime(data['date'], "%Y-%m-%d")
        new_event.name = data['name']
        new_event.point_category = point_category.key
        new_event.put()

        # Make sure there are point records for this event
        new_event.populate_records()

        response = jsonify()
        response.status_code = 201
        response.headers['location'] = "/api/events/" + new_event.name.replace(" ", "")
        return response
    def delete(self, name):
        category = PointCategory.get_from_name(name)
        if category is None:
            response = jsonify(message="Resource does not exist")
            response.status_code = 404
            return response

        category.key.delete();
    def reshape_categories(cat_info):
        """ Reshape flat list of catory information to include structure.

        Args:
            cat_info (dict): A flat dictionary of all categories, their point
                requirements, and their points earned.

        Returns:
            A structured dictionary with each category, its sub-categoires,
            points required, and points earned.
        """
        out = {}

        # TODO (phillip): this code is probably duplicated from the PointCategoryListAPI
        point_categories = PointCategory.query(ancestor=PointCategory.root_key())
        for cat in point_categories:
            if cat.parent is None:
                out[cat.name] = {
                    "required": cat_info["categories"][cat.name]["required"],
                    "received": cat_info["categories"][cat.name]["received"],
                    "level": 1,
                    "sub_categories": {},
                }

                invalid_keys = []
                for key in cat.sub_categories:
                    sub = key.get()

                    # The key in sub_categories is no longer valid
                    if sub is None:
                        invalid_keys.append(key)
                    else:
                        sub_cat = {
                            "required": cat_info["categories"][sub.name]["required"],
                            "received": cat_info["categories"][sub.name]["received"],
                            "level": 2,
                        }
                        out[cat.name]["sub_categories"][sub.name] = sub_cat

                for key in invalid_keys:
                    cat.sub_categories.remove(key)
                if invalid_keys:
                    cat.put()

        return out
    def get(self, name):
        category = PointCategory.get_from_name(name)
        if category is None:
            response = jsonify(message="Resource does not exist")
            response.status_code = 404
            return response

        sub_categories = []
        for cat in category.sub_categories:
            sub_categories.append(cat.get().name)

        data = {
            "sub_categories": sub_categories,
            "name": category.name,
        }
        return jsonify(**data)
    def patch(self, name):
        data = request.form
        baby_requirement = data.get('baby_requirement', None)
        member_requirement = data.get('member_requirement', None)

        category = PointCategory.get_from_name(name)
        if baby_requirement is not None:
            category.baby_requirement = int(baby_requirement)

        if member_requirement is not None:
            category.member_requirement = int(member_requirement)
        category.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
        return response
    def post(self):
        """ Creates a new point category or updates a duplicate one

        If this function is given a point category with the same name as one
        that already exists, it will update the existing point category. It
        handles removing and adding necessary keys of parent categories.
        """
        data = request.form
        new_category = None
        new_key = None

        # TODO (phillip): we shouldn't put anything in the datastore if there is an error.
        # Therefore, we should wait until the end of the function to call a
        # put if possible.

        # Check to see if the category already exists
        for category in PointCategory.query():
            if category.name == data['name']:
                new_category = category
                new_key = new_category.key

                parent = new_category.parent
                if parent is not None:
                    # Update old parent sub categories
                    parent.sub_categories.remove(new_key)
                    parent.put()

        # If the category doesn't exist, create a new one
        if not new_category:
            new_category = PointCategory(parent=PointCategory.root_key())
            new_category.name = data['name']
            new_key = new_category.put()

        # If necessary, add the proper key to the parent category
        parent = data.get('parent', None)
        # TODO (phillip): Should I really also be checking for "none"? Is there a
        # better way?
        if parent is not None and parent != "none":
            parent = PointCategory.get_from_name(parent)
            parent.sub_categories.append(new_key)
            parent.put()

        response = jsonify()
        response.status_code = 201
        response.headers['location'] = "/api/point-categories/" + new_category.name.replace(" ", "")
        return response
    def put(self, event):
        data = request.form

        new_event_name = data['name']
        dup_event = Event.get_from_name(new_event_name)
        # Don't allow duplicate events
        if dup_event is not None and new_event_name.replace(" ", "") != event.replace(" ", ""):
            response = jsonify(message="Duplicate resource")
            response.status_code = 409
            return response

        event = Event.get_from_name(event)
        if event is None:
            # TODO (phillip): this code is duplicated, maybe make some sort of default
            # handler that can be called for any resource that doesn't exist?
            response = jsonify(message="Resource does not exist")
            response.status_code = 404
            return response

        # Get the point category by name
        point_category = PointCategory.get_from_name(data['point-category'])
        if point_category is None:
            raise Exception("Unknonwn point category: " + data['point-category'])

        records = PointRecord.query(PointRecord.event_name == event.name)
        for record in records:
            record.event_name = data['name']
            record.put()

        event.name = data['name']
        event.date = datetime.strptime(data['date'], "%Y-%m-%d")
        event.point_category = point_category.key
        event.put()

        response = jsonify()
        response.status_code = 201
        response.headers['location'] = "/api/events/" + event.name.replace(" ", "")
        return response
    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)