From 0423900df95460279a7307f91569b0ddef6ff8b1 Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Sun, 6 Apr 2014 17:29:27 -0300 Subject: [PATCH] starting places --- luncho/blueprints/groups.py | 17 +----- luncho/blueprints/places.py | 103 ++++++++++++++++++++++++++++++++++++ luncho/exceptions.py | 16 ++++++ luncho/server.py | 41 +++++++++++--- tests/place_tests.py | 51 ++++++++++++++++++ 5 files changed, 204 insertions(+), 24 deletions(-) create mode 100644 luncho/blueprints/places.py create mode 100644 tests/place_tests.py diff --git a/luncho/blueprints/groups.py b/luncho/blueprints/groups.py index b5f79ca..44e8eea 100644 --- a/luncho/blueprints/groups.py +++ b/luncho/blueprints/groups.py @@ -18,22 +18,7 @@ from luncho.server import db from luncho.exceptions import LunchoException from luncho.exceptions import ElementNotFoundException - - -class AccountNotVerifiedException(LunchoException): - """The account isn't verified. - - .. sourcecode:: http - - HTTP/1.1 412 Precondition Failed - Content-Type: test/json - - { "status": "ERROR", "message": "Account not verified" } - """ - def __init__(self): - super(AccountNotVerifiedException, self).__init__() - self.status = 412 - self.message = 'Account not verified' +from luncho.exceptions import AccountNotVerifiedException class NewMaintainerDoesNotExistException(LunchoException): diff --git a/luncho/blueprints/places.py b/luncho/blueprints/places.py new file mode 100644 index 0000000..8885d6e --- /dev/null +++ b/luncho/blueprints/places.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from flask import Blueprint +from flask import request +from flask import jsonify + +from luncho.server import Place +from luncho.server import db + +from luncho.helpers import auth +from luncho.helpers import ForceJSON + +from luncho.exceptions import AccountNotVerifiedException + +places = Blueprint('places', __name__) + + +@places.route('', methods=['POST']) +@ForceJSON(required=['name']) +@auth +def create_place(): + """*Authenticated request* Create a new place. The user becomes the + maintainer of the place once it is created. + + **Example request**: + + .. sourcecode:: http + + { "name": "" } + + **Success (200)** + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK", "id": } + + **User not found (via token) (404)**: + :py:class:`UserNotFoundException` + + **Authorization required (412)**: + :py:class:`AuthorizationRequiredException` + + **Account not verified (412)**: + :py:class:`AccountNotVerifiedException` + """ + if not request.user.verified: + raise AccountNotVerifiedException() + + json = request.get_json(force=True) + new_place = Place(name=json['name'], owner=request.user.username) + db.session.add(new_place) + db.session.commit() + + return jsonify(status='OK', + id=new_place.id) + + +@places.route('', methods=['GET']) +@auth +def get_places(): + """*Authenticated request* Return the list of places the user is the + maintainer or belongs to one of the user's groups. + + **Success (200)** + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK", "places": [ { "id": "", + "name": "", + "maintainer": }, + ...] } + + **User not found (via token) (404)**: + :py:class:`UserNotFoundException` + + **Authorization required (412)**: + :py:class:`AuthorizationRequiredException` + """ + user = request.user + places = {} + for group in user.groups: + for place in group.places: + maintainer = place.owner == user.username + places[place.id] = {'id': place.id, + 'name': place.name, + 'maintainer': maintainer} + + for place in Place.query.filter_by(owner=user.username): + maintainer = place.owner == user.username + places[place.id] = {'id': place.id, + 'name': place.name, + 'maintainer': maintainer} + + return jsonify(status='OK', + places=places.values()) diff --git a/luncho/exceptions.py b/luncho/exceptions.py index c58f52b..cffe06a 100644 --- a/luncho/exceptions.py +++ b/luncho/exceptions.py @@ -122,3 +122,19 @@ class AuthorizationRequiredException(LunchoException): super(AuthorizationRequiredException, self).__init__() self.status = 401 self.message = 'Request requires authorization' + + +class AccountNotVerifiedException(LunchoException): + """The account isn't verified. + + .. sourcecode:: http + + HTTP/1.1 412 Precondition Failed + Content-Type: test/json + + { "status": "ERROR", "message": "Account not verified" } + """ + def __init__(self): + super(AccountNotVerifiedException, self).__init__() + self.status = 412 + self.message = 'Account not verified' diff --git a/luncho/server.py b/luncho/server.py index fd9fc93..408108a 100644 --- a/luncho/server.py +++ b/luncho/server.py @@ -36,13 +36,22 @@ app.config.from_envvar('LUCNHO_CONFIG', True) from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy(app) -userGroups = db.Table('user_groups', - db.Column('username', - db.String, - db.ForeignKey('user.username')), - db.Column('group_id', - db.Integer, - db.ForeignKey('group.id'))) +user_groups = db.Table('user_groups', + db.Column('username', + db.String, + db.ForeignKey('user.username')), + db.Column('group_id', + db.Integer, + db.ForeignKey('group.id'))) + + +group_places = db.Table('group_places', + db.Column('group', + db.Integer, + db.ForeignKey('group.id')), + db.Column('place', + db.Integer, + db.ForeignKey('place.id'))) class User(db.Model): @@ -54,7 +63,7 @@ class User(db.Model): validated = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, nullable=False) groups = db.relationship('Group', - secondary=userGroups, + secondary=user_groups, backref=db.backref('users', lazy='dynamic')) def __init__(self, username, fullname, passhash, token=None, @@ -88,6 +97,10 @@ class Group(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) owner = db.Column(db.String, db.ForeignKey('user.username')) + places = db.relationship('Place', + secondary=group_places, + backref='groups', + lazy='dynamic') def __init__(self, name, owner): self.name = name @@ -98,16 +111,28 @@ class Group(db.Model): name=self.name, owner=self.owner) + +class Place(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String, nullable=False) + owner = db.Column(db.String, db.ForeignKey('user.username')) + + def __init__(self, name, owner=None): + self.name = name + self.owner = owner + # ---------------------------------------------------------------------- # Blueprints # ---------------------------------------------------------------------- from blueprints.users import users from blueprints.token import token from blueprints.groups import groups +from blueprints.places import places app.register_blueprint(token, url_prefix='/token/') app.register_blueprint(users, url_prefix='/user/') app.register_blueprint(groups, url_prefix='/group/') +app.register_blueprint(places, url_prefix='/place/') # ---------------------------------------------------------------------- diff --git a/tests/place_tests.py b/tests/place_tests.py new file mode 100644 index 0000000..5bd8f65 --- /dev/null +++ b/tests/place_tests.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +import unittest + +from json import loads + +from luncho import server + +from luncho.server import User + +from base import LunchoTests + + +class TestPlaces(LunchoTests): + """Test places.""" + + def setUp(self): + super(TestPlaces, self).setUp() + self.user = User(username='test', + fullname='Test User', + passhash='hash') + self.user.verified = True + server.db.session.add(self.user) + server.db.session.commit() + self.user.get_token() + + def test_create_place(self): + """Try to create a place.""" + request = {'name': 'New Place'} + rv = self.post('/place/', + request, + token=self.user.token) + self.assertJsonOk(rv) + json = loads(rv.data) + self.assertTrue('id' in json) + + def test_get_places(self): + """Try to get the user places.""" + token = self.user.token + self.test_create_place() # create a place + rv = self.get('/place/', + token=token) + self.assertJsonOk(rv) + json = loads(rv.data) + self.assertTrue('places' in json) + self.assertEqual(len(json['places']), 1) # just the new place + + +if __name__ == '__main__': + unittest.main()