organize (tracking, work): Python3 fixes

This commit is contained in:
Helmut Merz 2024-09-26 16:25:00 +02:00
parent 956a6d01b3
commit 5e5e9aedfc
6 changed files with 89 additions and 151 deletions

View file

@ -1,27 +1,10 @@
#
# Copyright (c) 2017 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
#
# loops.expert.browser.export
"""
View classes for export of report results.
""" View classes for export of report results.
"""
import csv
from cStringIO import StringIO
from io import StringIO
import os
import time
from zope.cachedescriptors.property import Lazy
@ -67,8 +50,8 @@ class ResultsConceptCSVExport(ResultsConceptView):
title = field.title
if not isinstance(title, Message):
title = _(title)
return encode(translate(title, target_language=lang),
self.encoding)
#return encode(translate(title, target_language=lang), self.encoding)
return translate(title, target_language=lang)
def getFilenames(self):
"""@return (data_fn, result_fn)"""
@ -122,7 +105,7 @@ class ResultsConceptCSVExport(ResultsConceptView):
value = f.getExportValue(row, 'csv', lang)
if ILoopsObject.providedBy(value):
value = value.title
value = encode(value, self.encoding)
#value = encode(value, self.encoding)
data[f.name] = value
writer.writerow(data)
if csvRenderer:
@ -151,7 +134,7 @@ class ResultsConceptCSVExport(ResultsConceptView):
def encode(text, encoding):
if not isinstance(text, unicode):
if isinstance(text, bytes):
return text
try:
return text.encode(encoding)
@ -161,7 +144,7 @@ def encode(text, encoding):
try:
result.append(c.encode(encoding))
except UnicodeEncodeError:
result.append('?')
return ''.join(result)
return '???'
result.append(b'?')
return b''.join(result)
return b'???'

View file

@ -2,8 +2,6 @@
loops - Linked Objects for Organization and Processing Services
===============================================================
($Id$)
Let's do some basic setup
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
@ -147,10 +145,12 @@ Overview (cumulative) statistics
>>> from loops.organize.tracking.report import TrackingStats
>>> view = TrackingStats(statQuery, TestRequest())
>>> result = view.getData()
>>> result['macro'][4][1][u'define-macro']
u'overview'
>>> result['macro'][4][1]['define-macro']
'overview'
>>> result['data']
[{'access': 2, 'new': 0, 'changed': 1, 'period': '...', 'count': 3}]
[{'period': '...', ...}]
[{'access': 2, 'new': 0, 'changed': 1, 'period': '...', 'count': 3}]
Changes for a certain period (month)
@ -168,23 +168,27 @@ Recent changes
>>> from loops.organize.tracking.report import RecentChanges
>>> view = RecentChanges(statQuery, TestRequest())
>>> result = view.getData()
>>> result['macro'][4][1][u'define-macro']
u'recent_changes'
>>> result['macro'][4][1]['define-macro']
'recent_changes'
>>> data = result['data']
>>> data
[<ChangeRecord ['27', 2, '33', '...']: {'action': 'modify'}>]
>>> data[0].timeStamp
u'... ...:...'
'... ...:...'
>>> data[0].objectData
{'version': '', 'description': u'', 'title': 'Change Doc 001', 'url': '',
'object': <loops.resource.Resource object at ...>, 'type': u'Text',
'canAccess': True}
{'object': ...}
{'version': '', 'description': '', 'title': 'Change Doc 001', 'url': '',
'object': <loops.resource.Resource object at ...>, 'type': 'Text',
'canAccess': True}
>>> data[0].user
{'url': '', 'object': <loops.concept.Concept ...>, 'title': u'john'}
{'object': ...}
{'url': '', 'object': <loops.concept.Concept ...>, 'title': 'john'}
>>> data[0].action
'modify'

View file

@ -1,28 +1,11 @@
#
# Copyright (c) 2014 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
#
# loops.organize.tracking.change
"""
Recording changes to loops objects.
""" Recording changes to loops objects.
"""
from zope.app.container.interfaces import IObjectAddedEvent, IObjectRemovedEvent
from zope.app.container.interfaces import IObjectMovedEvent
from zope.interface import implements
from zope.interface import implementer
from zope.cachedescriptors.property import Lazy
from zope.component import adapter
from zope.lifecycleevent.interfaces import IObjectModifiedEvent, IObjectCreatedEvent
@ -100,10 +83,9 @@ class IChangeRecord(ITrack):
pass
@implementer(IChangeRecord)
class ChangeRecord(Track):
implements(IChangeRecord)
typeName = 'ChangeRecord'

