Added a *lot* of documentation
This commit is contained in:
		
							parent
							
								
									7850b322c5
								
							
						
					
					
						commit
						4fbd3c6651
					
				|  | @ -29,7 +29,7 @@ Open for discussion: | |||
| 	  - 15, besser 20:Man sucht auf ja nach etwas und will sich nicht totklicken ~~~TKroenert | ||||
| 
 | ||||
| Konrad: | ||||
| 	Abmeldenutton rechts oder rot? | ||||
| 	Abmeldebutton rechts oder rot? | ||||
| 	- die liste der zu einkaufenden items ist doof :( | ||||
| 	- /store/history/ ist noch kaputt + zeit unformatiert | ||||
| 	- /transaction/ sehen die gemachten transactions noch nicht so cool aus | ||||
|  |  | |||
|  | @ -4,16 +4,17 @@ from piston.utils import rc | |||
| from main.models import Plugin, PluginPermission | ||||
| 
 | ||||
| def manglePluginPerms(apiFunc): | ||||
| 	""" Changes to a given user when the authenticated user is an plugin | ||||
| 	""" Changes to a given user when the authenticated user is an plugin. | ||||
| 	 | ||||
| 	When the user which called the apifunc is a plugin this function | ||||
| 	goes through the following steps: | ||||
| 		- searches the user it should change to | ||||
| 		- checks if this user allowed the plugin to "speak for him" | ||||
| 		- change the request so it looks like the user called himself | ||||
| 		- add an plugin_user entry containing the previous request user | ||||
| 
 | ||||
| 		When the user which called the apifunc is an plugin this function | ||||
| 		goes through the following steps: | ||||
| 		    - searches the user it should change to | ||||
| 			- checks if this user allowed the plugin to "speak for him" | ||||
| 			- change the request so it looks like the user called himself | ||||
| 			- add an plugin_user entry containing the previous request user | ||||
| 		This decorator is intended to be used with django piston, so on error | ||||
| 		it will return the appropriate rc.* values. | ||||
| 	This decorator is intended to be used with django piston, so on error | ||||
| 	it will return the appropriate rc.* values. | ||||
| 	""" | ||||
| 	 | ||||
| 	@wraps(apiFunc) | ||||
|  |  | |||
|  | @ -1,12 +1,14 @@ | |||
| from decimal import Decimal, InvalidOperation | ||||
| 
 | ||||
| def getInt(d, key, default): | ||||
| 	""" Helper for dict.get to return the default on error. """ | ||||
| 	try: | ||||
| 		return int(d.get(key, default)) | ||||
| 	except ValueError: | ||||
| 		return default | ||||
| 
 | ||||
| def getDecimal(d, key, default): | ||||
| 	""" Helper for dict.get to return the default on error. """ | ||||
| 	try: | ||||
| 		return Decimal(d.get(key, default)) | ||||
| 	except InvalidOperation: | ||||
|  |  | |||
|  | @ -4,9 +4,13 @@ from piston.authentication import HttpBasicAuthentication | |||
| from api2.authentication import DjangoAuthentication, MultiAuthentication | ||||
| from api2.handlers import * | ||||
| 
 | ||||
| # taken from | ||||
| # http://www.robertshady.com/content/creating-very-basic-api-using-python-django-and-piston | ||||
| class CsrfExemptResource( Resource ): | ||||
| 	""" Except a :class:`Resource` from djangos CSRF-Framework. | ||||
| 
 | ||||
| 		This idea is taken from | ||||
| 		http://www.robertshady.com/content/creating-very-basic-api-using-python-django-and-piston | ||||
| 	""" | ||||
| 	 | ||||
| 	def __init__( self, handler, authentication = None ): | ||||
| 		super( CsrfExemptResource, self ).__init__( handler, authentication ) | ||||
| 		self.csrf_exempt = getattr( self.handler, 'csrf_exempt', True ) | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ class Buyable(models.Model): | |||
| 		return self.deposit > Decimal(0) | ||||
| 	 | ||||
| 	def createPurchase(self, isDeposit=False): | ||||
| 		""" Creates a :class:`Purchase` containing this :class:`Buyable`. """ | ||||
| 		p = Purchase() | ||||
| 		if isDeposit: | ||||
| 			p.price = self.deposit | ||||
|  | @ -47,11 +48,7 @@ class Buyable(models.Model): | |||
| 		return item | ||||
| 
 | ||||
| class Order(models.Model): | ||||
| 	""" Represents an order by the user. | ||||
| 
 | ||||
| 
 | ||||
| 	""" | ||||
| 	An Order object is referenced by all :class:`Purchases <Purchase>` | ||||
| 	""" Represents an order by the user, consists of several :class:`Purchases <Purchase>`. """ | ||||
| 	user = models.ForeignKey(User) | ||||
| 	price = models.DecimalField(max_digits=8, decimal_places=2) | ||||
| 	dateTime = models.DateTimeField() | ||||
|  | @ -93,7 +90,7 @@ class Order(models.Model): | |||
| 
 | ||||
| 
 | ||||
| class Purchase(models.Model): | ||||
| 	""" Represents a :class:``""" | ||||
| 	""" Represents a :class:`Buyable` in a :class:`Order`""" | ||||
| 	order = models.ForeignKey(Order) | ||||
| 	price = models.DecimalField(max_digits=8, decimal_places=2) | ||||
| 	isDeposit = models.BooleanField() | ||||
|  | @ -101,6 +98,7 @@ class Purchase(models.Model): | |||
| 
 | ||||
| 	@staticmethod | ||||
| 	def create(order, buyable, isDeposit=False): | ||||
| 		""" Create a Purchase from a buyable. Still needs to be saved. """ | ||||
| 		p = Purchase() | ||||
| 		p.order = order | ||||
| 		p.isDeposit = isDeposit | ||||
|  | @ -115,13 +113,4 @@ class Purchase(models.Model): | |||
| 
 | ||||
| 	def __unicode__(self): | ||||
| 		return "%s%s" % (self.buyable.name, (self.isDeposit and " (deposit)" or "")) | ||||
| 	 | ||||
| #	def save(self, *args, **kwargs): | ||||
| #		profile = self.user.get_profile() | ||||
| #		if self.id == None: | ||||
| #			# new item, get it! | ||||
| #			profile.balance -= self.price | ||||
| #			profile.save() | ||||
| #		super(Purchase, self).save(*args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ def buyItem(request, itemid, buymode=""): | |||
| 
 | ||||
| @login_required | ||||
| def boughtItem(request, orderid): | ||||
| 	""" View which shows what was bought.""" | ||||
| 	error = None | ||||
| 	try: | ||||
| 		item = Order.objects.get(id=orderid) | ||||
|  | @ -66,6 +67,7 @@ def boughtItem(request, orderid): | |||
| 
 | ||||
| @login_required | ||||
| def history(request): | ||||
| 	""" Show the users previous orders. """ | ||||
| 	hist = Order.objects.filter(user=request.user.id).order_by("-dateTime") | ||||
| 	paginator = Paginator(hist, 10, orphans=3) | ||||
| 	 | ||||
|  |  | |||
|  | @ -1,22 +1,24 @@ | |||
| API | ||||
| === | ||||
|  - explain a bit how piston/REST works | ||||
|  - how to access the read/write/put/create functions | ||||
|  - what this api does and what not | ||||
| 
 | ||||
| K4ever has a REST-like API. This means every URL represents an object which has | ||||
| four functions: `read`, `create`, `update` and `delete`. These functions are mapped | ||||
| to the `HTTP`-Methods `GET`, `POST`, `PUT` and `DELETE`. Most of the functionality | ||||
| uses only `GET` and `POST`, so everything is accessible via `wget`. | ||||
| four functions: ``read``, ``create``, ``update`` and ``delete``. These functions are mapped | ||||
| to the ``HTTP``-Methods ``GET``, ``POST``, ``PUT`` and ``DELETE``. Most of the functionality | ||||
| uses only ``GET`` and ``POST``, so everything is accessible via ``wget``. | ||||
| 
 | ||||
| The API enables you to list available items, buy them and transtact money to your | ||||
| accout. It also has a *plugin*-concept. Plugins can be allowed by other user | ||||
| accout. To get an idea what functions are available and how they are used scroll down | ||||
| to the URLs section. Some URLs take arguments. For a ``GET``-request you can attach | ||||
| them normaly, e.g. `/buyable/item/?barcode=4029764001807`. For a ``POST``, ``PUT`` or | ||||
| ``DELETE`` request the parameters have to be put in the ``HTTP``-requests body. They | ||||
| can be provided url, JSON or YAML-encoded - dealers choice. | ||||
| 
 | ||||
| Authentication can be done either per HTTP Basic Auth or, for AJAX-Requests, per | ||||
| cookie (as in "logged in via the webinterface"). Note that most ``HTTP``-libraries | ||||
| like pythons urllib2 (or as a tool ``wget``) don't send authenticationdata except | ||||
| when being challenged with a ``HTTP 401 - Authentication required``, meaning that | ||||
| every API-call requires the library to make two requests. In general this behaviour | ||||
| can be turned off. | ||||
| 
 | ||||
| Authentication can be done either per HTTP Basic Auth or for AJAX-Requests per | ||||
| cookie. The  | ||||
| 
 | ||||
| k4evers API  | ||||
| 
 | ||||
| Plugins | ||||
| -------------- | ||||
|  | @ -25,7 +27,24 @@ Plugins | |||
|  - when does a plugin need an user? | ||||
|  - how to change user names | ||||
| 
 | ||||
| k4evers API also has a *plugin*-concept. :class:`Plugins <main.models.Plugin>` | ||||
| can be allowed by users to buy items on their behalf. To do this the user | ||||
| has to allow the plugin via the webinterface. A :class:`PluginPermission | ||||
| <main.models.PluginPermission>` object will be created to keep track of this. | ||||
| 
 | ||||
| Each :class:`PluginPermission <main.models.PluginPermission>` object has an | ||||
| :attr:`authblob <main.models.PluginPermission.authblob>` in which the plugin | ||||
| can save or access arbitrary data on a per-user basis. The plugin has several | ||||
| configuration options for the `authblob`. They control read/write access for  | ||||
| plugin (via API) or user (via webinterface) to the `authblob` and if the  | ||||
| `authblob` has to be unique. The latter one can be useful if the `authblob` | ||||
| is needed to be primary key and identify exactly one user | ||||
| (see also :ref:`apilink <authblob-authentication>`). | ||||
| 
 | ||||
| To do something as another user the plugin can, if allowed by the user, add | ||||
| the `user`-parameter with the **username** (not the id) to all its requests. | ||||
| E.g. to get the account balance of the user *cat* this would look like | ||||
| `user/account/?user=cat`. | ||||
| 
 | ||||
| URLs | ||||
| ---- | ||||
|  | @ -111,6 +130,9 @@ to the handler or the responsible method for more documentation or code. | |||
| 
 | ||||
| 	:func:`POST <api2.handlers.AuthBlobHandler.create>` | ||||
|    		Set authblob if allowed | ||||
| 
 | ||||
|  .. _authblob-authentication: | ||||
| 
 | ||||
|  :class:`user/ <api2.handlers.AuthUserHandler>` | ||||
|    	:func:`GET <api2.handlers.AuthUserHandler.read>` | ||||
|    		get user by authblob string - this function works only when plugin | ||||
|  | @ -132,20 +154,21 @@ Handler | |||
| .. automodule:: api2.handlers | ||||
| 	:members: | ||||
| 
 | ||||
| Plugin Models | ||||
| ------------- | ||||
| .. autoclass:: main.models.Plugin | ||||
| 	:members: | ||||
| 
 | ||||
| .. autoclass:: main.models.PluginPermission | ||||
| 	:members: | ||||
| 
 | ||||
| Decorators | ||||
| ---------- | ||||
| .. automodule:: api2.decorators | ||||
| 	:members: | ||||
| 
 | ||||
| Examples | ||||
| -------- | ||||
|  - how to use the api | ||||
|  - examples with... wget.. python-rest? | ||||
| 
 | ||||
| As normal user | ||||
| ^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| .. note:: there will be cat content | ||||
| 
 | ||||
| wget | ||||
| """" | ||||
| wget part | ||||
| 
 | ||||
| As a plugin | ||||
| ^^^^^^^^^^^ | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +0,0 @@ | |||
| from django_auth_ldap.backend import LDAPBackend | ||||
| 
 | ||||
| CustomLDAPBackend(LDAPBackend): | ||||
| 	def populate_user(username): | ||||
| 		 | ||||
|  | @ -30,6 +30,7 @@ class CurrencyInput (forms.TextInput): | |||
| 
 | ||||
| 
 | ||||
| class CurrencyField (forms.RegexField): | ||||
| 	""" Represents a currency field for django forms... or something. """ | ||||
| 	widget = CurrencyInput | ||||
| 	currencyRe = re.compile(r'^[0-9]{1,5}([,\.][0-9][0-9]?)?$') | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ from django.contrib.auth.models import User | |||
| from decimal import Decimal | ||||
| 
 | ||||
| class UserProfile(models.Model): | ||||
| 	""" Contains data for a user, especially the account balance. """ | ||||
| 	user = models.ForeignKey(User, unique=True) | ||||
| 	balance = models.DecimalField(max_digits=9, decimal_places=2, default=Decimal(0)) | ||||
| 	 | ||||
|  | @ -11,6 +12,7 @@ class UserProfile(models.Model): | |||
| 		return "%s (Kontostand: %s)" % (self.user ,self.balance) | ||||
| 
 | ||||
| def createUserProfile(sender, instance, created, **kwargs): | ||||
| 	""" Hook to create a new :class:`UserProfile` if the user is created. """ | ||||
| 	if created: | ||||
| 		profile = UserProfile() | ||||
| 		profile.user = instance | ||||
|  | @ -19,6 +21,19 @@ def createUserProfile(sender, instance, created, **kwargs): | |||
| post_save.connect(createUserProfile, sender=User) | ||||
| 
 | ||||
| class Plugin(models.Model): | ||||
| 	""" This Model contains a plugin and its configuration. | ||||
| 	 | ||||
| 	A Plugin consists of its own information (name, author, version | ||||
| 	and descrption) which is displayed for the user on the plugin | ||||
| 	selection page, a configuration what a plugin is allowed to do | ||||
| 	and what not and an own user for authentication against the | ||||
| 	API. | ||||
| 	 | ||||
| 	:attr:`uniqueAuthblob` is used if the :class:`Plugin` has to uniquely | ||||
| 	identify an user by his/her :attr:`authblob <PluginPermission.authblob>`. | ||||
| 	The other attributes are used for plugin/user read/write access to the | ||||
| 	authblob. | ||||
| 	""" | ||||
| 	user = models.ForeignKey(User, unique=True) | ||||
| 	 | ||||
| 	# plugin info | ||||
|  | @ -39,8 +54,15 @@ class Plugin(models.Model): | |||
| 		return self.name | ||||
| 
 | ||||
| class PluginPermission(models.Model): | ||||
| 	""" States that a user allows access to his/her account to a :class:`Plugin`. | ||||
| 	 | ||||
| 		The :attr:`authblob` can be used by the Plugin to store | ||||
| 		authentication data, e.g. a barcode or hashed password. | ||||
| 	""" | ||||
| 	user = models.ForeignKey(User) | ||||
| 	plugin = models.ForeignKey('Plugin') | ||||
| 	 | ||||
| 	#: authblob which holds arbitrary authentication data. | ||||
| 	authblob = models.TextField(default='') | ||||
| 	 | ||||
| 	def __unicode__(self): | ||||
|  |  | |||
|  | @ -19,10 +19,12 @@ def startpage(request): | |||
| 	return render_to_response("main/startpage.html", {'allMost' : allMost,'usersMost': usersMost, 'usersLast' : usersLast[:12]}, RequestContext(request)) | ||||
| 
 | ||||
| def register(request): | ||||
| 	""" The "no registration available" page... """ | ||||
| 	return render_to_response("registration/register.html", RequestContext(request)) | ||||
| 
 | ||||
| 
 | ||||
| def getPluginDict(request): | ||||
| 	""" Generate a dict containing the users plugin information. """ | ||||
| 	plugins = Plugin.objects.all() | ||||
| 	allowed = Plugin.objects.filter(pluginpermission__user=request.user) | ||||
| 	unallowed = Plugin.objects.exclude(pluginpermission__user=request.user) | ||||
|  | @ -32,10 +34,12 @@ def getPluginDict(request): | |||
| 
 | ||||
| @login_required | ||||
| def settings(request): | ||||
| 	""" Render settings page. """ | ||||
| 	return render_to_response("settings/settings.html", getPluginDict(request), RequestContext(request)) | ||||
| 
 | ||||
| @login_required | ||||
| def pluginPermission(request, method, pluginId): | ||||
| 	""" View to edit the users :class:`Plugin` permissions. """ | ||||
| 	plugin = None | ||||
| 	try: | ||||
| 		plugin = Plugin.objects.get(id=pluginId) | ||||
|  | @ -65,6 +69,7 @@ def pluginPermission(request, method, pluginId): | |||
| 
 | ||||
| @login_required | ||||
| def pluginAuthblob(request, pluginId): | ||||
| 	""" View to edit the users :attr:`authblob <PluginPermission.authblob>`. """ | ||||
| 	if request.method != "POST": | ||||
| 		return HttpResponseRedirect("/user/settings/") | ||||
| 	plugin = None | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ from models import Transaction | |||
| from main.fields import CurrencyField | ||||
| 
 | ||||
| class TransactionForm(forms.ModelForm): | ||||
| 	""" ModelForm for :class:`Transactions <Transaction>` with a currency field """ | ||||
| 	amount = CurrencyField(label='Betrag') | ||||
| 	class Meta: | ||||
| 		model = Transaction | ||||
|  |  | |||
|  | @ -2,6 +2,9 @@ from django.db import models | |||
| from django.contrib.auth.models import User | ||||
| 
 | ||||
| class TransactionType(models.Model): | ||||
| 	""" Type for a :class:`Transaction`. States how the money has | ||||
| 	    been added to the account and if somebody needs to check | ||||
| 		if it was payed. """ | ||||
| 	name = models.CharField(max_length=100) | ||||
| 	needsCheck = models.BooleanField(default=True) | ||||
| 	 | ||||
|  | @ -9,6 +12,7 @@ class TransactionType(models.Model): | |||
| 		return unicode(self.name) | ||||
| 
 | ||||
| class Transaction(models.Model): | ||||
| 	"""Represents a money transaction into the users account. """ | ||||
| 	user = models.ForeignKey(User) | ||||
| 	transactionType = models.ForeignKey(TransactionType, verbose_name='Typ') | ||||
| 	dateTime = models.DateTimeField() | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import datetime | |||
| 
 | ||||
| @login_required | ||||
| def overview(request): | ||||
| 	""" Creates an overview over the users trasnactions, also handles adding money. """ | ||||
| 	history = Transaction.objects.filter(user=request.user).order_by("-dateTime") | ||||
| 	transacted = False | ||||
| 	error = False | ||||
|  | @ -26,14 +27,3 @@ def overview(request): | |||
| 		form = TransactionForm() | ||||
| 	return render_to_response("transaction/overview.html", {'history': history, 'form': form, 'transacted': transacted, 'error': error}, RequestContext(request)) | ||||
| 
 | ||||
| @login_required | ||||
| def transact(request): | ||||
| 	if request.method == 'POST': | ||||
| 		return render_to_response("transaction/transfered.html", RequestContext(request)) | ||||
| 	else: | ||||
| 		return HttpResponseRedirect("/transaction/") | ||||
| 
 | ||||
| #@kassenwart_required | ||||
| #def checkTransfers(request): | ||||
| #	transfers = Transaction.objects.filter(checked=False).order_by("dateTime") | ||||
| #	return render_to_response("transaction/uncheckedTransfers.html", {'transfers' : tranfers}, RequestContext(request)) | ||||
		Loading…
	
		Reference in New Issue
	
	 seba
						seba