Attachment 'sstable.py'
Download
Toggle line numbers
1
2 """
3 MoinMoin - sstable a Processor for spread sheet calculations using only Python.
4
5 @license: GNU GPL, see COPYING for details.
6
7 PURPOSE:
8 This processor is used to do some spread sheet calculation in a regular wiki
9 table using only Python. The first column/first line coordinate is A0.
10
11 This code is based on the spreadsheet code posted by Raymond Hettinger and
12 Richard Copeland at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/355045
13 It is also based on the sctable parser by Reimar Bauer (R.Bauer AT fz-juelich.de).
14
15 CALLING SEQUENCE:
16 {{{
17 #!sstable [-column_header, -row_header, -show_formular, -format,
18 -input_separator, -output_separator ]
19 }}}
20
21 OPTIONAL INPUTS:
22 -column_header: additional in the result the column header is shown
23 -row_header: additional in the result the line number header is shown
24 -show_formular: if set the formular instead of the result is shown,
25 data is arranged in textmode. Blanks in formulars are removed
26 -format: is used to the set the number of digits for the column values
27 -input_separator: used to read tables delimitted by something other than the
28 default '||'
29 -output_separator: used to write tables delimitted by something other than the
30 default '||'
31
32
33 EXAMPLE:
34 {{{
35 SUM over columns}}}
36 {{{
37 #!sstable
38 ||1||2||=A0+B0||
39 ||10||20||=@sum(A1:B1)||
40 }}}
41
42 RESULT:
43 ||<)>1.00||<)>2.00||<)>3.00||
44 ||<)>10.00||<)>20.00||<)>30.00||
45 -----
46
47 {{{
48 cell B1 no data}}}
49
50 {{{
51 #!sstable
52 ||A||B||C||D||
53 ||1||||2||=A1+C1||
54 }}}
55
56 RESULT:
57 ||<(>A||<(>B||<(>C||<(>D||
58 ||<)>1.00||<)>||<)>2.00||<)>3.00||
59
60 -----
61 {{{
62 SUM over rows}}}
63 {{{
64 #!sstable
65 ||1||2||=A0+B0||
66 ||10||20||30||
67 ||=@sum(A0:A1)||=@sum(B0:B1)||=@sum(C0:C1)||
68 }}}
69
70 RESULT:
71 ||<)>1.00||<)>2.00||<)>3.00||
72 ||<)>10.00||<)>20.00||<)>30.00||
73 ||<)>11.00||<)>22.00||<)>33.00||
74
75 -----
76 {{{
77 SUM over rows and columns}}}
78 {{{
79 #!sstable
80 ||A||B||C||
81 ||1||2||=A1+B1||
82 ||10||20||=@sum(A2:B2)||
83 ||=@sum(A1:A2)||=@sum(B1:B2)||=@sum(C1:C2)||
84 }}}
85
86 RESULT:
87 ||<(>A||<(>B||<(>C||
88 ||<)>1.00||<)>2.00||<)>3.00||
89 ||<)>10.00||<)>20.00||<)>30.00||
90 ||<)>11.00||<)>22.00||<)>33.00||
91
92 -----
93 {{{
94 -column_header}}}
95 {{{
96 #!sstable -column_header
97 ||1||2||
98 ||3||4||
99 ||5||6||
100 }}}
101
102 RESULT:
103 ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||
104 ||<)>1.00||<)>2.00||
105 ||<)>3.00||<)>4.00||
106 ||<)>5.00||<)>6.00||
107
108
109 -----
110 {{{
111 -row_header}}}
112 {{{
113 #!sstable -row_header
114 ||1||2||
115 ||3||4||
116 ||5||6||
117 }}}
118
119 RESULT:
120 ||<)5%#CCCCCC>'''0'''||<)>1.00||<)>2.00||
121 ||<)5%#CCCCCC>'''1'''||<)>3.00||<)>4.00||
122 ||<)5%#CCCCCC>'''2'''||<)>5.00||<)>6.00||
123
124 -----
125 {{{
126 -column_header -row_header}}}
127 {{{
128 #!sstable -column_header -row_header
129 ||1||2||
130 ||3||4||
131 ||5||6||
132 }}}
133
134 RESULT:
135 ||<:5%#CCCCCC> ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||
136 ||<)5%#CCCCCC>'''0'''||<)>1.00||<)>2.00||
137 ||<)5%#CCCCCC>'''1'''||<)>3.00||<)>4.00||
138 ||<)5%#CCCCCC>'''2'''||<)>5.00||<)>6.00||
139
140 -----
141 {{{
142 -show_formular -column_header -row_header}}}
143 {{{
144 #!sstable -show_formular -column_header -row_header
145 ||m||p||
146 ||1||=A1 * 5||
147 ||2||=A2-3||
148 ||3||4||
149 }}}
150
151 RESULT:
152 ||<:5%#CCCCCC> ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||
153 ||<)5%#CCCCCC>'''0'''||<(>m||<(>p||
154 ||<)5%#CCCCCC>'''1'''||<(>1||<(>=A1*5||
155 ||<)5%#CCCCCC>'''2'''||<(>2||<(>=A2-3||
156 ||<)5%#CCCCCC>'''3'''||<(>3||<(>4||
157
158 -----
159 {{{
160 -column_header and blanks in cells}}}
161 {{{
162 #!sstable -column_header
163 ||Name Vorname|| || || 3 || || 5||
164 ||Name Vorname|| 1 || 2 || || 4 || 5||
165 ||Name Vorname|| 1 || 2 || || || 5||
166 }}}
167
168 RESULT:
169 ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||<:#CCCCCC>'''C'''||<:#CCCCCC>'''D'''||<:#CCCCCC>'''E'''||<:#CCCCCC>'''F'''||
170 ||<(>Name Vorname|| || ||<)>3.00|| ||<)>5.00||
171 ||<(>Name Vorname||<)>1.00||<)>2.00|| ||<)>4.00||<)>5.00||
172 ||<(>Name Vorname||<)>1.00||<)>2.00|| || ||<)>5.00||
173
174 -----
175 {{{
176 -format 1,1}}}
177 {{{
178 #!sstable -format 1,1
179 ||1||2||
180 ||3||4||
181 ||=@sum(a0:a1)||=a2*4||
182 }}}
183
184 RESULT:
185 ||<)>1.0||<)>2.0||
186 ||<)>3.0||<)>4.0||
187 ||<)>4.0||<)>16.0||
188
189 -----
190 {{{ useage of variable names -show_formular -column_header -row_header
191 }}}
192 {{{#!sstable -show_formular -column_header -row_header
193 ||A||B||C||
194 ||1||{two}2||=A1+two||
195 ||10||20||=@sum(A2:B2)||
196 ||=@sum(A1:A2)||=@sum(B1:B2)||=@sum(C1:C2)||
197 }}}
198
199
200 RESULT:
201 ||<:5%#CCCCCC> ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||<:#CCCCCC>'''C'''||
202 ||<)5%#CCCCCC>'''0'''||<(>A||<(>B||<(>C||
203 ||<)5%#CCCCCC>'''1'''||<(>1||<(>{two}2||<(>=A1+two||
204 ||<)5%#CCCCCC>'''2'''||<(>10||<(>20||<(>=@sum(A2:B2)||
205 ||<)5%#CCCCCC>'''3'''||<(>=@sum(A1:A2)||<(>=@sum(B1:B2)||<(>=@sum(C1:C2)||
206
207 and if we calculate [[BR]]
208 RESULT:
209 {{{#!sstable -column_header -row_header
210 ||A||B||C||
211 ||1||{two}2||=A1+two||
212 ||10||20||=@sum(A2:B2)||
213 ||=@sum(A1:A2)||=@sum(B1:B2)||=@sum(C1:C2)||
214 }}}
215 -----
216 {{{color in cells
217 }}}
218 {{{#!sstable
219 ||<:rowbgcolor=lightcyan>'''A'''||<:>'''B'''||<:>'''C'''||
220 ||<)#dddddd>1||<)#dddddd>{two}2||<)#cccccc>=A1+two||
221 ||<(>10||<)>20||<:#dddddd>=@sum(A2:B2)||
222 ||<rowbgcolor="#cc99ff">=@sum(A1:A2)||=@sum(B1:B2)||<bgcolor=magenta>=@sum(C1:C2)||
223 }}}
224
225 RESULT:
226 ||<:rowbgcolor=lightcyan>'''A'''||<:>'''B'''||<:>'''C'''||
227 ||<)#dddddd>1.00||<)#dddddd>2.00||<)#cccccc>3.00||
228 ||<(>10.00||<)>20.00||<:#dddddd>30.00||
229 ||<rowbgcolor="#cc99ff">11.00||22.00||<bgcolor=magenta>33.00||
230 -----
231 {{{delimit input with ','
232 }}}
233 {{{
234 #!sstable -column_header -input_separator ,
235 ,1,2,
236 ,3,4,
237 ,5,6,
238 }}}
239
240 RESULT:
241 ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||
242 ||<)>1.00||<)>2.00||
243 ||<)>3.00||<)>4.00||
244 ||<)>5.00||<)>6.00||
245 -----
246 {{{delimit input and output with ','
247 }}}
248 {{{
249 #!sstable -column_header -row_header -input_separator , -output_separator ,
250 1,2
251 3,4
252 5,6
253 }}}
254
255 RESULT:
256 {{{
257 ,''' ''','''A''','''B''',
258 ,'''0''', 1.00, 2.00,
259 ,'''1''', 3.00, 4.00,
260 ,'''2''', 5.00, 6.00,
261 }}}
262 -----
263
264
265
266 PROCEDURE:
267 All formulars have to start by a "=" sign.
268 All formulars do not need to start with a "@", but they can.
269
270 Please remove the version number from the routine name!
271
272
273 MODIFICATION:
274 @copyright: 2006-03-25 by Andrew Shewmaker (agshew AT gmail) sstable-1.0
275 2008 MoinMoin:ReimarBauer (slightly changed for 1.7.1)
276
277 1.0.4: improved regular expression that detects numbers - Andrew Shewmaker
278 moved formula parsing into SpreadSheet class - Andrew Shewmaker
279 return 0 when eval results in type error in SpreadSheet class - Andrew Shewmaker
280 1.0.3: improved compatibility with other spreadsheets - Andrew Shewmaker
281 1.0.2: improved cell range functions - Andrew Shewmaker
282 correct special character handling - Reimar Bauer
283 1.0.1: use unicode function - Andrew Shewmaker
284 1.0: based on sctable-1.5.2-5 by Reimar Bauer (R.Bauer AT fz-juelich.de)
285 """
286
287 Dependencies = []
288 import re
289 from math import *
290 from MoinMoin.parser import text_moin_wiki as wiki
291 from MoinMoin.action import AttachFile
292 from MoinMoin.Page import Page
293 from MoinMoin import wikiutil
294
295 class SpreadSheet:
296 _cells = {}
297 tools = {}
298 _cache = None
299 def __init__(self):
300 self.eval_re = re.compile('^=')
301 self.num_re = re.compile('^-?(\d*)\.?[\d]+$')
302 self.range_re = re.compile('(\w+).(\w+:\w+).')
303 self.var_re = re.compile('\{(.+)\}(.+)')
304 self.tic_re = re.compile("^'(.?)'?")
305 self.tools.update({'__builtins__':None})
306 pmath = {'ceil': ceil, 'floor': floor,
307 'fabs': fabs,
308 'fmod': fmod, 'modf': modf,
309 'frexp': frexp, 'ldexp': ldexp,
310 'exp': exp,
311 'log': log, 'log10': log10,
312 'pow': pow,
313 'sqrt': sqrt,
314 'acos': acos, 'asin': asin, 'atan': atan, 'atan2': atan2,
315 'cos': cos, 'sin': sin, 'tan': tan,
316 'cosh': cosh, 'sinh': sinh, 'tanh': tanh,
317 'hypot': hypot,
318 'degrees': degrees, 'radians': radians,
319 'pi': pi, 'e': e,
320 'cmp': cmp,
321 'len': len,
322 'round': round,
323 'hex': hex, 'oct': oct,
324 'max': self.max, 'min': self.min,
325 'sum': self.sum, 'average': self.average, 'median': self.median,
326 }
327 self.tools.update(pmath)
328
329 def getformula(self, key):
330 return self._cells[key]
331
332 def max(self, keys):
333 return max(map(self.__getitem__, self.cellrange(keys)))
334
335 def min(self, keys):
336 return min(map(self.__getitem__, self.cellrange(keys)))
337
338 def sum(self, keys):
339 return sum(map(self.__getitem__, self.cellrange(keys)))
340
341 def average(self, keys):
342 cells = map(self.__getitem__, self.cellrange(keys))
343 return sum(cells)/float(len(cells))
344
345 def median(self, keys):
346 cells = map(self.__getitem__, self.cellrange(keys))
347 n = len(cells)
348 midpt = n / 2
349 if (n % 2):
350 return cells[midpt]
351 else:
352 return (cells[midpt-1] + cells[midpt]) / 2.0
353
354 def __setitem__(self, key, formula):
355 key = key.lower()
356
357 # save variable name to cell name mapping
358 m = self.var_re.search(formula)
359 if m:
360 formula = m.group(2)
361 self._cells[m.group(1).lower()] = key
362
363 if self.eval_re.match(formula):
364 # case insensitivity for everything after an '='
365 formula = formula.lower()
366
367 # cell ranges are passed as strings
368 m = self.range_re.search(formula)
369 if m:
370 formula = m.group(1) + "('" + m.group(2) + "')"
371
372 # '=' and '@' are unwanted for eval
373 formula = re.sub('^=@?', '', formula)
374 elif not self.num_re.match(formula):
375 m = self.tic_re.match(formula)
376 if m:
377 formula = m.group(1)
378 formula = "'" + formula + "'"
379
380 self._cells[key] = formula
381
382 def __getitem__(self, key):
383 bCache = self._cache is None
384 if bCache: self._cache = {}
385 while True:
386 try:
387 if (self._cells[key] == None or self._cells[key] == ''):
388 rv = ''
389 else:
390 rv = eval(self._cells[key], self.tools, self._cache)
391 break
392 except NameError, ne:
393 name = ne.args[0][6:-16] # Extract name from NameError
394 if name in self._cells:
395 self._cache[name] = self[name]
396 else:
397 self._cache[key] = None
398 self._cells[key] = None
399 except SyntaxError, se:
400 rv = self._cells[key]
401 break
402 except TypeError, te:
403 #rv = 'type error: ' + self._cells[key] + ', ' + str(te)
404 rv = 0
405 break
406
407 if bCache: self._cache = None
408 return rv
409
410 def cellrange(self, keys):
411 m = re.search('([a-z]+)(\d+):([a-z])+(\d+)?', keys)
412
413 result = []
414 if not m:
415 result = re.split(',', keys)
416 return result
417
418 (c1, r1, c2, r2) = m.groups()
419
420 mincol = min(c1, c2)
421 maxcol = max(c1, c2)
422 minrow = min(int(r1), int(r2))
423 maxrow = max(int(r1), int(r2))
424
425 col_names = 'abcdefghijklmnopqrstuvwxyz'
426 for i in range(col_names.index(mincol), col_names.index(maxcol)+1):
427 for j in range(minrow, maxrow+1):
428 result.append(col_names[i] + str(j))
429
430 return result
431
432
433 class Parser:
434
435 def __init__(self, raw, request, **kw):
436 self.ss = SpreadSheet()
437 self.raw = raw
438 self.request = request
439 self.form = request.form
440 self._ = request.getText
441 self.kw = []
442 for arg in kw.get('format_args', '').split():
443 self.kw.append(arg)
444
445
446 def format(self, formatter):
447 lines = self.raw.split('\n')
448
449 kw = self.kw
450 column_header = 0
451 row_header = 0
452 show_formular = 0
453 format = ''
454 insep = '||'
455 outsep = '||'
456 right_format = '<)>'
457 left_format = '<(>'
458 colheader_format = '<:#CCCCCC>'
459 rowheader_format = '<)5%#CCCCCC>'
460 zt = 0
461 for test in kw:
462 if test == '-column_header': column_header = 1
463 if test == '-row_header': row_header = 1
464 if test == '-show_formular': show_formular = 1
465 if test == '-format': format = re.split(',', kw[zt + 1])
466 if test == '-input_separator': insep = kw[zt + 1]
467 if test == '-output_separator':
468 outsep = kw[zt + 1]
469 right_format = ''
470 left_format = ''
471 colheader_format = ''
472 rowheader_format = ''
473 zt += 1
474
475 formats = {} # { (row,col): '<wikiformat>', ... }
476 r = 0 # row counter
477 c = 0 # column counter
478 col_names = 'abcdefghijklmnopqrstuvwxyz'
479 maxcolumns = 0
480
481 for txt in lines:
482 txt = txt.lstrip()
483 if txt == '':
484 continue
485 columns = txt.split(insep)
486 if re.search('^' + insep, txt):
487 columns = columns[1:]
488 if re.search(insep + '$', txt):
489 columns = columns[:-1]
490
491 if len(columns) > maxcolumns:
492 maxcolumns = len(columns)
493
494 c = 0
495 for cell in columns:
496 # check for wiki formatting string at beginning of ss data: "<format>..."
497 if cell.startswith('<'):
498 p = cell.find('>') + 1
499 if p > 1:
500 formats[(r, c)], cell = cell[:p], cell[p:]
501 else:
502 formats[(r, c)] = ""
503
504 self.ss[col_names[c]+str(r)] = cell.strip()
505
506 c += 1
507
508 r += 1
509
510 maxrows = r
511 result = ""
512
513 if outsep != '||':
514 self.request.write('<pre>')
515
516 if column_header == 1:
517 header_names = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
518 result += '\n' + outsep
519 if row_header == 1:
520 start = 0
521 else:
522 start = 1
523
524 for name in header_names[start:maxcolumns+1]:
525 result += colheader_format + "'''" + name + "'''" + outsep
526
527 for r in range(maxrows):
528 result += '\n' + outsep
529
530 if row_header == 1:
531 result += rowheader_format + "'''" + str(r) + "'''" + outsep
532
533 for c in range(maxcolumns):
534 cellname = col_names[c]+str(r)
535
536 cell = str(self.ss[cellname])
537 if cell == '':
538 cell = ' '
539
540 if show_formular == 0:
541 num_match = self.ss.num_re.match(cell)
542 if num_match:
543 fmt = right_format
544 if formats[(r,c)] != '':
545 fmt = formats[(r,c)]
546
547 if format == '':
548 cell = '%s %.*f' % (fmt, 2, float(self.ss[cellname]))
549 else:
550 cell = '%s %.*f' % (fmt, int(format[c-1]), float(self.ss[cellname]))
551
552 elif show_formular == 1:
553 cell = self.ss.getformula(cellname)
554 else:
555 if formats[(r, c)] == '':
556 cell = left_format + cell
557 else:
558 cell = formats[(r, c)] + cell
559
560 result += cell + outsep
561
562 if outsep != '||':
563 self.request.write(result + '</pre>')
564 else:
565 result = result[1:]
566 result = wikiutil.unquoteWikiname(result)
567 wikiizer = wiki.Parser(result, self.request) # parser for wiki tabular
568 wikiizer.format(formatter)
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.