aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--budget/api.py53
-rw-r--r--budget/models.py27
-rw-r--r--budget/rest.py19
-rw-r--r--budget/web.py13
4 files changed, 74 insertions, 38 deletions
diff --git a/budget/api.py b/budget/api.py
index c307fdf..c50d668 100644
--- a/budget/api.py
+++ b/budget/api.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
from flask import *
-import werkzeug
from models import db, Project, Person, Bill
from utils import for_all_methods
-from rest import RESTResource, need_auth # FIXME make it an ext
+from rest import RESTResource, need_auth# FIXME make it an ext
+from werkzeug import Response
api = Blueprint("api", __name__, url_prefix="/api")
@@ -33,7 +33,7 @@ class ProjectHandler(object):
@need_auth(check_project, "project")
def get(self, project):
- return "get"
+ return project
@need_auth(check_project, "project")
def delete(self, project):
@@ -47,7 +47,10 @@ class ProjectHandler(object):
class MemberHandler(object):
def get(self, project, member_id):
- pass
+ member = Person.query.get(member_id)
+ if not member or member.project != project:
+ return Response('Not Found', status=404)
+ return member
def list(self, project):
return project.members
@@ -59,25 +62,32 @@ class MemberHandler(object):
pass
def delete(self, project, member_id):
- pass
+ if project.remove_member(member_id):
+ return Response('OK', status=200)
class BillHandler(object):
- def get(self, project, member_id):
- pass
+ def get(self, project, bill_id):
+ bill = Bill.query.get(project, bill_id)
+ if not bill:
+ return Response('Not Found', status=404)
+ return bill
def list(self, project):
- pass
+ return project.get_bills().all()
def add(self, project):
pass
- def update(self, project, member_id):
+ def update(self, project, bill_id):
pass
- def delete(self, project, member_id):
- pass
+ def delete(self, project, bill_id):
+ bill = Bill.query.delete(project, bill_id)
+ if not bill:
+ return Response('Not Found', status=404)
+ return bill
project_resource = RESTResource(
@@ -102,24 +112,3 @@ bill_resource = RESTResource(
app=api,
handler=BillHandler(),
authentifier=check_project)
-
-# projects: add, delete, edit, get
-# GET /project/<id> → get
-# PUT /project/<id> → add & edit
-# DELETE /project/<id> → delete
-
-# project members: list, add, delete
-# GET /project/<id>/members → list
-# POST /project/<id>/members/ → add
-# PUT /project/<id>/members/<user_id> → edit
-# DELETE /project/<id>/members/<user_id> → delete
-
-# project bills: list, add, delete, edit, get
-# GET /project/<id>/bills → list
-# GET /project/<id>/bills/<bill_id> → get
-# DELETE /project/<id>/bills/<bill_id> → delete
-# POST /project/<id>/bills/ → add
-
-
-# GET, PUT, DELETE: /<id> : Get, update and delete
-# GET, POST: / Add & List
diff --git a/budget/models.py b/budget/models.py
index 8d68746..e56ae4e 100644
--- a/budget/models.py
+++ b/budget/models.py
@@ -3,6 +3,8 @@ from collections import defaultdict
from datetime import datetime
from flaskext.sqlalchemy import SQLAlchemy
+from sqlalchemy import orm
+
db = SQLAlchemy()
# define models
@@ -103,6 +105,29 @@ billowers = db.Table('billowers',
)
class Bill(db.Model):
+
+ class BillQuery(orm.query.Query):
+
+ def get(self, project, id):
+ try:
+ return self.join(Person, Project)\
+ .filter(Bill.payer_id == Person.id)\
+ .filter(Person.project_id == Project.id)\
+ .filter(Project.id == project.id)\
+ .filter(Bill.id == id).one()
+ except orm.exc.NoResultFound:
+ return None
+
+ def delete(self, project, id):
+ bill = self.get(project, id)
+ if bill:
+ db.session.delete(bill)
+ return bill
+
+ query_class = BillQuery
+
+ _to_serialize = ("id", "payer_id", "owers", "amount", "date", "what")
+
id = db.Column(db.Integer, primary_key=True)
payer_id = db.Column(db.Integer, db.ForeignKey("person.id"))
@@ -122,7 +147,6 @@ class Bill(db.Model):
return "<Bill of %s from %s for %s>" % (self.amount,
self.payer, ", ".join([o.name for o in self.owers]))
-
class Archive(db.Model):
id = db.Column(db.Integer, primary_key=True)
project_id = db.Column(db.Integer, db.ForeignKey("project.id"))
@@ -138,3 +162,4 @@ class Archive(db.Model):
def __repr__(self):
return "<Archive>"
+
diff --git a/budget/rest.py b/budget/rest.py
index a61f02c..e698e21 100644
--- a/budget/rest.py
+++ b/budget/rest.py
@@ -1,5 +1,6 @@
import json
from flask import request
+import werkzeug
class RESTResource(object):
"""Represents a REST resource, with the different HTTP verbs"""
@@ -117,18 +118,28 @@ def need_auth(authentifier, name=None, remove_attr=True):
return wrapped
return wrapper
-
# serializers
def serialize(func):
+ """If the object returned by the view is not already a Response, serialize
+ it using the ACCEPT header and return it.
+ """
def wrapped(*args, **kwargs):
+ # get the mimetype
mime = request.accept_mimetypes.best_match(SERIALIZERS.keys())
- return SERIALIZERS.get(mime, "text/json")\
- .encode(func(*args, **kwargs))
+ data = func(*args, **kwargs)
+
+ if isinstance(data, werkzeug.Response):
+ return data
+ else:
+ # serialize it
+ return SERIALIZERS.get(mime, "text/json").encode(data)
+
return wrapped
class JSONEncoder(json.JSONEncoder):
+ """Subclass of the default encoder to support custom objects"""
def default(self, o):
if hasattr(o, "_to_serialize"):
# build up the object
@@ -136,6 +147,8 @@ class JSONEncoder(json.JSONEncoder):
for attr in o._to_serialize:
data[attr] = getattr(o, attr)
return data
+ elif hasattr(o, "isoformat"):
+ return o.isoformat()
else:
return json.JSONEncoder.default(self, o)
diff --git a/budget/web.py b/budget/web.py
index f72a686..61d67e5 100644
--- a/budget/web.py
+++ b/budget/web.py
@@ -2,6 +2,7 @@ from collections import defaultdict
from flask import *
from flaskext.mail import Mail, Message
+import werkzeug
# local modules
from models import db, Project, Person, Bill
@@ -239,7 +240,11 @@ def add_bill():
@main.route("/<project_id>/delete/<int:bill_id>")
def delete_bill(bill_id):
- bill = Bill.query.get_or_404(bill_id)
+ # fixme: everyone is able to delete a bill
+ bill = Bill.query.get(g.project, bill_id)
+ if not bill:
+ raise werkzeug.exceptions.NotFound()
+
db.session.delete(bill)
db.session.commit()
flash("The bill has been deleted")
@@ -249,7 +254,11 @@ def delete_bill(bill_id):
@main.route("/<project_id>/edit/<int:bill_id>", methods=["GET", "POST"])
def edit_bill(bill_id):
- bill = Bill.query.get_or_404(bill_id)
+ # FIXME: Test this bill belongs to this project !
+ bill = Bill.query.get(g.project, bill_id)
+ if not bill:
+ raise werkzeug.exceptions.NotFound()
+
form = get_billform_for(g.project, set_default=False)
if request.method == 'POST' and form.validate():
form.save(bill)