set up knowledge stuff for loops

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1230 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-05-26 20:22:07 +00:00
parent 98c195492e
commit 8423fcea57
14 changed files with 569 additions and 9 deletions

View file

@ -142,8 +142,12 @@ a special concept type.
>>> from loops.concept import PredicateSourceList >>> from loops.concept import PredicateSourceList
>>> predicates = PredicateSourceList(cc1) >>> predicates = PredicateSourceList(cc1)
Note that the 'hasType' predicate is suppressed from this list as the
corresponding relation is only assigned via the conceptType attribute:
>>> sorted(t.title for t in predicates) >>> sorted(t.title for t in predicates)
[u'has type', u'subconcept'] [u'subconcept']
Concept Views Concept Views
------------- -------------
@ -209,8 +213,7 @@ types and predicates.
(u'Unknown Type', '.loops/concepts/unknown')] (u'Unknown Type', '.loops/concepts/unknown')]
>>> sorted((t.title, t.token) for t in view.predicates()) >>> sorted((t.title, t.token) for t in view.predicates())
[(u'has type', '.loops/concepts/hasType'), [(u'subconcept', '.loops/concepts/standard')]
(u'subconcept', '.loops/concepts/standard')]
Index attributes adapter Index attributes adapter
------------------------ ------------------------

View file

@ -272,6 +272,10 @@ class ConceptRelationView(BaseView):
return ':'.join((self.loopsRoot.getLoopsUri(self.context), return ':'.join((self.loopsRoot.getLoopsUri(self.context),
self.loopsRoot.getLoopsUri(self.predicate))) self.loopsRoot.getLoopsUri(self.predicate)))
@Lazy
def isProtected(self):
return zapi.getName(self.predicate) == 'hasType'
@Lazy @Lazy
def predicateTitle(self): def predicateTitle(self):
return self.predicate.title return self.predicate.title

View file

@ -32,6 +32,7 @@
<td class="field"> <td class="field">
<input class="formSelection" <input class="formSelection"
type="checkbox" name="tokens:list" id="#" value="" type="checkbox" name="tokens:list" id="#" value=""
tal:condition="not:item/isProtected|nothing"
tal:attributes="value item/token" /> tal:attributes="value item/token" />
</td> </td>
<td> <td>

View file

@ -271,11 +271,12 @@ class PredicateSourceList(object):
typePred = cm.getTypePredicate() typePred = cm.getTypePredicate()
if defPred is not None and typePred is not None: if defPred is not None and typePred is not None:
result.append(defPred) result.append(defPred)
result.append(typePred) #result.append(typePred)
predType = defPred.conceptType predType = defPred.conceptType
if predType is not None and predType != cm.getTypeConcept(): if predType is not None and predType != cm.getTypeConcept():
result.extend(p for p in predType.getChildren([typePred]) result.extend(p for p in predType.getChildren([typePred])
if p not in result) if p not in result
and p != typePred)
return result return result
def __len__(self): def __len__(self):

207
knowledge/README.txt Normal file
View file

