You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
k4ever/k4ever/api2/handlers.py

458 lines
14 KiB

from piston.handler import BaseHandler
from piston.utils import rc
from k4ever.buyable.models import *
from k4ever.transaction.models import *
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import Group
from django.core.exceptions import MultipleObjectsReturned
from decorators import *
from collections import Iterable
from decimal import Decimal, InvalidOperation
from helper import *
from django.db.models import Q
import datetime
class BuyableItemHandler(BaseHandler):
"""Handler responsible for getting and buying items."""
allowed_methods = ('GET', 'POST')
#fields = ('id', 'description')
model = Buyable
exclude = ('_*',)
BUY_ITEM, BUY_DEPOSIT, BUY_ITEM_AND_DEPOSIT = range(3)
def read(self, request, itemId=None, bulkBuy=False):
"""Get one or multiple items.
- type: Only get items belonging to this type
- barcode: Return only item(s) with this barcode
Note that neither type nor barcode is used if an item id
is specified.
"""
if bulkBuy:
ret = rc.NOT_IMPLEMENTED
ret.write("\nBulk buying does not support GET\n")
return ret
barcode = request.GET.get('barcode', None)
if itemId == None:
obj = Buyable.objects.all()
if barcode and barcode != '':
# try to get specific object or return 404
try:
return Buyable.objects.get(barcode=barcode)
except MultipleObjectsReturned:
ret = rc.DUPLICATE_ENTRY
ret.write("\nWe found more than one entry with this barcode. Bad.\n")
return ret
except Buyable.DoesNotExist:
return rc.NOT_FOUND
else:
if request.GET.has_key('type'):
obj = Buyable.objects.filter(buyableType__name=request.GET['type'])
return obj
try:
return Buyable.objects.get(id=itemId)
except Buyable.DoesNotExist:
error = rc.NOT_FOUND
error.write("This buyable does not exist in our database")
return error
@manglePluginPerms
def create(self, request, itemId=None, bulkBuy=False):
"""Buy one or multiple :class:`Buyable <buyable.models.Buyable>` items.
"""
if not request.content_type:
request.data = request.POST
if bulkBuy:
return self.bulkBuy(request)
else:
return self.buyItem(request, itemId)
def buyItem(self, request, itemId):
"""Buy a :class:`Buyable <buyable.models.Buyable>` item.
- deposit: Set to 0 for no deposit, 1 for deposit and 2 for item+deposit (default 0)
- amount: amount of items to buy (default 1)
"""
if not itemId:
return rc.BAD_REQUEST
item = None
try:
item = Buyable.objects.get(id=itemId)
except Buyable.DoesNotExist:
return rc.NOT_FOUND
# parse post data
deposit = getInt(request.data, 'deposit', self.BUY_ITEM)
amount = getInt(request.data, 'amount', 1)
if amount < 1:
return rc.BAD_REQUEST
if amount > 30:
ret = rc.BAD_REQUEST
ret.write("\nYou are trying to buy more than 30 items at once. This is not permitted. If you think it should, mail the admins / fix this in the handlers.py\n");
return ret
if (not item.hasDeposit() and deposit != self.BUY_ITEM) or \
deposit not in (self.BUY_ITEM, self.BUY_DEPOSIT, self.BUY_ITEM_AND_DEPOSIT):
return rc.BAD_REQUEST
order = Order()
order.create(request.user)
order.save()
for i in range(amount):
if deposit == self.BUY_ITEM or deposit == self.BUY_ITEM_AND_DEPOSIT:
p = Purchase.create(order, item, isDeposit=False)
p.save()
if deposit == self.BUY_DEPOSIT or deposit == self.BUY_ITEM_AND_DEPOSIT:
p = Purchase.create(order, item, isDeposit=True)
p.save()
order.updatePrice(commit=True)
order.save()
return rc.CREATED
def bulkBuy(self, request):
"""Buy a :class:`Buyable <buyable.models.Buyable>` item.
To buy multiple items, the body of the POST-request has to
be either JSON or YAML.
- items: List of items to buy.
- deposits: List of items to buy deposit for.
"""
if not request.content_type:
ret = rc.BAD_REQUEST
ret.write("\nThe content-type of the request must not be empty/urlencoded\n")
return ret
if not request.data.has_key("items") and not request.data.has_key("deposits"):
ret = rc.BAD_REQUEST
ret.write("\nYou need to specify either items or deposits (or both).\n")
return ret
if not request.data.has_key("items"):
request.data['items'] = []
if not request.data.has_key("deposits"):
request.data['deposits'] = []
itemList = []
try:
if not isinstance(request.data['items'], Iterable):
raise TypeError()
itemList += request.data['items']
if request.data.has_key('items'):
if not isinstance(request.data['deposits'], Iterable):
raise TypeError()
itemList += request.data['deposits']
except TypeError:
ret = rc.BAD_REQUEST
ret.write("\nThe items/deposists parameter have to be a list.\n")
return ret
if len(itemList) > 30:
ret = rc.BAD_REQUEST
ret.write("\nYou are trying to buy more than 30 items at once. This is not permitted. If you think it should, mail the admins / fix this in the handlers.py\n");
return ret
if len(itemList) == 0:
ret = rc.BAD_REQUEST
ret.write("\nYour request contains no items/deposits.\n")
return ret
ids = {}
for item in itemList:
if not ids.has_key(item):
try:
ids[item] = Buyable.objects.get(id=item)
except Buyable.DoesNotExist:
ret = rc.NOT_FOUND
ret.write("\nThe item with the id '%s' could not be found\n" % (item,))
return ret
except ValueError:
ret = rc.NOT_FOUND
ret.write("\nItem ids should be numeric (and preferably integers)\n")
return ret
if item in request.data['deposits'] and not ids[item].hasDeposit():
ret = rc.BAD_REQUEST
ret.write("\nItem '%s' cant be bought with deposit\n" % (item,))
return ret
order = Order()
order.create(request.user)
order.save()
purchases = []
# buy items
for item in request.data['items']:
p = Purchase.create(order, ids[item], isDeposit=False)
p.save()
# buy deposits
for item in request.data['deposits']:
p = Purchase.create(order, ids[item], isDeposit=True)
p.save()
order.updatePrice(commit=True)
order.save()
return rc.CREATED
class BuyableTypeHandler(BaseHandler):
"""Handler for listing all :class:`BuyableType <buyable.models.BuyableType>`.
This class only supports read requests which won't accept
any arguments. It will give you a list of buyable-types.
Nothing more, nothing less.
"""
allowed_methods = ('GET',)
model = BuyableType
class HistoryHandler(BaseHandler):
"""Handler providing access to the user's history """
allowed_methods = ('GET',)
fields = ('id', 'price', 'dateTime', ('purchase_set', (('buyable', ('id', )), 'price', 'name')))
@manglePluginPerms
def read(self, request):
"""Get the user's history
- num: Number of entries to return
"""
num = getInt(request.GET, 'num', 0)
qset = Order.objects.filter(user=request.user).order_by("-dateTime")
if num > 0:
return qset[:num]
return qset
class TransactionTransactHandler(BaseHandler):
"""Handler for transaction.
This handler takes care of adding money to accounts and returning
previous money transfers
"""
allowed_methods = ('GET', 'POST')
model = Transaction
fields = ('amount', 'dateTime', 'checked', ('transactionType', ('id', 'name')))
@manglePluginPerms
def read(self, request):
"""Return the user's last transactions
- num: Number of entries to return
"""
num = getInt(request.GET, 'num', 0)
if num < 0:
return rc.BAD_REQUEST
userTrans = Transaction.objects.filter(user=request.user).order_by("-dateTime")
if num > 0:
return userTrans[:num]
return userTrans
@manglePluginPerms
def create(self, request):
"""Transact money to an account
- amount: [req] Amount to add to the user's account
- type: [req] Type of transaction (id)
"""
amount = getDecimal(request.POST, 'amount', Decimal(0))
tTypeId = getInt(request.POST, 'type', -1)
if amount < Decimal("0.01"):
ret = rc.BAD_REQUEST
ret.write("\nA negative amount (or zeroed) is not supported right now (there has not been put enough thought into the 'lending money' process\n")
return ret
tType = None
try:
tType = TransactionType.objects.get(id=tTypeId)
except TransactionType.DoesNotExist:
ret = rc.BAD_REQUEST
ret.write("\nYour TransactionType could not be found\n")
return ret
trans = Transaction()
trans.user = request.user
trans.transactionType = tType
trans.dateTime = datetime.datetime.now()
trans.amount = amount
trans.save()
return rc.ALL_OK
class TransactionTypeHandler(BaseHandler):
"""Handler for :class:`Transaction Types <transaction.models.TransactionType>`
Supplies a list of Transaction Types
"""
allowed_methods = ('GET',)
model = TransactionType
class TransactionVirtualHandler(BaseHandler):
""" Handler for :class:`Virtual Transaction <transaction.models.VirtualTransaction>`
Allows to make transactions between users. """
allowed_methods = ('GET', 'POST')
model = VirtualTransaction
fields = ('id', 'amount', 'dateTime', 'comment', ('user', ('id', 'username',)), ('recipient', ('id', 'username',)))
@manglePluginPerms
def read(self, request):
"""Return the user's last virtual transactions (inbound and outbound)
- num: Number of entries to return
"""
num = getInt(request.GET, 'num', 0)
if num < 0:
return rc.BAD_REQUEST
userTrans = VirtualTransaction.objects.filter(Q(user=request.user) | Q(recipient=request.user)).order_by("-dateTime")
if num > 0:
return userTrans[:num]
return userTrans
@manglePluginPerms
def create(self, request):
""" Transact money from the users to another users account.
- amount: [req] Amount to transact
- recipient: [req] User that will get the money
- comment: [req] Comment, why the money was transacted
"""
amount = getDecimal(request.data, 'amount', Decimal(0))
if amount < Decimal("0.01"):
ret = rc.BAD_REQUEST
ret.write("\nYou can't transact negatives amount to another users account.\n")
return ret
comment = request.data.get('comment', None)
if not comment:
ret = rc.BAD_REQUEST
ret.write("\nPlease supply a comment for the transaction\n")
return ret
recipientStr = request.data.get('recipient', None)
recipient = None
try:
recipient = User.objects.get(username=recipientStr)
except User.DoesNotExist:
ret = rc.BAD_REQUEST
ret.write("\nThe recipient user does not exist.\n")
return ret
trans = VirtualTransaction(user=request.user, recipient=recipient, amount=amount, comment=comment)
trans.save()
return rc.ALL_OK
class AccountBalanceHandler(BaseHandler):
"""Handler for the user's account balance"""
allowed_methods = ('GET',)
@manglePluginPerms
def read(self, request):
"""Returns the user's current account balance"""
balance = request.user.get_profile().balance
return {'balance': balance}
class AuthBlobHandler(BaseHandler):
"""Handler to read and write a user's authblob
Currently these functions are only available for a plugin user.
Other users will get a rc.FORBIDDEN. Keep in mind that to use
these functions, a plugin needs the permissions to do this in its
configuration.
"""
allowed_methods = ('GET', 'POST')
@requirePlugin
@manglePluginPerms
def read(self, request):
"""Read the user's authblob
To use this function the plugin needs
:attr:`main.models.Plugin.pluginCanReadAuthblob` to be true.
"""
if not request.plugin.pluginCanReadAuthblob:
ret = rc.FORBIDDEN
ret.write("\nThis plugin is not allowed to read the user's authblob\n")
return ret
return {'authblob': request.pluginperms.authblob}
@requirePlugin
@manglePluginPerms
def create(self, request):
"""Write the user's authblob.
To use this function the plugin needs
:attr:`main.models.Plugin.pluginCanWriteAuthblob` to be true.
.. Note:: In fact this method *should* be an update method, but for
compability reasons (wget usage) it was decided to make
this accessible as a create (POST) hook.
"""
if not request.plugin.pluginCanWriteAuthblob:
ret = rc.FORBIDDEN
ret.write("\nThis plugin is not allowed to write the user's authblob\n")
return ret
if not request.data.has_key('authblob'):
ret = rc.BAD_REQUEST
ret.write("\nTo change the user's auth blob you actually need to provide one\n")
request.pluginperms.authblob = request.data['authblob']
request.pluginperms.authblob.save()
return rc.ALL_OK
class AuthUserHandler(BaseHandler):
""" Handler for mapping an authblob to a user
This handler is only available to plugins and only if
:attr:`unique authblob <main.models.Plugin.uniqueAuthblob>`
is set for this plugin. Then it will provide a mapping from
an authblob to a specific user.
"""
allowed_methods = ('GET')
fields = ('id', 'username')
@requirePlugin
def read(self, request):
"""Returns an user if one can be found, else rc.NOT_FOUND
- authblob: [required] Authblob to search
"""
if not request.plugin.uniqueAuthblob:
ret = rc.BAD_REQUEST
ret.write("\nThis plugin does not support unique auth blobs, therefore we can't identify a user uniquely by their authblob\n")
return ret
if not request.GET.has_key('authblob'):
return rc.BAD_REQUEST
try:
perm = PluginPermission.objects.get(plugin=request.plugin, authblob=request.GET['authblob'])
return perm.user
except PluginPermission.DoesNotExist:
return rc.NOT_FOUND
class ConfigHandler(BaseHandler):
""" Handler for API configuration values
This handler provides some API related configuration values"""
allowed_methods = ('GET',)
def read(self, request):
"""Get API configuration values
Currently returns the API version and mediaurl (where to
find images etc.)
"""
return {
'version': '0.1',
'mediaurl': 'http://devcat.someserver.de:13805/media',
}