organize: Python3 fixes

This commit is contained in:
Helmut Merz 2024-09-23 11:00:35 +02:00
parent 52990d1df6
commit 8037ac38be
9 changed files with 97 additions and 150 deletions

View file

@ -1,31 +1,12 @@
# # cybertools.composer.message.instance
# Copyright (c) 2011 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
#
""" """ Message instance and related classes.
Message instance and related classes.
$Id$
""" """
from cgi import parse_qs #from cgi import parse_qs
from urllib.parse import parse_qs
from string import Template from string import Template
from zope import component from zope import component
from zope.interface import implements
from zope.publisher.browser import TestRequest from zope.publisher.browser import TestRequest
try: try:
from zope.traversing.browser.absoluteurl import absoluteURL from zope.traversing.browser.absoluteurl import absoluteURL
@ -149,7 +130,7 @@ class MessageInstance(Instance):
#path = aq_inner(self.client.manager).getPhysicalPath() #path = aq_inner(self.client.manager).getPhysicalPath()
path = self.client.manager.getPhysicalPath() path = self.client.manager.getPhysicalPath()
if len(path) >= 3 and path[-3] == 'sm_clients': if len(path) >= 3 and path[-3] == 'sm_clients':
print '*** path correction:', path print('*** path correction:', path)
# evil hack for aqcuisition-wrapped manager object # evil hack for aqcuisition-wrapped manager object
path = path[:-3] path = path[:-3]
url = request.physicalPathToURL(path) url = request.physicalPathToURL(path)

View file

@ -1,30 +1,10 @@
# # cybertools.composer.rule.mail
# Copyright (c) 2007 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
#
""" """ Action handler for sending emails.
Action handler for sending emails.
$Id$
""" """
from email.MIMEText import MIMEText from email.mime.text import MIMEText
from zope import component from zope import component
from zope.interface import implements
from cybertools.composer.interfaces import IInstance from cybertools.composer.interfaces import IInstance
from cybertools.composer.rule.interfaces import IActionHandler from cybertools.composer.rule.interfaces import IActionHandler
@ -47,7 +27,7 @@ class MailActionHandler(ActionHandler):
return data return data
def prepareMessage(self, subject, text, sender, recipient): def prepareMessage(self, subject, text, sender, recipient):
text = text.encode('utf-8') #text = text.encode('utf-8')
msg = MIMEText(text, 'plain', 'utf-8') msg = MIMEText(text, 'plain', 'utf-8')
msg['Subject'] = subject msg['Subject'] = subject
msg['From'] = sender msg['From'] = sender

View file

@ -1,28 +1,7 @@
# # cybertools.composer.rule.message
# Copyright (c) 2007 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
#
""" Action handler for providing messages.
""" """
Action handler for providing messages.
$Id$
"""
from zope.interface import implements
from cybertools.composer.message.interfaces import IMessageManager from cybertools.composer.message.interfaces import IMessageManager
from cybertools.composer.message.instance import MessageInstance from cybertools.composer.message.instance import MessageInstance

View file

@ -14,7 +14,7 @@ class TestMailer(object):
msg = message_from_string(message) msg = message_from_string(message)
print('subject:', msg['Subject']) print('subject:', msg['Subject'])
print('message:') print('message:')
print(msg.get_payload(decode=True)) print(msg.get_payload(decode=True).decode('UTF-8'))
class Test(unittest.TestCase): class Test(unittest.TestCase):

View file

@ -13,11 +13,11 @@ Persons and Addresses
Let's start with a Person: Let's start with a Person:
>>> from cybertools.organize.party import Person >>> from cybertools.organize.party import Person
>>> john = Person(u'Smith') >>> john = Person('Smith')
>>> john.lastName >>> john.lastName
u'Smith' 'Smith'
>>> john.firstName >>> john.firstName
u'' ''
>>> john.birthDate is None >>> john.birthDate is None
True True
>>> john.addresses >>> john.addresses
@ -35,9 +35,9 @@ A Person object knows the age of the person:
>>> john.age >= 26 >>> john.age >= 26
True True
>>> john.firstName = u'John' >>> john.firstName = 'John'
>>> john.firstName >>> john.firstName
u'John' 'John'
Addresses Addresses
--------- ---------
@ -45,10 +45,10 @@ Addresses
Let's create an address and assign it to a person: Let's create an address and assign it to a person:
>>> from cybertools.organize.party import Address >>> from cybertools.organize.party import Address
>>> addr = Address(u'New York', u'Broadway 1') >>> addr = Address('New York', 'Broadway 1')
>>> john.addresses['standard'] = addr >>> john.addresses['standard'] = addr
>>> john.addresses['standard'].street >>> john.addresses['standard'].street
u'Broadway 1' 'Broadway 1'
Tasks Tasks

