collective.beancounter
======================

:Author:    $Author: seletz $
:Date:      $Date: 2007-11-14 19:46:16 +0100 (Wed, 14 Nov 2007) $
:Revision:  $Revision: 53844 $

Abstract
--------

This package scratches an itch of mine in providing a very simple viewlet
which displays the percentage a content is filled by an user.

setup stuff
------------

::

    >>> class Mock(object):
    ...    def __init__(self, **kw): self.__sict__.update(kw)

Blurb
-----

First we define an interface to be used to mark bean-countable content::

    >>> from zope import interface
    >>> class IBeanContable(interface.Interface):
    ...     """ a content which is bean countable """

The counting itself is very simple and done by an adapter. We simply count
which fields in the **default** schemata are filled. We there count only
the writable fields. From that we calculate a percentage.

Lets define a interface for that functionality::

    >>> class IBeanCounter(interface.Interface):
    ...     percentage = interface.Attribute(u"The percentage filled")

Now lets create some content class to test our stuff::

    >>> _ = self.folder.invokeFactory("Document", "doc")
    >>> doc = self.folder.get(_)

Count the fields which are in the **default** schemata and are rewd/write::

    >>> len([f for f in doc.Schema().fields() if f.schemata=="default" and f.mode =="rw"])
    4

Ok, now how many of them are filled?::

    >>> l = [f for f in doc.Schema().fields() if f.schemata=="default" and f.mode =="rw"]
    >>> [f.getName() for f in l if f.get(doc)]
    ['id']

Ok, fair enough. Now lets do the opposite::

    >>> [f.getName() for f in l if not f.get(doc)]
    ['title', 'description', 'text']

Ok, thats enough. Lets wrap it up.

Implementation
--------------

We have an adapter::

    >>> from collective.beancounter.adapter import ATBeanCounter
    >>> ct = ATBeanCounter(doc)
    >>> print ct.percentage
    25.0

Fill out completely::

    >>> doc.update( title="muha", text="haha", description="desc")
    >>> ct = ATBeanCounter(doc)
    >>> print ct.percentage
    100.0

Yay.

Field filter
------------

You may provide an adapter to specify a filter to decide which
fields you consider to be "countable"::

    >>> from collective.beancounter.interfaces import IBeanCounterFieldFilter

We provide adefault adapter for AT objects::

    >>> from collective.beancounter.adapter import ATFieldFilter

That adapter provides a filter which filters out fields which:
      - are not user settable
      - are not in the "default" schemata
      - are not in the special plone field blacklist
      - are not boolean fields (these are true or false, i.e. always "filled")

::

    >>> IBeanCounterFieldFilter(doc)
    <collective.beancounter.adapter.ATFieldFilter object at ...>

Lets test that::

    >>> from collective.beancounter.adapter import countable_fields
    >>> sorted([f.getName() for f in countable_fields(doc)])
    ['description', 'id', 'text', 'title']

Ok, that filters nothing, which is fine. Now lets provide an adapter
which does filter out the "title","id" and "description" fields::

    >>> from zope import component
    >>> from Products.Archetypes.interfaces import IBaseObject
    >>> class TestFilter(object):
    ...     component.adapts(IBaseObject)
    ...     interface.implements(IBeanCounterFieldFilter)
    ...     def __init__(self,context): self.context = context
    ...     def __call__(self, field):
    ...         return field.getName() not in "title id description".split()
    >>> component.provideAdapter(TestFilter)

We should now get our adapter::

    >>> IBeanCounterFieldFilter(doc)
    <TestFilter object at ...>

We should now get ALL fields but the ones we filtered out::

    >>> set("title id description".split()) & set([f.getName() for f in countable_fields(doc)]) 
    set([])
    
 
::

 vim: set ft=rst tw=75 nocin nosi ai sw=4 ts=4 expandtab:
