[gen] Wrapper.reindex: allow to specify attribute 'fields' containing the list of fields that must be reindexed. If not given, all object fields are reindexed. [pod] POD ODS templates: into POD results from ODS templates, Python expressions that produce ints, floats or dates (Zope DateTime only for the moment) produce cells with typed values.
This commit is contained in:
		
							parent
							
								
									43261fde60
								
							
						
					
					
						commit
						2307a284cc
					
				
					 7 changed files with 1947 additions and 1820 deletions
				
			
		|  | @ -372,14 +372,20 @@ class AbstractWrapper(object): | ||||||
|             exec expression |             exec expression | ||||||
|         return ctx |         return ctx | ||||||
| 
 | 
 | ||||||
|     def reindex(self): |     def reindex(self, fields=None, unindex=False): | ||||||
|         '''Asks a direct object reindexing. In most cases you don't have to |         '''Asks a direct object reindexing. In most cases you don't have to | ||||||
|            reindex objects "manually" with this method. When an object is |            reindex objects "manually" with this method. When an object is | ||||||
|            modified after some user action has been performed, Appy reindexes |            modified after some user action has been performed, Appy reindexes | ||||||
|            this object automatically. But if your code modifies other objects, |            this object automatically. But if your code modifies other objects, | ||||||
|            Appy may not know that they must be reindexed, too. So use this |            Appy may not know that they must be reindexed, too. So use this | ||||||
|            method in those cases.''' |            method in those cases. | ||||||
|         self.o.reindex() |         ''' | ||||||
|  |         if fields: | ||||||
|  |             # Get names of indexes from field names. | ||||||
|  |             indexes = [Search.getIndexName(name) for name in fields] | ||||||
|  |         else: | ||||||
|  |             indexes = None | ||||||
|  |         self.o.reindex(indexes=indexes, unindex=unindex) | ||||||
| 
 | 
 | ||||||
|     def export(self, at='string', format='xml', include=None, exclude=None): |     def export(self, at='string', format='xml', include=None, exclude=None): | ||||||
|         '''Creates an "exportable" version of this object. p_format is "xml" by |         '''Creates an "exportable" version of this object. p_format is "xml" by | ||||||
|  |  | ||||||
|  | @ -139,12 +139,24 @@ class Buffer: | ||||||
| 
 | 
 | ||||||
|     def getLength(self): pass # To be overridden |     def getLength(self): pass # To be overridden | ||||||
| 
 | 
 | ||||||
|     def dumpStartElement(self, elem, attrs={}, ignoreAttrs=()): |     def dumpStartElement(self, elem, attrs={}, ignoreAttrs=(), | ||||||
|  |                          insertAttributesHook=False): | ||||||
|  |         '''Inserts into this buffer the start tag p_elem, with its p_attrs, | ||||||
|  |            excepted those listed in p_ignoreAttrs. If p_insertAttributesHook | ||||||
|  |            is True (works only for MemoryBuffers), we will insert an Attributes | ||||||
|  |            instance at the end of the list of dumped attributes, in order to be | ||||||
|  |            able, when evaluating the buffer, to dump additional attributes, not | ||||||
|  |            known at this dump time.''' | ||||||
|         self.write('<%s' % elem) |         self.write('<%s' % elem) | ||||||
|         for name, value in attrs.items(): |         for name, value in attrs.items(): | ||||||
|             if ignoreAttrs and (name in ignoreAttrs): continue |             if ignoreAttrs and (name in ignoreAttrs): continue | ||||||
|             self.write(' %s=%s' % (name, quoteattr(value))) |             self.write(' %s=%s' % (name, quoteattr(value))) | ||||||
|  |         if insertAttributesHook: | ||||||
|  |             res = self.addAttributes() | ||||||
|  |         else: | ||||||
|  |             res = None | ||||||
|         self.write('>') |         self.write('>') | ||||||
|  |         return res | ||||||
| 
 | 
 | ||||||
