1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 '''Sequence insertion
86
87 A sequence may be inserted using an 'in' command. The 'in'
88 command specifies the name of a sequence object and text to
89 be inserted for each element in the sequence.
90
91 The EPFS syntax for the in command is::
92
93 %(in name)[
94 text
95 %(in name)]
96
97 The HTML syntax for the in command is::
98
99 <!--#in name-->
100 text
101 <!--#/in name-->
102
103 See the example below that shows how 'if', 'else', and 'in' commands
104 may be combined to display a possibly empty list of objects.
105
106 The text included within an 'in' command will be refered to
107 as an 'in' block.
108
109 Synopsis
110
111 If the variable 'sequence' exists as a sequence, a simple case
112 of the 'in' tag is used as follows::
113
114 <!--#in sequence-->some markup<!--#/in-->
115
116 A more complete case is used as follows::
117
118 <!--#in sequence sort=age-->
119 <!--#var sequence-number-->) <!--#var age-->
120 <!--#/in-->
121
122 Attributes
123
124 sort -- Define the sort order for sequence items. If an item in
125 the sequence does not define
126
127 reverse -- Reverse the sequence (may be combined with sort). Note
128 that this can cause a huge memory use in lazy activation instances.
129
130 Within an 'in' block, variables are substituted from the
131 elements of the iteration. The elements may be either
132 instance or mapping objects. In addition, the variables:
133
134 'sequence-item' -- The element.
135
136 'sequence-var-nnn' -- The value of a specific named attribute
137 of the item, where 'nnn' is the name. For example, to get
138 an items 'title' attribute, use 'sequence-var-title'. This
139 construct is most useful in an 'if' tag to test whether an
140 attribute is present, because the attribute lookup will be
141 extended to the full document template namespace.
142
143 'sequence-key' -- The key associated with the element in an
144 items list. See below.
145
146 'sequence-index' -- The index, starting from 0, of the
147 element within the sequence.
148
149 'sequence-number' -- The index, starting from 1, of the
150 element within the sequence.
151
152 'sequence-letter' -- The index, starting from 'a', of the
153 element within the sequence.
154
155 'sequence-Letter' -- The index, starting from 'A', of the
156 element within the sequence.
157
158 'sequence-roman' -- The index, starting from 'i', of the
159 element within the sequence.
160
161 'sequence-Roman' -- The index, starting from 'I', of the
162 element within the sequence.
163
164 'sequence-start' -- A variable that is true if the element
165 being displayed is the first of the displayed elements,
166 and false otherwise.
167
168 'sequence-end' -- A variable that is true if the element
169 being displayed is the last of the displayed elements,
170 and false otherwise.
171
172 are defined for each element.
173
174 Normally, 'in' blocks are used to iterate over sequences of
175 instances. If the optional parameter 'mapping' is specified
176 after the sequence name, then the elements of the sequence
177 will be treated as mapping objects.
178
179 An 'in' command may be used to iterate over a sequence of
180 dictionary items. If the elements of the iteration are
181 two-element tuples, then then the template code given in the
182 'in' block will be applied to the second element of each
183 tuple and may use a variable, 'sequence-key' to access the
184 first element in each tuple.
185
186 Batch sequence insertion
187
188 When displaying a large number of objects, it is sometimes
189 desireable to display just a sub-sequence of the data.
190 An 'in' command may have optional parameters,
191 as in::
192
193 <!--#in values start=start_var size=7-->
194
195 The parameter values may be either integer literals or
196 variable names.
197
198 Up to five parameters may be set:
199
200 'start' -- The number of the first element to be shown,
201 where elements are numbered from 1.
202
203 'end' -- The number of the last element to be shown,
204 where elements are numbered from 1.
205
206 'size' -- The desired number of elements to be shown at
207 once.
208
209 'orphan' -- The desired minimum number of objects to be
210 displayed. The default value for this
211 parameter is 3.
212
213 'overlap' -- The desired overlap between batches. The
214 default is no overlap.
215
216 Typically, only 'start' and 'size' will be specified.
217
218 When batch insertion is used, several additional variables are
219 defined for use within the sequence insertion text:
220
221 'sequence-query' -- The original query string given in a get
222 request with the form variable named in the 'start'
223 attribute removed. This is extremely useful when
224 building URLs to fetch another batch.
225
226 To see how this is used, consider the following example::
227
228 <!--#in search_results size=20 start=batch_start-->
229
230 ... display rows
231
232 <!--#if sequence-end--> <!--#if next-sequence-->
233 <a href="<!--#var URL-->/<!--#var sequence-query
234 -->&batch_start=<!--#var
235 next-sequence-start-number-->">
236 (Next <!--#var next-sequence-size--> results)
237 </a>
238 <!--#/if--> <!--#/if-->
239
240 <!--#/in-->
241
242 If the original URL is: 'foo/bar?x=1&y=2', then the
243 rendered text (after row data are displated) will be::
244
245 <a href="foo/bar?x=1&y=2&batch_start=20">
246 (Next 20 results)
247 </a>
248
249 If the original URL is: 'foo/bar?batch_start=10&x=1&y=2',
250 then the rendered text (after row data are displated)
251 will be::
252
253 <a href="foo/bar?x=1&y=2&batch_start=30">
254 (Next 20 results)
255 </a>
256
257 'sequence-step-start-index' -- The index, starting from 0,
258 of the start of the current batch.
259
260 'sequence-step-end-index' -- The index, starting from 0, of
261 the end of the current batch.
262
263 'sequence-step-size' -- The batch size used.
264
265 'previous-sequence' -- This variable will be true when the
266 first element is displayed and when the first element
267 displayed is not the first element in the sequence.
268
269 'previous-sequence-start-index' -- The index, starting from
270 0, of the start of the batch previous to the current
271 batch.
272
273 'previous-sequence-end-index' -- The index, starting from
274 0, of the end of the batch previous to the current
275 batch.
276
277 'previous-sequence-size' -- The size of the batch previous to
278 the current batch.
279
280 'previous-batches' -- A sequence of mapping objects
281 containing information about all of the batches prior
282 to the batch being displayed.
283
284 Each of these mapping objects include the following
285 variables:
286
287 batch-start-index -- The index, starting from
288 0, of the beginning of the batch.
289
290 batch-end-index -- The index, starting from
291 0, of the end of the batch.
292
293 batch-size -- The size of the batch.
294
295 'next-sequence' -- This variable will be true when the last
296 element is displayed and when the last element
297 displayed is not the last element in the sequence.
298
299 'next-sequence-start-index' -- The index, starting from
300 0, of the start of the batch after the current
301 batch.
302
303 'next-sequence-end-index' -- The index, starting from
304 0, of the end of the batch after the current
305 batch.
306
307 'next-sequence-size' -- The size of the batch after
308 the current batch.
309
310 'next-batches' -- A sequence of mapping objects
311 containing information about all of the batches after
312 the batch being displayed.
313
314 Each of these mapping objects include the following
315 variables:
316
317 batch-start-index -- The index, starting from
318 0, of the beginning of the batch.
319
320 batch-end-index -- The index, starting from
321 0, of the end of the batch.
322
323 batch-size -- The size of the batch.
324
325 For each of the variables listed above with names ending in
326 "-index", there are variables with names ending in "-number",
327 "-roman", "-Roman", "-letter", and "-Letter" that are indexed
328 from 1, "i", "I", "a", and "A", respectively. In addition,
329 for every one of these variables there are variables with
330 names ending in "-var-xxx", where "xxx" is an element
331 attribute name or key.
332
333 Summary statistics
334
335 When performing sequence insertion, special variables may be
336 used to obtain summary statistics. To obtain a summary
337 statistic for a variable, use the variable name:
338 'statistic-name', where 'statistic' is a statistic name and
339 'name' is the name of a data variable.
340
341 Currently supported statistic names are:
342
343 total -- The total of numeric values.
344
345 count -- The total number of non-missing values.
346
347 min -- The minimum of non-missing values.
348
349 max -- The maximum of non-missing values.
350
351 median -- The median of non-missing values.
352
353 mean -- The mean of numeric values values.
354
355 variance -- The variance of numeric values computed with a
356 degrees of freedom qeual to the count - 1.
357
358 variance-n -- The variance of numeric values computed with a
359 degrees of freedom qeual to the count.
360
361 standard-deviation -- The standard deviation of numeric values
362 computed with a degrees of freedom qeual to the count - 1.
363
364 standard-deviation-n -- The standard deviation of numeric
365 values computed with a degrees of freedom qeual to the count.
366
367 Missing values are either 'None' or the attribute 'Value'
368 of the module 'Missing', if present.
369
370 'else' continuation tag within in
371
372 An 'else' tag may be used as a continuation tag in the 'in' tag.
373 The source after the 'else' tag is inserted if:
374
375 - The sequence given to the 'in' tag is of zero length, or
376
377 - The 'previous' attribute was used and their are no
378 previous batches, or
379
380 - The 'next' attribute was used and their are no
381 next batches, or
382
383 '''
384
385 __rcs_id__='$Id: DT_In.py 1007 2007-02-10 01:07:28Z stefan $'
386 __version__='$Revision: 1007 $'[11:-2]
387
388 from DT_Util import ParseError, parse_params, name_param, str
389 from DT_Util import render_blocks, InstanceDict, ValidationError, VSEval, expr_globals
390 import re
391 from DT_InSV import sequence_variables, opt
392 TupleType=type(())
393
402
403 In=InFactory()
404
406 elses=None
407 expr=sort=batch=mapping=None
408 start_name_re=None
409 reverse=None
410 sort_expr=reverse_expr=None
411
413 tname, args, section = blocks[0]
414 args=parse_params(args, name='', start='1',end='-1',size='10',
415 orphan='3',overlap='1',mapping=1,
416 skip_unauthorized=1,
417 previous=1, next=1, expr='', sort='',
418 reverse=1, sort_expr='', reverse_expr='')
419 self.args=args
420 has_key=args.has_key
421
422 if has_key('sort'):
423 self.sort=sort=args['sort']
424 if sort=='sequence-item': self.sort=''
425
426 if has_key('sort_expr'):
427 self.sort_expr=VSEval.Eval(args['sort_expr'], expr_globals)
428
429 if has_key('reverse_expr'):
430 self.reverse_expr=VSEval.Eval(args['reverse_expr'], expr_globals)
431
432 if has_key('reverse'):
433 self.reverse=args['reverse']
434
435 if has_key('mapping'): self.mapping=args['mapping']
436 for n in 'start', 'size', 'end':
437 if has_key(n): self.batch=1
438
439 for n in 'orphan','overlap','previous','next':
440 if has_key(n) and not self.batch:
441 raise ParseError, (
442 """
443 The %s attribute was used but neither of the
444 <code>start</code>, <code>end</code>, or <code>size</code>
445 attributes were used.
446 """ % n, 'in')
447
448 if has_key('start'):
449 v=args['start']
450 if type(v)==type(''):
451 try: int(v)
452 except:
453 self.start_name_re=re.compile(
454 '&+'+
455 ''.join(["[%s]" % c for c in v])+
456 '=[0-9]+&+')
457
458 name,expr=name_param(args,'in',1)
459 if expr is not None: expr=expr.eval
460 self.__name__, self.expr = name, expr
461 self.section=section.blocks
462 if len(blocks) > 1:
463 if len(blocks) != 2: raise ParseError, (
464 'too many else blocks', 'in')
465 tname, args, section = blocks[1]
466 args=parse_params(args, name='')
467 if args:
468 ename=name_param(args)
469 if ename != name:
470 raise ParseError, (
471 'name in else does not match in', 'in')
472 self.elses=section.blocks
473
474
476 expr=self.expr
477 name=self.__name__
478 if expr is None:
479 sequence=md[name]
480 cache={ name: sequence }
481 else:
482 sequence=expr(md)
483 cache=None
484
485 if not sequence:
486 if self.elses: return render_blocks(self.elses, md)
487 return ''
488
489 if type(sequence) is type(''):
490 raise 'InError', (
491 'Strings are not allowed as input to the in tag.')
492
493
494 section=self.section
495 params=self.args
496
497 mapping=self.mapping
498
499 if self.sort_expr is not None:
500 self.sort=self.sort_expr.eval(md)
501 sequence=self.sort_sequence(sequence)
502 elif self.sort is not None:
503 sequence=self.sort_sequence(sequence)
504
505 if self.reverse_expr is not None and self.reverse_expr.eval(md):
506 sequence=self.reverse_sequence(sequence)
507 elif self.reverse is not None:
508 sequence=self.reverse_sequence(sequence)
509
510 next=previous=0
511 try: start=int_param(params,md,'start',0)
512 except: start=1
513 end=int_param(params,md,'end',0)
514 size=int_param(params,md,'size',0)
515 overlap=int_param(params,md,'overlap',0)
516 orphan=int_param(params,md,'orphan','3')
517 start,end,sz=opt(start,end,size,orphan,sequence)
518 if params.has_key('next'): next=1
519 if params.has_key('previous'): previous=1
520
521 last=end-1
522 first=start-1
523
524 try: query_string=md['QUERY_STRING']
525 except: query_string=''
526
527 vars=sequence_variables(sequence,'?'+query_string,self.start_name_re)
528 kw=vars.data
529 kw['mapping']=mapping
530 kw['sequence-step-size']=sz
531 kw['sequence-step-overlap']=overlap
532 kw['sequence-step-start']=start
533 kw['sequence-step-end']=end
534 kw['sequence-step-start-index']=start-1
535 kw['sequence-step-end-index']=end-1
536 kw['sequence-step-orphan']=orphan
537
538 push=md._push
539 pop=md._pop
540 render=render_blocks
541
542 if cache: push(cache)
543 push(vars)
544 try:
545 if previous:
546 if first > 0:
547 pstart,pend,psize=opt(0,first+overlap,
548 sz,orphan,sequence)
549 kw['previous-sequence']=1
550 kw['previous-sequence-start-index']=pstart-1
551 kw['previous-sequence-end-index']=pend-1
552 kw['previous-sequence-size']=pend+1-pstart
553 result=render(section,md)
554
555 elif self.elses: result=render(self.elses, md)
556 else: result=''
557 elif next:
558 try:
559
560
561
562 sequence[end]
563 except IndexError:
564 if self.elses: result=render(self.elses, md)
565 else: result=''
566 else:
567 pstart,pend,psize=opt(end+1-overlap,0,
568 sz,orphan,sequence)
569 kw['next-sequence']=1
570 kw['next-sequence-start-index']=pstart-1
571 kw['next-sequence-end-index']=pend-1
572 kw['next-sequence-size']=pend+1-pstart
573 result=render(section,md)
574 else:
575 result = []
576 append=result.append
577 validate=md.validate
578 for index in range(first,end):
579 if index==first and index > 0:
580 pstart,pend,psize=opt(0,index+overlap,
581 sz,orphan,sequence)
582 kw['previous-sequence']=1
583 kw['previous-sequence-start-index']=pstart-1
584 kw['previous-sequence-end-index']=pend-1
585 kw['previous-sequence-size']=pend+1-pstart
586 else:
587 kw['previous-sequence']=0
588 if index==last:
589 try:
590
591
592
593 sequence[end]
594 pstart,pend,psize=opt(end+1-overlap,0,
595 sz,orphan,sequence)
596 kw['previous-sequence']=0
597 kw['next-sequence']=1
598 kw['next-sequence-start-index']=pstart-1
599 kw['next-sequence-end-index']=pend-1
600 kw['next-sequence-size']=pend+1-pstart
601 except: pass
602
603 if index==last: kw['sequence-end']=1
604
605 client=sequence[index]
606
607 if validate is not None:
608 try: vv=validate(sequence,sequence,None,client,md)
609 except: vv=0
610 if not vv:
611 if (params.has_key('skip_unauthorized') and
612 params['skip_unauthorized']):
613 if index==first: kw['sequence-start']=0
614 continue
615 raise ValidationError, index
616
617 kw['sequence-index']=index
618 if type(client)==TupleType and len(client)==2:
619 client=client[1]
620
621 if mapping: push(client)
622 else: push(InstanceDict(client, md))
623
624 try: append(render(section, md))
625 finally: pop(1)
626
627 if index==first: kw['sequence-start']=0
628
629
630 result=''.join(result)
631
632 finally:
633 if cache: pop()
634 pop()
635
636 return result
637
721
723
724
725
726
727
728 sort=self.sort
729 sortfields = sort.split(',')
730 multsort = len(sortfields) > 1
731 mapping=self.mapping
732 isort=not sort
733 s=[]
734 for client in sequence:
735 k = None
736 if type(client)==TupleType and len(client)==2:
737 if isort: k=client[0]
738 v=client[1]
739 else:
740 if isort: k=client
741 v=client
742
743 if sort:
744 if multsort:
745 k = []
746 for sk in sortfields:
747 try:
748 if mapping: akey = v[sk]
749 else: akey = getattr(v, sk)
750 except AttributeError, KeyError: akey = None
751 if not basic_type(akey):
752 try: akey = akey()
753 except: pass
754 k.append(akey)
755 else:
756 try:
757 if mapping: k = v[sort]
758 else: k = getattr(v, sort)
759 except AttributeError, KeyError: k = None
760 if not basic_type(k):
761 try: k = k()
762 except: pass
763
764 s.append((k,client))
765
766 s.sort()
767
768 sequence=[]
769 for k, client in s:
770 sequence.append(client)
771 return sequence
772
777
778
779 basic_type={type(''): 1, type(0): 1, type(0.0): 1, type(()): 1, type([]): 1,
780 type(None) : 1 }.has_key
781
782 -def int_param(params,md,name,default=0, st=type('')):
783 try: v=params[name]
784 except: v=default
785 if v:
786 try: v=int(v)
787 except:
788 v=md[v]
789 if type(v) is st: v=int(v)
790 return v
791