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

View file

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

View file

@ -1,28 +1,7 @@
#
# 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
#
# cybertools.composer.rule.message
""" 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.instance import MessageInstance

View file

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

View file

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

View file

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

View file

@ -31,9 +31,9 @@ parts (events, lectures, ...).
>>> workshop.__name__ = '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,
... title=u'Event 2')
... title='Event 2')
>>> workshop.services.append(event1)
>>> 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.
>>> workshop.clientSchemas.append(schema.Schema(
... schema.Field('lastName', u'Last Name', required=True,
... schema.Field('lastName', 'Last Name', required=True,
... standardFieldName='lastName'),
... schema.Field('firstName', u'First Name'),
... schema.Field('email', u'Email Address', required=True,
... schema.Field('firstName', 'First Name'),
... schema.Field('email', 'Email Address', required=True,
... fieldType='email', standardFieldName='email'),
... schema.Field('age', u'Age', fieldType='number'),
... schema.Field('addr', u'Personal Address', required=True,
... schema.Field('age', 'Age', fieldType='number'),
... schema.Field('addr', 'Personal Address', required=True,
... fieldtype='radiobuttons',
... vocabulary=u'Mrs\nMr'),
... schema.Field('acadTitles', u'Academic Titles',
... vocabulary='Mrs\nMr'),
... schema.Field('acadTitles', 'Academic Titles',
... fieldtype='checkboxes',
... vocabulary=u'Prof.\nDr.'),
... vocabulary='Prof.\nDr.'),
... name='person',
... manager=workshop,
... ))
>>> workshop.clientSchemas.append(schema.Schema(
... schema.Field('street', u'Street'),
... schema.Field('city', u'City', required=True),
... schema.Field('country', u'Country', required=True,
... fieldType='dropdown', vocabulary=u'USA\nGermany'),
... schema.Field('street', 'Street'),
... schema.Field('city', 'City', required=True),
... schema.Field('country', 'Country', required=True,
... fieldType='dropdown', vocabulary='USA\nGermany'),
... name='address',
... manager=workshop,
... ))
@ -76,7 +76,7 @@ So we are now ready to register participants.
>>> client1 = IClientFactory(workshop)()
>>> 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.template = workshop.clientSchemas['person']
>>> state = inst.applyTemplate(data=data)
@ -85,7 +85,7 @@ So we are now ready to register participants.
['schema.client.__standard__', 'schema.client.person']
>>> client1.__schema_attributes__['schema.client.person']['lastName']
u'Skywalker'
'Skywalker'
Instead of directly peeking into the attributes we can also use a
suitable instance adapter.
@ -93,10 +93,12 @@ suitable instance adapter.
>>> inst = IInstance(client1)
>>> inst.template = workshop.clientSchemas['person']
>>> inst.applyTemplate()
{'acadTitles': u'', 'standard.lastName': u'Skywalker', 'addr': u'Mr',
'firstName': u'', 'lastName': u'Skywalker', 'age': '', '__name__': '...',
'email': u'luke@skywalker.universe',
'standard.email': u'luke@skywalker.universe'}
{..., 'lastName': 'Skywalker', ...}
{'acadTitles': '', 'standard.lastName': 'Skywalker', 'addr': 'Mr',
'firstName': '', 'lastName': 'Skywalker', 'age': '', '__name__': '...',
'email': 'luke@skywalker.universe',
'standard.email': 'luke@skywalker.universe'}
Note that the ``standardFieldName`` setting for the ``lastName`` field
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.
>>> from zope.interface import implementer
>>> from zope.session.interfaces import IClientId
>>> from zope.session.interfaces import IClientIdManager, ISessionDataContainer
>>> from zope.session import session
>>> component.provideAdapter(session.ClientId)
>>> component.provideAdapter(session.ClientId, provides=IClientId)
>>> component.provideAdapter(session.Session)
>>> component.provideUtility(session.RAMSessionDataContainer(), ISessionDataContainer)
>>> class ClientIdManager(object):
... implements(IClientIdManager)
... def getClientId(self, request): return 'dummy'
>>> ClientIdManager = implementer(IClientIdManager)(ClientIdManager)
>>> component.provideUtility(ClientIdManager())
@ -143,17 +145,19 @@ the corresponding client.
>>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request)
>>> view.data
{'acadTitles': u'', 'standard.lastName': u'Skywalker', 'addr': u'Mr',
'firstName': u'', 'lastName': u'Skywalker', 'age': '', '__name__': '...',
'email': u'luke@skywalker.universe',
'standard.email': u'luke@skywalker.universe'}
{..., 'lastName': 'Skywalker', ...}
{'acadTitles': '', 'standard.lastName': 'Skywalker', 'addr': 'Mr',
'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
be created - if we clear the client name in the session first via
``view.setClientName('')``.
>>> input = dict(lastName='Solo', firstName='Han', email='han.solo@space.net',
... addr=u'Mr', action='update')
... addr='Mr', action='update')
>>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request)
>>> view.setClientName('')
@ -168,15 +172,17 @@ be created - if we clear the client name in the session first via
>>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request)
>>> view.data
{'acadTitles': u'', 'standard.lastName': u'Solo', 'addr': u'Mr',
'firstName': u'Han', 'lastName': u'Solo',
'age': '', '__name__': '...', 'email': u'han.solo@space.net',
'standard.email': u'han.solo@space.net'}
{..., 'lastName': 'Solo', ...}
{'acadTitles': '', 'standard.lastName': 'Solo', 'addr': 'Mr',
'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.
>>> input = dict(lastName=u'Skywalker', firstName=u'Luke', id=client1Name,
... email='luke@skywalker.universe', addr=u'Mr', action='update')
>>> input = dict(lastName='Skywalker', firstName='Luke', id=client1Name,
... email='luke@skywalker.universe', addr='Mr', action='update')
>>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request)
>>> view.update()
@ -186,15 +192,17 @@ If we provide an id parameter we may also change an existing client.
>>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request)
>>> view.data
{'acadTitles': u'', 'standard.lastName': u'Skywalker', 'addr': u'Mr',
'firstName': u'Luke', 'lastName': u'Skywalker', 'age': '', '__name__': '...',
'email': u'luke@skywalker.universe',
'standard.email': u'luke@skywalker.universe'}
{..., 'lastName': 'Skywalker', ...}
{'acadTitles': '', 'standard.lastName': 'Skywalker', 'addr': 'Mr',
'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
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)
>>> view = SchemaView(schema, request)
>>> 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
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')
>>> request = TestRequest(form=input)
>>> view = SchemaView(schema, request)
@ -216,7 +224,7 @@ More on special field types
>>> schema2 = workshop.clientSchemas['address']
>>> countryField = schema2.fields.country
>>> 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
@ -437,7 +445,7 @@ We are now ready to trigger a registration checkout.
>>> result = ruleManager.handleEvent(Event(checkoutEvent, client1))
sender: manager@workshops.com
recipients: [u'luke@skywalker.universe']
recipients: ['luke@skywalker.universe']
subject: Workshop Registration
message:
Dear Luke Skywalker,
@ -474,7 +482,7 @@ rendered message.
>>> input = dict(message='feedback_html')
>>> view = MessageView(workshop, TestRequest(form=input))
>>> view.getMessage()
u'<p>Dear Luke Skywalker...'
'<p>Dear Luke Skywalker...'
Service Manager and Service Views
@ -527,7 +535,7 @@ because the ``waitingList`` flag has not been set.)
>>> srvView.registeredTotalSubmitted
0
>>> srvView.registeredTotalsSubmitted
{'numberWaiting': '', 'number': 0}
{'number': 0, 'numberWaiting': ''}
Checkout
--------
@ -545,7 +553,7 @@ her registrations.
>>> checkout.getRegistrationsInfo()
[...]
>>> 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
actions will be carried out.
@ -556,7 +564,7 @@ actions will be carried out.
>>> checkout = CheckoutView(workshop, request)
>>> checkout.update()
sender: unknown@sender.com
recipients: [u'luke@skywalker.universe']
recipients: ['luke@skywalker.universe']
subject: Workshop Registration
message: ...
False
@ -579,7 +587,7 @@ for security reasons.)
>>> checkout = CheckoutView(workshop, request)
>>> checkout.update()
sender: unknown@sender.com
recipients: [u'luke@skywalker.universe']
recipients: ['luke@skywalker.universe']
subject: Workshop Registration
message: ...
False
@ -621,7 +629,7 @@ possible.
True
>>> input = dict(lastName='Walker', firstName='John', email='john@walker.tv',
... addr=u'Mr', action='update')
... addr='Mr', action='update')
>>> request = TestRequest(form=input)
>>> schema = workshop.clientSchemas['person']
>>> view = SchemaView(schema, request)
@ -755,7 +763,7 @@ conference that is organized in parallel tracks.
>>> conference.__name__ = 'conference'
>>> main = service.Service('conf_main', manager=conference,
... title=u'Conference', capacity=5)
... title='Conference', capacity=5)
>>> conference.services.append(main)

View file

@ -2,8 +2,6 @@
Organizations: Persons, Institutions, Addresses, Work, ...
==========================================================
($Id$)
>>> from zope import component
@ -36,7 +34,7 @@ so we do not have to set up real objects.
>>> wi01 = workItems.add('001', 'john')
>>> wi01
<WorkItem ['001', 1, 'john', '...', 'new']:
{'created': ..., 'creator': 'john'}>
{'creator': 'john', 'created': ...}>
Properties that have not been set explicitly have a default of None; properties
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
<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 effort is taken from the duration if not set explicitly.
@ -84,8 +82,7 @@ be excluded from queries.
'planned_x'
>>> wi02
<WorkItem ['001', 1, 'jim', '2008-12-22 15:22', 'accepted']:
{'duration': 700, 'start': 1229955772, 'created': ...,
'end': 1229956372, 'creator': 'jim'}>
{'creator': 'jim', 'created': ..., ...}>
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
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'running']:
{'duration': 0, 'start': 1229958000, 'created': ..., 'creator': 'jim'}>
{'creator': 'jim', 'created': ..., ...}>
Stopping and finishing work
---------------------------
@ -113,10 +110,10 @@ as "running" will be replaced by a new one.
>>> wi03
<WorkItem ['001', 1, 'jim', '2008-12-22 16:00', 'replaced']:
{'duration': 0, 'start': 1229958000, 'created': ..., 'creator': 'jim'}>
{'creator': 'jim', 'created': ..., ...}>
>>> wi04
<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
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
<WorkItem ['001', 1, 'jim', '2008-12-22 17:00', 'finished']:
{'start': 1229961600, 'created': ..., 'end': 1229962200, 'creator': 'jim'}>
{'creator': 'jim', 'created': ..., ...}>
>>> wi05.duration, wi05.effort
(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
<WorkItem ['001', 1, 'jim', '... ...', 'closed']:
{'start': ..., 'created': ..., 'end': ..., 'creator': 'john'}>
{'creator': 'john', 'created': ..., ...}>
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
<WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'delegated']:
{'start': 1229970800, 'created': ..., 'creator': 'john'}>
{'creator': 'john', 'created': ..., 'start': 1229970800, 'target': '0000008'}>
>>> wi08
<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))
8
@ -180,10 +177,10 @@ Note that nevertheless only the last work item of a run may be modified.
>>> wi08
<WorkItem ['001', 3, 'annie', '2008-12-22 19:33', 'replaced']:
{'start': 1229970800, 'created': ..., 'creator': 'john'}>
{'creator': 'john', 'created': ..., 'start': 1229970800, 'source': '0000007'}>
>>> wi09
<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
@ -192,7 +189,7 @@ Moving Work Items to Other Tasks
>>> wi10 = wi07.doAction('move', 'john')
>>> wi10
<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
@ -215,8 +212,8 @@ Close a run with some delegated items.
>>> wi07a = workItems.query(run=2)[-2]
>>> wi07b = workItems.query(run=2)[-1]
>>> wi07b
<WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'moved']: {'start': 1229970800,
'created': ..., 'target': '0000011', 'creator': 'john'}>
<WorkItem ['001', 2, 'john', '2008-12-22 19:33', 'moved']:
{'creator': 'john', 'created': ..., 'start': 1229970800, 'target': '0000011'}>
>>> 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'):
# or: fallback='CP852'
if isinstance(value, unicode):
if isinstance(value, str):
return value
elif isinstance(value, str):
elif isinstance(value, bytes):
try:
return value.decode(encoding)
except UnicodeDecodeError:
return value.decode(fallback)
else:
return u''
return ''