=============================================================== loops - Linked Objects for Organization and Processing Services =============================================================== Let's do some basic setup >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown >>> site = placefulSetUp(True) >>> from zope import component, interface >>> from zope.publisher.browser import TestRequest and set up 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.organize.setup import SetupManager >>> component.provideAdapter(SetupManager, name='organize') >>> from loops.organize.work.setup import SetupManager >>> component.provideAdapter(SetupManager, name='organize.work') >>> from loops.tests.setup import TestSite >>> t = TestSite(site) >>> concepts, resources, views = t.setup() Work Items - Plannning and Recording Activities for Tasks ========================================================= >>> loopsRoot = concepts.getLoopsRoot() >>> records = loopsRoot.getRecordManager() >>> from cybertools.organize.work import WorkItems >>> component.provideAdapter(WorkItems) >>> from cybertools.organize.interfaces import IWorkItems >>> workItems = IWorkItems(records['work']) >>> from cybertools.organize.work import workItemStates >>> component.provideUtility(workItemStates(), name='organize.workItemStates') More setup ---------- In order to be able to login and store favorites and other personal data we have to prepare our environment. We need some basic adapter registrations, and a pluggable authentication utility with a principal folder. >>> from loops.organize.tests import setupObjectsForTesting >>> setupData = setupObjectsForTesting(site, concepts) >>> johnC = setupData.johnC >>> from zope.app.authentication.principalfolder import Principal >>> pJohn = Principal('users.john', 'xxx', u'John') >>> from loops.tests.auth import login >>> login(pJohn) We also assign a task as a target to the home node so that we are able to assign work items to this task. >>> tTask = concepts['task'] >>> home = views['home'] >>> from loops.concept import Concept >>> from loops.setup import addAndConfigureObject >>> task01 = addAndConfigureObject(concepts, Concept, 'loops_dev', ... title=u'loops Development', conceptType=tTask) >>> home.target = task01 Forms for adding and editing work items --------------------------------------- New work items are created using a CreateWorkItemForm. >>> from loops.organize.work.browser import CreateWorkItemForm, CreateWorkItem >>> form = CreateWorkItemForm(home, TestRequest()) When this form is submitted, a form controller is automatically created for the view on the currently shown node. The data from the form is processed by calling the form controller's update method >>> input = {u'form.action': u'create_workitem', u'workitem.action': u'finish', ... u'description': u'Description', u'comment': u'Comment', ... u'start_date': u'2008-12-28', u'start_time': u'T19:00:00', ... u'end_time': u'T20:15:00', u'duration': u'1:15', u'effort': u'0:15'} >>> request = TestRequest(form=input) >>> request.setPrincipal(pJohn) >>> from loops.browser.node import NodeView >>> nodeView = NodeView(home, request) >>> cwiController = CreateWorkItem(nodeView, request) >>> cwiController.update() False >>> list(workItems) [] Work items views ---------------- >>> from loops.organize.work.browser import WorkItemView, TaskWorkItems >>> wi01 = workItems['0000001'] >>> view = WorkItemView(wi01, TestRequest()) >>> view.taskUrl 'http://127.0.0.1/loops/concepts/loops_dev/@@SelectedManagementView.html' >>> work = TaskWorkItems(task01, request) >>> from loops.organize.work.browser import WorkItemDetails >>> view = WorkItemDetails(work, wi01) >>> view.day, view.start, view.end (u'08/12/28', u'19:00', u'20:15') Work items life cycle --------------------- Let's create another work item, now in state planned. >>> input = {u'form.action': u'create_workitem', u'workitem.action': u'plan', ... u'title': u'Install Zope', ... u'start_date': u'2009-01-19', u'start_time': u'T09:00:00'} >>> request = TestRequest(form=input) >>> request.setPrincipal(pJohn) >>> nodeView = NodeView(home, request) >>> cwiController = CreateWorkItem(nodeView, request) >>> cwiController.update() False If we now open another form, providing the identifier of the newly created work item, the form will be pre-filled with some of the item's data. >>> form = CreateWorkItemForm(home, TestRequest(form=dict(id='0000002'))) >>> form.title u'Install Zope' The 'delegate' transition is omitted from the actions list because it is only available for privileged users. >>> form.actions [{'selected': False, 'name': 'plan', 'title': 'plan'}, {'selected': False, 'name': 'accept', 'title': 'accept'}, {'selected': False, 'name': 'start', 'title': 'start working'}, {'selected': False, 'name': 'work', 'title': 'work'}, {'selected': False, 'name': 'finish', 'title': 'finish'}, {'selected': False, 'name': 'delegate', 'title': 'delegate'}, {'selected': False, 'name': 'move', 'title': 'move'}, {'selected': False, 'name': 'cancel', 'title': 'cancel'}, {'selected': False, 'name': 'modify', 'title': 'modify'}] Work Item Queries ================= >>> from loops.common import adapted >>> from loops.expert.concept import IQueryConcept >>> from loops.organize.work.browser import UserWorkItems, PersonWorkItems >>> tQuery = addAndConfigureObject(concepts, Concept, 'query', ... title=u'Query', conceptType=concepts.getTypeConcept(), ... typeInterface=IQueryConcept) >>> query = addAndConfigureObject(concepts, Concept, 'userworkitems', ... conceptType=tQuery) The UserWorkItems view does not give any results because there is no current user (principal) available in our test setting. >>> work = UserWorkItems(query, TestRequest()) >>> work.listWorkItems() So we use the PersonWorkItems view, assigning john to the query. >>> query.assignParent(johnC) >>> adapted(query).options = ['wi_from:2009-01-01', 'wi_to:today'] >>> input = dict() >>> work = PersonWorkItems(query, TestRequest(form=input)) >>> work.listWorkItems() [] Work Reports ============ First we have to make sure that there is a report concept type available. In addition we need a predicate that connects one or more tasks with a report. >>> from loops.expert.report import IReport, Report >>> component.provideAdapter(Report, provides=IReport) >>> tReport = addAndConfigureObject(concepts, Concept, 'report', ... title=u'Report', conceptType=concepts.getTypeConcept(), ... typeInterface=IReport) >>> hasReport = addAndConfigureObject(concepts, Concept, 'hasreport', ... title=u'has Report', conceptType=concepts.getPredicateType()) Work statement report --------------------- Now we can create a report and register it as the report for the task used above. >>> workStatement = addAndConfigureObject(concepts, Concept, 'work_statement', ... title=u'Work Statement', conceptType=tReport, ... reportType='work_report') >>> workStatement.assignChild(task01, hasReport) The executable report is a report instance that is an adapter to the (adapted) report instance. >>> from loops.organize.work.report import WorkReportInstance >>> from loops.expert.report import IReportInstance >>> component.provideAdapter(WorkReportInstance, ... provides=IReportInstance, ... name='work_report') The user interface is a ReportConceptView subclass that is directly associated with the task. >>> from loops.organize.work.report import WorkStatementView >>> input = dict(dayFrom='2008-01-01') >>> reportView = WorkStatementView(task01, TestRequest(form=input)) >>> reportView.nodeView = nodeView >>> results = reportView.results() >>> len(list(results)) 1 >>> for row in results: ... for col in reportView.displayedColumns: ... print col.getDisplayValue(row), ... print 08/12/28 19:00 20:15 {'url': '.../home/.36', 'title': u'loops Development'} {'url': '.../home/.33', 'title': u'john'} 00:15 {'actions': [...]} >>> results.totals.data {'effort': 900.0} Export of work data ------------------- >>> from loops.organize.work.report import WorkStatementCSVExport >>> reportView = WorkStatementCSVExport(task01, TestRequest(form=input)) >>> reportView.nodeView = nodeView >>> output = reportView() >>> print output Day;Start;End;Task;Party;Title;LA;Effort;State 08/12/28;19:00;20:15;loops Development;john;;;15;finished Meeting Minutes =============== We can use an event with assigned tasks as the basis for planning a meeting and recording information about the tasks. Let's start with creating an a event and assigning it a task. >>> from loops.organize.interfaces import IEvent, IAgendaItem >>> tEvent = addAndConfigureObject(concepts, Concept, 'event', ... title=u'Event', conceptType=concepts.getTypeConcept(), ... typeInterface=IEvent) >>> tAgendaItem = addAndConfigureObject(concepts, Concept, 'agendaitem', ... title=u'AgendaItem', conceptType=concepts.getTypeConcept(), ... typeInterface=IAgendaItem) >>> ev01 = addAndConfigureObject(concepts, Concept, 'ev01', ... title=u'loops Meeting', conceptType=tEvent) >>> aitem01 = addAndConfigureObject(concepts, Concept, 'aitem01', ... title=u'Documentation of Features', conceptType=tAgendaItem) >>> ev01.assignChild(aitem01) Now we create the meeting minutes report. We assign the event type as a child in order to provide the information for which types of objects the report is available. >>> from loops.organize.work.report import MeetingMinutes >>> component.provideAdapter(MeetingMinutes, provides=IReportInstance, ... name='meeting_minutes') >>> meetingMinutes = addAndConfigureObject(concepts, Concept, ... 'meeting_minutes', title=u'Meeting Minutes', conceptType=tReport, ... reportType='meeting_minutes') >>> meetingMinutes.assignChild(tEvent, hasReport) We can now access the report using a corresponding report-based view. >>> from loops.organize.work.meeting import MeetingMinutes >>> reportView = MeetingMinutes(ev01, TestRequest()) >>> reportView.nodeView = nodeView >>> results = reportView.results() >>> len(list(results)) 1 >>> for row in results: ... for col in reportView.displayedColumns: ... print col.getDisplayValue(row), ... print {'url': 'http://127.0.0.1/loops/views/home/.58', 'title': u'Documentation of Features'} Fin de partie ============= >>> placefulTearDown()