View file

@ -4,7 +4,7 @@
""" """
import csv import csv
from io import StringIO from io import BytesIO, StringIO
import itertools import itertools
from zope import component from zope import component
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
@ -106,7 +106,7 @@ class RegistrationsExportCsv(BaseView):
#waiting.append(reg.numberWaiting) #waiting.append(reg.numberWaiting)
if reg.number or reg.numberWaiting: if reg.number or reg.numberWaiting:
hasRegs = True hasRegs = True
if reg.timeStamp < timeStamp: if timeStamp == '' or reg.timeStamp < timeStamp:
timeStamp = reg.timeStamp timeStamp = reg.timeStamp
if not hasRegs: if not hasRegs:
continue continue
@ -134,6 +134,7 @@ class RegistrationsExportCsv(BaseView):
delimiter = ';' delimiter = ';'
methodName = self.request.get('get_data_method', 'getAllDataInColumns') methodName = self.request.get('get_data_method', 'getAllDataInColumns')
method = getattr(self, methodName, self.getData) method = getattr(self, methodName, self.getData)
#output = BytesIO()
output = StringIO() output = StringIO()
try: try:
csv.writer(output, dialect='excel', delimiter=delimiter, csv.writer(output, dialect='excel', delimiter=delimiter,
@ -153,6 +154,7 @@ class RegistrationsExportCsv(BaseView):
return result return result
def encode(self, text): def encode(self, text):
return text
if isinstance(text, str): if isinstance(text, str):
result = [] result = []
for c in text: for c in text:

View file

@ -31,9 +31,9 @@ parts (events, lectures, ...).
>>> workshop.__name__ = 'workshop' >>> workshop.__name__ = 'workshop'
>>> event1 = service.ScheduledService('event1', category='event', manager=workshop, >>> event1 = service.ScheduledService('event1', category='event', manager=workshop,
... title=u'Event 1', capacity=5) ... title='Event 1', capacity=5)
>>> event2 = service.ScheduledService('event2', category='event', manager=workshop, >>> event2 = service.ScheduledService('event2', category='event', manager=workshop,
... title=u'Event 2') ... title='Event 2')
>>> workshop.services.append(event1) >>> workshop.services.append(event1)
>>> workshop.services.append(event2) >>> workshop.services.append(event2)
@ -43,26 +43,26 @@ clients. This is done via to form descriptions (schemas), one for the
personal data (first name, last name), and one for the address. personal data (first name, last name), and one for the address.
>>> workshop.clientSchemas.append(schema.Schema( >>> workshop.clientSchemas.append(schema.Schema(
... schema.Field('lastName', u'Last Name', required=True, ... schema.Field('lastName', 'Last Name', required=True,
... standardFieldName='lastName'), ... standardFieldName='lastName'),
... schema.Field('firstName', u'First Name'), ... schema.Field('firstName', 'First Name'),
... schema.Field('email', u'Email Address', required=True, ... schema.Field('email', 'Email Address', required=True,
... fieldType='email', standardFieldName='email'), ... fieldType='email', standardFieldName='email'),
... schema.Field('age', u'Age', fieldType='number'), ... schema.Field('age', 'Age', fieldType='number'),
... schema.Field('addr', u'Personal Address', required=True, ... schema.Field('addr', 'Personal Address', required=True,
... fieldtype='radiobuttons', ... fieldtype='radiobuttons',
... vocabulary=u'Mrs\nMr'), ... vocabulary='Mrs\nMr'),
... schema.Field('acadTitles', u'Academic Titles', ... schema.Field('acadTitles', 'Academic Titles',
... fieldtype='checkboxes', ... fieldtype='checkboxes',
... vocabulary=u'Prof.\nDr.'), ... vocabulary='Prof.\nDr.'),
... name='person', ... name='person',
... manager=workshop, ... manager=workshop,
... )) ... ))
>>> workshop.clientSchemas.append(schema.Schema( >>> workshop.clientSchemas.append(schema.Schema(
... schema.Field('street', u'Street'), ... schema.Field('street', 'Street'),
... schema.Field('city', u'City', required=True), ... schema.Field('city', 'City', required=True),
... schema.Field('country', u'Country', required=True, ... schema.Field('country', 'Country', required=True,
... fieldType='dropdown', vocabulary=u'USA\nGermany'), ... fieldType='dropdown', vocabulary='USA\nGermany'),
... name='address', ... name='address',
... manager=workshop, ... manager=workshop,
... )) ... ))
@ -76,7 +76,7 @@ So we are now ready to register participants.
>>> client1 = IClientFactory(workshop)() >>> client1 = IClientFactory(workshop)()
>>> client1Name = workshop.addClient(client1) >>> client1Name = workshop.addClient(client1)
>>> data = dict(addr=u'Mr', lastName=u'Skywalker', email='luke@skywalker.universe') >>> data = dict(addr='Mr', lastName='Skywalker', email='luke@skywalker.universe')
>>> inst = component.getAdapter(client1, IInstance, name='editor') >>> inst = component.getAdapter(client1, IInstance, name='editor')
>>> inst.template = workshop.clientSchemas['person'] >>> inst.template = workshop.clientSchemas['person']
>>> state = inst.applyTemplate(data=data) >>> state = inst.applyTemplate(data=data)
@ -85,7 +85,7 @@ So we are now ready to register participants.
['schema.client.__standard__', 'schema.client.person'] ['schema.client.__standard__', 'schema.client.person']
>>> client1.__schema_attributes__['schema.client.person']['lastName'] >>> client1.__schema_attributes__['schema.client.person']['lastName']
u'Skywalker' 'Skywalker'
Instead of directly peeking into the attributes we can also use a Instead of directly peeking into the attributes we can also use a
suitable instance adapter. suitable instance adapter.
@ -93,10 +93,12 @@ suitable instance adapter.
>>> inst = IInstance(client1) >>> inst = IInstance(client1)
>>> inst.template = workshop.clientSchemas['person'] >>> inst.template = workshop.clientSchemas['person']
>>> inst.applyTemplate() >>> inst.applyTemplate()
{'acadTitles': u'', 'standard.lastName': u'Skywalker', 'addr': u'Mr', {..., 'lastName': 'Skywalker', ...}
'firstName': u'', 'lastName': u'Skywalker', 'age': '', '__name__': '...',
'email': u'luke@skywalker.universe', {'acadTitles': '', 'standard.lastName': 'Skywalker', 'addr': 'Mr',
'standard.email': u'luke@skywalker.universe'} 'firstName': '', 'lastName': 'Skywalker', 'age': '', '__name__': '...',
'email': 'luke@skywalker.universe',
'standard.email': 'luke@skywalker.universe'}
Note that the ``standardFieldName`` setting for the ``lastName`` field Note that the ``standardFieldName`` setting for the ``lastName`` field
results in a 'standard.lastName' entry; this technique may be used to results in a 'standard.lastName' entry; this technique may be used to
@ -110,13 +112,13 @@ We need some additional setup for working with schema views - so we have to
supply some session handling stuff in order to work with client names. supply some session handling stuff in order to work with client names.
>>> from zope.interface import implementer >>> from zope.interface import implementer
>>> from zope.session.interfaces import IClientId
>>> from zope.session.interfaces import IClientIdManager, ISessionDataContainer >>> from zope.session.interfaces import IClientIdManager, ISessionDataContainer
>>> from zope.session import session >>> from zope.session import session
>>> component.provideAdapter(session.ClientId) >>> component.provideAdapter(session.ClientId, provides=IClientId)
>>> component.provideAdapter(session.Session) >>> component.provideAdapter(session.Session)
>>> component.provideUtility(session.RAMSessionDataContainer(), ISessionDataContainer) >>> component.provideUtility(session.RAMSessionDataContainer(), ISessionDataContainer)
>>> class ClientIdManager(object): >>> class ClientIdManager(object):
... implements(IClientIdManager)
... def getClientId(self, request): return 'dummy' ... def getClientId(self, request): return 'dummy'
>>> ClientIdManager = implementer(IClientIdManager)(ClientIdManager) >>> ClientIdManager = implementer(IClientIdManager)(ClientIdManager)
>>> component.provideUtility(ClientIdManager()) >>> component.provideUtility(ClientIdManager())
@ -143,17 +145,19 @@ the corresponding client.
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
>>> view.data >>> view.data
{'acadTitles': u'', 'standard.lastName': u'Skywalker', 'addr': u'Mr', {..., 'lastName': 'Skywalker', ...}
'firstName': u'', 'lastName': u'Skywalker', 'age': '', '__name__': '...',
'email': u'luke@skywalker.universe', {'acadTitles': '', 'standard.lastName': 'Skywalker', 'addr': 'Mr',
'standard.email': u'luke@skywalker.universe'} 'firstName': '', 'lastName': 'Skywalker', 'age': '', '__name__': '...',
'email': 'luke@skywalker.universe',
'standard.email': 'luke@skywalker.universe'}
When we provide data and an 'update' action a new client object will When we provide data and an 'update' action a new client object will
be created - if we clear the client name in the session first via be created - if we clear the client name in the session first via
``view.setClientName('')``. ``view.setClientName('')``.
>>> input = dict(lastName='Solo', firstName='Han', email='han.solo@space.net', >>> input = dict(lastName='Solo', firstName='Han', email='han.solo@space.net',
... addr=u'Mr', action='update') ... addr='Mr', action='update')
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
>>> view.setClientName('') >>> view.setClientName('')
@ -168,15 +172,17 @@ be created - if we clear the client name in the session first via
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
>>> view.data >>> view.data
{'acadTitles': u'', 'standard.lastName': u'Solo', 'addr': u'Mr', {..., 'lastName': 'Solo', ...}
'firstName': u'Han', 'lastName': u'Solo',
'age': '', '__name__': '...', 'email': u'han.solo@space.net', {'acadTitles': '', 'standard.lastName': 'Solo', 'addr': 'Mr',
'standard.email': u'han.solo@space.net'} 'firstName': 'Han', 'lastName': 'Solo',
'age': '', '__name__': '...', 'email': 'han.solo@space.net',
'standard.email': 'han.solo@space.net'}
If we provide an id parameter we may also change an existing client. If we provide an id parameter we may also change an existing client.
>>> input = dict(lastName=u'Skywalker', firstName=u'Luke', id=client1Name, >>> input = dict(lastName='Skywalker', firstName='Luke', id=client1Name,
... email='luke@skywalker.universe', addr=u'Mr', action='update') ... email='luke@skywalker.universe', addr='Mr', action='update')
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
>>> view.update() >>> view.update()
@ -186,15 +192,17 @@ If we provide an id parameter we may also change an existing client.
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
>>> view.data >>> view.data
{'acadTitles': u'', 'standard.lastName': u'Skywalker', 'addr': u'Mr', {..., 'lastName': 'Skywalker', ...}
'firstName': u'Luke', 'lastName': u'Skywalker', 'age': '', '__name__': '...',
'email': u'luke@skywalker.universe', {'acadTitles': '', 'standard.lastName': 'Skywalker', 'addr': 'Mr',
'standard.email': u'luke@skywalker.universe'} 'firstName': 'Luke', 'lastName': 'Skywalker', 'age': '', '__name__': '...',
'email': 'luke@skywalker.universe',
'standard.email': 'luke@skywalker.universe'}
If we do not provide a value for a required attribute we get a validation If we do not provide a value for a required attribute we get a validation
error and the form will be displayed again. error and the form will be displayed again.
>>> input = dict(firstName=u'Anakin', action='update') >>> input = dict(firstName='Anakin', action='update')
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
>>> view.update() >>> view.update()
@ -203,7 +211,7 @@ error and the form will be displayed again.
The same happens if we provide a number field with a string that cannot The same happens if we provide a number field with a string that cannot
be converted to an integer. be converted to an integer.
>>> input = dict(firstName=u'Anakin', lastName=u'Skywalker', age='foo', >>> input = dict(firstName='Anakin', lastName='Skywalker', age='foo',
... action='update') ... action='update')
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
@ -216,7 +224,7 @@ More on special field types
>>> schema2 = workshop.clientSchemas['address'] >>> schema2 = workshop.clientSchemas['address']
>>> countryField = schema2.fields.country >>> countryField = schema2.fields.country
>>> countryField.getVocabularyItems() >>> countryField.getVocabularyItems()
[{'token': u'USA', 'title': u'USA'}, {'token': u'Germany', 'title': u'Germany'}] [{'token': 'USA', 'title': 'USA'}, {'token': 'Germany', 'title': 'Germany'}]
Registering for Services Using a Registration Template Registering for Services Using a Registration Template
@ -437,7 +445,7 @@ We are now ready to trigger a registration checkout.
>>> result = ruleManager.handleEvent(Event(checkoutEvent, client1)) >>> result = ruleManager.handleEvent(Event(checkoutEvent, client1))
sender: manager@workshops.com sender: manager@workshops.com
recipients: [u'luke@skywalker.universe'] recipients: ['luke@skywalker.universe']
subject: Workshop Registration subject: Workshop Registration
message: message:
Dear Luke Skywalker, Dear Luke Skywalker,
@ -474,7 +482,7 @@ rendered message.
>>> input = dict(message='feedback_html') >>> input = dict(message='feedback_html')
>>> view = MessageView(workshop, TestRequest(form=input)) >>> view = MessageView(workshop, TestRequest(form=input))
>>> view.getMessage() >>> view.getMessage()
u'<p>Dear Luke Skywalker...' '<p>Dear Luke Skywalker...'
Service Manager and Service Views Service Manager and Service Views
@ -527,7 +535,7 @@ because the ``waitingList`` flag has not been set.)
>>> srvView.registeredTotalSubmitted >>> srvView.registeredTotalSubmitted
0 0
>>> srvView.registeredTotalsSubmitted >>> srvView.registeredTotalsSubmitted
{'numberWaiting': '', 'number': 0} {'number': 0, 'numberWaiting': ''}
Checkout Checkout
-------- --------
@ -545,7 +553,7 @@ her registrations.
>>> checkout.getRegistrationsInfo() >>> checkout.getRegistrationsInfo()
[...] [...]
>>> checkout.listRegistrationsText() >>> checkout.listRegistrationsText()
u'Event 1\nDatum: -\nUhrzeit: -\n\n' 'Event 1\nDatum: -\nUhrzeit: -\n\n'
When the user clicks the "Confirm Registration" button the corresponding When the user clicks the "Confirm Registration" button the corresponding
actions will be carried out. actions will be carried out.
@ -556,7 +564,7 @@ actions will be carried out.
>>> checkout = CheckoutView(workshop, request) >>> checkout = CheckoutView(workshop, request)
>>> checkout.update() >>> checkout.update()
sender: unknown@sender.com sender: unknown@sender.com
recipients: [u'luke@skywalker.universe'] recipients: ['luke@skywalker.universe']
subject: Workshop Registration subject: Workshop Registration
message: ... message: ...
False False
@ -579,7 +587,7 @@ for security reasons.)
>>> checkout = CheckoutView(workshop, request) >>> checkout = CheckoutView(workshop, request)
>>> checkout.update() >>> checkout.update()
sender: unknown@sender.com sender: unknown@sender.com
recipients: [u'luke@skywalker.universe'] recipients: ['luke@skywalker.universe']
subject: Workshop Registration subject: Workshop Registration
message: ... message: ...
False False
@ -621,7 +629,7 @@ possible.
True True
>>> input = dict(lastName='Walker', firstName='John', email='john@walker.tv', >>> input = dict(lastName='Walker', firstName='John', email='john@walker.tv',
... addr=u'Mr', action='update') ... addr='Mr', action='update')
>>> request = TestRequest(form=input) >>> request = TestRequest(form=input)
>>> schema = workshop.clientSchemas['person'] >>> schema = workshop.clientSchemas['person']
>>> view = SchemaView(schema, request) >>> view = SchemaView(schema, request)
@ -755,7 +763,7 @@ conference that is organized in parallel tracks.
>>> conference.__name__ = 'conference' >>> conference.__name__ = 'conference'
>>> main = service.Service('conf_main', manager=conference, >>> main = service.Service('conf_main', manager=conference,
... title=u'Conference', capacity=5) ... title='Conference', capacity=5)
>>> conference.services.append(main) >>> conference.services.append(main)

