;--Doctest--

Grouparchy Groups

Grouparchy uses objects in context to represent structure and relationships of individuals. Objects representing the application specific context of members are Grouparchy contexts. Objects representing individuals within contexts are Grouparchy members.

Grouparchy groups aren't necessarily about groups of users. They can be used to represent the structure and relationships of any set of entities where the same entity has different involvements in different contexts. One example might be a fleet of vehicles which are used in different ways in different contexts. One might wish to reflect that those different uses necessitate different maintenance schedules.

Similarly, Grouparchy is agnostic as to the system it is applied to and is intended to use the structure represented in an existing system to represent the relationships of individuals in context. To that end, grouparchy.group provides no implementation of contexts or members. The basic contract that must be fulfilled is that a context must be able to provide it's members and members must be able to provide the contexts they belong to.

The most common implementation of Grouparchy will probably involve contexts that are containers and members that are added to those containers with a reference to the container they live in. Also, an adapter providing IMemberProspects for a given context must be available so that a context knows what objects might be members.

A given group type designates a kind of group. Context type and member type pairs are registered for group types. A group of that type is the collection of members of that member type within a context of that context type.

Group Types

A group type is an interface that provides IGroupType:

>>> from grouparchy.group import interfaces
>>> interfaces.IGroupType.providedBy(interfaces.IGroup)
True

Unlike member types and context types, group types are not intended to mark content, but rather are looked up from contexts or combinations of contexts and members or identities. As such, a group type must be registered with a context type and a member type:

>>> import grouparchy.context.interfaces
>>> import grouparchy.member.interfaces
>>> from grouparchy.group import types
>>> types.registerGroupType(
...     group_type=interfaces.IGroup,
...     context_type=grouparchy.context.interfaces.IContext,
...     member_type=grouparchy.member.interfaces.IMember)

A group is an adapter that adapts a context and provides the group type:

>>> import grouparchy.context.interfaces
>>> import grouparchy.context.testing
>>> context = grouparchy.context.testing.Context('context')
>>> grouparchy.context.interfaces.IContextTypes(
...     context).contexttypes = (
...         grouparchy.context.interfaces.IContext,)

>>> from zope import component
>>> group = interfaces.IGroup(context)
>>> interfaces.IGroup.providedBy(group)
True
>>> group.context is context
True

The members of a group are those objects in a context's IMemberProspects that provide the member type of the group type:

>>> import grouparchy.member.testing
>>> import grouparchy.member.interfaces
>>> member = grouparchy.member.testing.createIdenfifiedMember(
...     'member')
>>> grouparchy.member.interfaces.IMemberTypes(
...     member).membertypes = (
...         grouparchy.member.interfaces.IMember,)
>>> context.append(member)

>>> from grouparchy.member.testing import Content
>>> non_member = Content('non_member')
>>> context.append(non_member)
>>> (first, second) = context
>>> first is member
True
>>> second is non_member
True

>>> (only,) = group
>>> only is member
True

Groups can also be looked up by context and member:

>>> group = component.getMultiAdapter(
...     (context, member), interfaces.IGroup)
>>> group.context is context
True

Likewise, an iterator over all the groups of all the relevant group types and member types can be retrieved for a given context and member identity:

>>> identity = grouparchy.identity.interfaces.IIdentity(member)
>>> (only,) = component.getMultiAdapter(
...     (context, identity), interfaces.IGroups)
>>> only.context is context
True