Package qm :: Package external :: Package DocumentTemplate :: Module DT_In
[hide private]
[frames] | no frames]

Source Code for Module qm.external.DocumentTemplate.DT_In

  1  ############################################################################## 
  2  # 
  3  # Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved. 
  4  # 
  5  # This software is subject to the provisions of the Zope Public License, 
  6  # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution. 
  7  # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED 
  8  # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
  9  # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 
 10  # FOR A PARTICULAR PURPOSE 
 11  # 
 12  ############################################################################## 
 13  '''Sequence insertion 
 14   
 15         A sequence may be inserted using an 'in' command.  The 'in' 
 16         command specifies the name of a sequence object and text to 
 17         be inserted for each element in the sequence. 
 18   
 19         The EPFS syntax for the in command is:: 
 20   
 21            %(in name)[ 
 22                 text 
 23            %(in name)] 
 24   
 25         The HTML syntax for the in command is:: 
 26   
 27            <!--#in name--> 
 28                 text 
 29            <!--#/in name--> 
 30   
 31        See the example below that shows how 'if', 'else', and 'in' commands 
 32        may be combined to display a possibly empty list of objects. 
 33   
 34        The text included within an 'in' command will be refered to 
 35        as an 'in' block. 
 36   
 37      Synopsis 
 38   
 39        If the variable 'sequence' exists as a sequence, a simple case 
 40        of the 'in' tag is used as follows:: 
 41   
 42           <!--#in sequence-->some markup<!--#/in--> 
 43   
 44        A more complete case is used as follows:: 
 45   
 46          <!--#in sequence sort=age--> 
 47            <!--#var sequence-number-->) <!--#var age--> 
 48          <!--#/in--> 
 49   
 50      Attributes 
 51   
 52        sort -- Define the sort order for sequence items.  Parameter to the 
 53        attribute is either a sort option, or list of sort options separated 
 54        by comma.  Every sort option consists of variable name, optional 
 55        comparison function name (default is cmp) and optional sort order 
 56        (default is asc). 
 57            Examples: sort="date" or sort="date,time" or 
 58        sort="title/locale,date/cmp/desc". If you want to specify sort order, 
 59        you cannot omit the function; use cmp for standard comparison. 
 60            Few predefined comparison functions available: standard cmp, 
 61        nocase (ignore string case), strcoll (alias "locale"), 
 62        strcoll_nocase (alias "locale_nocase"). Locale functions are 
 63        available only if module locale is already imported (you started Zope 
 64        with -L locale). 
 65   
 66        sort_expr -- The "sort" attribute accepts only static list of 
 67        sort options. This calculated parameter allows you to calculate the 
 68        list of sort options on the fly. 
 69   
 70        sort_expr -- This allows an expression to control sort order. 
 71   
 72        reverse -- Reverse the sequence (may be combined with sort).  Note 
 73        that this can cause a huge memory use in lazy activation instances. 
 74   
 75        reverse_expr -- This calculated parameter allows you to calculate the 
 76        need of reversing on the fly. 
 77   
 78        Within an 'in' block, variables are substituted from the 
 79        elements of the iteration unless the 'no_push_item' optional 
 80        is specified.  The elements may be either instance or mapping 
 81        objects.  In addition, the variables: 
 82   
 83           'sequence-item' -- The element. 
 84   
 85           'sequence-var-nnn' -- The value of a specific named attribute 
 86             of the item, where 'nnn' is the name.  For example, to get 
 87             an items 'title' attribute, use 'sequence-var-title'.  This 
 88             construct is most useful in an 'if' tag to test whether an 
 89             attribute is present, because the attribute lookup will be 
 90             extended to the full document template namespace. 
 91   
 92           'sequence-key' -- The key associated with the element in an 
 93             items list. See below. 
 94   
 95           'sequence-index' -- The index, starting from 0, of the 
 96             element within the sequence. 
 97   
 98           'sequence-number' -- The index, starting from 1, of the 
 99             element within the sequence. 
100   
101           'sequence-letter' -- The index, starting from 'a', of the 
102             element within the sequence. 
103   
104           'sequence-Letter' -- The index, starting from 'A', of the 
105             element within the sequence. 
106   
107           'sequence-roman' -- The index, starting from 'i', of the 
108             element within the sequence. 
109   
110           'sequence-Roman' -- The index, starting from 'I', of the 
111             element within the sequence. 
112   
113           'sequence-start' -- A variable that is true if the element 
114             being displayed is the first of the displayed elements, 
115             and false otherwise. 
116   
117           'sequence-end' -- A variable that is true if the element 
118             being displayed is the last of the displayed elements, 
119             and false otherwise. 
120   
121        are defined for each element. 
122   
123        Normally, 'in' blocks are used to iterate over sequences of 
124        instances.  If the optional parameter 'mapping' is specified 
125        after the sequence name, then the elements of the sequence 
126        will be treated as mapping objects. 
127   
128        An 'in' command may be used to iterate over a sequence of 
129        dictionary items.  If the elements of the iteration are 
130        two-element tuples, then then the template code given in the 
131        'in' block will be applied to the second element of each 
132        tuple and may use a variable, 'sequence-key' to access the 
133        first element in each tuple. 
134   
135      Batch sequence insertion 
136   
137        When displaying a large number of objects, it is sometimes 
138        desirable to display just a sub-sequence of the data. 
139        An 'in' command may have optional parameters, 
140        as in:: 
141   
142            <!--#in values start=start_var size=7--> 
143   
144        The parameter values may be either integer literals or 
145        variable names. 
146   
147        Up to five parameters may be set: 
148   
149            'start'   -- The number of the first element to be shown, 
150                         where elements are numbered from 1. 
151   
152            'end'     -- The number of the last element to be shown, 
153                         where elements are numbered from 1. 
154   
155            'size'    -- The desired number of elements to be shown at 
156                         once. 
157   
158            'orphan'  -- The desired minimum number of objects to be 
159                         displayed.  The default value for this 
160                         parameter is 3. 
161   
162            'overlap' -- The desired overlap between batches. The 
163                         default is no overlap. 
164   
165        Typically, only 'start' and 'size' will be specified. 
166   
167        When batch insertion is used, several additional variables are 
168        defined for use within the sequence insertion text: 
169   
170            'sequence-query' -- The original query string given in a get 
171               request with the form variable named in the 'start' 
172               attribute removed.  This is extremely useful when 
173               building URLs to fetch another batch. 
174   
175               To see how this is used, consider the following example:: 
176   
177                   <!--#in search_results size=20 start=batch_start--> 
178   
179                      ... display rows 
180   
181                      <!--#if sequence-end--> <!--#if next-sequence--> 
182                        <a href="<!--#var URL-->/<!--#var sequence-query 
183                            -->&batch_start=<!--#var 
184                            next-sequence-start-number-->"> 
185                        (Next <!--#var next-sequence-size--> results) 
186                        </a> 
187                      <!--#/if--> <!--#/if--> 
188   
189                   <!--#/in--> 
190   
191               If the original URL is: 'foo/bar?x=1&y=2', then the 
192               rendered text (after row data are displayed) will be:: 
193   
194                        <a href="foo/bar?x=1&amp;y=2&amp;batch_start=20"> 
195                        (Next 20 results) 
196                        </a> 
197   
198               If the original URL is: 'foo/bar?batch_start=10&x=1&y=2', 
199               then the rendered text (after row data are displayed) 
200               will be:: 
201   
202                        <a href="foo/bar?x=1&amp;y=2&amp;batch_start=30"> 
203                        (Next 20 results) 
204                        </a> 
205   
206            'sequence-step-start-index' -- The index, starting from 0, 
207               of the start of the current batch. 
208   
209            'sequence-step-end-index' -- The index, starting from 0, of 
210               the end of the current batch. 
211   
212            'sequence-step-size' -- The batch size used. 
213   
214            'previous-sequence' -- This variable will be true when the 
215               first element is displayed and when the first element 
216               displayed is not the first element in the sequence. 
217   
218            'previous-sequence-start-index' -- The index, starting from 
219               0, of the start of the batch previous to the current 
220               batch. 
221   
222            'previous-sequence-end-index' -- The index, starting from 
223               0, of the end of the batch previous to the current 
224               batch. 
225   
226            'previous-sequence-size' -- The size of the batch previous to 
227               the current batch. 
228   
229            'previous-batches' -- A sequence of mapping objects 
230               containing information about all of the batches prior 
231               to the batch being displayed. 
232   
233               Each of these mapping objects include the following 
234               variables: 
235   
236                  batch-start-index -- The index, starting from 
237                     0, of the beginning of the batch. 
238   
239                  batch-end-index -- The index, starting from 
240                     0, of the end of the batch. 
241   
242                  batch-size -- The size of the batch. 
243   
244            'next-sequence' -- This variable will be true when the last 
245               element is displayed and when the last element 
246               displayed is not the last element in the sequence. 
247   
248            'next-sequence-start-index' -- The index, starting from 
249               0, of the start of the batch after the current 
250               batch. 
251   
252            'next-sequence-end-index' -- The index, starting from 
253               0, of the end of the batch after the current 
254               batch. 
255   
256            'next-sequence-size' -- The size of the batch after 
257               the current batch. 
258   
259            'next-batches' -- A sequence of mapping objects 
260               containing information about all of the batches after 
261               the batch being displayed. 
262   
263               Each of these mapping objects include the following 
264               variables: 
265   
266                  batch-start-index -- The index, starting from 
267                     0, of the beginning of the batch. 
268   
269                  batch-end-index -- The index, starting from 
270                     0, of the end of the batch. 
271   
272                  batch-size -- The size of the batch. 
273   
274        For each of the variables listed above with names ending in 
275        "-index", there are variables with names ending in "-number", 
276        "-roman", "-Roman", "-letter", and "-Letter" that are indexed 
277        from 1, "i", "I", "a", and "A", respectively.  In addition, 
278        for every one of these variables there are variables with 
279        names ending in "-var-xxx", where "xxx" is an element 
280        attribute name or key. 
281   
282      Summary statistics 
283   
284        When performing sequence insertion, special variables may be 
285        used to obtain summary statistics.  To obtain a summary 
286        statistic for a variable, use the variable name: 
287        'statistic-name', where 'statistic' is a statistic name and 
288        'name' is the name of a data variable. 
289   
290        Currently supported statistic names are: 
291   
292          total -- The total of numeric values. 
293   
294          count -- The total number of non-missing values. 
295   
296          min -- The minimum of non-missing values. 
297   
298          max -- The maximum of non-missing values. 
299   
300          median -- The median of non-missing values. 
301   
302          mean -- The mean of numeric values values. 
303   
304          variance -- The variance of numeric values computed with a 
305            degrees of freedom equal to the count - 1. 
306   
307          variance-n -- The variance of numeric values computed with a 
308            degrees of freedom equal to the count. 
309   
310          standard-deviation -- The standard deviation of numeric values 
311            computed with a degrees of freedom equal to the count - 1. 
312   
313          standard-deviation-n -- The standard deviation of numeric 
314            values computed with a degrees of freedom equal to the count. 
315   
316        Missing values are either 'None' or the attribute 'Value' 
317        of the module 'Missing', if present. 
318   
319      'else' continuation tag within in 
320   
321        An 'else' tag may be used as a continuation tag in the 'in' tag. 
322        The source after the 'else' tag is inserted if: 
323   
324          - The sequence given to the 'in' tag is of zero length, or 
325   
326          - The 'previous' attribute was used and their are no 
327            previous batches, or 
328   
329          - The 'next' attribute was used and their are no 
330            next batches, or 
331   
332  ''' #' 
333   
334  __rcs_id__='$Id: DT_In.py 1069 2008-11-13 21:55:43Z stefan $' 
335  __version__='$Revision: 1069 $'[11:-2] 
336   
337  from DT_Util import ParseError, parse_params, name_param, str 
338  from DT_Util import render_blocks, InstanceDict, ValidationError, VSEval, expr_globals 
339  import re 
340  from DT_InSV import sequence_variables, opt 
341  TupleType=type(()) 
342   
343 -class InFactory:
344 blockContinuations=('else',) 345 name='in' 346
347 - def __call__(self, blocks):
348 i=InClass(blocks) 349 if i.batch: return i.renderwb 350 else: return i.renderwob
351 352 In=InFactory() 353
354 -class InClass:
355 elses=None 356 expr=sort=batch=mapping=None 357 start_name_re=None 358 reverse=None 359 sort_expr=reverse_expr=None 360
361 - def __init__(self, blocks):
362 tname, args, section = blocks[0] 363 args=parse_params(args, name='', start='1',end='-1',size='10', 364 orphan='3',overlap='1',mapping=1, 365 skip_unauthorized=1, 366 previous=1, next=1, expr='', sort='', 367 reverse=1, sort_expr='', reverse_expr='') 368 self.args=args 369 has_key=args.has_key 370 371 if has_key('sort'): 372 self.sort=sort=args['sort'] 373 if sort=='sequence-item': self.sort='' 374 375 if has_key('sort_expr'): 376 self.sort_expr=VSEval.Eval(args['sort_expr'], expr_globals) 377 378 if has_key('reverse_expr'): 379 self.reverse_expr=VSEval.Eval(args['reverse_expr'], expr_globals) 380 381 if has_key('reverse'): 382 self.reverse=args['reverse'] 383 384 if has_key('mapping'): self.mapping=args['mapping'] 385 for n in 'start', 'size', 'end': 386 if has_key(n): self.batch=1 387 388 for n in 'orphan','overlap','previous','next': 389 if has_key(n) and not self.batch: 390 raise ParseError, ( 391 """ 392 The %s attribute was used but neither of the 393 <code>start</code>, <code>end</code>, or <code>size</code> 394 attributes were used. 395 """ % n, 'in') 396 397 if has_key('start'): 398 v=args['start'] 399 if type(v)==type(''): 400 try: int(v) 401 except: 402 403 self.start_name_re=re.compile( 404 '&+'+ 405 ''.join(["[%s]" % c for c in v])+ 406 '=[0-9]+&+') 407 408 name,expr=name_param(args,'in',1) 409 if expr is not None: expr=expr.eval 410 self.__name__, self.expr = name, expr 411 self.section=section.blocks 412 if len(blocks) > 1: 413 if len(blocks) != 2: raise ParseError, ( 414 'too many else blocks', 'in') 415 tname, args, section = blocks[1] 416 args=parse_params(args, name='') 417 if args: 418 ename=name_param(args) 419 if ename != name: 420 raise ParseError, ( 421 'name in else does not match in', 'in') 422 self.elses=section.blocks
423 424
425 - def renderwb(self, md):
426 expr=self.expr 427 name=self.__name__ 428 if expr is None: 429 sequence=md[name] 430 cache={ name: sequence } 431 else: 432 sequence=expr(md) 433 cache=None 434 435 if not sequence: 436 if self.elses: return render_blocks(self.elses, md) 437 return '' 438 439 if type(sequence) is type(''): 440 raise 'InError', ( 441 'Strings are not allowed as input to the in tag.') 442 443 444 section=self.section 445 params=self.args 446 447 mapping=self.mapping 448 449 if self.sort_expr is not None: 450 self.sort=self.sort_expr.eval(md) 451 sequence=self.sort_sequence(sequence) 452 elif self.sort is not None: 453 sequence=self.sort_sequence(sequence) 454 455 if self.reverse_expr is not None and self.reverse_expr.eval(md): 456 sequence=self.reverse_sequence(sequence) 457 elif self.reverse is not None: 458 sequence=self.reverse_sequence(sequence) 459 460 next=previous=0 461 try: start=int_param(params,md,'start',0) 462 except: start=1 463 end=int_param(params,md,'end',0) 464 size=int_param(params,md,'size',0) 465 overlap=int_param(params,md,'overlap',0) 466 orphan=int_param(params,md,'orphan','3') 467 start,end,sz=opt(start,end,size,orphan,sequence) 468 if params.has_key('next'): next=1 469 if params.has_key('previous'): previous=1 470 471 last=end-1 472 first=start-1 473 474 try: query_string=md['QUERY_STRING'] 475 except: query_string='' 476 477 vars=sequence_variables(sequence,'?'+query_string,self.start_name_re) 478 kw=vars.data 479 kw['mapping']=mapping 480 kw['sequence-step-size']=sz 481 kw['sequence-step-overlap']=overlap 482 kw['sequence-step-start']=start 483 kw['sequence-step-end']=end 484 kw['sequence-step-start-index']=start-1 485 kw['sequence-step-end-index']=end-1 486 kw['sequence-step-orphan']=orphan 487 488 push=md._push 489 pop=md._pop 490 render=render_blocks 491 492 if cache: push(cache) 493 push(vars) 494 try: 495 if previous: 496 if first > 0: 497 pstart,pend,psize=opt(0,first+overlap, 498 sz,orphan,sequence) 499 kw['previous-sequence']=1 500 kw['previous-sequence-start-index']=pstart-1 501 kw['previous-sequence-end-index']=pend-1 502 kw['previous-sequence-size']=pend+1-pstart 503 result=render(section,md) 504 505 elif self.elses: result=render(self.elses, md) 506 else: result='' 507 elif next: 508 try: 509 # The following line is a sneaky way to test whether 510 # there are more items, without actually 511 # computing a length: 512 sequence[end] 513 except IndexError: 514 if self.elses: result=render(self.elses, md) 515 else: result='' 516 else: 517 pstart,pend,psize=opt(end+1-overlap,0, 518 sz,orphan,sequence) 519 kw['next-sequence']=1 520 kw['next-sequence-start-index']=pstart-1 521 kw['next-sequence-end-index']=pend-1 522 kw['next-sequence-size']=pend+1-pstart 523 result=render(section,md) 524 else: 525 result = [] 526 append=result.append 527 validate=md.validate 528 for index in range(first,end): 529 if index==first and index > 0: 530 pstart,pend,psize=opt(0,index+overlap, 531 sz,orphan,sequence) 532 kw['previous-sequence']=1 533 kw['previous-sequence-start-index']=pstart-1 534 kw['previous-sequence-end-index']=pend-1 535 kw['previous-sequence-size']=pend+1-pstart 536 else: 537 kw['previous-sequence']=0 538 if index==last: 539 try: 540 # The following line is a sneaky way to 541 # test whether there are more items, 542 # without actually computing a length: 543 sequence[end] 544 pstart,pend,psize=opt(end+1-overlap,0, 545 sz,orphan,sequence) 546 kw['previous-sequence']=0 547 kw['next-sequence']=1 548 kw['next-sequence-start-index']=pstart-1 549 kw['next-sequence-end-index']=pend-1 550 kw['next-sequence-size']=pend+1-pstart 551 except: pass 552 553 if index==last: kw['sequence-end']=1 554 555 client=sequence[index] 556 557 if validate is not None: 558 try: vv=validate(sequence,sequence,None,client,md) 559 except: vv=0 560 if not vv: 561 if (params.has_key('skip_unauthorized') and 562 params['skip_unauthorized']): 563 if index==first: kw['sequence-start']=0 564 continue 565 raise ValidationError, index 566 567 kw['sequence-index']=index 568 if type(client)==TupleType and len(client)==2: 569 client=client[1] 570 571 if mapping: push(client) 572 else: push(InstanceDict(client, md)) 573 574 try: append(render(section, md)) 575 finally: pop(1) 576 577 if index==first: kw['sequence-start']=0 578 579 580 result=''.join(result) 581 582 finally: 583 if cache: pop() 584 pop() 585 586 return result
587
588 - def renderwob(self, md):
589 """RENDER WithOutBatch""" 590 expr=self.expr 591 name=self.__name__ 592 if expr is None: 593 sequence=md[name] 594 cache={ name: sequence } 595 else: 596 sequence=expr(md) 597 cache=None 598 599 if not sequence: 600 if self.elses: return render_blocks(self.elses, md) 601 return '' 602 603 if type(sequence) is type(''): 604 raise 'InError', ( 605 'Strings are not allowed as input to the in tag.') 606 607 section=self.section 608 mapping=self.mapping 609 610 611 if self.sort_expr is not None: 612 self.sort=self.sort_expr.eval(md) 613 sequence=self.sort_sequence(sequence) 614 elif self.sort is not None: 615 sequence=self.sort_sequence(sequence) 616 617 if self.reverse_expr is not None and self.reverse_expr.eval(md): 618 sequence=self.reverse_sequence(sequence) 619 elif self.reverse is not None: 620 sequence=self.reverse_sequence(sequence) 621 622 vars=sequence_variables(sequence) 623 kw=vars.data 624 kw['mapping']=mapping 625 626 l=len(sequence) 627 last=l-1 628 629 push=md._push 630 pop=md._pop 631 render=render_blocks 632 633 if cache: push(cache) 634 push(vars) 635 try: 636 result = [] 637 append=result.append 638 validate=md.validate 639 for index in range(l): 640 if index==last: kw['sequence-end']=1 641 client=sequence[index] 642 643 if validate is not None: 644 try: vv=validate(sequence,sequence,None,client,md) 645 except: vv=0 646 if not vv: 647 if (self.args.has_key('skip_unauthorized') and 648 self.args['skip_unauthorized']): 649 if index==1: kw['sequence-start']=0 650 continue 651 raise ValidationError, index 652 653 kw['sequence-index']=index 654 if type(client)==TupleType and len(client)==2: 655 client=client[1] 656 657 if mapping: push(client) 658 else: push(InstanceDict(client, md)) 659 660 try: append(render(section, md)) 661 finally: pop() 662 if index==0: kw['sequence-start']=0 663 664 result=''.join(result) 665 666 finally: 667 if cache: pop() 668 pop() 669 670 return result
671
672 - def sort_sequence(self, sequence):
673 674 # Modified with multiple sort fields by Ross Lazarus 675 # April 7 2000 rossl@med.usyd.edu.au 676 # eg <dtml in "foo" sort=akey,anotherkey> 677 678 sort=self.sort 679 sortfields = sort.split(',') # multi sort = key1,key2 680 multsort = len(sortfields) > 1 # flag: is multiple sort 681 mapping=self.mapping 682 isort=not sort 683 684 s=[] 685 for client in sequence: 686 k = None 687 if type(client)==TupleType and len(client)==2: 688 if isort: k=client[0] 689 v=client[1] 690 else: 691 if isort: k=client 692 v=client 693 694 if sort: 695 if multsort: # More than one sort key. 696 k = [] 697 for sk in sortfields: 698 try: 699 if mapping: akey = v[sk] 700 else: akey = getattr(v, sk) 701 except AttributeError, KeyError: akey = None 702 if not basic_type(akey): 703 try: akey = akey() 704 except: pass 705 k.append(akey) 706 else: # One sort key. 707 try: 708 if mapping: k = v[sort] 709 else: k = getattr(v, sort) 710 except AttributeError, KeyError: k = None 711 if not basic_type(k): 712 try: k = k() 713 except: pass 714 715 s.append((k,client)) 716 717 s.sort() 718 719 sequence=[] 720 for k, client in s: 721 sequence.append(client) 722 return sequence
723
724 - def reverse_sequence(self, sequence):
725 s=list(sequence) 726 s.reverse() 727 return s
728 729 730 basic_type={type(''): 1, type(0): 1, type(0.0): 1, type(()): 1, type([]): 1, 731 type(None) : 1 }.has_key 732
733 -def int_param(params,md,name,default=0, st=type('')):
734 try: v=params[name] 735 except: v=default 736 if v: 737 try: v=int(v) 738 except: 739 v=md[v] 740 if type(v) is st: v=int(v) 741 return v
742