diff --git a/composer/message/README.txt b/composer/message/README.txt index 1fdc44c..6b5cca4 100644 --- a/composer/message/README.txt +++ b/composer/message/README.txt @@ -11,7 +11,7 @@ Message Management >>> messageText = '''Dear $person.firstname $person.lastname, ... You have been registered for the following $services. - ... $@@list_registrations + ... $@@list_registrations_text ... $footer ... ''' @@ -27,7 +27,7 @@ Message interpolation >>> t = MessageTemplate(messageText) >>> print t.safe_substitute({ ... 'person.firstname': 'John', 'person.lastname': 'Smith', - ... '@@list_registrations': '0815: Python Introduction', + ... '@@list_registrations_text': '0815: Python Introduction', ... 'services': 'events', ... 'footer': 'Regards, $sender'}) Dear John Smith, @@ -43,9 +43,9 @@ Working with message instances >>> mi = MessageInstance(None, manager.messages['feedback_text'], manager) >>> for key, value in mi.applyTemplate().items(): ... print key + ':', value - subjectLine: text: Dear $person.firstname $person.lastname, You have been registered for the following events. - $@@list_registrations + $@@list_registrations_text Best regards, Jack + subjectLine: diff --git a/composer/message/instance.py b/composer/message/instance.py index 4087173..e18c5b3 100644 --- a/composer/message/instance.py +++ b/composer/message/instance.py @@ -42,16 +42,20 @@ class MessageInstance(Instance): self.manager = manager def applyTemplate(self, data=None, **kw): - data = DataProvider(self) - text = MessageTemplate(self.template.text).safe_substitute(data) + if data is None: + data = {} + dp = DataProvider(self, data) + text = MessageTemplate(self.template.text).safe_substitute(dp) subject = self.template.subjectLine - return Jeep((('subjectLine', subject), ('text', text))) + data.update(dict(subjectLine=subject, text=text)) + return data class DataProvider(object): - def __init__(self, context): + def __init__(self, context, data): self.context = context + self.data = data def __getitem__(self, key): client = self.context.client @@ -61,8 +65,9 @@ class DataProvider(object): viewName = key[2:] if client is None: return '$' + key + request = self.data.get('request') or TestRequest() view = component.queryMultiAdapter( - (client.manager, TestRequest()), name=viewName) + (client.manager, request), name=viewName) if view is not None: return view() else: @@ -72,7 +77,7 @@ class DataProvider(object): # (client, messageManager.messages[key]), IInstance) mi = MessageInstance(client, messageManager.messages[key], messageManager) - return mi.applyTemplate().text + return mi.applyTemplate()['text'] elif '.' in key: if client is None: return '$' + key diff --git a/composer/rule/README.txt b/composer/rule/README.txt index 5b39ff9..a26de75 100644 --- a/composer/rule/README.txt +++ b/composer/rule/README.txt @@ -34,4 +34,4 @@ Rule-based Execution of Actions >>> client = Client() >>> manager.handleEvent(Event(checkoutEvent, client)) - [{}] + [{'request': None}] diff --git a/composer/rule/base.py b/composer/rule/base.py index 7227eb2..a24f7dd 100644 --- a/composer/rule/base.py +++ b/composer/rule/base.py @@ -99,11 +99,12 @@ class Event(object): implements(IEvent) - def __init__(self, eventType, context=None): + def __init__(self, eventType, context=None, request=None): self.eventType = eventType self.name = eventType.name self.title = eventType.title self.context = context + self.request = request # conditions diff --git a/composer/rule/instance.py b/composer/rule/instance.py index b1961d0..e28fc20 100644 --- a/composer/rule/instance.py +++ b/composer/rule/instance.py @@ -43,7 +43,7 @@ class RuleInstance(Instance): cond = component.getAdapter(self, ICondition, name=c) if not cond(): continue - data = {} + data = dict(request=self.event.request) for action in self.template.actions: handler = component.getAdapter(self, IActionHandler, name=action.handlerName) diff --git a/composer/rule/interfaces.py b/composer/rule/interfaces.py index ea89012..57f75ec 100644 --- a/composer/rule/interfaces.py +++ b/composer/rule/interfaces.py @@ -96,6 +96,8 @@ class IEvent(Interface): name = Attribute('The name by which the event will be identified.') title = Attribute('A human readable title or label.') context = Attribute('An object that is associated with the event.') + request = Attribute('Optional: the browser request that is associated ' + 'with the event.') class ICondition(Interface): diff --git a/composer/rule/mail.py b/composer/rule/mail.py index 391e075..e596674 100644 --- a/composer/rule/mail.py +++ b/composer/rule/mail.py @@ -37,7 +37,8 @@ class MailActionHandler(ActionHandler): client = self.context.context clientData = IInstance(client).applyTemplate() recipient = clientData['standard.email'] - msg = self.prepareMessage(data.subjectLine, data.text, sender, recipient) + msg = self.prepareMessage(data['subjectLine'], data['text'], + sender, recipient) data['mailInfo'] = self.sendMail(msg.as_string(), sender, [recipient]) return data diff --git a/organize/browser/service.py b/organize/browser/service.py index ef6f1ac..5e22cd0 100644 --- a/organize/browser/service.py +++ b/organize/browser/service.py @@ -118,15 +118,15 @@ class ServiceManagerView(BaseView): result = [] classific = [] category = None - maxLevel = 0 svcs = sorted((svc.getCategory(), idx, svc) for idx, svc in enumerate(self.context.getServices())) for cat, idx, svc in svcs: if includeCategories and cat not in includeCategories: continue + level = 0 if cat != category: term = serviceCategories.getTermByToken(cat) - result.append(dict(isHeadline=True, level=0, title=term.title, + result.append(dict(isHeadline=True, level=level, title=term.title, name=cat, object=None)) category = cat @@ -141,10 +141,8 @@ class ServiceManagerView(BaseView): title=element.title, object=element.object, view=None)) - classific = clsf - if level > maxLevel: - maxLevel = level - result.append(dict(isHeadline=False, level=maxLevel+1, + classific = clsf[:idx+1] + result.append(dict(isHeadline=False, level=level+1, name=svc.getName(), title=svc.title or svc.getName(), fromTo=self.getFromTo(svc), @@ -190,12 +188,27 @@ class CheckoutView(ServiceManagerView): # send mail rm = IRuleManager(self.manager) rm.addRule(getCheckoutRule(self.manager.senderEmail)) - rm.handleEvent(Event(eventTypes['service.checkout'], client)) + rm.handleEvent(Event(eventTypes['service.checkout'], client, self.request)) # find thank you message and redirect to it params = '?message=thankyou&id=' + self.clientName self.request.response.redirect(self.url + '/checkout.html' + params) return False + def listRegistrationsText(self): + client = self.getClient() + if client is None: + return 'Error: no client given.' + result = [] + regs = IClientRegistrations(client) + regs = sorted(regs.getRegistrations(), key=self.sortKey) + for reg in regs: + service = reg.service + line = '%-30s %27s' % (service.title, self.getFromTo(service)) + if service.allowRegWithNumber: + line += ' %4i' % reg.number + result.append(line) + return '\n'.join(result) + class ServiceView(BaseView): @@ -215,6 +228,16 @@ class ServiceView(BaseView): tpl = self.getRegistrationTemplate() return self.getUrlForObject(tpl) + def allowRegistration(self): + context = self.context + if not context.allowDirectRegistration: + return False + return (self.capacityAvailable() + or self.getClientName() in context.registrations) + + def capacityAvailable(self): + return not self.context.capacity or self.context.availableCapacity + def getClientData(self): clientName = self.getClientName() if clientName is None: @@ -271,7 +294,9 @@ class ServiceView(BaseView): nextUrl = self.getSchemaUrl() regs = self.state = IClientRegistrations(client) try: - number = int(form.get('number', 1)) + number = int(form.get('number', 0)) + if number < 0: + number = 0 except ValueError: number = 1 regs.validate(clientName, [self.context], [number]) @@ -318,6 +343,13 @@ class RegistrationTemplateView(BaseView): def sortKey(self, svc): return (svc.category, svc.getClassification(), svc.start) + def allowRegistration(self, service): + return (self.capacityAvailable(service) + or service in self.getRegisteredServices()) + + def capacityAvailable(self, service): + return not service.capacity or service.availableCapacity + def getRegistrations(self): clientName = self.getClientName() if not clientName: @@ -330,8 +362,11 @@ class RegistrationTemplateView(BaseView): regs.template = self.context return regs.getRegistrations() + def getRegisteredServices(self): + return [r.service for r in self.getRegistrations()] + def getRegisteredServicesTokens(self): - return [r.service.token for r in self.getRegistrations()] + return [s.token for s in self.getRegisteredServices()] def getRegistrationsDict(self): return dict((r.service.token, r) for r in self.getRegistrations()) @@ -377,7 +412,7 @@ class RegistrationTemplateView(BaseView): try: value = int(form.get('service.' + token, 0)) except ValueError: - value = 1 + value = 0 if value > 0: newServices.append(svc) numbers.append(value) diff --git a/organize/service.py b/organize/service.py index 97196f9..0b18cbe 100644 --- a/organize/service.py +++ b/organize/service.py @@ -39,6 +39,7 @@ from cybertools.stateful.base import StatefulAdapter from cybertools.stateful.definition import registerStatesDefinition from cybertools.stateful.definition import StatesDefinition from cybertools.stateful.definition import State, Transition +from cybertools.stateful.interfaces import IStateful from cybertools.util.jeep import Jeep from cybertools.util.randomname import generateName from cybertools.organize.interfaces import IServiceManager @@ -179,9 +180,11 @@ class Service(object): if clientName in self.registrations: del self.registrations[clientName] - def getNumberRegistered(self): + def getNumberRegistered(self, ignoreTemporary=True): result = 0 for r in self.registrations.values(): + if ignoreTemporary and IStateful(r).state == 'temporary': + continue result += r.number return result @@ -277,7 +280,11 @@ class ClientRegistrations(object): for svc, n in zip(services, numbers): if clientName: oldReg = svc.registrations.get(clientName, None) - oldN = oldReg and oldReg.number or 0 + if oldReg is None or IStateful(oldReg).state == 'temporary': + # availableCapacity does not consider temporary registrations + oldN = 0 + else: + oldN = oldReg.number or 0 else: oldN = 0 if svc.capacity and svc.capacity > 0 and svc.availableCapacity < n - oldN: