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 ( " \n Bulk 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 ( " \n We 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 ( " \n You 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 ( " \n The 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 ( " \n You 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 ( " \n The items/deposists parameter have to be a list. \n " )
return ret
if len ( itemList ) > 30 :
ret = rc . BAD_REQUEST
ret . write ( " \n You 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 ( " \n Your 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 ( " \n The item with the id ' %s ' could not be found \n " % ( item , ) )
return ret
except ValueError :
ret = rc . NOT_FOUND
ret . write ( " \n Item 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 ( " \n Item ' %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 ( " \n A 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 ( " \n Your 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 ( " \n You 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 ( " \n Please 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 ( " \n The 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 ( " \n This 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 ( " \n This 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 ( " \n To 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 ( " \n This 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 ' ,
}