<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><Project><name>collective.beancounter</name>
<shortdesc>package to display a fill-percentage for AT based content</shortdesc>
<description>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
------------

::

    &gt;&gt;&gt; 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::

    &gt;&gt;&gt; from zope import interface
    &gt;&gt;&gt; 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::

    &gt;&gt;&gt; class IBeanCounter(interface.Interface):
    ...     percentage = interface.Attribute(u"The percentage filled")

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

    &gt;&gt;&gt; _ = self.folder.invokeFactory("Document", "doc")
    &gt;&gt;&gt; doc = self.folder.get(_)

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

    &gt;&gt;&gt; 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?::

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

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

    &gt;&gt;&gt; [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::

    &gt;&gt;&gt; from collective.beancounter.adapter import ATBeanCounter
    &gt;&gt;&gt; ct = ATBeanCounter(doc)
    &gt;&gt;&gt; print ct.percentage
    25.0

Fill out completely::

    &gt;&gt;&gt; doc.update( title="muha", text="haha", description="desc")
    &gt;&gt;&gt; ct = ATBeanCounter(doc)
    &gt;&gt;&gt; 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"::

    &gt;&gt;&gt; from collective.beancounter.interfaces import IBeanCounterFieldFilter

We provide adefault adapter for AT objects::

    &gt;&gt;&gt; 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")

::

    &gt;&gt;&gt; IBeanCounterFieldFilter(doc)
    &lt;collective.beancounter.adapter.ATFieldFilter object at ...&gt;

Lets test that::

    &gt;&gt;&gt; from collective.beancounter.adapter import countable_fields
    &gt;&gt;&gt; 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::

    &gt;&gt;&gt; from zope import component
    &gt;&gt;&gt; from Products.Archetypes.interfaces import IBaseObject
    &gt;&gt;&gt; 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()
    &gt;&gt;&gt; component.provideAdapter(TestFilter)

We should now get our adapter::

    &gt;&gt;&gt; IBeanCounterFieldFilter(doc)
    &lt;TestFilter object at ...&gt;

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

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

 vim: set ft=rst tw=75 nocin nosi ai sw=4 ts=4 expandtab:</description>
<homepage rdf:resource="https://svn.plone.org/svn/collective/collective.beancounter/trunk" />
<maintainer><foaf:Person><foaf:name>Stefan Eletzhofer</foaf:name>
<foaf:mbox_sha1sum>48dc380c018dee1237a13c8c975ac9144b3bee18</foaf:mbox_sha1sum></foaf:Person></maintainer>
<release><Version><revision>0.3.1</revision></Version></release>
</Project></rdf:RDF>