View file

@ -1,29 +1,9 @@
#
# Copyright (c) 2008 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
#
# loops.organize.tracking.setup
"""
Automatic setup of a loops site for the organize.tracking package.
$Id$
""" Automatic setup of a loops site for the organize.tracking package.
"""
from zope.component import adapts
from zope.interface import implements, Interface
from cybertools.tracking.btree import TrackingStorage
from loops.organize.tracking.change import ChangeRecord

View file

@ -50,7 +50,7 @@ and a pluggable authentication utility with a principal folder.
>>> johnC = setupData.johnC
>>> from zope.app.authentication.principalfolder import Principal
>>> pJohn = Principal('users.john', 'xxx', u'John')
>>> pJohn = Principal('users.john', 'xxx', 'John')
>>> from loops.tests.auth import login
>>> login(pJohn)
@ -62,7 +62,7 @@ to assign work items to this task.
>>> from loops.concept import Concept
>>> from loops.setup import addAndConfigureObject
>>> task01 = addAndConfigureObject(concepts, Concept, 'loops_dev',
... title=u'loops Development', conceptType=tTask)
... title='loops Development', conceptType=tTask)
>>> home.target = task01
Forms for adding and editing work items
@ -77,10 +77,10 @@ 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'}
>>> input = {'form.action': 'create_workitem', 'workitem.action': 'finish',
... 'description': 'Description', 'comment': 'Comment',
... 'start_date': '2008-12-28', 'start_time': 'T19:00:00',
... 'end_time': 'T20:15:00', 'duration': '1:15', 'effort': '0:15'}
>>> request = TestRequest(form=input)
>>> request.setPrincipal(pJohn)
@ -92,10 +92,11 @@ by calling the form controller's update method
False
>>> list(workItems)
[<WorkItem ['36', 1, '33', '2008-12-28 19:00', 'finished']:
{'comment': u'Comment', 'end': 1230491700, 'description': u'Description',
'created': ..., 'creator': '33', 'start': 1230487200,
'duration': 4500, 'effort': 900}>]
[<WorkItem ['36', 1, '33', '2008-12-28 19:00', 'finished']: {'creator': ...}>]
{'comment': 'Comment', 'end': 1230491700, 'description': 'Description',
'created': ..., 'creator': '33', 'start': 1230487200,
'duration': 4500, 'effort': 900}>]
Work items views
----------------
@ -110,16 +111,16 @@ Work items views
>>> 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')
('08/12/28', '19:00', '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'}
>>> input = {'form.action': 'create_workitem', 'workitem.action': 'plan',
... 'title': 'Install Zope',
... 'start_date': '2009-01-19', 'start_time': 'T09:00:00'}
>>> request = TestRequest(form=input)
>>> request.setPrincipal(pJohn)
>>> nodeView = NodeView(home, request)
@ -132,21 +133,24 @@ 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'
'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'}]
>>> len(form.actions)
9
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
=================
@ -156,7 +160,7 @@ Work Item Queries
>>> from loops.organize.work.browser import UserWorkItems, PersonWorkItems
>>> tQuery = addAndConfigureObject(concepts, Concept, 'query',
... title=u'Query', conceptType=concepts.getTypeConcept(),
... title='Query', conceptType=concepts.getTypeConcept(),
... typeInterface=IQueryConcept)
>>> query = addAndConfigureObject(concepts, Concept, 'userworkitems',
@ -176,9 +180,10 @@ So we use the PersonWorkItems view, assigning john to the query.
>>> input = dict()
>>> work = PersonWorkItems(query, TestRequest(form=input))
>>> work.listWorkItems()
[<WorkItem ['36', 2, '33', '2009-01-19 09:00', 'planned']:
{'title': u'Install Zope', 'created': ..., 'end': 1232352000,
'start': 1232352000, 'creator': '33'}>]
[<WorkItem ['36', 2, '33', '2009-01-19 09:00', 'planned']: {'creator': ...}>]
{'title': 'Install Zope', 'created': ..., 'end': 1232352000,
'start': 1232352000, 'creator': '33'}>]
Work Reports
@ -190,10 +195,10 @@ 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(),
... title='Report', conceptType=concepts.getTypeConcept(),
... typeInterface=IReport)
>>> hasReport = addAndConfigureObject(concepts, Concept, 'hasreport',
... title=u'has Report', conceptType=concepts.getPredicateType())
... title='has Report', conceptType=concepts.getPredicateType())
Work statement report
---------------------
@ -202,7 +207,7 @@ 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,
... title='Work Statement', conceptType=tReport,
... reportType='work_report')
>>> workStatement.assignChild(task01, hasReport)
@ -228,12 +233,13 @@ The user interface is a ReportConceptView subclass that is directly associated w
>>> 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': [...]}
... print(col.getDisplayValue(row), end=' ')
... print()
08/12/28 19:00 20:15 {'title': ...}
{'url': '.../home/.36', 'title': 'loops Development'}
{'url': '.../home/.33', 'title': 'john'} 00:15
{'actions': [...]}
>>> results.totals.data
{'effort': 900.0}
@ -246,7 +252,7 @@ Export of work data
>>> reportView.nodeView = nodeView
>>> output = reportView()
>>> print output
>>> print(output)
Day;Start;End;Task;Party;Title;LA;Effort;State
08/12/28;19:00;20:15;loops Development;john;;;15;finished
@ -261,16 +267,16 @@ 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(),
... title='Event', conceptType=concepts.getTypeConcept(),
... typeInterface=IEvent)
>>> tAgendaItem = addAndConfigureObject(concepts, Concept, 'agendaitem',
... title=u'AgendaItem', conceptType=concepts.getTypeConcept(),
... title='AgendaItem', conceptType=concepts.getTypeConcept(),
... typeInterface=IAgendaItem)
>>> ev01 = addAndConfigureObject(concepts, Concept, 'ev01',
... title=u'loops Meeting', conceptType=tEvent)
... title='loops Meeting', conceptType=tEvent)
>>> aitem01 = addAndConfigureObject(concepts, Concept, 'aitem01',
... title=u'Documentation of Features', conceptType=tAgendaItem)
... title='Documentation of Features', conceptType=tAgendaItem)
>>> ev01.assignChild(aitem01)
Now we create the meeting minutes report. We assign the event type as a
@ -282,7 +288,7 @@ report is available.
... name='meeting_minutes')
>>> meetingMinutes = addAndConfigureObject(concepts, Concept,
... 'meeting_minutes', title=u'Meeting Minutes', conceptType=tReport,
... 'meeting_minutes', title='Meeting Minutes', conceptType=tReport,
... reportType='meeting_minutes')
>>> meetingMinutes.assignChild(tEvent, hasReport)
@ -297,10 +303,10 @@ We can now access the report using a corresponding report-based view.
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'}
... print(col.getDisplayValue(row), end=' ')
... print()
{'title': 'Documentation of Features',
'url': 'http://127.0.0.1/loops/views/home/.58'}
<cybertools.composer.report.result.ResultSet object ...>

View file

@ -1,27 +1,10 @@
#
# Copyright (c) 2018 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
#
# loops.organize.work.report
"""
Work report definitions.
""" Work report definitions.
"""
from datetime import date, timedelta
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.browserpage import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.component import adapter, getAdapter
from zope.i18n.locales import locales