From 8037ac38beaaeed70034320608c77d0e1078fc1c Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 23 Sep 2024 11:00:35 +0200 Subject: [PATCH] organize: Python3 fixes --- cybertools/composer/message/instance.py | 29 ++----- cybertools/composer/rule/mail.py | 28 +------ cybertools/composer/rule/message.py | 25 +----- cybertools/composer/rule/tests.py | 2 +- cybertools/organize/README.txt | 14 ++-- cybertools/organize/browser/report.py | 6 +- cybertools/organize/servicemanager.txt | 104 +++++++++++++----------- cybertools/organize/work.txt | 33 ++++---- cybertools/util/format.py | 6 +- 9 files changed, 97 insertions(+), 150 deletions(-) diff --git a/cybertools/composer/message/instance.py b/cybertools/composer/message/instance.py index b6f01f8..8f8a89d 100644 --- a/cybertools/composer/message/instance.py +++ b/cybertools/composer/message/instance.py @@ -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) diff --git a/cybertools/composer/rule/mail.py b/cybertools/composer/rule/mail.py index af89463..028e0c1 100644 --- a/cybertools/composer/rule/mail.py +++ b/cybertools/composer/rule/mail.py @@ -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 diff --git a/cybertools/composer/rule/message.py b/cybertools/composer/rule/message.py index 6926aee..5a8cb46 100644 --- a/cybertools/composer/rule/message.py +++ b/cybertools/composer/rule/message.py @@ -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 diff --git a/cybertools/composer/rule/tests.py b/cybertools/composer/rule/tests.py index 280e09e..a515f98 100755 --- a/cybertools/composer/rule/tests.py +++ b/cybertools/composer/rule/tests.py @@ -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): diff --git a/cybertools/organize/README.txt b/cybertools/organize/README.txt index e272251..f6c6665 100644 --- a/cybertools/organize/README.txt +++ b/cybertools/organize/README.txt @@ -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 diff --git a/cybertools/organize/browser/report.py b/cybertools/organize/browser/report.py index 128f1fa..b4386eb 100644 --- a/cybertools/organize/browser/report.py +++ b/cybertools/organize/browser/report.py @@ -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: diff --git a/cybertools/organize/servicemanager.txt b/cybertools/organize/servicemanager.txt index b538c5b..82cb2bb 100644 --- a/cybertools/organize/servicemanager.txt +++ b/cybertools/organize/servicemanager.txt @@ -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'

Dear Luke Skywalker...' + '

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) diff --git a/cybertools/organize/work.txt b/cybertools/organize/work.txt index c276fc1..a7848e9 100644 --- a/cybertools/organize/work.txt +++ b/cybertools/organize/work.txt @@ -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 + {'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 + {'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 + {'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 + {'creator': 'jim', 'created': ..., ...}> Stopping and finishing work --------------------------- @@ -113,10 +110,10 @@ as "running" will be replaced by a new one. >>> wi03 + {'creator': 'jim', 'created': ..., ...}> >>> wi04 + {'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 + {'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 + {'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 + {'creator': 'john', 'created': ..., 'start': 1229970800, 'target': '0000008'}> >>> wi08 + {'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 + {'creator': 'john', 'created': ..., 'start': 1229970800, 'source': '0000007'}> >>> wi09 + {'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 + {'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 - + >>> wi07c = wi07b.doAction('close', 'john') diff --git a/cybertools/util/format.py b/cybertools/util/format.py index 1eddc52..6ed4bad 100644 --- a/cybertools/util/format.py +++ b/cybertools/util/format.py @@ -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 ''