|     def dumpEndElement(self, elem): |     def dumpEndElement(self, elem): | ||||||
|         self.write('</%s>' % elem) |         self.write('</%s>' % elem) | ||||||
|  | @ -179,12 +191,21 @@ class FileBuffer(Buffer): | ||||||
|         except UnicodeDecodeError: |         except UnicodeDecodeError: | ||||||
|             self.content.write(something) |             self.content.write(something) | ||||||
| 
 | 
 | ||||||
|     def addExpression(self, expression): |     def addExpression(self, expression, tiedHook=None): | ||||||
|  |         # At 2013-02-06, this method was not called within the whole test suite. | ||||||
|         try: |         try: | ||||||
|             self.dumpContent(Expression(expression).evaluate(self.env.context)) |             self.dumpContent(Expression(expression).evaluate(self.env.context)) | ||||||
|         except Exception, e: |         except Exception, e: | ||||||
|             PodError.dump(self, EVAL_EXPR_ERROR % (expression, e), dumpTb=False) |             PodError.dump(self, EVAL_EXPR_ERROR % (expression, e), dumpTb=False) | ||||||
| 
 | 
 | ||||||
|  |     def addAttributes(self): | ||||||
|  |         # Into a FileBuffer, it is not possible to insert Attributes. Every | ||||||
|  |         # Attributes instance is tied to an Expression; because dumping | ||||||
|  |         # expressions directly into FileBuffer instances seems to be a rather | ||||||
|  |         # theorical case (see comment inside the previous method), it does not | ||||||
|  |         # seem to be a real problem. | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|     def pushSubBuffer(self, subBuffer): pass |     def pushSubBuffer(self, subBuffer): pass | ||||||
| 
 | 
 | ||||||
| # ------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------ | ||||||
|  | @ -298,14 +319,21 @@ class MemoryBuffer(Buffer): | ||||||
|                 # Remember where this cell is in the table |                 # Remember where this cell is in the table | ||||||
|                 newElem.colIndex = newElem.tableInfo.curColIndex |                 newElem.colIndex = newElem.tableInfo.curColIndex | ||||||
| 
 | 
 | ||||||
|     def addExpression(self, expression): |     def addExpression(self, expression, tiedHook=None): | ||||||
|         # Create the POD expression |         # Create the POD expression | ||||||
|         expr = Expression(expression) |         expr = Expression(expression) | ||||||
|         expr.expr = expression |         if tiedHook: tiedHook.tiedExpression = expr | ||||||
|         self.elements[self.getLength()] = expr |         self.elements[self.getLength()] = expr | ||||||
|         self.content += u' '# To be sure that an expr and an elem can't be found |         self.content += u' '# To be sure that an expr and an elem can't be found | ||||||
|                             # at the same index in the buffer. |                             # at the same index in the buffer. | ||||||
| 
 | 
 | ||||||
|  |     def addAttributes(self): | ||||||
|  |         # Create the Attributes instance | ||||||
|  |         attrs = Attributes(self.env) | ||||||
|  |         self.elements[self.getLength()] = attrs | ||||||
|  |         self.content += u' ' | ||||||
|  |         return attrs | ||||||
|  | 
 | ||||||
|     def createAction(self, statementGroup): |     def createAction(self, statementGroup): | ||||||
|         '''Tries to create an action based on p_statementGroup. If the statement |         '''Tries to create an action based on p_statementGroup. If the statement | ||||||
|            is not correct, r_ is -1. Else, r_ is the index of the element within |            is not correct, r_ is -1. Else, r_ is the index of the element within | ||||||
|  | @ -547,6 +575,8 @@ class MemoryBuffer(Buffer): | ||||||
|                     except Exception, e: |                     except Exception, e: | ||||||
|                         PodError.dump(result, EVAL_EXPR_ERROR % ( |                         PodError.dump(result, EVAL_EXPR_ERROR % ( | ||||||
|                             evalEntry.expr, e), dumpTb=False) |                             evalEntry.expr, e), dumpTb=False) | ||||||
|  |                 elif isinstance(evalEntry, Attributes): | ||||||
|  |                     result.write(evalEntry.evaluate(self.env.context)) | ||||||
|                 else: # It is a subBuffer |                 else: # It is a subBuffer | ||||||
|                     if evalEntry.action: |                     if evalEntry.action: | ||||||
|                         evalEntry.action.execute() |                         evalEntry.action.execute() | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,USA. | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,USA. | ||||||
| 
 | 
 | ||||||
| # ------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------ | ||||||
|  | from xml.sax.saxutils import quoteattr | ||||||
| from appy.shared.xml_parser import XmlElement | from appy.shared.xml_parser import XmlElement | ||||||
| from appy.pod.odf_parser import OdfEnvironment as ns | from appy.pod.odf_parser import OdfEnvironment as ns | ||||||
| from appy.pod import PodError | from appy.pod import PodError | ||||||
|  | @ -71,11 +72,38 @@ class Table(PodElement): | ||||||
|         self.tableInfo = None # ~OdTable~ |         self.tableInfo = None # ~OdTable~ | ||||||
| 
 | 
 | ||||||
| class Expression(PodElement): | class Expression(PodElement): | ||||||
|  |     '''Instances of this class represent Python expressions that are inserted | ||||||
|  |        into a POD template.''' | ||||||
|     OD = None |     OD = None | ||||||
|     def __init__(self, pyExpr): |     def __init__(self, pyExpr): | ||||||
|  |         # The Python expression | ||||||
|         self.expr = pyExpr |         self.expr = pyExpr | ||||||
|  |         # We will store here the expression's true result (before being | ||||||
|  |         # converted to a string) | ||||||
|  |         self.result = None | ||||||
|  |         # This boolean indicates if this Expression instance has already been | ||||||
|  |         # evaluated or not. Expressions which are tied to attribute hooks are | ||||||
|  |         # already evaluated when the tied hook is evaluated: this boolean | ||||||
|  |         # prevents the expression from being evaluated twice. | ||||||
|  |         self.evaluated = False | ||||||
|  | 
 | ||||||
|     def evaluate(self, context): |     def evaluate(self, context): | ||||||
|         res = eval(self.expr, context) |         '''Evaluates the Python expression (self.expr) with a given | ||||||
|  |            p_context.''' | ||||||
|  |         # Evaluate the expression, or get it from self.result if it has already | ||||||
|  |         # been computed. | ||||||
|  |         if self.evaluated: | ||||||
|  |             res = self.result | ||||||
|  |             # It can happen only once, to ask to evaluate an expression that | ||||||
|  |             # was already evaluated (from the tied hook). We reset here the | ||||||
|  |             # boolean "evaluated" to allow for the next evaluation, probably | ||||||
|  |             # with another context. | ||||||
|  |             self.evaluated = False | ||||||
|  |         else: | ||||||
|  |             # Evaluates the Python expression | ||||||
|  |             res = self.result = eval(self.expr, context) | ||||||
|  |         # Converts the expression result to a string that can be inserted into | ||||||
|  |         # the POD result. | ||||||
|         if res == None: |         if res == None: | ||||||
|             res = u'' |             res = u'' | ||||||
|         elif isinstance(res, str): |         elif isinstance(res, str): | ||||||
|  | @ -85,4 +113,53 @@ class Expression(PodElement): | ||||||
|         else: |         else: | ||||||
|             res = unicode(res) |             res = unicode(res) | ||||||
|         return res |         return res | ||||||
|  | 
 | ||||||
|  | class Attributes(PodElement): | ||||||
|  |     '''Represents a bunch of XML attributes that will be dumped for a given tag | ||||||
|  |        in the result.''' | ||||||
|  |     OD = None | ||||||
|  |     floatTypes = ('int', 'long', 'float') | ||||||
|  |     dateTypes = ('DateTime',) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, env): | ||||||
|  |         self.attrs = {} | ||||||
|  |         # Depending on the result of a tied expression, we will dump, for | ||||||
|  |         # another tag, the series of attrs that this instance represents. | ||||||
|  |         self.tiedExpression = None | ||||||
|  |         # We will need the env to get the full names of attributes to dump. | ||||||
|  |         self.env = env | ||||||
|  | 
 | ||||||
