diff --git a/devel/TODO b/devel/TODO index eb70cb9..3397aed 100644 --- a/devel/TODO +++ b/devel/TODO @@ -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 diff --git a/k4ever/api2/decorators.py b/k4ever/api2/decorators.py index bcaf8b3..f4ee01a 100644 --- a/k4ever/api2/decorators.py +++ b/k4ever/api2/decorators.py @@ -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) diff --git a/k4ever/api2/helper.py b/k4ever/api2/helper.py index 5693315..832bbb1 100644 --- a/k4ever/api2/helper.py +++ b/k4ever/api2/helper.py @@ -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: diff --git a/k4ever/api2/urls.py b/k4ever/api2/urls.py index 309d28b..552a004 100644 --- a/k4ever/api2/urls.py +++ b/k4ever/api2/urls.py @@ -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 ) diff --git a/k4ever/buyable/models.py b/k4ever/buyable/models.py index 3916bc5..363c1d2 100644 --- a/k4ever/buyable/models.py +++ b/k4ever/buyable/models.py @@ -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 ` + """ Represents an order by the user, consists of several :class:`Purchases `. """ 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) - diff --git a/k4ever/buyable/views.py b/k4ever/buyable/views.py index 60f79ef..1181570 100644 --- a/k4ever/buyable/views.py +++ b/k4ever/buyable/views.py @@ -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) diff --git a/k4ever/docs/django/api.rst b/k4ever/docs/django/api.rst index 66d06a9..ed8c49e 100644 --- a/k4ever/docs/django/api.rst +++ b/k4ever/docs/django/api.rst @@ -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 ` +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 +` object will be created to keep track of this. +Each :class:`PluginPermission ` object has an +:attr:`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 `). + +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 ` Set authblob if allowed + + .. _authblob-authentication: + :class:`user/ ` :func:`GET ` 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 -^^^^^^^^^^^ - diff --git a/k4ever/main/backend.py b/k4ever/main/backend.py deleted file mode 100644 index 93f72f0..0000000 --- a/k4ever/main/backend.py +++ /dev/null @@ -1,5 +0,0 @@ -from django_auth_ldap.backend import LDAPBackend - -CustomLDAPBackend(LDAPBackend): - def populate_user(username): - diff --git a/k4ever/main/fields.py b/k4ever/main/fields.py index 9460520..b2f1ea4 100644 --- a/k4ever/main/fields.py +++ b/k4ever/main/fields.py @@ -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]?)?$') diff --git a/k4ever/main/models.py b/k4ever/main/models.py index 107363e..3187007 100644 --- a/k4ever/main/models.py +++ b/k4ever/main/models.py @@ -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 `. + 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): diff --git a/k4ever/main/views.py b/k4ever/main/views.py index ed73fd5..e539c7e 100644 --- a/k4ever/main/views.py +++ b/k4ever/main/views.py @@ -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 `. """ if request.method != "POST": return HttpResponseRedirect("/user/settings/") plugin = None diff --git a/k4ever/transaction/forms.py b/k4ever/transaction/forms.py index 1bf71fe..90f7bda 100644 --- a/k4ever/transaction/forms.py +++ b/k4ever/transaction/forms.py @@ -3,6 +3,7 @@ from models import Transaction from main.fields import CurrencyField class TransactionForm(forms.ModelForm): + """ ModelForm for :class:`Transactions ` with a currency field """ amount = CurrencyField(label='Betrag') class Meta: model = Transaction diff --git a/k4ever/transaction/models.py b/k4ever/transaction/models.py index 3916c0e..3b8afcd 100644 --- a/k4ever/transaction/models.py +++ b/k4ever/transaction/models.py @@ -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() diff --git a/k4ever/transaction/views.py b/k4ever/transaction/views.py index b79f9c5..4276a6e 100644 --- a/k4ever/transaction/views.py +++ b/k4ever/transaction/views.py @@ -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)) \ No newline at end of file