@ -0,0 +1,207 @@
===============================================================
loops - Linked Objects for Organization and Processing Services
===============================================================
($Id$)
Note: This package depends on cybertools.knowledge and cybertools.organize.
Let's do some basic set up
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
>>> from zope import component, interface
>>> from zope.app import zapi
and setup a simple loops site with a concept manager and some concepts
(with all the type machinery, what in real life is done via standard
ZCML setup):
>>> from loops import Loops
>>> from loops.concept import ConceptManager, Concept
>>> from loops.resource import ResourceManager
>>> from loops.interfaces import IResource, IConcept, ITypeConcept
>>> loopsRoot = site['loops'] = Loops()
>>> from cybertools.relation.interfaces import IRelationRegistry
>>> from cybertools.relation.registry import DummyRelationRegistry
>>> relations = DummyRelationRegistry()
>>> component.provideUtility(relations, IRelationRegistry)
>>> from cybertools.typology.interfaces import IType
>>> from loops.type import ConceptType, TypeConcept
>>> component.provideAdapter(ConceptType, (IConcept,), IType)
>>> component.provideAdapter(TypeConcept, (IConcept,), ITypeConcept)
>>> concepts = loopsRoot['concepts'] = ConceptManager()
>>> resources = loopsRoot['resources'] = ResourceManager()
>>> hasType = concepts['hasType'] = Concept(u'has type')
>>> type = concepts['type'] = Concept(u'Type')
>>> type.conceptType = type
>>> predicate = concepts['predicate'] = Concept(u'Predicate')
>>> predicate.conceptType = type
>>> hasType.conceptType = predicate
We need some predicates to set up the relationships between our concepts:
>>> standard = concepts['standard'] = Concept(u'subobject')
>>> depends = concepts['depends'] = Concept(u'depends')
>>> knows = concepts['knows'] = Concept(u'knows')
>>> requires = concepts['requires'] = Concept(u'requires')
>>> provides = concepts['provides'] = Concept(u'provides')
>>> for p in (standard, depends, knows, requires, provides):
... p.conceptType = predicate
And last not least we need some type concepts for controlling the
meaning of the concepts objects:
>>> from cybertools.knowledge.interfaces import IKnowledgeElement
>>> topic = concepts['topic'] = Concept(u'Topic')
>>> topic.conceptType = type
>>> ITypeConcept(topic).typeInterface = IKnowledgeElement
>>> from loops.knowledge.interfaces import IPerson
>>> person = concepts['person'] = Concept(u'Person')
>>> person.conceptType = type
>>> ITypeConcept(person).typeInterface = IPerson
>>> from loops.knowledge.interfaces import ITask
>>> task = concepts['task'] = Concept(u'Task')
>>> task.conceptType = type
>>> ITypeConcept(task).typeInterface = ITask
Manage knowledge and knowledge requirements
===========================================
The classes used in this package are just adapters to IConcept.
>>> from loops.knowledge.knowledge import Person, Topic, Task
>>> component.provideAdapter(Person, (IConcept,), IPerson)
>>> component.provideAdapter(Topic, (IConcept,), IKnowledgeElement)
>>> component.provideAdapter(Task, (IConcept,), ITask)
First we want to set up a tree of knowledge elements (topics) and their
interdependencies. Note that in order to discern the concepts created
from their typeInterface adapters we here append a 'C' to the name of
the variables:
>>> progLangC = concepts['progLang'] = Concept(u'Programming Language')
>>> ooProgC = concepts['ooProg'] = Concept(u'Object-oriented Programming')
>>> pythonC = concepts['python'] = Concept(u'Python')
>>> pyBasicsC = concepts['pyBasics'] = Concept(u'Python Basics')
>>> pyOoC = concepts['pyOo'] = Concept(u'OO Programming with Python')
>>> pySpecialsC = concepts['pySpecials'] = Concept(u'Python Specials')
>>> topicConcepts = (progLangC, ooProgC, pythonC, pyBasicsC, pyOoC, pySpecialsC)
>>> for c in topicConcepts: c.conceptType = topic
>>> progLang, ooProg, python, pyBasics, pyOo, pySpecials = (IKnowledgeElement(c)
... for c in topicConcepts)
>>> python.parent = progLang
>>> pyBasics.parent = python
>>> pyOo.parent = python
>>> pySpecials.parent = python
>>> pyOo.dependsOn(ooProg)
>>> pyOo.dependsOn(pyBasics)
We now create a person and assign some knowledge to it:
>>> johnC = concepts['john'] = Concept(u'John')
>>> johnC.conceptType = person
>>> john = IPerson(johnC)
>>> john.knows(pyBasics)
>>> list(john.getKnowledge())[0].title
u'Python Basics'
Now let's get to tasks - a task is used as a requirement profile, i.e.
it requires a certain set of knowledge elements:
>>> task01C = concepts['task01C'] = Concept('Task: needs Python OO')
>>> task01C.conceptType = task
>>> task01 = ITask(task01C)
>>> task01.requires(pyOo)
Now we can ask what knowledge john is lacking if he would like to take
a position with the requirement profile:
>>> missing = john.getMissingKnowledge(task01)
>>> [m.title for m in missing]
[u'Object-oriented Programming', u'OO Programming with Python']
Luckily there are a few elearning content objects out there that
provide some of the knowledge needed:
>>> from cybertools.knowledge.interfaces import IKnowledgeProvider
>>> from loops.knowledge.knowledge import ConceptKnowledgeProvider
>>> component.provideAdapter(ConceptKnowledgeProvider, (IConcept,))
>>> from loops.knowledge.knowledge import ResourceKnowledgeProvider
>>> component.provideAdapter(ResourceKnowledgeProvider, (IResource,))
>>> doc01C = concepts['doc01'] = Concept('Objectorienting Programming')
>>> doc01 = IKnowledgeProvider(doc01C)
>>> from loops.resource import Document
>>> doc02D = resources['doc02'] = Document('oopython.pdf')
>>> doc02 = IKnowledgeProvider(doc02D)
>>> doc01.provides(ooProg)
>>> doc02.provides(pyOo)
So that we are now able to find out what john has to study in order to
fulfill the position offered:
>>> prov = list(john.getProvidersNeeded(task01))
>>> len(prov)
2
>>> [list(d)[0].title for k, d in prov]
['Objectorienting Programming', 'oopython.pdf']
Views that make use of the knowledge management modules
-------------------------------------------------------
One of the practical applications of this stuff is searching for missing
knowledge and corresponding knowledge providers for the user currently
logged in.
For testing, we first have to provide the needed utilities and settings
(in real life this is all done during Zope startup):
>>> from zope.app.security.interfaces import IAuthentication
>>> from zope.app.security.principalregistry import PrincipalRegistry
>>> auth = PrincipalRegistry()
>>> component.provideUtility(auth, IAuthentication)
>>> from zope.app.principalannotation.interfaces import IPrincipalAnnotationUtility
>>> from zope.app.principalannotation import PrincipalAnnotationUtility
>>> principalAnnotations = PrincipalAnnotationUtility()
>>> component.provideUtility(principalAnnotations, IPrincipalAnnotationUtility)
>>> principal = auth.definePrincipal('users.john', u'John', login='john')
>>> john.userId = 'users.john'
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> request.setPrincipal(principal)
>>> from loops.knowledge.browser import MyKnowledge
>>> view = MyKnowledge(task01C, request)
>>> prov = view.myKnowledgeProvidersForTask()
Fin de partie
=============
>>> placefulTearDown()

