mirror of
https://github.com/YunoHost-Apps/ihatemoney_ynh.git
synced 2024-09-03 19:26:15 +02:00
REST API is now able to list stuff \o/
This commit is contained in:
parent
a60b0c2b48
commit
e13ceaf351
4 changed files with 74 additions and 38 deletions
|
@ -1,11 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from flask import *
|
from flask import *
|
||||||
import werkzeug
|
|
||||||
|
|
||||||
from models import db, Project, Person, Bill
|
from models import db, Project, Person, Bill
|
||||||
from utils import for_all_methods
|
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")
|
api = Blueprint("api", __name__, url_prefix="/api")
|
||||||
|
@ -33,7 +33,7 @@ class ProjectHandler(object):
|
||||||
|
|
||||||
@need_auth(check_project, "project")
|
@need_auth(check_project, "project")
|
||||||
def get(self, project):
|
def get(self, project):
|
||||||
return "get"
|
return project
|
||||||
|
|
||||||
@need_auth(check_project, "project")
|
@need_auth(check_project, "project")
|
||||||
def delete(self, project):
|
def delete(self, project):
|
||||||
|
@ -47,7 +47,10 @@ class ProjectHandler(object):
|
||||||
class MemberHandler(object):
|
class MemberHandler(object):
|
||||||
|
|
||||||
def get(self, project, member_id):
|
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):
|
def list(self, project):
|
||||||
return project.members
|
return project.members
|
||||||
|
@ -59,25 +62,32 @@ class MemberHandler(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def delete(self, project, member_id):
|
def delete(self, project, member_id):
|
||||||
pass
|
if project.remove_member(member_id):
|
||||||
|
return Response('OK', status=200)
|
||||||
|
|
||||||
|
|
||||||
class BillHandler(object):
|
class BillHandler(object):
|
||||||
|
|
||||||
def get(self, project, member_id):
|
def get(self, project, bill_id):
|
||||||
pass
|
bill = Bill.query.get(project, bill_id)
|
||||||
|
if not bill:
|
||||||
|
return Response('Not Found', status=404)
|
||||||
|
return bill
|
||||||
|
|
||||||
def list(self, project):
|
def list(self, project):
|
||||||
pass
|
return project.get_bills().all()
|
||||||
|
|
||||||
def add(self, project):
|
def add(self, project):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update(self, project, member_id):
|
def update(self, project, bill_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def delete(self, project, member_id):
|
def delete(self, project, bill_id):
|
||||||
pass
|
bill = Bill.query.delete(project, bill_id)
|
||||||
|
if not bill:
|
||||||
|
return Response('Not Found', status=404)
|
||||||
|
return bill
|
||||||
|
|
||||||
|
|
||||||
project_resource = RESTResource(
|
project_resource = RESTResource(
|
||||||
|
@ -102,24 +112,3 @@ bill_resource = RESTResource(
|
||||||
app=api,
|
app=api,
|
||||||
handler=BillHandler(),
|
handler=BillHandler(),
|
||||||
authentifier=check_project)
|
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
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ from collections import defaultdict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flaskext.sqlalchemy import SQLAlchemy
|
from flaskext.sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
|
from sqlalchemy import orm
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
|
||||||
# define models
|
# define models
|
||||||
|
@ -103,6 +105,29 @@ billowers = db.Table('billowers',
|
||||||
)
|
)
|
||||||
|
|
||||||
class Bill(db.Model):
|
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)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
|
||||||
payer_id = db.Column(db.Integer, db.ForeignKey("person.id"))
|
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,
|
return "<Bill of %s from %s for %s>" % (self.amount,
|
||||||
self.payer, ", ".join([o.name for o in self.owers]))
|
self.payer, ", ".join([o.name for o in self.owers]))
|
||||||
|
|
||||||
|
|
||||||
class Archive(db.Model):
|
class Archive(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
project_id = db.Column(db.Integer, db.ForeignKey("project.id"))
|
project_id = db.Column(db.Integer, db.ForeignKey("project.id"))
|
||||||
|
@ -138,3 +162,4 @@ class Archive(db.Model):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Archive>"
|
return "<Archive>"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
from flask import request
|
from flask import request
|
||||||
|
import werkzeug
|
||||||
|
|
||||||
class RESTResource(object):
|
class RESTResource(object):
|
||||||
"""Represents a REST resource, with the different HTTP verbs"""
|
"""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 wrapped
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
# serializers
|
# serializers
|
||||||
|
|
||||||
def serialize(func):
|
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):
|
def wrapped(*args, **kwargs):
|
||||||
|
# get the mimetype
|
||||||
mime = request.accept_mimetypes.best_match(SERIALIZERS.keys())
|
mime = request.accept_mimetypes.best_match(SERIALIZERS.keys())
|
||||||
return SERIALIZERS.get(mime, "text/json")\
|
data = func(*args, **kwargs)
|
||||||
.encode(func(*args, **kwargs))
|
|
||||||
|
if isinstance(data, werkzeug.Response):
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
# serialize it
|
||||||
|
return SERIALIZERS.get(mime, "text/json").encode(data)
|
||||||
|
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
class JSONEncoder(json.JSONEncoder):
|
class JSONEncoder(json.JSONEncoder):
|
||||||
|
"""Subclass of the default encoder to support custom objects"""
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
if hasattr(o, "_to_serialize"):
|
if hasattr(o, "_to_serialize"):
|
||||||
# build up the object
|
# build up the object
|
||||||
|
@ -136,6 +147,8 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
for attr in o._to_serialize:
|
for attr in o._to_serialize:
|
||||||
data[attr] = getattr(o, attr)
|
data[attr] = getattr(o, attr)
|
||||||
return data
|
return data
|
||||||
|
elif hasattr(o, "isoformat"):
|
||||||
|
return o.isoformat()
|
||||||
else:
|
else:
|
||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from collections import defaultdict
|
||||||
|
|
||||||
from flask import *
|
from flask import *
|
||||||
from flaskext.mail import Mail, Message
|
from flaskext.mail import Mail, Message
|
||||||
|
import werkzeug
|
||||||
|
|
||||||
# local modules
|
# local modules
|
||||||
from models import db, Project, Person, Bill
|
from models import db, Project, Person, Bill
|
||||||
|
@ -239,7 +240,11 @@ def add_bill():
|
||||||
|
|
||||||
@main.route("/<project_id>/delete/<int:bill_id>")
|
@main.route("/<project_id>/delete/<int:bill_id>")
|
||||||
def delete_bill(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.delete(bill)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("The bill has been deleted")
|
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"])
|
@main.route("/<project_id>/edit/<int:bill_id>", methods=["GET", "POST"])
|
||||||
def edit_bill(bill_id):
|
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)
|
form = get_billform_for(g.project, set_default=False)
|
||||||
if request.method == 'POST' and form.validate():
|
if request.method == 'POST' and form.validate():
|
||||||
form.save(bill)
|
form.save(bill)
|
||||||
|
|
Loading…
Reference in a new issue