|  |     def computeAttributes(self, expr): | ||||||
|  |         '''p_expr has been evaluated: its result is in expr.result. Depending | ||||||
|  |            on its type, we will dump the corresponding attributes in | ||||||
|  |            self.attrs.''' | ||||||
|  |         exprType = expr.result.__class__.__name__ | ||||||
|  |         tags = self.env.tags | ||||||
|  |         attrs = self.attrs | ||||||
|  |         if exprType in self.floatTypes: | ||||||
|  |             attrs[tags['value-type']] = 'float' | ||||||
|  |             attrs[tags['value']] = str(expr.result) | ||||||
|  |         elif exprType in self.dateTypes: | ||||||
|  |             attrs[tags['value-type']] = 'date' | ||||||
|  |             attrs[tags['value']] = expr.result.strftime('%Y-%m-%d') | ||||||
|  |         else: | ||||||
|  |             attrs[tags['value-type']] = 'string' | ||||||
|  | 
 | ||||||
|  |     def evaluate(self, context): | ||||||
|  |         # Evaluate first the tied expression, in order to determine its type. | ||||||
|  |         try: | ||||||
|  |             self.tiedExpression.evaluate(context) | ||||||
|  |             self.evaluated = True | ||||||
|  |         except Exception, e: | ||||||
|  |             # Don't set "evaluated" to True. This way, when the buffer will | ||||||
|  |             # evaluate the expression directly, we will really evaluate it, so | ||||||
|  |             # the error will be dumped into the pod result. | ||||||
|  |             pass | ||||||
|  |         # Analyse the return type of the expression. | ||||||
|  |         self.computeAttributes(self.tiedExpression) | ||||||
|  |         # Now, self.attrs has been populated. Transform it into a string. | ||||||
|  |         res = '' | ||||||
|  |         for name, value in self.attrs.iteritems(): | ||||||
|  |             res += ' %s=%s' % (name, quoteattr(value)) | ||||||
|  |         return res | ||||||
| # ------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|  | @ -110,6 +110,7 @@ class PodEnvironment(OdfEnvironment): | ||||||
|         self.namedIfActions = {} #~{s_statementName: IfAction}~ |         self.namedIfActions = {} #~{s_statementName: IfAction}~ | ||||||
|         # Currently parsed expression within an ODS template |         # Currently parsed expression within an ODS template | ||||||
|         self.currentOdsExpression = None |         self.currentOdsExpression = None | ||||||
|  |         self.currentOdsHook = None | ||||||
|         # Names of some tags, that we will compute after namespace propagation |         # Names of some tags, that we will compute after namespace propagation | ||||||
|         self.tags = None |         self.tags = None | ||||||
| 
 | 
 | ||||||
