1 | MondoReport Documentation |
---|
2 | Version 0.01 alpha 24-Nov-2001. iron@mso.oz.net or mso@oz.net. |
---|
3 | Copyright (c) 2001 Mike Orr. License: same as Python or Cheetah. |
---|
4 | |
---|
5 | * * * * * |
---|
6 | STATUS: previous/next batches and query string are not implemented yet. |
---|
7 | Sorting not designed yet. Considering "click on this column header to sort by |
---|
8 | this field" and multiple ascending/descending sort fields for a future version. |
---|
9 | |
---|
10 | Tested with Python 2.2b1. May work with Python 2.1 or 2.0. |
---|
11 | |
---|
12 | * * * * * |
---|
13 | OVERVIEW |
---|
14 | |
---|
15 | MondoReport -- provide information about a list that is useful in generating |
---|
16 | any kind of report. The module consists of one main public class, and some |
---|
17 | generic functions you may find useful in other programs. This file contains an |
---|
18 | overview, syntax reference and examples. The module is designed both for |
---|
19 | standalone use and for integration with the Cheetah template system |
---|
20 | (http://www.cheetahtemplate.org/), so the examples are in both Python and |
---|
21 | Cheetah. The main uses of MondoReport are: |
---|
22 | |
---|
23 | (A) to iterate through a list. In this sense MR is a for-loop enhancer, |
---|
24 | providing information that would be verbose to calculate otherwise. |
---|
25 | |
---|
26 | (B) to separate a list into equal-size "pages" (or "batches"--the two terms are |
---|
27 | interchangeable) and only display the current page, plus limited information |
---|
28 | about the previous and next pages. |
---|
29 | |
---|
30 | (C) to extract summary statistics about a certain column ("field") in the list. |
---|
31 | |
---|
32 | * * * * * |
---|
33 | MAIN PUBLIC CLASS |
---|
34 | |
---|
35 | To create a MondoReport instance, supply a list to operate on. |
---|
36 | |
---|
37 | mr = MondoReport(origList) |
---|
38 | |
---|
39 | The list may be a list of anything, but if you use the 'field' argument in any |
---|
40 | of the methods below, the elements must be instances or dictionaries. |
---|
41 | |
---|
42 | MondoReport assumes it's operating on an unchanging list. Do not modify the |
---|
43 | list or any of its elements until you are completely finished with the |
---|
44 | ModoReport object and its sub-objects. Otherwise, you may get an exception or |
---|
45 | incorrect results. |
---|
46 | |
---|
47 | MondoReport instances have three methods: |
---|
48 | |
---|
49 | .page(size, start, overlap=0, orphan=0 |
---|
50 | sort=None, reverse=False) => list of (r, a, b). |
---|
51 | |
---|
52 | 'size' is an integer >= 1. 'start', 'overlap' and 'orphan' are integers >= 0. |
---|
53 | The list returned contains one triple for each record in the current page. 'r' |
---|
54 | is the original record. 'a' is a BatchRecord instance for the current record |
---|
55 | in relation to all records in the origList. 'b' is a BatchRecord instance for |
---|
56 | the current record in relation to all the records in that batch/page. (There |
---|
57 | is a .batch method that's identical to .page.) |
---|
58 | |
---|
59 | The other options aren't implemented yet, but 'overlap' duplicates this many |
---|
60 | records on adjacent batches. 'orphan' moves this many records or fewer, if |
---|
61 | they are on a page alone, onto the neighboring page. 'sort' (string) specifies |
---|
62 | a field to sort the records by. It may be suffixed by ":desc" to sort in |
---|
63 | descending order. 'reverse' (boolean) reverses the sort order. If both |
---|
64 | ":desc" and 'reverse' are specified, they will cancel each other out. This |
---|
65 | sorting/reversal happens on a copy of the origList, and all objects returned |
---|
66 | by this method use the sorted list, except when resorting the next time. |
---|
67 | To do more complicated sorting, such as a hierarchy of columns, do it to the |
---|
68 | original list before creating the ModoReport object. |
---|
69 | |
---|
70 | .all(sort=None, reverse=False) => list of (r, a). |
---|
71 | |
---|
72 | Same, but the current page spans the entire origList. |
---|
73 | |
---|
74 | .summary() => Summary instance. |
---|
75 | |
---|
76 | Summary statistics for the entire origList. |
---|
77 | |
---|
78 | In Python, use .page or .all in a for loop: |
---|
79 | |
---|
80 | from Cheetah.Tools.MondoReport import MondoReport |
---|
81 | mr = MondoReport(myList) |
---|
82 | for r, a, b in mr.page(20, 40): |
---|
83 | # Do something with r, a and b. The current page is the third page, |
---|
84 | # with twenty records corresponding to origList[40:60]. |
---|
85 | if not myList: |
---|
86 | # Warn the user there are no records in the list. |
---|
87 | |
---|
88 | It works the same way in Cheetah, just convert to Cheetah syntax. This example |
---|
89 | assumes the template doubles as a Webware servlet, so we use the servlet's |
---|
90 | '$request' method to look up the CGI parameter 'start'. The default value is 0 |
---|
91 | for the first page. |
---|
92 | |
---|
93 | #from Cheetah.Tools.MondoReport import MondoReport |
---|
94 | #set $mr = $MondoReport($bigList) |
---|
95 | #set $start = $request.field("start", 0) |
---|
96 | #for $o, $a, $b in $mr.page(20, $start) |
---|
97 | ... do something with $o, $a and $b ... |
---|
98 | #end for |
---|
99 | #unless $bigList |
---|
100 | This is displayed if the original list has no elements. |
---|
101 | It's equivalent to the "else" part Zope DTML's <dtml-in>. |
---|
102 | #end unless |
---|
103 | |
---|
104 | * * * * * |
---|
105 | USING 'r' RECORDS |
---|
106 | |
---|
107 | Use 'r' just as you would the original element. For instance: |
---|
108 | |
---|
109 | print r.attribute # If r is an instance. |
---|
110 | print r['key'] # If r is a dictionary. |
---|
111 | print r # If r is numeric or a string. |
---|
112 | |
---|
113 | In Cheetah, you can take advantage of Universal Dotted Notation and autocalling: |
---|
114 | |
---|
115 | $r.name ## 'name' may be an attribute or key of 'r'. If 'r' and/or |
---|
116 | ## 'name' is a function or method, it will be called without |
---|
117 | ## arguments. |
---|
118 | $r.attribute |
---|
119 | $r['key'] |
---|
120 | $r |
---|
121 | $r().attribute()['key']() |
---|
122 | |
---|
123 | If origList is a list of name/value pairs (2-tuples or 2-lists), you may |
---|
124 | prefer to do this: |
---|
125 | |
---|
126 | for (key, value), a, b in mr.page(20, 40): |
---|
127 | print key, "=>", value |
---|
128 | |
---|
129 | #for ($key, $value), $a, $b in $mr.page(20, $start) |
---|
130 | $key => $value |
---|
131 | #end for |
---|
132 | |
---|
133 | * * * * * |
---|
134 | STATISTICS METHODS AND FIELD VALUES |
---|
135 | |
---|
136 | Certain methods below have an optional argument 'field'. If specified, |
---|
137 | MondoReport will look up that field in each affected record and use its value |
---|
138 | in the calculation. MondoReport uses Cheetah's NameMapper if available, |
---|
139 | otherwise it uses a minimal NameMapper substitute that looks for an attribute |
---|
140 | or dictionary key called "field". You'll get an exception if any record is a |
---|
141 | type without attributes or keys, or if one or more records is missing that |
---|
142 | attribute/key. |
---|
143 | |
---|
144 | If 'field' is None, MondoReport will use the entire record in its |
---|
145 | calculation. This makes sense mainly if the records are a numeric type. |
---|
146 | |
---|
147 | All statistics methods filter out None values from their calculations, and |
---|
148 | reduce the number of records accordingly. Most filter out non-numeric fields |
---|
149 | (or records). Some raise NegativeError if a numeric field (or record) is |
---|
150 | negative. |
---|
151 | |
---|
152 | |
---|
153 | * * * * * |
---|
154 | BatchRecord METHODS |
---|
155 | |
---|
156 | The 'a' and 'b' objects of MondoReport.page() and MondoReport.all() provide |
---|
157 | these methods. |
---|
158 | |
---|
159 | .index() |
---|
160 | |
---|
161 | The current subscript. For 'a', this is the true subscript into origList. |
---|
162 | For 'b', this is relative to the current page, so the first record will be 0. |
---|
163 | Hint: In Cheetah, use autocalling to skip the parentheses: '$b.index'. |
---|
164 | |
---|
165 | .number() |
---|
166 | |
---|
167 | The record's position starting from 1. This is always '.index() + 1'. |
---|
168 | |
---|
169 | .Letter() |
---|
170 | |
---|
171 | The letter ("A", "B", "C") corresponding to .number(). Undefined if .number() |
---|
172 | > 26. The current implementation just adds the offset to 'a' and returns |
---|
173 | whatever character it happens to be. |
---|
174 | |
---|
175 | To make a less dumb implementation (e.g., "Z, AA, BB" or "Z, A1, B1"): |
---|
176 | 1) Subclass BatchRecord and override the .Letter method. |
---|
177 | 2) Subclass MondoReport and set the class variable .BatchRecordClass to your |
---|
178 | new improved class. |
---|
179 | |
---|
180 | .letter() |
---|
181 | |
---|
182 | Same but lower case. |
---|
183 | |
---|
184 | .Roman() |
---|
185 | |
---|
186 | The Roman numeral corresponding to .number(). |
---|
187 | |
---|
188 | .roman() |
---|
189 | |
---|
190 | Same but lower case. |
---|
191 | |
---|
192 | .even() |
---|
193 | |
---|
194 | True if .number() is even. |
---|
195 | |
---|
196 | .odd() |
---|
197 | |
---|
198 | True if .number() is odd. |
---|
199 | |
---|
200 | .even_i() |
---|
201 | |
---|
202 | True if .index() is even. |
---|
203 | |
---|
204 | .odd_i() |
---|
205 | |
---|
206 | True if .index() is odd. |
---|
207 | |
---|
208 | .length() |
---|
209 | |
---|
210 | For 'a', number of records in origList. For 'b', number of records on this |
---|
211 | page. |
---|
212 | |
---|
213 | .item() |
---|
214 | |
---|
215 | The record itself. You don't need this in the normal case since it's the same |
---|
216 | as 'r', but it's useful for previous/next batches. |
---|
217 | |
---|
218 | .size() |
---|
219 | |
---|
220 | The 'size' argument used when this BatchRecord was created. |
---|
221 | 'a.size() == b.size()'. |
---|
222 | |
---|
223 | .first() |
---|
224 | |
---|
225 | True if this is the first record. |
---|
226 | |
---|
227 | .last() |
---|
228 | |
---|
229 | True if this is the last record. |
---|
230 | |
---|
231 | .firstValue(field=None) |
---|
232 | |
---|
233 | True if there is no previous record, or if the previous field/record has a |
---|
234 | different value. Used for to print section headers. For instance, if you |
---|
235 | are printing addresses by country, this will be true at the first occurrance |
---|
236 | of each country. Or for indexes, you can have a non-printing field showing |
---|
237 | which letter of the alphablet this entry starts with, and then print a "B" |
---|
238 | header before printing the first record starting with "B". |
---|
239 | |
---|
240 | .lastValue(field=None) |
---|
241 | |
---|
242 | True if this is the last record containing the current value in the |
---|
243 | field/record. |
---|
244 | |
---|
245 | .percentOfTotal(field=None, suffix="%", default="N/A", decimals=2) |
---|
246 | |
---|
247 | Returns the percent that the current field/record is of all fields/records. |
---|
248 | If 'suffix' is None, returns a number; otherwise it returns a string with |
---|
249 | 'suffix' suffixed. If the current value is non-numeric, returns 'default' |
---|
250 | instead (without 'suffix'). 'decimals' tells the number of decimal places to |
---|
251 | return; if 0, there will be no decimal point. |
---|
252 | |
---|
253 | .prev() |
---|
254 | |
---|
255 | Returns a PrevNextBatch instance for the previous page. If there is no |
---|
256 | previous page, returns None. [Not implemented yet.] |
---|
257 | |
---|
258 | .next() |
---|
259 | |
---|
260 | Returns a PrevNextBatch instance for the next page. If there is no next page, |
---|
261 | returns None. [Not implemented yet.] |
---|
262 | |
---|
263 | .prevPages() |
---|
264 | |
---|
265 | Returns a list of PrevNextPage instances for every previous page, or [] if no |
---|
266 | previous pages. [Not implemented yet.] |
---|
267 | |
---|
268 | .nextPages() |
---|
269 | |
---|
270 | Returns a list of PrevNextPage instances for every next page, or [] if no next |
---|
271 | pages. [Not implemented yet.] |
---|
272 | |
---|
273 | .query(start=None, label=None, attribName="start", attribs=[]) |
---|
274 | |
---|
275 | [Not implemented yet.] |
---|
276 | |
---|
277 | With no arguments, returns the HTML query string with start value removed (so |
---|
278 | you can append a new start value in your hyperlink). The query string is taken |
---|
279 | from the 'QUERY_STRING' environmental variable, or "" if missing. (This is |
---|
280 | Webware compatible.) |
---|
281 | |
---|
282 | With 'start' (an integer >= 0), returns the query string with an updated start |
---|
283 | value, normally for the next or previous batch. |
---|
284 | |
---|
285 | With 'label' (a string), returns a complete HTML hyperlink: |
---|
286 | '<A HREF="?new_query_string">label</A>'. You'll get a TypeError if you specify |
---|
287 | 'label' but not 'start'. |
---|
288 | |
---|
289 | With 'attribName' (a string), uses this attribute name rather than "start". |
---|
290 | Useful if you have another CGI parameter "start" that's used for something |
---|
291 | else. |
---|
292 | |
---|
293 | With 'attribs' (a dictionary), adds these attributes to the hyperlink. |
---|
294 | For instance, 'attribs={"target": "_blank"}'. Ignored unless 'label' is |
---|
295 | specified too. |
---|
296 | |
---|
297 | This method assumes the start parameter is a GET variable, not a POST variable. |
---|
298 | |
---|
299 | .summary() |
---|
300 | |
---|
301 | Returns a Summary instance. 'a.summary()' refers to all records in the |
---|
302 | origList, so it's the same as MondoReport.summary(). 'b.summary()' refers only |
---|
303 | to the records on the current page. [Not implemented yet.] |
---|
304 | |
---|
305 | * * * * * |
---|
306 | PrevNextPage INSTANCES |
---|
307 | |
---|
308 | [Not implemented yet.] |
---|
309 | |
---|
310 | PrevNextPage instances have the following methods: |
---|
311 | |
---|
312 | .start() |
---|
313 | |
---|
314 | The index (true index of origList) that that page starts at. You may also use |
---|
315 | '.start().index()', '.start().number()', etc. Also |
---|
316 | '.start().item(field=None)'. (Oh, so *that*'s what .item is for!) |
---|
317 | |
---|
318 | .end() |
---|
319 | |
---|
320 | The index (true index of origList) that that page ends at. You may also use |
---|
321 | '.end().index()', '.end().number()', etc. Also |
---|
322 | '.end().item(field=None)'. |
---|
323 | |
---|
324 | .length() |
---|
325 | |
---|
326 | Number of records on that page. |
---|
327 | |
---|
328 | .query(label=None, attribName="start", attribs={}, before="", after="") |
---|
329 | |
---|
330 | [Not implemented yet.] |
---|
331 | |
---|
332 | Similar to 'a.query()' and 'b.query()', but automatically calculates the start |
---|
333 | value for the appropriate page. |
---|
334 | |
---|
335 | For fancy HTML formatting, 'before' is prepended to the returned text and |
---|
336 | 'after' is appended. (There was an argument 'else_' for if there is no such |
---|
337 | batch, but it was removed because you can't even get to this method at all in |
---|
338 | that case.) |
---|
339 | |
---|
340 | * * * * * * |
---|
341 | SUMMARY STATISTICS |
---|
342 | |
---|
343 | These methods are supported by the Summary instances returned by |
---|
344 | MondoReport.Summary(): |
---|
345 | |
---|
346 | .sum(field=None) |
---|
347 | |
---|
348 | Sum of all numeric values in a field, or sum of all records. |
---|
349 | |
---|
350 | .total(field=None) |
---|
351 | |
---|
352 | Same. |
---|
353 | |
---|
354 | .count(field=None) |
---|
355 | |
---|
356 | Number of fields/records with non-None values. |
---|
357 | |
---|
358 | .min(field=None) |
---|
359 | |
---|
360 | Minimum value in that field/record. Ignores None values. |
---|
361 | |
---|
362 | .max(field=None) |
---|
363 | |
---|
364 | Maximum value in that field/record. Ignores None values. |
---|
365 | |
---|
366 | .mean(field=None) |
---|
367 | |
---|
368 | The mean (=average) of all numeric values in that field/record. |
---|
369 | |
---|
370 | .average(field=None) |
---|
371 | |
---|
372 | Same. |
---|
373 | |
---|
374 | .median(field=None) |
---|
375 | |
---|
376 | The median of all numeric values in that field/record. This is done by sorting |
---|
377 | the values and taking the middle value. |
---|
378 | |
---|
379 | .variance(field=None), .variance_n(field=None) |
---|
380 | .standardDeviation(field=None), .standardDeviation_n(field=None) |
---|
381 | |
---|
382 | [Not implemented yet.] |
---|
383 | |
---|
384 | |
---|
385 | * * * * * |
---|
386 | To run the regression tests (requires unittest.py, which is standard with |
---|
387 | Python 2.2), run MondoReportTest.py from the command line. The regression test |
---|
388 | double as usage examples. |
---|
389 | |
---|
390 | |
---|
391 | # vim: shiftwidth=4 tabstop=4 expandtab textwidth=79 |
---|