From d8c7c286b4a03da8ca56eab496cd8453e303ee37 Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Tue, 8 Apr 2014 20:58:43 -0300 Subject: [PATCH] added the group+places management, still missing tests --- doc/client/groups.rst | 4 + luncho/blueprints/groups.py | 250 ++++++++++++++++++++++++++++-------- 2 files changed, 203 insertions(+), 51 deletions(-) diff --git a/doc/client/groups.rst b/doc/client/groups.rst index ad7da13..b95c5e8 100644 --- a/doc/client/groups.rst +++ b/doc/client/groups.rst @@ -6,3 +6,7 @@ Groups are used to select which people will go to which place. .. autoflask:: luncho.server:app :blueprints: groups :undoc-endpoints: static, show_api + +.. autoflask:: luncho.server:app + :blueprints: group_users + :undoc-endpoints: static, show_api diff --git a/luncho/blueprints/groups.py b/luncho/blueprints/groups.py index 0a367e6..48bc2d6 100644 --- a/luncho/blueprints/groups.py +++ b/luncho/blueprints/groups.py @@ -12,9 +12,10 @@ from flask import jsonify from luncho.helpers import ForceJSON from luncho.helpers import auth +from luncho.server import db from luncho.server import User from luncho.server import Group -from luncho.server import db +from luncho.server import Place from luncho.exceptions import LunchoException from luncho.exceptions import ElementNotFoundException @@ -23,6 +24,10 @@ from luncho.exceptions import NewMaintainerDoesNotExistException from luncho.exceptions import UserIsNotAdminException +# ---------------------------------------------------------------------- +# Exceptions +# ---------------------------------------------------------------------- + class UserIsNotMemberException(LunchoException): """The user is not the admin of the group. @@ -39,33 +44,9 @@ class UserIsNotMemberException(LunchoException): self.message = 'User is not member of this group' -class SomeUsersNotFoundException(LunchoException): - """Some users in the add list do not exist. - - .. sourcecode:: http - - HTTP/1.1 404 Not Found - Content-Type: text/json - - { "status": "ERROR", - "message", "Some users in the add list do not exist", - "users": ["", "", ...]} - """ - def __init__(self, users=None): - super(SomeUsersNotFoundException, self).__init__() - self.status = 404 - self.message = 'Some users in the add list do not exist' - self.users = users - - def response(self): - json = {'status': 'ERROR', - 'message': self.message} - if self.users: - json['users'] = self.users - response = jsonify(json) - response.status_code = self.status - return response - +# ---------------------------------------------------------------------- +# The base group management +# ---------------------------------------------------------------------- groups = Blueprint('groups', __name__) @@ -163,10 +144,10 @@ def create_group(): id=new_group.id) -@groups.route('/', methods=['PUT']) +@groups.route('/', methods=['PUT']) @ForceJSON() @auth -def update_group(groupId): +def update_group(group_id): """*Authenticated request* Update group information. The user must be the administrator of the group @@ -197,7 +178,7 @@ def update_group(groupId): (:py:class:`AuthorizationRequiredException`) """ user = request.user - group = Group.query.get(groupId) + group = Group.query.get(group_id) if not group: raise ElementNotFoundException('Group') @@ -223,14 +204,14 @@ def update_group(groupId): return jsonify(status='OK') -@groups.route('/', methods=['DELETE']) +@groups.route('/', methods=['DELETE']) @auth -def delete_group(groupId): +def delete_group(group_id): """*Authenticated request* Delete a group. Only the administrator of the group can delete it. - :param groupId: The group Id + :param group_id: The group Id :header Authorization: Access token from `/token/`. @@ -243,7 +224,7 @@ def delete_group(groupId): (:py:class:`AuthorizationRequiredException`) """ user = request.user - group = Group.query.get(groupId) + group = Group.query.get(group_id) if not group: raise ElementNotFoundException('Group') @@ -255,19 +236,23 @@ def delete_group(groupId): return jsonify(status='OK') +# ---------------------------------------------------------------------- +# Group users +# ---------------------------------------------------------------------- + group_users = Blueprint('group_users', __name__) -@group_users.route('/users/', methods=['PUT']) +@group_users.route('/users/', methods=['PUT']) @ForceJSON(required=['usernames']) @auth -def add_users_to_group(groupId): +def add_users_to_group(group_id): """*Authenticated request* Add users to the group. Only the group administrator can add users to their groups. - :param groupId: The group Id + :param group_id: The group Id **Example request**: @@ -277,21 +262,27 @@ def add_users_to_group(groupId): :header Authorization: Access token from `/token/`. - :status 200: Success + :status 200: Success. Users that couldn't be found will be returned in + the "not_found" field. + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK", "not_found": [, , ...] } + :status 400: Request not in JSON format (:py:class:`RequestMustBeJSONException`) :status 403: User is not the group administrator (:py:class:`UserIsNotAdminException`) :status 404: User not found (via token) (:py:class:`UserNotFoundException`) - :status 404: Incomplete request, some users not found; users that couldn't - be found will return in the "missing" field. - (:py:class:`SomeUsersNotFoundException`) :status 412: Authorization required (:py:class:`AuthorizationRequiredException`) """ user = request.user - group = Group.query.get(groupId) + group = Group.query.get(group_id) if not group: raise ElementNotFoundException('Group') @@ -307,22 +298,21 @@ def add_users_to_group(groupId): continue user_obj.groups.append(group) + db.session.commit() - if unknown: - raise SomeUsersNotFoundException(unknown) - - return jsonify(status='OK') + return jsonify(status='OK', + not_found=unknown) -@group_users.route('/users/', methods=['GET']) +@group_users.route('/users/', methods=['GET']) @auth -def list_group_members(groupId): +def list_group_members(group_id): """*Authenticated request* Return a list of the users in the group. The user must be part of the group to request this list. - :parma groupId: The group Id + :param group_id: The group Id :header Authorization: Access token from `/token/`. @@ -341,10 +331,12 @@ def list_group_members(groupId): (:py:class:`UserIsNotMemberException`) :status 404: User not found (via token) (:py:class:`UserNotFoundException`) + :status 404: Group not found + (:py:class:`ElementNotFoundException`) :status 412: Authorization required (:py:class:`AuthorizationRequiredException`) """ - group = Group.query.filter_by(id=groupId).first() + group = Group.query.filter_by(id=group_id).first() if not group: raise ElementNotFoundException('Group') @@ -355,5 +347,161 @@ def list_group_members(groupId): for user in group.users: users.append({'username': user.username, 'full_name': user.fullname}) + db.session.commit() return jsonify(status='OK', users=users) + + +# ---------------------------------------------------------------------- +# Group places +# ---------------------------------------------------------------------- + +group_places = Blueprint('group_places', __name__) + + +@group_places.route('/places/', methods=['GET']) +@auth +def get_group_places(group_id): + """*Authenticated request* + + Return the list of places for the group. The user must be a member of + the group the get the list of places. + + :param group_id: The group Id + + :header Authorization: Access token from `/token/`. + + :status 200: Success + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK", "places": [ { "id": "", + "name": ""}, + ...] } + + :status 403: The user is not a member of the group + (:py:class:`UserIsNotMemberException`) + :status 404: User not found (via token) + (:py:class:`UserNotFoundException`) + :status 404: Group does not exist + (:py:class:`ElementNotFoundException`) + :status 412: Authorization required + (:py:class:`AuthorizationRequiredException`) + """ + group = Group.query.get(group_id) + if not group: + raise ElementNotFoundException('Group') + + if request.user not in group.users: + raise UserIsNotMemberException() + + places = [] + for place in group.places: + places.append({'id': place.id, + 'name': place.name}) + + return jsonify(status='OK', + places=places) + + +@group_places.route('/places/', methods=['POST']) +@ForceJSON(required=['places']) +@auth +def group_add_places(group_id): + """*Authenticated request* + + Add a list of places to the group. The user must be the admin of the group + to add a place; the place must belong to one of the group members to be + able to be added to the group. + + :param group_id: The group Id + + :header Authorization: Access token from `/token/`. + + :status 200: Success. If there are any places that do not belong to group + members, those will be returned in the "rejected" field; places that + don't exist will be returned in the "not_found" field. + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK", + "rejected": [, , ...], + "not_found": [, , ...] } + + :status 400: Request not in JSON format + (:py:class:`RequestMustBeJSONException`) + :status 403: User is not the group administrator + (:py:class:`UserIsNotAdminException`) + :status 404: User not found (via token) + (:py:class:`UserNotFoundException`) + :status 412: Authorization required + (:py:class:`AuthorizationRequiredException`) + """ + group = Group.query.get(group_id) + if not group: + raise ElementNotFoundException('Group') + + if not group.owner == request.user: + raise UserIsNotAdminException() + + not_found = [] + rejected = [] + for place_id in request.as_json.get('places', []): + place = Place.query.get(place_id) + if not place: + not_found.append(place_id) + continue + + if place.owner not in group.users: + rejected.append(place_id) + + group.places.append(place) + db.session.commit() + + return jsonify(status='OK', + not_found=not_found, + rejected=rejected) + + +@group_places.route('/places//', methods=['DELETE']) +@auth +def group_remove_place(group_id, place_id): + """*Authenticated request* + + Remove a place from the group. The user must be the group owner. + + :param group_id: The group Id + :param place_id: The place Id + + :header Authorization: Access token from `/token/`. + + :status 200: Success + :status 403: User is not the group administrator + (:py:class:`UserIsNotAdminException`) + :status 404: User not found (via token) + (:py:class:`UserNotFoundException`) + :status 404: Group not found (:py:class:`ElementNotFoundException`) + :status 404: Place is not part of the group + (:py:class:`ElementNotFoundException`) + :status 412: Authorization required + (:py:class:`AuthorizationRequiredException`) + """ + group = Group.query.get(group_id) + if not group: + raise ElementNotFoundException('Group') + + index = None + try: + index = group.places.index(place_id) + except ValueError: + raise ElementNotFoundException('Place') + + del group.places[index] + db.session.commit() + return jsonify(status='OK')