1
2
3
4
5
6
7
8
9
10
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&y=2&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&y=2&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
351
352 In=InFactory()
353
355 elses=None
356 expr=sort=batch=mapping=None
357 start_name_re=None
358 reverse=None
359 sort_expr=reverse_expr=None
360
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
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
510
511
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
541
542
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
671
673
674
675
676
677
678 sort=self.sort
679 sortfields = sort.split(',')
680 multsort = len(sortfields) > 1
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:
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:
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
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