4
knowledge/__init__.py Normal file
View file

@ -0,0 +1,4 @@
"""
$Id$
"""

64
knowledge/browser.py Normal file
View file

@ -0,0 +1,64 @@
#
# Copyright (c) 2004 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 view classes and other browser related stuff for the
loops.knowledge package.
$Id$
"""
from zope import interface, component
from zope.app import zapi
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.formlib.namedtemplate import NamedTemplate
from zope.i18nmessageid import MessageFactory
from cybertools.typology.interfaces import IType
from loops.browser.common import BaseView
from loops.knowledge.interfaces import IPerson, ITask
from loops.organize.browser import getPersonForLoggedInUser
_ = MessageFactory('zope')
class MyKnowledge(BaseView):
def __init__(self, context, request):
self.context = context
self.request = request
person = getPersonForLoggedInUser(request)
if person is not None:
person = IPerson(person)
self.person = person
def myKnowledge(self):
if self.person is None:
return ()
knowledge = self.person.getKnowledge()
return knowledge
def myKnowledgeProvidersForTask(self):
if self.person is None:
return ()
task = ITask(self.context)
# TODO: check correct conceptType for context!
providers = self.person.getProvidersNeeded(task)
return providers

20
knowledge/configure.zcml Normal file
View file

@ -0,0 +1,20 @@
<!-- $Id$ -->
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="zope"
>
<!-- knowledge/learning management stuff -->
<zope:adapter factory="loops.knowledge.knowledge.Person"
provides="loops.knowledge.interfaces.IPerson"
trusted="True" />
<zope:class class="loops.knowledge.knowledge.Person">
<require permission="zope.View"
interface="loops.knowledge.interfaces.IPerson" />
</zope:class>
</configure>

45
knowledge/interfaces.py Normal file
View file

@ -0,0 +1,45 @@
#
# Copyright (c) 2006 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
#
"""
Interfaces for knowledge management and elearning with loops.
$Id$
"""
from zope.interface import Interface, Attribute
from zope import interface, component, schema
from zope.app import zapi
from zope.i18nmessageid import MessageFactory
from zope.security.proxy import removeSecurityProxy
from cybertools.knowledge.interfaces import IKnowing, IRequirementProfile
from cybertools.organize.interfaces import IPerson as IBasePerson
from cybertools.organize.interfaces import ITask as IBaseTask
_ = MessageFactory('zope')
class IPerson(IBasePerson, IKnowing):
""" A person, including knowledge/learning management features.
"""
class ITask(IBaseTask, IRequirementProfile):
""" A task, also acting as a knowledge requirement profile.
"""

183
knowledge/knowledge.py Normal file
View file

@ -0,0 +1,183 @@
#
# Copyright (c) 2006 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
#
"""
Adapters for IConcept providing interfaces from the
cybertools.knowledge package.
$Id$
"""
from zope import interface, component
from zope.app import zapi
from zope.component import adapts
from zope.interface import implements
from zope.cachedescriptors.property import Lazy
from cybertools.typology.interfaces import IType
from cybertools.knowledge.interfaces import IKnowledgeElement, IKnowledgeProvider
from cybertools.knowledge.knowing import Knowing
from loops.interfaces import IConcept
from loops.knowledge.interfaces import IPerson, ITask
from loops.organize.party import Person as BasePerson
from loops.organize.task import Task as BaseTask
from loops.type import TypeInterfaceSourceList, AdapterBase
# register type interfaces - (TODO: use a function for this)
TypeInterfaceSourceList.typeInterfaces += (IPerson, IKnowledgeElement, ITask)
class KnowledgeAdapterMixin(object):
@Lazy
def conceptManager(self):
return self.context.getLoopsRoot().getConceptManager()
@Lazy
def standardPred(self):
return self.conceptManager.getDefaultPredicate()
@Lazy
def dependsPred(self):
return self.conceptManager['depends']
@Lazy
def knowsPred(self):
return self.conceptManager['knows']
@Lazy
def requiresPred(self):
return self.conceptManager['requires']
@Lazy
def providesPred(self):
return self.conceptManager['provides']
def __eq__(self, other):
return self.context == other.context
class Person(BasePerson, Knowing, KnowledgeAdapterMixin):
""" A typeInterface adapter for concepts of type 'person', including
knowledge/learning management features.
"""
implements(IPerson)
def getKnowledge(self):
return (IKnowledgeElement(c)
for c in self.context.getParents((self.knowsPred,)))
def knows(self, obj):
self.context.assignParent(obj.context, self.knowsPred)
def removeKnowledge(self, obj):
self.context.deassignParent(obj.context, (self.knowsPred,))
class Topic(AdapterBase, KnowledgeAdapterMixin):
""" A typeInterface adapter for concepts of type 'topic' that
may act as a knowledge element.
"""
implements(IKnowledgeElement)
_attributes = ('context', '__parent__', 'parent')
def getParent(self):
parents = self.context.getParents((self.standardPred,))
return parents and IKnowledgeElement(parents[0]) or None
def setParent(self, obj):
old = self.getParent()
if old is not None and old.context != self.context:
self.context.deassignParent(old.context, (self.standardPred,))
self.context.assignParent(obj.context, self.standardPred)
parent = property(getParent, setParent)
def getDependencies(self):
return (IKnowledgeElement(c)
for c in self.context.getParents((self.dependsPred,)))
def dependsOn(self, obj):
self.context.assignParent(obj.context, self.dependsPred)
def removeDependency(self, obj):
self.context.deassignParent(obj.context, (self.dependsPred,))
def getDependents(self):
return (IKnowledgeElement(c)
for c in self.context.getChildren((self.dependsPred,)))
def getKnowers(self):
return (IPerson(c)
for c in self.context.getChildren((self.knowsPred,)))
def getProviders(self):
return (IKnowledgeProvider(c)
for c in self.context.getChildren((self.providesPred,))
+ self.context.getResources((self.providesPred,)))
class Task(BaseTask, KnowledgeAdapterMixin):
""" A typeInterface adapter for concepts of type 'task' that
may act as a knowledge requirement profile.
"""
implements(ITask)
def getRequirements(self):
return (IKnowledgeElement(c)
for c in self.context.getParents((self.requiresPred,)))
def requires(self, obj):
self.context.assignParent(obj.context, self.requiresPred)
def removeRequirement(self, obj):
self.context.deassignParent(obj.context, (self.requiresPred,))
class ConceptKnowledgeProvider(AdapterBase, KnowledgeAdapterMixin):
implements(IKnowledgeProvider)
def getProvidedKnowledge(self):
return (IKnowledgeElement(c)
for c in self.context.getParents((self.providesPred,)))
def provides(self, obj):
self.context.assignParent(obj.context, self.providesPred)
def removeProvidedKnowledge(self, obj):
self.context.deassignParent(obj.context, (self.providesPred,))
class ResourceKnowledgeProvider(AdapterBase, KnowledgeAdapterMixin):
implements(IKnowledgeProvider)
def getProvidedKnowledge(self):
return (IKnowledgeElement(c)
for c in self.context.getConcepts((self.providesPred,)))
def provides(self, obj):
self.context.assignConcept(obj.context, self.providesPred)
def removeProvidedKnowledge(self, obj):
self.context.deassignConcept(obj.context, (self.providesPred,))

24
knowledge/tests.py Executable file
View file

@ -0,0 +1,24 @@
# $Id$
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
from zope.app.testing import ztapi
from zope.interface.verify import verifyClass
from loops.organize.party import Person
class Test(unittest.TestCase):
"Basic tests for the knowledge sub-package."
def testSomething(self):
pass
def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite((
unittest.makeSuite(Test),
DocFileSuite('README.txt', optionflags=flags),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View file

@ -44,13 +44,17 @@ from loops.organize.interfaces import IMemberRegistration
_ = MessageFactory('zope') _ = MessageFactory('zope')
def getPersonForLoggedInUser(request):
pa = annotations(request.principal)
return pa.get(ANNOTATION_KEY, None)
class MyStuff(ConceptView): class MyStuff(ConceptView):
def __init__(self, context, request): def __init__(self, context, request):
self.context = context self.context = context
self.request = request self.request = request
pa = annotations(request.principal) self.person = getPersonForLoggedInUser(request)
self.person = pa.get(ANNOTATION_KEY, None)
if self.person is not None: if self.person is not None:
self.context = self.person self.context = self.person

View file

@ -41,7 +41,7 @@ from loops.organize.interfaces import IPerson, ANNOTATION_KEY
from loops.type import TypeInterfaceSourceList, AdapterBase from loops.type import TypeInterfaceSourceList, AdapterBase
# register IPerson as a type interface - (TODO: use a function for this) # register type interfaces - (TODO: use a function for this)
TypeInterfaceSourceList.typeInterfaces += (IPerson, IAddress) TypeInterfaceSourceList.typeInterfaces += (IPerson, IAddress)

View file

@ -224,7 +224,7 @@ class AdapterBase(object):
adapts(IConcept) adapts(IConcept)
_attributes = ('context', '__parent__', ) _attributes = ('context', '__parent__', )
_schemas = (IConcept,) _schemas = list(IConcept)
def __init__(self, context): def __init__(self, context):
self.context = context # to get the permission stuff right self.context = context # to get the permission stuff right