source: main/waeup.kofa/trunk/src/waeup/kofa/accesscodes/batching.py @ 10040

Last change on this file since 10040 was 9706, checked in by Henrik Bettermann, 12 years ago

Rework logging of batch processing. Remove redundant text but add name of import file.

  • Property svn:keywords set to Id
File size: 8.2 KB
Line 
1## $Id: batching.py 9706 2012-11-21 22:37:03Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""Batch processing components for accesscodes.
19
20"""
21import grok
22from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
23from zope.interface import Interface
24from waeup.kofa.interfaces import (
25    IBatchProcessor, IGNORE_MARKER, IObjectHistory, IObjectConverter)
26from waeup.kofa.utils.batching import BatchProcessor
27from waeup.kofa.accesscodes.interfaces import IAccessCodeBatch, IAccessCode
28from waeup.kofa.accesscodes.workflow import INITIALIZED, USED, DISABLED
29
30IMPORTABLE_TRANSITIONS = (
31    'init', 'use', 'disable_used', 'disable_unused', 'reeanble')
32
33IMPORTABLE_STATES = (INITIALIZED, USED, DISABLED)
34
35class AccessCodeBatchProcessor(BatchProcessor):
36    """A batch processor for IAccessCodeBatch objects.
37    """
38    grok.implements(IBatchProcessor)
39    grok.provides(IBatchProcessor)
40    grok.context(Interface)
41    util_name = 'accesscodebatchprocessor'
42    grok.name(util_name)
43
44    name = u'AccessCodeBatch Processor'
45    iface = IAccessCodeBatch
46
47    location_fields = ['batch_id',]
48    factory_name = 'waeup.AccessCodeBatch'
49
50    mode = None
51
52    def parentsExist(self, row, site):
53        return 'accesscodes' in site.keys()
54
55    def entryExists(self, row, site):
56        return row['batch_id'] in site['accesscodes'].keys()
57
58    def getParent(self, row, site):
59        return site['accesscodes']
60
61    def getEntry(self, row, site):
62        if not self.entryExists(row, site):
63            return None
64        parent = self.getParent(row, site)
65        return parent.get(row['batch_id'])
66
67    def addEntry(self, obj, row, site):
68        parent = self.getParent(row, site)
69        parent.addBatchByImport(obj, row['batch_id'])
70        return
71
72    def updateEntry(self, obj, row, site, filename):
73        """Update obj to the values given in row.
74
75        Returns a string describing the fields changed.
76        """
77        items_changed = super(AccessCodeBatchProcessor, self).updateEntry(
78            obj, row, site, filename)
79        # Log actions...
80        location_field = self.location_fields[0]
81        grok.getSite()['accesscodes'].logger.info(
82            '%s - %s - %s - updated: %s'
83            % (self.name, filename, row[location_field], items_changed))
84        return
85
86class AccessCodeProcessor(BatchProcessor):
87    """A batch processor for IAccessCode objects.
88    """
89    grok.implements(IBatchProcessor)
90    grok.provides(IBatchProcessor)
91    grok.context(Interface)
92    util_name = 'accesscodeprocessor'
93    grok.name(util_name)
94
95    name = u'AccessCode Processor'
96    iface = IAccessCode
97
98    location_fields = ['representation', 'batch_prefix', 'batch_num']
99    factory_name = 'waeup.AccessCode'
100
101    mode = None
102
103    @property
104    def available_fields(self):
105        return sorted(list(set(
106                    self.location_fields + [
107                        'random_num', 'owner', 'cost',
108                        'state', 'transition', 'batch_serial',]
109                    )))
110
111    @property
112    def required_fields(self):
113        return ['random_num', 'batch_serial', 'history',]
114
115    def parentsExist(self, row, site):
116        return self.getParent(row,site) is not None
117
118    def entryExists(self, row, site):
119        parent = self.getParent(row, site)
120        if parent is None:
121            return False
122        return row['representation'] in parent.keys()
123
124    def getParent(self, row, site):
125        if not 'accesscodes' in site.keys():
126            return None
127        batch_id = '%s-%s' % (row['batch_prefix'], row['batch_num'])
128        return site['accesscodes'].get(batch_id, None)
129
130    def getEntry(self, row, site):
131        if not self.entryExists(row, site):
132            return None
133        parent = self.getParent(row, site)
134        return parent.get(row['representation'], None)
135
136    def addEntry(self, obj, row, site):
137        parent = self.getParent(row, site)
138        obj.batch_serial = row['batch_serial']
139        obj.random_num = row['random_num']
140        parent[row['representation']] = obj
141        return
142
143    def checkConversion(self, row, mode='create'):
144        """Validates all values in row.
145        """
146        converter = IObjectConverter(self.iface)
147        errs, inv_errs, conv_dict =  converter.fromStringDict(
148            row, self.factory_name, mode=mode)
149        if 'transition' in row and 'state' in row:
150            if row['transition'] not in (IGNORE_MARKER, '') and \
151                row['state'] not in (IGNORE_MARKER, ''):
152                errs.append(('workflow','not allowed'))
153                return errs, inv_errs, conv_dict
154        if 'transition' in row:
155            if row['transition'] not in IMPORTABLE_TRANSITIONS:
156                if row['transition'] not in (IGNORE_MARKER, ''):
157                    errs.append(('transition','not allowed'))
158        if 'state' in row:
159            if row['state'] not in IMPORTABLE_STATES:
160                if row['state'] not in (IGNORE_MARKER, ''):
161                    errs.append(('state','not allowed'))
162                else:
163                    # State is an attribute of AccessCode and must not
164                    # be changed if empty.
165                    conv_dict['state'] = IGNORE_MARKER
166        return errs, inv_errs, conv_dict
167
168    def checkUpdateRequirements(self, obj, row, site):
169        """Checks requirements the object must fulfill when being updated.
170
171        This method is not used in case of deleting or adding objects.
172
173        Returns error messages as strings in case of requirement
174        problems.
175        """
176        transition = row.get('transition', IGNORE_MARKER)
177        if transition not in (IGNORE_MARKER, ''):
178            allowed_transitions = IWorkflowInfo(obj).getManualTransitionIds()
179            if transition not in allowed_transitions:
180                return 'Transition not allowed.'
181        return None
182
183    def updateEntry(self, obj, row, site, filename):
184        """Update obj to the values given in row.
185
186        Returns a string describing the fields changed.
187        """
188        items_changed = ''
189        # Update state
190        # Attention: When importing states the counters remain unchanged.
191        if 'state' in row:
192            state = row.get('state', IGNORE_MARKER)
193            if state not in (IGNORE_MARKER, ''):
194                value = row['state']
195                IWorkflowState(obj).setState(value)
196                items_changed += ('%s=%s, ' % ('state', state))
197                msg = "state '%s' set" % state
198                IObjectHistory(obj).addMessage(msg)
199            row.pop('state')
200        # Trigger transition. Counters are properly changed.
201        if 'transition' in row:
202            transition = row.get('transition', IGNORE_MARKER)
203            if transition not in (IGNORE_MARKER, ''):
204                value = row['transition']
205                IWorkflowInfo(obj).fireTransition(value)
206                items_changed += ('%s=%s, ' % ('transition', transition))
207            row.pop('transition')
208
209        # In import files we can use the hash symbol at the end of a
210        # random_num string to avoid annoying automatic number transformation
211        # by Excel or Calc
212        if 'random_num' in row:
213            random_num = row.get('random_num', IGNORE_MARKER)
214            if random_num not in (IGNORE_MARKER, ''):
215                row['random_num'] = random_num.strip('#')
216
217        items_changed += super(AccessCodeProcessor, self).updateEntry(
218            obj, row, site, filename)
219
220        # Log actions...
221        grok.getSite()['accesscodes'].logger.info(
222            '%s - %s - %s - updated: %s'
223            % (self.name, filename, row['representation'], items_changed))
224        return
Note: See TracBrowser for help on using the repository browser.