diff --git a/README.txt b/README.txt index d51602c..1e0721b 100755 --- a/README.txt +++ b/README.txt @@ -3,17 +3,25 @@ loops - Linked Objects for Organizational Process Services ($Id$) -Concepts and Relations -~~~~~~~~~~~~~~~~~~~~~~ +Concepts and Relations between them +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Let's start with creating a few example concepts: +Let's start with creating a few example concepts, putting them in a +top-level loops container and a concept manager: - >>> from loops.concept import Concept + >>> from loops import Loops + >>> loops = Loops() + + >>> from loops.concept import ConceptManager, Concept + >>> loops['concepts'] = ConceptManager() + >>> concepts = loops['concepts'] >>> c1 = Concept() + >>> concepts['c1'] = c1 >>> c1.title u'' >>> c2 = Concept(u'c2', u'Second Concept') + >>> concepts['c2'] = c2 >>> c2.title u'Second Concept' @@ -33,7 +41,7 @@ We also need a Relation class to be used for connecting concepts: Now we can assign the concept c2 to c1: - >>> c1.assignConcept(c2, DyadicRelation) + >>> c1.assignConcept(c2) We can now ask our concepts for their related concepts: diff --git a/__init__.py b/__init__.py index 4bc90fb..72219eb 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,33 @@ +# -*- coding: UTF-8 -*- +# -*- Mode: Python; py-indent-offset: 4 -*- +# +# Copyright (c) 2005 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + """ + $Id$ """ +from zope.interface import implements +from zope.app.folder.folder import Folder +from interfaces import ILoops + +class Loops(Folder): + + implements(ILoops) + diff --git a/browser/configure.zcml b/browser/configure.zcml index e565a7e..e58a2ca 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -6,6 +6,50 @@ i18n_domain="zope" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/concept.py b/concept.py index d06813c..33db9a5 100644 --- a/concept.py +++ b/concept.py @@ -23,6 +23,7 @@ $Id$ """ from zope.app import zapi +from zope.app.container.btree import BTreeContainer from zope.app.container.contained import Contained from zope.interface import implements from persistent import Persistent @@ -31,11 +32,14 @@ from cybertools.relation.interfaces import IRelationsRegistry from cybertools.relation import DyadicRelation from interfaces import IConcept +from interfaces import IConceptManager, IConceptManagerContained +from interfaces import ILoopsContained +from relations import ConceptRelation -class Concept(Persistent, Contained): +class Concept(Contained, Persistent): - implements(IConcept) + implements(IConcept, IConceptManagerContained) _title = u'' def getTitle(self): return self._title @@ -56,7 +60,7 @@ class Concept(Persistent, Contained): rels = getRelations(second=self, relationships=relationships) return [r.first for r in rels] - def assignConcept(self, concept, relationship): + def assignConcept(self, concept, relationship=ConceptRelation): registry = zapi.getUtility(IRelationsRegistry) registry.register(relationship(self, concept)) # TODO (?): avoid duplicates @@ -65,7 +69,12 @@ class Concept(Persistent, Contained): pass # TODO -# TODO: move this to the relation package +class ConceptManager(BTreeContainer): + + implements(IConceptManager, ILoopsContained) + + +# TODO: move this to the cybertools.relation package def getRelations(first=None, second=None, third=None, relationships=None): registry = zapi.getUtility(IRelationsRegistry) diff --git a/configure.zcml b/configure.zcml index fb3fb48..0eca8e3 100644 --- a/configure.zcml +++ b/configure.zcml @@ -7,7 +7,51 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - @@ -24,10 +66,6 @@ id="loops.Concept" description="Concept object" /> - - @@ -38,6 +76,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interfaces.py b/interfaces.py index 5212968..4056469 100644 --- a/interfaces.py +++ b/interfaces.py @@ -25,20 +25,28 @@ $Id$ from zope.interface import Interface from zope.i18nmessageid import MessageFactory from zope import schema +from zope.app.container.constraints import contains, containers +from zope.app.container.interfaces import IContainer +from zope.app.file.interfaces import IFile as IBaseFile +from zope.app.folder.interfaces import IFolder _ = MessageFactory('loops') +# concept interfaces + class IConcept(Interface): - """ The 'concept' is the central element of the loops framework. - A concept is related to other concepts. + """ The concept is the central element of the loops framework. + + A concept is related to other concepts, may have resources + associated with it and may be referenced by views. """ title = schema.TextLine( title=_(u'Title'), - description=_(u'Name or short title of the concept'), + description=_(u'Title of the concept'), default=u'', - required=True) + required=False) def getSubConcepts(relationships=None): """ Return a tuple of concepts related to self as sub-concepts, @@ -62,3 +70,115 @@ class IConcept(Interface): restricting them to the relationships given. """ +class IConceptManager(IContainer): + """ A manager/container for concepts. + """ + contains(IConcept) + + +class IConceptManagerContained(Interface): + containers(IConceptManager) + + +# resource interfaces + +class IResource(Interface): + """ A resource is an atomic information element that is usually + available via a concept. + """ + + title = schema.TextLine( + title=_(u'Title'), + description=_(u'Title of the document'), + required=False) + + +class IDocument(IResource): + """ A resource containing an editable body. + """ + + body = schema.Text( + title=_(u'Body'), + description=_(u'Body of the document'), + required=False) + + format = schema.TextLine( + title=_(u'Format'), + description=_(u'Format of the body field, default is "text/xml"'), + default=_(u'text/xml'), + required=False) + + +class IFile(IResource, IBaseFile): + """ A resource containing a (typically binary) file-like content + or an image. + """ + + +class IResourceManager(IContainer): + """ A manager/container for resources. + """ + contains(IResource) + + +class IResourceManagerContained(Interface): + containers(IResourceManager) + + +# view interfaces + +class IView(Interface): + """ A view is a user interface component that provides access to one + or more concepts. + """ + + title = schema.TextLine( + title=_(u'Title'), + description=_(u'Title of the view; this will appear e.g. in menus'), + default=u'', + required=True) + + description = schema.Text( + title=_(u'Description'), + description=_(u'Detailed description, e.g. for tooltips or listings'), + default=u'', + required=False) + + def getConcepts(): + """ Return an ordered sequence of concepts referenced by this view. + """ + + +class INode(IContainer): + """ A node is a view that may contain other views, thus building a + menu or folder hierarchy. + """ + contains(IView) + + +class IViewManager(IContainer): + """ A manager/container for views. + """ + contains(IView) + + +class INodeContained(Interface): + containers(INode, IViewManager) + + +class IViewManagerContained(Interface): + containers(IViewManager) + + +# the loops top-level container + +class ILoops(IFolder): + """ The top-level object of a loops site. + """ + contains(IConceptManager, IResourceManager, IViewManager) + + +class ILoopsContained(Interface): + containers(ILoops) + + diff --git a/relations.py b/relations.py new file mode 100644 index 0000000..8a0688a --- /dev/null +++ b/relations.py @@ -0,0 +1,44 @@ +# +# Copyright (c) 2005 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Definition of relation classes. + +$Id$ +""" + +from zope.app import zapi +from zope.interface import implements + +from cybertools.relation import DyadicRelation + + +class ConceptRelation(DyadicRelation): + """ A relation between concept objects. + """ + + +class ResourceRelation(DyadicRelation): + """ A relation between concept and resource objects. + """ + + +class ViewRelation(DyadicRelation): + """ A relation between view and concept objects. + """ + diff --git a/resource.py b/resource.py new file mode 100644 index 0000000..fc582d0 --- /dev/null +++ b/resource.py @@ -0,0 +1,68 @@ +# +# Copyright (c) 2005 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Definition of the Concept class. + +$Id$ +""" + +from zope.app import zapi +from zope.app.container.btree import BTreeContainer +from zope.app.container.contained import Contained +from zope.interface import implements +from persistent import Persistent + +from interfaces import IResource, IDocument +from interfaces import IResourceManager, IResourceManagerContained +from interfaces import ILoopsContained + + +class Resource(Contained, Persistent): + + implements(IResource, IResourceManagerContained) + + _title = u'' + def getTitle(self): return self._title + def setTitle(self, title): self._title = title + title = property(getTitle, setTitle) + + def __init__(self, name=None, title=u''): + self.title = title + + +class Document(Resource): + + implements(IDocument) + + _body = u'' + def setBody(self, body): self._body = body + def getBody(self): return self._body + body = property(getBody, setBody) + + _format = u'text/xml' + def setFormat(self, format): self._format = format + def getFormat(self): return self._format + format = property(getFormat, setFormat) + + +class ResourceManager(BTreeContainer): + + implements(IResourceManager, ILoopsContained) + + diff --git a/tests.py b/tests.py index 372e44b..02de377 100755 --- a/tests.py +++ b/tests.py @@ -8,15 +8,21 @@ from zope.interface import implements from zope.app import zapi from zope.app.intid.interfaces import IIntIds -from interfaces import IConcept -from concept import Concept +from interfaces import ILoops +from loops import Loops +from interfaces import IConcept, IConceptManager +from loops.concept import Concept, ConceptManager class Test(unittest.TestCase): "Basic tests for the loops package." def testInterfaces(self): + verifyClass(ILoops, Loops) + self.assert_(ILoops.providedBy(Loops())) verifyClass(IConcept, Concept) self.assert_(IConcept.providedBy(Concept())) + verifyClass(IConceptManager, ConceptManager) + self.assert_(IConceptManager.providedBy(ConceptManager())) def test_suite():