"Fossies" - the Fresh Open Source Software Archive

Member "cheetah3-3.2.6.post2/Cheetah/Tools/MondoReport.py" (20 Apr 2021, 13162 Bytes) of package /linux/www/cheetah3-3.2.6.post2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "MondoReport.py" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3-3.1.0_vs_3-3.2.0.

    1 """
    2 @@TR: This code is pretty much unsupported.
    3 
    4 MondoReport.py -- Batching module for Python and Cheetah.
    5 
    6 Version 2001-Nov-18.  Doesn't do much practical yet, but the companion
    7 testMondoReport.py passes all its tests.
    8 -Mike Orr (Iron)
    9 
   10 TODO: BatchRecord.prev/next/prev_batches/next_batches/query, prev.query,
   11 next.query.
   12 
   13 How about Report: .page(), .all(), .summary()?  Or PageBreaker.
   14 """
   15 import operator
   16 try:
   17     from functools import reduce
   18 except ImportError:
   19     # If functools doesn't exist, we must be on an old
   20     # enough version that has reduce() in builtins
   21     pass
   22 
   23 try:
   24     from Cheetah.NameMapper import valueForKey as lookup_func
   25 except ImportError:
   26     def lookup_func(obj, name):
   27         if hasattr(obj, name):
   28             return getattr(obj, name)
   29         else:
   30             return obj[name]  # Raises KeyError.
   31 
   32 ########################################
   33 # PUBLIC GENERIC FUNCTIONS
   34 
   35 
   36 class NegativeError(ValueError):
   37     pass
   38 
   39 
   40 def isNumeric(v):
   41     return isinstance(v, (int, float))
   42 
   43 
   44 def isNonNegative(v):
   45     ret = isNumeric(v)
   46     if ret and v < 0:
   47         raise NegativeError(v)
   48 
   49 
   50 def isNotNone(v):
   51     return v is not None
   52 
   53 
   54 def Roman(n):
   55     n = int(n)  # Raises TypeError.
   56     if n < 1:
   57         raise ValueError("roman numeral for zero or negative undefined: " + n)
   58     roman = ''
   59     while n >= 1000:
   60         n = n - 1000
   61         roman = roman + 'M'
   62     while n >= 500:
   63         n = n - 500
   64         roman = roman + 'D'
   65     while n >= 100:
   66         n = n - 100
   67         roman = roman + 'C'
   68     while n >= 50:
   69         n = n - 50
   70         roman = roman + 'L'
   71     while n >= 10:
   72         n = n - 10
   73         roman = roman + 'X'
   74     while n >= 5:
   75         n = n - 5
   76         roman = roman + 'V'
   77     while n < 5 and n >= 1:
   78         n = n - 1
   79         roman = roman + 'I'
   80     roman = roman.replace('DCCCC', 'CM')
   81     roman = roman.replace('CCCC', 'CD')
   82     roman = roman.replace('LXXXX', 'XC')
   83     roman = roman.replace('XXXX', 'XL')
   84     roman = roman.replace('VIIII', 'IX')
   85     roman = roman.replace('IIII', 'IV')
   86     return roman
   87 
   88 
   89 def sum(lis):
   90     return reduce(operator.add, lis, 0)
   91 
   92 
   93 def mean(lis):
   94     """Always returns a floating-point number.
   95     """
   96     lis_len = len(lis)
   97     if lis_len == 0:
   98         return 0.00  # Avoid ZeroDivisionError (not raised for floats anyway)
   99     total = float(sum(lis))
  100     return total / lis_len
  101 
  102 
  103 def median(lis):
  104     lis = sorted(lis[:])
  105     return lis[int(len(lis)/2)]  # noqa: E226,E501 missing whitespace around operator
  106 
  107 
  108 def variance(lis):
  109     raise NotImplementedError()
  110 
  111 
  112 def variance_n(lis):
  113     raise NotImplementedError()
  114 
  115 
  116 def standardDeviation(lis):
  117     raise NotImplementedError()
  118 
  119 
  120 def standardDeviation_n(lis):
  121     raise NotImplementedError()
  122 
  123 
  124 class IndexFormats:
  125     """Eight ways to display a subscript index.
  126        ("Fifty ways to leave your lover....")
  127     """
  128     def __init__(self, index, item=None):
  129         self._index = index
  130         self._number = index + 1
  131         self._item = item
  132 
  133     def index(self):
  134         return self._index
  135 
  136     __call__ = index
  137 
  138     def number(self):
  139         return self._number
  140 
  141     def even(self):
  142         return self._number % 2 == 0
  143 
  144     def odd(self):
  145         return not self.even()
  146 
  147     def even_i(self):
  148         return self._index % 2 == 0
  149 
  150     def odd_i(self):
  151         return not self.even_i()
  152 
  153     def letter(self):
  154         return self.Letter().lower()
  155 
  156     def Letter(self):
  157         n = ord('A') + self._index
  158         return chr(n)
  159 
  160     def roman(self):
  161         return self.Roman().lower()
  162 
  163     def Roman(self):
  164         return Roman(self._number)
  165 
  166     def item(self):
  167         return self._item
  168 
  169 
  170 ########################################
  171 # PRIVATE CLASSES
  172 
  173 
  174 class ValuesGetterMixin:
  175     def __init__(self, origList):
  176         self._origList = origList
  177 
  178     def _getValues(self, field=None, criteria=None):
  179         if field:
  180             ret = [lookup_func(elm, field) for elm in self._origList]
  181         else:
  182             ret = self._origList
  183         if criteria:
  184             ret = list(filter(criteria, ret))
  185         return ret
  186 
  187 
  188 class RecordStats(IndexFormats, ValuesGetterMixin):
  189     """The statistics that depend on the current record.
  190     """
  191     def __init__(self, origList, index):
  192         record = origList[index]  # Raises IndexError.
  193         IndexFormats.__init__(self, index, record)
  194         ValuesGetterMixin.__init__(self, origList)
  195 
  196     def length(self):
  197         return len(self._origList)
  198 
  199     def first(self):
  200         return self._index == 0
  201 
  202     def last(self):
  203         return self._index >= len(self._origList) - 1
  204 
  205     def _firstOrLastValue(self, field, currentIndex, otherIndex):
  206         currentValue = self._origList[currentIndex]  # Raises IndexError.
  207         try:
  208             otherValue = self._origList[otherIndex]
  209         except IndexError:
  210             return True
  211         if field:
  212             currentValue = lookup_func(currentValue, field)
  213             otherValue = lookup_func(otherValue, field)
  214         return currentValue != otherValue
  215 
  216     def firstValue(self, field=None):
  217         return self._firstOrLastValue(field, self._index, self._index - 1)
  218 
  219     def lastValue(self, field=None):
  220         return self._firstOrLastValue(field, self._index, self._index + 1)
  221 
  222     # firstPage and lastPage not implemented.  Needed?
  223 
  224     def percentOfTotal(self, field=None, suffix='%',
  225                        default='N/A', decimals=2):
  226         rec = self._origList[self._index]
  227         if field:
  228             val = lookup_func(rec, field)
  229         else:
  230             val = rec
  231         try:
  232             lis = self._getValues(field, isNumeric)
  233         except NegativeError:
  234             return default
  235         total = sum(lis)
  236         if total == 0.00:  # Avoid ZeroDivisionError.
  237             return default
  238         val = float(val)
  239         try:
  240             percent = (val / total) * 100
  241         except ZeroDivisionError:
  242             return default
  243         if decimals == 0:
  244             percent = int(percent)
  245         else:
  246             percent = round(percent, decimals)
  247         if suffix:
  248             return str(percent) + suffix  # String.
  249         else:
  250             return percent  # Numeric.
  251 
  252     def __call__(self):  # Overrides IndexFormats.__call__
  253         """This instance is not callable, so we override the super method.
  254         """
  255         raise NotImplementedError()
  256 
  257     def prev(self):
  258         if self._index == 0:
  259             return None
  260         else:
  261             length = self.length()
  262             start = self._index - length
  263             return PrevNextPage(self._origList, length, start)
  264 
  265     def next(self):
  266         if self._index + self.length() == self.length():
  267             return None
  268         else:
  269             length = self.length()
  270             start = self._index + length
  271             return PrevNextPage(self._origList, length, start)
  272 
  273     def prevPages(self):
  274         raise NotImplementedError()
  275 
  276     def nextPages(self):
  277         raise NotImplementedError()
  278 
  279     prev_batches = prevPages
  280     next_batches = nextPages
  281 
  282     def summary(self):
  283         raise NotImplementedError()
  284 
  285     def _prevNextHelper(self, start, end, size, orphan, sequence):
  286         """Copied from Zope's DT_InSV.py's "opt" function.
  287         """
  288         if size < 1:
  289             if start > 0 and end > 0 and end >= start:
  290                 size = end + 1 - start
  291             else:
  292                 size = 7
  293 
  294         if start > 0:
  295 
  296             try:
  297                 sequence[start - 1]
  298             except Exception:
  299                 start = len(sequence)
  300             # if start > l: start=l
  301 
  302             if end > 0:
  303                 if end < start:
  304                     end = start
  305             else:
  306                 end = start + size - 1
  307                 try:
  308                     sequence[end + orphan - 1]
  309                 except Exception:
  310                     end = len(sequence)
  311                 # if l - end < orphan: end=l
  312         elif end > 0:
  313             try:
  314                 sequence[end - 1]
  315             except Exception:
  316                 end = len(sequence)
  317             # if end > l: end=l
  318             start = end + 1 - size
  319             if start - 1 < orphan:
  320                 start = 1
  321         else:
  322             start = 1
  323             end = start + size - 1
  324             try:
  325                 sequence[end + orphan - 1]
  326             except Exception:
  327                 end = len(sequence)
  328             # if l - end < orphan: end=l
  329         return start, end, size
  330 
  331 
  332 class Summary(ValuesGetterMixin):
  333     """The summary statistics, that don't depend on the current record.
  334     """
  335     def __init__(self, origList):
  336         ValuesGetterMixin.__init__(self, origList)
  337 
  338     def sum(self, field=None):
  339         lis = self._getValues(field, isNumeric)
  340         return sum(lis)
  341 
  342     total = sum
  343 
  344     def count(self, field=None):
  345         lis = self._getValues(field, isNotNone)
  346         return len(lis)
  347 
  348     def min(self, field=None):
  349         lis = self._getValues(field, isNotNone)
  350         return min(lis)  # Python builtin function min.
  351 
  352     def max(self, field=None):
  353         lis = self._getValues(field, isNotNone)
  354         return max(lis)  # Python builtin function max.
  355 
  356     def mean(self, field=None):
  357         """Always returns a floating point number.
  358         """
  359         lis = self._getValues(field, isNumeric)
  360         return mean(lis)
  361 
  362     average = mean
  363 
  364     def median(self, field=None):
  365         lis = self._getValues(field, isNumeric)
  366         return median(lis)
  367 
  368     def variance(self, field=None):
  369         raise NotImplementedError()
  370 
  371     def variance_n(self, field=None):
  372         raise NotImplementedError()
  373 
  374     def standardDeviation(self, field=None):
  375         raise NotImplementedError()
  376 
  377     def standardDeviation_n(self, field=None):
  378         raise NotImplementedError()
  379 
  380 
  381 class PrevNextPage:
  382     def __init__(self, origList, size, start):
  383         end = start + size
  384         self.start = IndexFormats(start, origList[start])
  385         self.end = IndexFormats(end, origList[end])
  386         self.length = size
  387 
  388 
  389 ########################################
  390 # MAIN PUBLIC CLASS
  391 class MondoReport:
  392     _RecordStatsClass = RecordStats
  393     _SummaryClass = Summary
  394 
  395     def __init__(self, origlist):
  396         self._origList = origlist
  397 
  398     def page(self, size, start, overlap=0, orphan=0):
  399         """Returns list of ($r, $a, $b)
  400         """
  401         if overlap != 0:
  402             raise NotImplementedError("non-zero overlap")
  403         if orphan != 0:
  404             raise NotImplementedError("non-zero orphan")
  405         origList = self._origList
  406         start = max(0, start)
  407         end = min(start + size, len(self._origList))
  408         mySlice = origList[start:end]
  409         ret = []
  410         for rel in range(size):
  411             abs_ = start + rel
  412             r = mySlice[rel]
  413             a = self._RecordStatsClass(origList, abs_)
  414             b = self._RecordStatsClass(mySlice, rel)
  415             tup = r, a, b
  416             ret.append(tup)
  417         return ret
  418 
  419     batch = page
  420 
  421     def all(self):
  422         origList_len = len(self._origList)
  423         return self.page(origList_len, 0, 0, 0)
  424 
  425     def summary(self):
  426         return self._SummaryClass(self._origList)
  427 
  428 
  429 """
  430 **********************************
  431     Return a pageful of records from a sequence, with statistics.
  432 
  433        in : origlist, list or tuple.  The entire set of records.  This is
  434               usually a list of objects or a list of dictionaries.
  435             page, int >= 0.  Which page to display.
  436             size, int >= 1.  How many records per page.
  437             widow, int >=0.  Not implemented.
  438             orphan, int >=0.  Not implemented.
  439             base, int >=0.  Number of first page (usually 0 or 1).
  440 
  441        out: list of (o, b) pairs.  The records for the current page.  'o' is
  442               the original element from 'origlist' unchanged.  'b' is a Batch
  443               object containing meta-info about 'o'.
  444        exc: IndexError if 'page' or 'size' is < 1.  If 'origlist' is empty or
  445               'page' is too high, it returns an empty list rather than raising
  446               an error.
  447 
  448         origlist_len = len(origlist)
  449         start = (page + base) * size
  450         end = min(start + size, origlist_len)
  451         ret = []
  452         # widow, orphan calculation: adjust 'start' and 'end' up and down,
  453         # set 'widow', 'orphan', 'first_nonwidow',
  454         # 'first_nonorphan' attributes.
  455         for i in range(start, end):
  456             o = origlist[i]
  457             b = Batch(origlist, size, i)
  458             tup = o, b
  459             ret.append(tup)
  460         return ret
  461 
  462     def prev(self):
  463         # return a PrevNextPage or None
  464 
  465     def next(self):
  466         # return a PrevNextPage or None
  467 
  468     def prev_batches(self):
  469         # return a list of SimpleBatch for the previous batches
  470 
  471     def next_batches(self):
  472         # return a list of SimpleBatch for the next batches
  473 
  474 ########## PUBLIC MIXIN CLASS FOR CHEETAH TEMPLATES ##############
  475 class MondoReportMixin:
  476     def batch(self, origList, size=None, start=0, overlap=0, orphan=0):
  477         bat = MondoReport(origList)
  478         return bat.batch(size, start, overlap, orphan)
  479     def batchstats(self, origList):
  480         bat = MondoReport(origList)
  481         return bat.stats()
  482 """
  483 
  484 # vim: shiftwidth=4 tabstop=4 expandtab textwidth=79