|  | @ -204,6 +205,7 @@ class PodEnvironment(OdfEnvironment): | ||||||
|           'table-cell': '%s:table-cell' % ns[self.NS_TABLE], |           'table-cell': '%s:table-cell' % ns[self.NS_TABLE], | ||||||
|           'formula': '%s:formula' % ns[self.NS_TABLE], |           'formula': '%s:formula' % ns[self.NS_TABLE], | ||||||
|           'value-type': '%s:value-type' % ns[self.NS_OFFICE], |           'value-type': '%s:value-type' % ns[self.NS_OFFICE], | ||||||
|  |           'value': '%s:value' % ns[self.NS_OFFICE], | ||||||
|           'string-value': '%s:string-value' % ns[self.NS_OFFICE], |           'string-value': '%s:string-value' % ns[self.NS_OFFICE], | ||||||
|           'span': '%s:span' % ns[self.NS_TEXT], |           'span': '%s:span' % ns[self.NS_TEXT], | ||||||
|           'number-columns-spanned': '%s:number-columns-spanned' % \ |           'number-columns-spanned': '%s:number-columns-spanned' % \ | ||||||
|  | @ -246,10 +248,12 @@ class PodParser(OdfParser): | ||||||
|             e.exprHasStyle = False |             e.exprHasStyle = False | ||||||
|         elif (elem == e.tags['table-cell']) and \ |         elif (elem == e.tags['table-cell']) and \ | ||||||
|              attrs.has_key(e.tags['formula']) and \ |              attrs.has_key(e.tags['formula']) and \ | ||||||
|              (attrs[e.tags['value-type']] == 'string'): |              (attrs[e.tags['value-type']] == 'string') and \ | ||||||
|  |              attrs[e.tags['formula']].startswith('of:="'): | ||||||
|             # In an ODS template, any cell containing a formula of type "string" |             # In an ODS template, any cell containing a formula of type "string" | ||||||
|             # is considered to contain a POD expression. But here it is a |             # and whose content is expressed as a string between double quotes | ||||||
|             # special case: we need to dump the cell; the expression is not |             # (="...") is considered to contain a POD expression. But here it | ||||||
|  |             # is a special case: we need to dump the cell; the expression is not | ||||||
|             # directly contained within this cell; the expression will be |             # directly contained within this cell; the expression will be | ||||||
|             # contained in the next inner paragraph. So we must here dump the |             # contained in the next inner paragraph. So we must here dump the | ||||||
|             # cell, but without some attributes, because the "formula" will be |             # cell, but without some attributes, because the "formula" will be | ||||||
|  | @ -257,10 +261,13 @@ class PodParser(OdfParser): | ||||||
|             if e.mode == e.ADD_IN_SUBBUFFER: |             if e.mode == e.ADD_IN_SUBBUFFER: | ||||||
|                 e.addSubBuffer() |                 e.addSubBuffer() | ||||||
|             e.currentBuffer.addElement(e.currentElem.name) |             e.currentBuffer.addElement(e.currentElem.name) | ||||||
|             e.currentBuffer.dumpStartElement(elem, attrs, |             hook = e.currentBuffer.dumpStartElement(elem, attrs, | ||||||
|                 ignoreAttrs=(e.tags['formula'], e.tags['string-value'])) |                      ignoreAttrs=(e.tags['formula'], e.tags['string-value'], | ||||||
|  |                                   e.tags['value-type']), | ||||||
|  |                      insertAttributesHook=True) | ||||||
|             # We already have the POD expression: remember it on the env. |             # We already have the POD expression: remember it on the env. | ||||||
|             e.currentOdsExpression = attrs[e.tags['string-value']] |             e.currentOdsExpression = attrs[e.tags['string-value']] | ||||||
|  |             e.currentOdsHook = hook | ||||||
|         else: |         else: | ||||||
|             if e.state == e.IGNORING: |             if e.state == e.IGNORING: | ||||||
|                 pass |                 pass | ||||||
|  | @ -304,8 +311,10 @@ class PodParser(OdfParser): | ||||||
|             elif e.state == e.READING_CONTENT: |             elif e.state == e.READING_CONTENT: | ||||||
|                 # Dump the ODS POD expression if any |                 # Dump the ODS POD expression if any | ||||||
|                 if e.currentOdsExpression: |                 if e.currentOdsExpression: | ||||||
|                     e.currentBuffer.addExpression(e.currentOdsExpression) |                     e.currentBuffer.addExpression(e.currentOdsExpression, | ||||||
|  |                                                   tiedHook=e.currentOdsHook) | ||||||
|                     e.currentOdsExpression = None |                     e.currentOdsExpression = None | ||||||
|  |                     e.currentOdsHook = None | ||||||
|                 # Dump the ending tag |                 # Dump the ending tag | ||||||
|                 e.currentBuffer.dumpEndElement(elem) |                 e.currentBuffer.dumpEndElement(elem) | ||||||
|                 if elem in e.impactableElements: |                 if elem in e.impactableElements: | ||||||
|  |  | ||||||
							
								
								
									
										3617
									
								
								pod/test/Tests.rtf
									
										
									
									
									
								
							
							
						
						
									
										3617
									
								
								pod/test/Tests.rtf
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay