From 7a0a2e27902d64b2b93b8fb0ff1e58a5621ddbcf Mon Sep 17 00:00:00 2001 From: helmutm Date: Sat, 3 Dec 2005 09:54:11 +0000 Subject: [PATCH] Work in progress: re-building loops package as a 'concept management framework' git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@809 fd906abe-77d9-0310-91a1-e0d9ade77398 --- README.txt | 56 ++++---- browser/concept.py | 14 +- browser/concept_assign.pt | 28 ++-- browser/concept_details.pt | 22 ++-- browser/configure.zcml | 51 ++++---- concept.py | 206 ++++++------------------------ configure.zcml | 15 +-- interfaces.py | 255 ++++--------------------------------- tests.py | 9 +- 9 files changed, 157 insertions(+), 499 deletions(-) diff --git a/README.txt b/README.txt index f99ac5b..d51602c 100755 --- a/README.txt +++ b/README.txt @@ -3,21 +3,21 @@ loops - Linked Objects for Organizational Process Services ($Id$) -Tasks and Subtasks -~~~~~~~~~~~~~~~~~~ +Concepts and Relations +~~~~~~~~~~~~~~~~~~~~~~ -Let's start with creating a few example tasks: +Let's start with creating a few example concepts: - >>> from loops.task import Task - >>> t1 = Task() - >>> t1.title + >>> from loops.concept import Concept + >>> c1 = Concept() + >>> c1.title u'' - >>> t2 = Task(u't2', u'Second Task') - >>> t2.title - u'Second Task' + >>> c2 = Concept(u'c2', u'Second Concept') + >>> c2.title + u'Second Concept' -Now we want to make the second task a subtask of the first one. +Now we want to relate the second concept to the first one. In order to do this we first have to provide a relations registry. For testing we use a simple dummy implementation. @@ -25,28 +25,32 @@ testing we use a simple dummy implementation. >>> from cybertools.relation.interfaces import IRelationsRegistry >>> from cybertools.relation.registry import DummyRelationsRegistry >>> from zope.app.testing import ztapi - >>> ztapi.provideUtility(IRelationsRegistry, DummyRelationsRegistry()) -Now we can assign the task t2 as a subtask to t1: +We also need a Relation class to be used for connecting concepts: + + >>> from cybertools.relation import DyadicRelation + +Now we can assign the concept c2 to c1: - >>> t1.assignSubtask(t2) + >>> c1.assignConcept(c2, DyadicRelation) -We can now ask our tasks for their subtasks and parent tasks: +We can now ask our concepts for their related concepts: - >>> st1 = t1.getSubtasks() - >>> len(st1) + >>> sc1 = c1.getSubConcepts() + >>> len(sc1) 1 - >>> t2 in st1 + >>> c2 in sc1 True - >>> len(t1.getParentTasks()) + >>> len(c1.getParentConcepts()) + 0 + + >>> pc2 = c2.getParentConcepts() + >>> len(pc2) + 1 + + >>> c1 in pc2 + True + >>> len(c2.getSubConcepts()) 0 - >>> pt2 = t2.getParentTasks() - >>> len(pt2) - 1 - >>> t1 in pt2 - True - >>> len(t2.getSubtasks()) - 0 - \ No newline at end of file diff --git a/browser/concept.py b/browser/concept.py index f12f209..f513bd8 100644 --- a/browser/concept.py +++ b/browser/concept.py @@ -26,7 +26,7 @@ from zope.app import zapi from zope.app.dublincore.interfaces import ICMFDublinCore from zope.security.proxy import removeSecurityProxy -from loops.interfaces import ITask +from loops.interfaces import IConcept class Details(object): @@ -38,15 +38,15 @@ class Details(object): return d and d.strftime('%Y-%m-%d %H:%M') or '' -class SubtaskAssignments(Details): +class ConceptRelations(Details): def assignSubtask(self): """ Add a subtask denoted by the path given in the request variable subtaskPath. """ - subtaskPath = self.request.get('subtaskPath') - #if subtaskPath: - subtask = zapi.traverse(zapi.getRoot(self.context), subtaskPath, None, self.request) - #if subtask: - self.context.assignSubtask(removeSecurityProxy(subtask)) + conceptName = self.request.get('concept_name') + #if conceptName: + concept = zapi.getParent(self.context)[conceptName] + #if concept: + self.context.assignConcept(removeSecurityProxy(concept)) self.request.response.redirect('.') diff --git a/browser/concept_assign.pt b/browser/concept_assign.pt index a596433..7b5ad10 100644 --- a/browser/concept_assign.pt +++ b/browser/concept_assign.pt @@ -6,24 +6,24 @@
-

Task Title

+

Concept Title

- Subtasks: + Sub-Concepts: - **deleted** - subtask - - + tal:repeat="concept context/getSubConcepts"> + **deleted** + subconcept + -
- Parent Tasks: + Parent Concepts: - parent task - - + tal:repeat="concept context/getParentConcepts"> + parent concept + -
@@ -34,13 +34,13 @@
- Subtasks: + Concept Name: - +
- +
diff --git a/browser/concept_details.pt b/browser/concept_details.pt index 8a67eb8..58c2fcb 100644 --- a/browser/concept_details.pt +++ b/browser/concept_details.pt @@ -6,24 +6,24 @@
-

Task Title

+

Concept Title

- Subtasks: + Sub-Concepts: - **deleted** - subtask - - + tal:repeat="concept context/getSubConcepts"> + **deleted** + subtask + -
- Parent Tasks: + Parent Concepts: - parent task - - + tal:repeat="concept context/getParentConcepts"> + parent concept + -
diff --git a/browser/configure.zcml b/browser/configure.zcml index cc85c29..a30e766 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -7,67 +7,60 @@ > - - diff --git a/concept.py b/concept.py index f51c27b..f0bb321 100644 --- a/concept.py +++ b/concept.py @@ -17,193 +17,67 @@ # """ -Definition of the Task class. +Definition of the Concept class. $Id$ """ -from zope.interface import implements -from zope.app.container.ordered import OrderedContainer -from zope.app.copypastemove import ObjectCopier from zope.app import zapi -from zope.schema.fieldproperty import FieldProperty -from zope.component.interfaces import IFactory +from zope.interface import implements +from persistent import Persistent from cybertools.relation.interfaces import IRelationsRegistry from cybertools.relation import DyadicRelation -#from relation import Relation, Relations -from resource import Resource -from interfaces import ITask - -from copy import copy +from interfaces import IConcept -class SubtaskRelation(DyadicRelation): - """ Relation of a first task (the parent task) to a second task - (the subtask). - """ +class Concept(Persistent): + implements(IConcept) -class Task(OrderedContainer): - - implements(ITask) - - title = u'' - qualifier = u'' - priority = FieldProperty(ITask['priority']) + _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''): - OrderedContainer.__init__(self) - if name: - self.__name__ = name - if title: - self.title = title + self.title = title - # subtasks: + # concept relations: - def getSubtasks(self, taskTypes=None): + def getSubConcepts(self, relationships=None): + rels = getRelations(first=self, relationships=relationships) + return [r.second for r in rels] + # TODO: sort... + + def getParentConcepts(self, relationships=None): + rels = getRelations(second=self, relationships=relationships) + return [r.first for r in rels] + + def assignConcept(self, concept, relationship): registry = zapi.getUtility(IRelationsRegistry) - return [r.second - for r in registry.query(relationship=SubtaskRelation, - first=self)] - # TODO: sort according to priority - - def getParentTasks(self, taskTypes=None): - registry = zapi.getUtility(IRelationsRegistry) - return [r.first - for r in registry.query(relationship=SubtaskRelation, - second=self)] - - def assignSubtask(self, task): - registry = zapi.getUtility(IRelationsRegistry) - registry.register(SubtaskRelation(self, task)) + registry.register(relationship(self, concept)) # TODO (?): avoid duplicates - def createSubtask(self, taskType=None, container=None, name=None): - container = container or zapi.getParent(self) - task = Task() - name = name or task._createTaskName(container) - container[name] = task - self.assignSubtask(task) - return task + def deassignConcept(self, concept, relationships=None): + pass # TODO - def deassignSubtask(self, task): - hits = [ r for r in self._subtasks if r._target == task ] - if hits: - self._subtasks.remove(hits[0]) - task._parentTasks.remove(hits[0]) - # resource allocations: +# TODO: move this to the relation package - def getAllocatedResources(self, allocTypes=None, resTypes=None): - from sets import Set - allocs = self._resourceAllocs - res = Set() - for at in allocTypes or allocs.keys(): - res.union_update(allocs[at]) - if resTypes: - res = [ r for r in res if r in resTypes ] - return tuple(res) +def getRelations(first=None, second=None, third=None, relationships=None): + registry = zapi.getUtility(IRelationsRegistry) + query = {} + if first: query['first'] = first + if second: query['second'] = second + if third: query['third'] = third + if not relationships: + return registry.query(**query) + else: + result = [] + for r in relationships: + query['relationship'] = r + result.extend(registry.query(**query)) + return result - def allocateResource(self, resource, allocType='standard'): - allocs = self._resourceAllocs - rList = allocs.get(allocType, []) - if resource not in rList: - rList.append(resource) - allocs[allocType] = rList - resource._addAllocation(self, allocType) - - def createAndAllocateResource(self, resourceType=None, allocType='standard', - container=None, name=None): - container = container or zapi.getParent(self) - rClass = resourceType or Resource - resource = rClass() - name = name or resource._createResourceName(container) - container[name] = resource - self.allocateResource(resource, allocType) - return resource - - def deallocateResource(self, resource, allocTypes=None): - allocs = self._resourceAllocs - for at in allocTypes or allocs.keys(): - if resource in allocs[at]: - allocs[at].remove(resource) - resource._removeAllocations(self, allocTypes) - - def allocatedUserIds(self): - return () - - def getAllocTypes(self, resource): - return ('standard',) - - def getAllAllocTypes(self): - return ('standard',) - - # resource constraint stuff: - - def isResourceAllowed(self, resource, rcDontCheck=None): - rc = self.resourceConstraints - if not rc: - return True - for c in rc: - if rcDontCheck and c == rcDontCheck: # don't check constraint already checked - continue - # that's too simple, we must check all constraints for constraintType: - if not c.isResourceAllowed(resource): - return False - return True - - def getCandidateResources(self): - rc = self.resourceConstraints - if not rc: - return () - for c in rc: - candidates = c.getAllowedResources() - if candidates is not None: - return tuple([ r for r in candidates if self.isResourceAllowed(r, c) ]) - return () - - def getAllowedResources(self, candidates=None): - rc = self.resourceConstraints - if not rc: - return None - if candidates is None: - result = self.getCandidateResources() - # Empty result means: can't tell - return result and result or None - return tuple([ r for r in candidates if self.isResourceAllowed(r) ]) - - def isValid(self, checkSubtasks=True): - if self.resourceConstraints is not None: - for r in self.getAllocatedResources(): - if not self.isResourceAllowed(r): - return False - if checkSubtasks: - for t in self.getSubtasks(): - if not t.isValid(): - return False - return True - - # Task object as prototype: - - def copyTask(self, targetContainer=None): - targetContainer = targetContainer or zapi.getParent(self) - newName = self._createTaskName(targetContainer) - newTask = copy(self) - targetContainer[newName] = newTask - subtasks = self.getSubtasks() - newTask._subtasks.clear() - for st in subtasks: - newSt = st.copyTask(targetContainer) - newSt._parentTasks.clear() - newTask.assignSubtask(newSt) - return newTask - - # Helper methods: - - def _createTaskName(self, container=None): - prefix = 'tsk' - container = container or zapi.getParent(self) - last = max([ int(n[len(prefix):]) for n in container.keys() ] or [0]) - return prefix + str(last+1) diff --git a/configure.zcml b/configure.zcml index 5eb5965..fb3fb48 100644 --- a/configure.zcml +++ b/configure.zcml @@ -10,22 +10,19 @@ - + - - + id="loops.Concept" + description="Concept object" />