Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: third_party/logilab/common/table.py

Issue 10447014: Add pylint to depot_tools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix unittests. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/logilab/common/sphinxutils.py ('k') | third_party/logilab/common/tasksqueue.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
4 # This file is part of logilab-common.
5 #
6 # logilab-common is free software: you can redistribute it and/or modify it unde r
7 # the terms of the GNU Lesser General Public License as published by the Free
8 # Software Foundation, either version 2.1 of the License, or (at your option) an y
9 # later version.
10 #
11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 # details.
15 #
16 # You should have received a copy of the GNU Lesser General Public License along
17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>.
18 """Table management module."""
19 __docformat__ = "restructuredtext en"
20
21
22 class Table(object):
23 """Table defines a data table with column and row names.
24 inv:
25 len(self.data) <= len(self.row_names)
26 forall(self.data, lambda x: len(x) <= len(self.col_names))
27 """
28
29 def __init__(self, default_value=0, col_names=None, row_names=None):
30 self.col_names = []
31 self.row_names = []
32 self.data = []
33 self.default_value = default_value
34 if col_names:
35 self.create_columns(col_names)
36 if row_names:
37 self.create_rows(row_names)
38
39 def _next_row_name(self):
40 return 'row%s' % (len(self.row_names)+1)
41
42 def __iter__(self):
43 return iter(self.data)
44
45 def __eq__(self, other):
46 if other is None:
47 return False
48 else:
49 return list(self) == list(other)
50
51 def __ne__(self, other):
52 return not self == other
53
54 def __len__(self):
55 return len(self.row_names)
56
57 ## Rows / Columns creation #################################################
58 def create_rows(self, row_names):
59 """Appends row_names to the list of existing rows
60 """
61 self.row_names.extend(row_names)
62 for row_name in row_names:
63 self.data.append([self.default_value]*len(self.col_names))
64
65 def create_columns(self, col_names):
66 """Appends col_names to the list of existing columns
67 """
68 for col_name in col_names:
69 self.create_column(col_name)
70
71 def create_row(self, row_name=None):
72 """Creates a rowname to the row_names list
73 """
74 row_name = row_name or self._next_row_name()
75 self.row_names.append(row_name)
76 self.data.append([self.default_value]*len(self.col_names))
77
78
79 def create_column(self, col_name):
80 """Creates a colname to the col_names list
81 """
82 self.col_names.append(col_name)
83 for row in self.data:
84 row.append(self.default_value)
85
86 ## Sort by column ##########################################################
87 def sort_by_column_id(self, col_id, method = 'asc'):
88 """Sorts the table (in-place) according to data stored in col_id
89 """
90 try:
91 col_index = self.col_names.index(col_id)
92 self.sort_by_column_index(col_index, method)
93 except ValueError:
94 raise KeyError("Col (%s) not found in table" % (col_id))
95
96
97 def sort_by_column_index(self, col_index, method = 'asc'):
98 """Sorts the table 'in-place' according to data stored in col_index
99
100 method should be in ('asc', 'desc')
101 """
102 sort_list = sorted([(row[col_index], row, row_name)
103 for row, row_name in zip(self.data, self.row_names)])
104 # Sorting sort_list will sort according to col_index
105 # If we want reverse sort, then reverse list
106 if method.lower() == 'desc':
107 sort_list.reverse()
108
109 # Rebuild data / row names
110 self.data = []
111 self.row_names = []
112 for val, row, row_name in sort_list:
113 self.data.append(row)
114 self.row_names.append(row_name)
115
116 def groupby(self, colname, *others):
117 """builds indexes of data
118 :returns: nested dictionaries pointing to actual rows
119 """
120 groups = {}
121 colnames = (colname,) + others
122 col_indexes = [self.col_names.index(col_id) for col_id in colnames]
123 for row in self.data:
124 ptr = groups
125 for col_index in col_indexes[:-1]:
126 ptr = ptr.setdefault(row[col_index], {})
127 ptr = ptr.setdefault(row[col_indexes[-1]],
128 Table(default_value=self.default_value,
129 col_names=self.col_names))
130 ptr.append_row(tuple(row))
131 return groups
132
133 def select(self, colname, value):
134 grouped = self.groupby(colname)
135 try:
136 return grouped[value]
137 except KeyError:
138 return []
139
140 def remove(self, colname, value):
141 col_index = self.col_names.index(colname)
142 for row in self.data[:]:
143 if row[col_index] == value:
144 self.data.remove(row)
145
146
147 ## The 'setter' part #######################################################
148 def set_cell(self, row_index, col_index, data):
149 """sets value of cell 'row_indew', 'col_index' to data
150 """
151 self.data[row_index][col_index] = data
152
153
154 def set_cell_by_ids(self, row_id, col_id, data):
155 """sets value of cell mapped by row_id and col_id to data
156 Raises a KeyError if row_id or col_id are not found in the table
157 """
158 try:
159 row_index = self.row_names.index(row_id)
160 except ValueError:
161 raise KeyError("Row (%s) not found in table" % (row_id))
162 else:
163 try:
164 col_index = self.col_names.index(col_id)
165 self.data[row_index][col_index] = data
166 except ValueError:
167 raise KeyError("Column (%s) not found in table" % (col_id))
168
169
170 def set_row(self, row_index, row_data):
171 """sets the 'row_index' row
172 pre:
173 type(row_data) == types.ListType
174 len(row_data) == len(self.col_names)
175 """
176 self.data[row_index] = row_data
177
178
179 def set_row_by_id(self, row_id, row_data):
180 """sets the 'row_id' column
181 pre:
182 type(row_data) == types.ListType
183 len(row_data) == len(self.row_names)
184 Raises a KeyError if row_id is not found
185 """
186 try:
187 row_index = self.row_names.index(row_id)
188 self.set_row(row_index, row_data)
189 except ValueError:
190 raise KeyError('Row (%s) not found in table' % (row_id))
191
192
193 def append_row(self, row_data, row_name=None):
194 """Appends a row to the table
195 pre:
196 type(row_data) == types.ListType
197 len(row_data) == len(self.col_names)
198 """
199 row_name = row_name or self._next_row_name()
200 self.row_names.append(row_name)
201 self.data.append(row_data)
202 return len(self.data) - 1
203
204 def insert_row(self, index, row_data, row_name=None):
205 """Appends row_data before 'index' in the table. To make 'insert'
206 behave like 'list.insert', inserting in an out of range index will
207 insert row_data to the end of the list
208 pre:
209 type(row_data) == types.ListType
210 len(row_data) == len(self.col_names)
211 """
212 row_name = row_name or self._next_row_name()
213 self.row_names.insert(index, row_name)
214 self.data.insert(index, row_data)
215
216
217 def delete_row(self, index):
218 """Deletes the 'index' row in the table, and returns it.
219 Raises an IndexError if index is out of range
220 """
221 self.row_names.pop(index)
222 return self.data.pop(index)
223
224
225 def delete_row_by_id(self, row_id):
226 """Deletes the 'row_id' row in the table.
227 Raises a KeyError if row_id was not found.
228 """
229 try:
230 row_index = self.row_names.index(row_id)
231 self.delete_row(row_index)
232 except ValueError:
233 raise KeyError('Row (%s) not found in table' % (row_id))
234
235
236 def set_column(self, col_index, col_data):
237 """sets the 'col_index' column
238 pre:
239 type(col_data) == types.ListType
240 len(col_data) == len(self.row_names)
241 """
242
243 for row_index, cell_data in enumerate(col_data):
244 self.data[row_index][col_index] = cell_data
245
246
247 def set_column_by_id(self, col_id, col_data):
248 """sets the 'col_id' column
249 pre:
250 type(col_data) == types.ListType
251 len(col_data) == len(self.col_names)
252 Raises a KeyError if col_id is not found
253 """
254 try:
255 col_index = self.col_names.index(col_id)
256 self.set_column(col_index, col_data)
257 except ValueError:
258 raise KeyError('Column (%s) not found in table' % (col_id))
259
260
261 def append_column(self, col_data, col_name):
262 """Appends the 'col_index' column
263 pre:
264 type(col_data) == types.ListType
265 len(col_data) == len(self.row_names)
266 """
267 self.col_names.append(col_name)
268 for row_index, cell_data in enumerate(col_data):
269 self.data[row_index].append(cell_data)
270
271
272 def insert_column(self, index, col_data, col_name):
273 """Appends col_data before 'index' in the table. To make 'insert'
274 behave like 'list.insert', inserting in an out of range index will
275 insert col_data to the end of the list
276 pre:
277 type(col_data) == types.ListType
278 len(col_data) == len(self.row_names)
279 """
280 self.col_names.insert(index, col_name)
281 for row_index, cell_data in enumerate(col_data):
282 self.data[row_index].insert(index, cell_data)
283
284
285 def delete_column(self, index):
286 """Deletes the 'index' column in the table, and returns it.
287 Raises an IndexError if index is out of range
288 """
289 self.col_names.pop(index)
290 return [row.pop(index) for row in self.data]
291
292
293 def delete_column_by_id(self, col_id):
294 """Deletes the 'col_id' col in the table.
295 Raises a KeyError if col_id was not found.
296 """
297 try:
298 col_index = self.col_names.index(col_id)
299 self.delete_column(col_index)
300 except ValueError:
301 raise KeyError('Column (%s) not found in table' % (col_id))
302
303
304 ## The 'getter' part #######################################################
305
306 def get_shape(self):
307 """Returns a tuple which represents the table's shape
308 """
309 return len(self.row_names), len(self.col_names)
310 shape = property(get_shape)
311
312 def __getitem__(self, indices):
313 """provided for convenience"""
314 rows, multirows = None, False
315 cols, multicols = None, False
316 if isinstance(indices, tuple):
317 rows = indices[0]
318 if len(indices) > 1:
319 cols = indices[1]
320 else:
321 rows = indices
322 # define row slice
323 if isinstance(rows, str):
324 try:
325 rows = self.row_names.index(rows)
326 except ValueError:
327 raise KeyError("Row (%s) not found in table" % (rows))
328 if isinstance(rows, int):
329 rows = slice(rows, rows+1)
330 multirows = False
331 else:
332 rows = slice(None)
333 multirows = True
334 # define col slice
335 if isinstance(cols, str):
336 try:
337 cols = self.col_names.index(cols)
338 except ValueError:
339 raise KeyError("Column (%s) not found in table" % (cols))
340 if isinstance(cols, int):
341 cols = slice(cols, cols+1)
342 multicols = False
343 else:
344 cols = slice(None)
345 multicols = True
346 # get sub-table
347 tab = Table()
348 tab.default_value = self.default_value
349 tab.create_rows(self.row_names[rows])
350 tab.create_columns(self.col_names[cols])
351 for idx, row in enumerate(self.data[rows]):
352 tab.set_row(idx, row[cols])
353 if multirows :
354 if multicols:
355 return tab
356 else:
357 return [item[0] for item in tab.data]
358 else:
359 if multicols:
360 return tab.data[0]
361 else:
362 return tab.data[0][0]
363
364 def get_cell_by_ids(self, row_id, col_id):
365 """Returns the element at [row_id][col_id]
366 """
367 try:
368 row_index = self.row_names.index(row_id)
369 except ValueError:
370 raise KeyError("Row (%s) not found in table" % (row_id))
371 else:
372 try:
373 col_index = self.col_names.index(col_id)
374 except ValueError:
375 raise KeyError("Column (%s) not found in table" % (col_id))
376 return self.data[row_index][col_index]
377
378 def get_row_by_id(self, row_id):
379 """Returns the 'row_id' row
380 """
381 try:
382 row_index = self.row_names.index(row_id)
383 except ValueError:
384 raise KeyError("Row (%s) not found in table" % (row_id))
385 return self.data[row_index]
386
387 def get_column_by_id(self, col_id, distinct=False):
388 """Returns the 'col_id' col
389 """
390 try:
391 col_index = self.col_names.index(col_id)
392 except ValueError:
393 raise KeyError("Column (%s) not found in table" % (col_id))
394 return self.get_column(col_index, distinct)
395
396 def get_columns(self):
397 """Returns all the columns in the table
398 """
399 return [self[:, index] for index in range(len(self.col_names))]
400
401 def get_column(self, col_index, distinct=False):
402 """get a column by index"""
403 col = [row[col_index] for row in self.data]
404 if distinct:
405 col = list(set(col))
406 return col
407
408 def apply_stylesheet(self, stylesheet):
409 """Applies the stylesheet to this table
410 """
411 for instruction in stylesheet.instructions:
412 eval(instruction)
413
414
415 def transpose(self):
416 """Keeps the self object intact, and returns the transposed (rotated)
417 table.
418 """
419 transposed = Table()
420 transposed.create_rows(self.col_names)
421 transposed.create_columns(self.row_names)
422 for col_index, column in enumerate(self.get_columns()):
423 transposed.set_row(col_index, column)
424 return transposed
425
426
427 def pprint(self):
428 """returns a string representing the table in a pretty
429 printed 'text' format.
430 """
431 # The maximum row name (to know the start_index of the first col)
432 max_row_name = 0
433 for row_name in self.row_names:
434 if len(row_name) > max_row_name:
435 max_row_name = len(row_name)
436 col_start = max_row_name + 5
437
438 lines = []
439 # Build the 'first' line <=> the col_names one
440 # The first cell <=> an empty one
441 col_names_line = [' '*col_start]
442 for col_name in self.col_names:
443 col_names_line.append(col_name.encode('iso-8859-1') + ' '*5)
444 lines.append('|' + '|'.join(col_names_line) + '|')
445 max_line_length = len(lines[0])
446
447 # Build the table
448 for row_index, row in enumerate(self.data):
449 line = []
450 # First, build the row_name's cell
451 row_name = self.row_names[row_index].encode('iso-8859-1')
452 line.append(row_name + ' '*(col_start-len(row_name)))
453
454 # Then, build all the table's cell for this line.
455 for col_index, cell in enumerate(row):
456 col_name_length = len(self.col_names[col_index]) + 5
457 data = str(cell)
458 line.append(data + ' '*(col_name_length - len(data)))
459 lines.append('|' + '|'.join(line) + '|')
460 if len(lines[-1]) > max_line_length:
461 max_line_length = len(lines[-1])
462
463 # Wrap the table with '-' to make a frame
464 lines.insert(0, '-'*max_line_length)
465 lines.append('-'*max_line_length)
466 return '\n'.join(lines)
467
468
469 def __repr__(self):
470 return repr(self.data)
471
472 def as_text(self):
473 data = []
474 # We must convert cells into strings before joining them
475 for row in self.data:
476 data.append([str(cell) for cell in row])
477 lines = ['\t'.join(row) for row in data]
478 return '\n'.join(lines)
479
480
481
482 class TableStyle:
483 """Defines a table's style
484 """
485
486 def __init__(self, table):
487
488 self._table = table
489 self.size = dict([(col_name, '1*') for col_name in table.col_names])
490 # __row_column__ is a special key to define the first column which
491 # actually has no name (<=> left most column <=> row names column)
492 self.size['__row_column__'] = '1*'
493 self.alignment = dict([(col_name, 'right')
494 for col_name in table.col_names])
495 self.alignment['__row_column__'] = 'right'
496
497 # We shouldn't have to create an entry for
498 # the 1st col (the row_column one)
499 self.units = dict([(col_name, '') for col_name in table.col_names])
500 self.units['__row_column__'] = ''
501
502 # XXX FIXME : params order should be reversed for all set() methods
503 def set_size(self, value, col_id):
504 """sets the size of the specified col_id to value
505 """
506 self.size[col_id] = value
507
508 def set_size_by_index(self, value, col_index):
509 """Allows to set the size according to the column index rather than
510 using the column's id.
511 BE CAREFUL : the '0' column is the '__row_column__' one !
512 """
513 if col_index == 0:
514 col_id = '__row_column__'
515 else:
516 col_id = self._table.col_names[col_index-1]
517
518 self.size[col_id] = value
519
520
521 def set_alignment(self, value, col_id):
522 """sets the alignment of the specified col_id to value
523 """
524 self.alignment[col_id] = value
525
526
527 def set_alignment_by_index(self, value, col_index):
528 """Allows to set the alignment according to the column index rather than
529 using the column's id.
530 BE CAREFUL : the '0' column is the '__row_column__' one !
531 """
532 if col_index == 0:
533 col_id = '__row_column__'
534 else:
535 col_id = self._table.col_names[col_index-1]
536
537 self.alignment[col_id] = value
538
539
540 def set_unit(self, value, col_id):
541 """sets the unit of the specified col_id to value
542 """
543 self.units[col_id] = value
544
545
546 def set_unit_by_index(self, value, col_index):
547 """Allows to set the unit according to the column index rather than
548 using the column's id.
549 BE CAREFUL : the '0' column is the '__row_column__' one !
550 (Note that in the 'unit' case, you shouldn't have to set a unit
551 for the 1st column (the __row__column__ one))
552 """
553 if col_index == 0:
554 col_id = '__row_column__'
555 else:
556 col_id = self._table.col_names[col_index-1]
557
558 self.units[col_id] = value
559
560
561 def get_size(self, col_id):
562 """Returns the size of the specified col_id
563 """
564 return self.size[col_id]
565
566
567 def get_size_by_index(self, col_index):
568 """Allows to get the size according to the column index rather than
569 using the column's id.
570 BE CAREFUL : the '0' column is the '__row_column__' one !
571 """
572 if col_index == 0:
573 col_id = '__row_column__'
574 else:
575 col_id = self._table.col_names[col_index-1]
576
577 return self.size[col_id]
578
579
580 def get_alignment(self, col_id):
581 """Returns the alignment of the specified col_id
582 """
583 return self.alignment[col_id]
584
585
586 def get_alignment_by_index(self, col_index):
587 """Allors to get the alignment according to the column index rather than
588 using the column's id.
589 BE CAREFUL : the '0' column is the '__row_column__' one !
590 """
591 if col_index == 0:
592 col_id = '__row_column__'
593 else:
594 col_id = self._table.col_names[col_index-1]
595
596 return self.alignment[col_id]
597
598
599 def get_unit(self, col_id):
600 """Returns the unit of the specified col_id
601 """
602 return self.units[col_id]
603
604
605 def get_unit_by_index(self, col_index):
606 """Allors to get the unit according to the column index rather than
607 using the column's id.
608 BE CAREFUL : the '0' column is the '__row_column__' one !
609 """
610 if col_index == 0:
611 col_id = '__row_column__'
612 else:
613 col_id = self._table.col_names[col_index-1]
614
615 return self.units[col_id]
616
617
618 import re
619 CELL_PROG = re.compile("([0-9]+)_([0-9]+)")
620
621 class TableStyleSheet:
622 """A simple Table stylesheet
623 Rules are expressions where cells are defined by the row_index
624 and col_index separated by an underscore ('_').
625 For example, suppose you want to say that the (2,5) cell must be
626 the sum of its two preceding cells in the row, you would create
627 the following rule :
628 2_5 = 2_3 + 2_4
629 You can also use all the math.* operations you want. For example:
630 2_5 = sqrt(2_3**2 + 2_4**2)
631 """
632
633 def __init__(self, rules = None):
634 rules = rules or []
635 self.rules = []
636 self.instructions = []
637 for rule in rules:
638 self.add_rule(rule)
639
640
641 def add_rule(self, rule):
642 """Adds a rule to the stylesheet rules
643 """
644 try:
645 source_code = ['from math import *']
646 source_code.append(CELL_PROG.sub(r'self.data[\1][\2]', rule))
647 self.instructions.append(compile('\n'.join(source_code),
648 'table.py', 'exec'))
649 self.rules.append(rule)
650 except SyntaxError:
651 print "Bad Stylesheet Rule : %s [skipped]"%rule
652
653
654 def add_rowsum_rule(self, dest_cell, row_index, start_col, end_col):
655 """Creates and adds a rule to sum over the row at row_index from
656 start_col to end_col.
657 dest_cell is a tuple of two elements (x,y) of the destination cell
658 No check is done for indexes ranges.
659 pre:
660 start_col >= 0
661 end_col > start_col
662 """
663 cell_list = ['%d_%d'%(row_index, index) for index in range(start_col,
664 end_col + 1)]
665 rule = '%d_%d=' % dest_cell + '+'.join(cell_list)
666 self.add_rule(rule)
667
668
669 def add_rowavg_rule(self, dest_cell, row_index, start_col, end_col):
670 """Creates and adds a rule to make the row average (from start_col
671 to end_col)
672 dest_cell is a tuple of two elements (x,y) of the destination cell
673 No check is done for indexes ranges.
674 pre:
675 start_col >= 0
676 end_col > start_col
677 """
678 cell_list = ['%d_%d'%(row_index, index) for index in range(start_col,
679 end_col + 1)]
680 num = (end_col - start_col + 1)
681 rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num
682 self.add_rule(rule)
683
684
685 def add_colsum_rule(self, dest_cell, col_index, start_row, end_row):
686 """Creates and adds a rule to sum over the col at col_index from
687 start_row to end_row.
688 dest_cell is a tuple of two elements (x,y) of the destination cell
689 No check is done for indexes ranges.
690 pre:
691 start_row >= 0
692 end_row > start_row
693 """
694 cell_list = ['%d_%d'%(index, col_index) for index in range(start_row,
695 end_row + 1)]
696 rule = '%d_%d=' % dest_cell + '+'.join(cell_list)
697 self.add_rule(rule)
698
699
700 def add_colavg_rule(self, dest_cell, col_index, start_row, end_row):
701 """Creates and adds a rule to make the col average (from start_row
702 to end_row)
703 dest_cell is a tuple of two elements (x,y) of the destination cell
704 No check is done for indexes ranges.
705 pre:
706 start_row >= 0
707 end_row > start_row
708 """
709 cell_list = ['%d_%d'%(index, col_index) for index in range(start_row,
710 end_row + 1)]
711 num = (end_row - start_row + 1)
712 rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num
713 self.add_rule(rule)
714
715
716
717 class TableCellRenderer:
718 """Defines a simple text renderer
719 """
720
721 def __init__(self, **properties):
722 """keywords should be properties with an associated boolean as value.
723 For example :
724 renderer = TableCellRenderer(units = True, alignment = False)
725 An unspecified property will have a 'False' value by default.
726 Possible properties are :
727 alignment, unit
728 """
729 self.properties = properties
730
731
732 def render_cell(self, cell_coord, table, table_style):
733 """Renders the cell at 'cell_coord' in the table, using table_style
734 """
735 row_index, col_index = cell_coord
736 cell_value = table.data[row_index][col_index]
737 final_content = self._make_cell_content(cell_value,
738 table_style, col_index +1)
739 return self._render_cell_content(final_content,
740 table_style, col_index + 1)
741
742
743 def render_row_cell(self, row_name, table, table_style):
744 """Renders the cell for 'row_id' row
745 """
746 cell_value = row_name.encode('iso-8859-1')
747 return self._render_cell_content(cell_value, table_style, 0)
748
749
750 def render_col_cell(self, col_name, table, table_style):
751 """Renders the cell for 'col_id' row
752 """
753 cell_value = col_name.encode('iso-8859-1')
754 col_index = table.col_names.index(col_name)
755 return self._render_cell_content(cell_value, table_style, col_index +1)
756
757
758
759 def _render_cell_content(self, content, table_style, col_index):
760 """Makes the appropriate rendering for this cell content.
761 Rendering properties will be searched using the
762 *table_style.get_xxx_by_index(col_index)' methods
763
764 **This method should be overridden in the derived renderer classes.**
765 """
766 return content
767
768
769 def _make_cell_content(self, cell_content, table_style, col_index):
770 """Makes the cell content (adds decoration data, like units for
771 example)
772 """
773 final_content = cell_content
774 if 'skip_zero' in self.properties:
775 replacement_char = self.properties['skip_zero']
776 else:
777 replacement_char = 0
778 if replacement_char and final_content == 0:
779 return replacement_char
780
781 try:
782 units_on = self.properties['units']
783 if units_on:
784 final_content = self._add_unit(
785 cell_content, table_style, col_index)
786 except KeyError:
787 pass
788
789 return final_content
790
791
792 def _add_unit(self, cell_content, table_style, col_index):
793 """Adds unit to the cell_content if needed
794 """
795 unit = table_style.get_unit_by_index(col_index)
796 return str(cell_content) + " " + unit
797
798
799
800 class DocbookRenderer(TableCellRenderer):
801 """Defines how to render a cell for a docboook table
802 """
803
804 def define_col_header(self, col_index, table_style):
805 """Computes the colspec element according to the style
806 """
807 size = table_style.get_size_by_index(col_index)
808 return '<colspec colname="c%d" colwidth="%s"/>\n' % \
809 (col_index, size)
810
811
812 def _render_cell_content(self, cell_content, table_style, col_index):
813 """Makes the appropriate rendering for this cell content.
814 Rendering properties will be searched using the
815 table_style.get_xxx_by_index(col_index)' methods.
816 """
817 try:
818 align_on = self.properties['alignment']
819 alignment = table_style.get_alignment_by_index(col_index)
820 if align_on:
821 return "<entry align='%s'>%s</entry>\n" % \
822 (alignment, cell_content)
823 except KeyError:
824 # KeyError <=> Default alignment
825 return "<entry>%s</entry>\n" % cell_content
826
827
828 class TableWriter:
829 """A class to write tables
830 """
831
832 def __init__(self, stream, table, style, **properties):
833 self._stream = stream
834 self.style = style or TableStyle(table)
835 self._table = table
836 self.properties = properties
837 self.renderer = None
838
839
840 def set_style(self, style):
841 """sets the table's associated style
842 """
843 self.style = style
844
845
846 def set_renderer(self, renderer):
847 """sets the way to render cell
848 """
849 self.renderer = renderer
850
851
852 def update_properties(self, **properties):
853 """Updates writer's properties (for cell rendering)
854 """
855 self.properties.update(properties)
856
857
858 def write_table(self, title = ""):
859 """Writes the table
860 """
861 raise NotImplementedError("write_table must be implemented !")
862
863
864
865 class DocbookTableWriter(TableWriter):
866 """Defines an implementation of TableWriter to write a table in Docbook
867 """
868
869 def _write_headers(self):
870 """Writes col headers
871 """
872 # Define col_headers (colstpec elements)
873 for col_index in range(len(self._table.col_names)+1):
874 self._stream.write(self.renderer.define_col_header(col_index,
875 self.style))
876
877 self._stream.write("<thead>\n<row>\n")
878 # XXX FIXME : write an empty entry <=> the first (__row_column) column
879 self._stream.write('<entry></entry>\n')
880 for col_name in self._table.col_names:
881 self._stream.write(self.renderer.render_col_cell(
882 col_name, self._table,
883 self.style))
884
885 self._stream.write("</row>\n</thead>\n")
886
887
888 def _write_body(self):
889 """Writes the table body
890 """
891 self._stream.write('<tbody>\n')
892
893 for row_index, row in enumerate(self._table.data):
894 self._stream.write('<row>\n')
895 row_name = self._table.row_names[row_index]
896 # Write the first entry (row_name)
897 self._stream.write(self.renderer.render_row_cell(row_name,
898 self._table,
899 self.style))
900
901 for col_index, cell in enumerate(row):
902 self._stream.write(self.renderer.render_cell(
903 (row_index, col_index),
904 self._table, self.style))
905
906 self._stream.write('</row>\n')
907
908 self._stream.write('</tbody>\n')
909
910
911 def write_table(self, title = ""):
912 """Writes the table
913 """
914 self._stream.write('<table>\n<title>%s></title>\n'%(title))
915 self._stream.write(
916 '<tgroup cols="%d" align="left" colsep="1" rowsep="1">\n'%
917 (len(self._table.col_names)+1))
918 self._write_headers()
919 self._write_body()
920
921 self._stream.write('</tgroup>\n</table>\n')
922
923
OLDNEW
« no previous file with comments | « third_party/logilab/common/sphinxutils.py ('k') | third_party/logilab/common/tasksqueue.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698