View file

@ -2,8 +2,6 @@
Organizations: Persons, Institutions, Addresses, Work, ... Organizations: Persons, Institutions, Addresses, Work, ...
========================================================== ==========================================================
($Id$)
>>> from zope import component >>> from zope import component
@ -36,7 +34,7 @@ so we do not have to set up real objects.
>>> wi01 = workItems.add('001', 'john') >>> wi01 = workItems.add('001', 'john')
>>> wi01 >>> wi01
<WorkItem ['001', 1, 'john', '...', 'new']: <WorkItem ['001', 1, 'john', '...', 'new']:
{'created': ..., 'creator': 'john'}> {'creator': 'john', 'created': ...}>
Properties that have not been set explicitly have a default of None; properties Properties that have not been set explicitly have a default of None; properties
not specified in the IWorkItem interface will lead to an AttributeError. not specified in the IWorkItem interface will lead to an AttributeError.
@ -53,7 +51,7 @@ Properties may be set as long as the work item is in status "new".
>>> wi01.setData(start=1229955772, party='annie') >>> wi01.setData(start=1229955772, party='annie')
>>> wi01 >>> wi01
<WorkItem ['001', 1, 'annie', '2008-12-22 15:22', 'new']: <WorkItem ['001', 1, 'annie', '2008-12-22 15:22', 'new']:
{'start': 1229955772, 'created': ..., 'creator': 'john'}> {'creator': 'john', 'created': ..., ...}>
The duration value is calculated automatically when start and end are set; The duration value is calculated automatically when start and end are set;
the effort is taken from the duration if not set explicitly. the effort is taken from the duration if not set explicitly.
@ -84,8 +82,7 @@ be excluded from queries.
'planned_x' 'planned_x'
>>> wi02 >>> wi02
<WorkItem ['001', 1, 'jim', '2008-12-22 15:22', 'accepted']: <WorkItem ['001', 1, 'jim', '2008-12-22 15:22', 'accepted']:
{'duration': 700, 'start': 1229955772, 'created': ..., {'creator': 'jim', 'created': ..., ...}>
'end': 1229956372, 'creator': 'jim'}>
It is not possible to change a value of a work item that is not in state "new". It is not possible to change a value of a work item that is not in state "new".
@ -100,7 +97,7 @@ but may also be specified explicitly.
>>> wi03 = wi02.doAction('start', 'jim', start=1229958000) >>> wi03 = wi02.doAction('start', 'jim', start=1229958000)
>>> wi03 >>> wi03
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'running']: <WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'running']:
{'duration': 0, 'start': 1229958000, 'created': ..., 'creator': 'jim'}> {'creator': 'jim', 'created': ..., ...}>
Stopping and finishing work Stopping and finishing work
--------------------------- ---------------------------
@ -113,10 +110,10 @@ as "running" will be replaced by a new one.
>>> wi03 >>> wi03
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'replaced']: <WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'replaced']:
{'duration': 0, 'start': 1229958000, 'created': ..., 'creator': 'jim'}> {'creator': 'jim', 'created': ..., ...}>
>>> wi04 >>> wi04
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'done']: <WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'done']:
{'start': 1229958000, 'created': ..., 'end': 1229958300, 'creator': 'jim'}> {'creator': 'jim', 'created': ..., ...}>
After another hour Jim works again on the task; he now finishes it within After another hour Jim works again on the task; he now finishes it within
ten minutes and records this in one step. ten minutes and records this in one step.
@ -124,7 +121,7 @@ ten minutes and records this in one step.
>>> wi05 = wi04.doAction('finish', 'jim', start=1229961600, end=1229962200) >>> wi05 = wi04.doAction('finish', 'jim', start=1229961600, end=1229962200)
>>> wi05 >>> wi05
<WorkItem ['001', 1, 'jim', '2008-12-22 17:00', 'finished']: <WorkItem ['001', 1, 'jim', '2008-12-22 17:00', 'finished']:
{'start': 1229961600, 'created': ..., 'end': 1229962200, 'creator': 'jim'}> {'creator': 'jim', 'created': ..., ...}>
>>> wi05.duration, wi05.effort >>> wi05.duration, wi05.effort
(600, 600) (600, 600)
@ -137,7 +134,7 @@ As the work is now finished, the work item may be closed; the corresponding
>>> wi06 = wi05.doAction('close', 'john') >>> wi06 = wi05.doAction('close', 'john')
>>> wi06 >>> wi06
<WorkItem ['001', 1, 'jim', '... ...', 'closed']: <WorkItem ['001', 1, 'jim', '... ...', 'closed']:
{'start': ..., 'created': ..., 'end': ..., 'creator': 'john'}> {'creator': 'john', 'created': ..., ...}>
Let's now check how many work items have been generated. Let's now check how many work items have been generated.
@ -158,10 +155,10 @@ delegated so that it may be selected by queries.
>>> wi07 >>> wi07
<WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'delegated']: <WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'delegated']:
{'start': 1229970800, 'created': ..., 'creator': 'john'}> {'creator': 'john', 'created': ..., 'start': 1229970800, 'target': '0000008'}>
>>> wi08 >>> wi08
<WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'planned']: <WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'planned']:
{'start': 1229970800, 'created': ..., 'source': '0000007', 'creator': 'john'}> {'creator': 'john', 'created': ..., 'start': 1229970800, 'source': '0000007'}>
>>> len(list(workItems)) >>> len(list(workItems))
8 8
@ -180,10 +177,10 @@ Note that nevertheless only the last work item of a run may be modified.
>>> wi08 >>> wi08
<WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'replaced']: <WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'replaced']:
{'start': 1229970800, 'created': ..., 'creator': 'john'}> {'creator': 'john', 'created': ..., 'start': 1229970800, 'source': '0000007'}>
>>> wi09 >>> wi09
<WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'planned']: <WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'planned']:
{'duration': 3600, 'start': 1229970800, 'created': ..., 'creator': 'annie'}> {'creator': 'annie', 'created': ..., ...}>
Moving Work Items to Other Tasks Moving Work Items to Other Tasks
@ -192,7 +189,7 @@ Moving Work Items to Other Tasks
>>> wi10 = wi07.doAction('move', 'john') >>> wi10 = wi07.doAction('move', 'john')
>>> wi10 >>> wi10
<WorkItem ['001', 4, 'john', '2008-12-22 19:33', 'delegated']: <WorkItem ['001', 4, 'john', '2008-12-22 19:33', 'delegated']:
{'start': 1229970800, 'created': ..., 'source': '0000010', 'creator': 'john'}> {'creator': 'john', 'created': ..., 'start': 1229970800, 'source': '0000010'}>
Queries Queries
@ -215,8 +212,8 @@ Close a run with some delegated items.
>>> wi07a = workItems.query(run=2)[-2] >>> wi07a = workItems.query(run=2)[-2]
>>> wi07b = workItems.query(run=2)[-1] >>> wi07b = workItems.query(run=2)[-1]
>>> wi07b >>> wi07b
<WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'moved']: {'start': 1229970800, <WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'moved']:
'created': ..., 'target': '0000011', 'creator': 'john'}> {'creator': 'john', 'created': ..., 'start': 1229970800, 'target': '0000011'}>
>>> wi07c = wi07b.doAction('close', 'john') >>> wi07c = wi07b.doAction('close', 'john')

View file

@ -66,12 +66,12 @@ def toStr(value, encoding='UTF-8'):
def toUnicode(value, encoding='UTF-8', fallback='ISO8859-15'): def toUnicode(value, encoding='UTF-8', fallback='ISO8859-15'):
# or: fallback='CP852' # or: fallback='CP852'
if isinstance(value, unicode): if isinstance(value, str):
return value return value
elif isinstance(value, str): elif isinstance(value, bytes):
try: try:
return value.decode(encoding) return value.decode(encoding)
except UnicodeDecodeError: except UnicodeDecodeError:
return value.decode(fallback) return value.decode(fallback)
else: else:
return u'' return ''