Ticket #4394: fraction.diff

File fraction.diff, 5.6 KB (added by max.rabkin@…, 17 years ago)

Adds fractions to humanize

  • django/contrib/humanize/templatetags/humanize.py

     
    6767        return value
    6868    return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
    6969register.filter(apnumber)
     70
     71def _to_frac(x, maxdenom=10):
     72    """
     73    Convert x to a common fraction.
     74   
     75    Chooses the closest fraction to x with denominator <= maxdenom.
     76    If x is closest to an integer, return that integer; otherwise,
     77    return an (integer, numerator, denominator) tuple.
     78    """
     79   
     80    assert x >= 0, "_to_frac only works on positive numbers."
     81   
     82    intpart = int(x)
     83    x -= intpart
     84   
     85    bestfrac = 0,1
     86    mindiff = x
     87   
     88    for denom in range(1,maxdenom+1):
     89        # for each denominator, there are two numerators to consider:
     90        # the one below x and the one above x
     91        for num in (int(x*denom), int(x*denom+1)):
     92            diff = abs(float(num)/denom - x)
     93           
     94            # compare using '<' rather than '<=' to ensure that the
     95            # fraction with the smallest denominator is preferred
     96            if diff < mindiff:
     97                bestfrac = num, denom
     98                mindiff = diff
     99   
     100    if bestfrac[0] == 0:
     101        return intpart
     102    elif mindiff >= 1-x:
     103        return intpart+1
     104    else:
     105        return intpart, bestfrac[0], bestfrac[1]
     106
     107def html_fraction (number, maxdenom=10):
     108    """
     109    Convert a float to a common fraction (or an integer if it is closer).
     110   
     111    If the output is a fraction, the fraction part is wrapped in a span
     112    with class "fraction" to enable styling of fractions.
     113    """
     114   
     115    number = float(number)
     116    frac = _to_frac(abs(number), maxdenom)
     117   
     118    if type(frac) == int:
     119        string = str(frac)
     120    else:
     121        intpart, numerator, denominator = frac
     122        if intpart == 0:
     123            string = '<span class="fraction"><sup>%i</sup>/<sub>%i</sub></span>' % frac[1:]
     124        else:
     125            string = '%i<span class="fraction"><sup>%i</sup>/<sub>%i</sub></span>' % frac
     126   
     127    if number < 0:
     128        return '-'+string
     129    else:
     130        return string
     131register.filter(html_fraction)
     132
     133def text_fraction (number, maxdenom=10):
     134    """Convert a float to a common fraction (or integer if it is closer)."""
     135   
     136    number = float(number)
     137    frac = _to_frac(abs(number), maxdenom)
     138   
     139    if type(frac) == int:
     140        string = str(frac)
     141    else:
     142        intpart, numerator, denominator = frac
     143        if intpart == 0:
     144            string = '%i/%i' % frac[1:]
     145        else:
     146            string = '%i %i/%i' % frac
     147   
     148    if number < 0:
     149        return '-'+string
     150    else:
     151        return string
     152register.filter(text_fraction)
  • tests/regressiontests/humanize/tests.py

     
    4848                       'seven', 'eight', 'nine', '10')
    4949
    5050        self.humanize_tester(test_list, result_list, 'apnumber')
     51   
     52    def test_html_fraction(self):
     53        test_list = ['0', '0.5', '1.667', '-0.25', '-1.75', '9.99']
     54        result_list = ['0',
     55                       '<span class="fraction"><sup>1</sup>/<sub>2</sub></span>',
     56                       '1<span class="fraction"><sup>2</sup>/<sub>3</sub></span>',
     57                       '-<span class="fraction"><sup>1</sup>/<sub>4</sub></span>',
     58                       '-1<span class="fraction"><sup>3</sup>/<sub>4</sub></span>',
     59                       '10']
     60                       
     61        test_list = test_list + [float(num) for num in test_list]
     62        result_list *= 2
     63       
     64        self.humanize_tester(test_list, result_list, 'html_fraction')
     65   
     66    def test_text_fraction(self):
     67        test_list = ['0', '0.5', '1.667', '-0.25', '-1.75', '9.99']
     68        result_list = ['0',
     69                       '1/2',
     70                       '1 2/3',
     71                       '-1/4',
     72                       '-1 3/4',
     73                       '10']
     74       
     75        test_list = test_list + [float(num) for num in test_list]
     76        result_list *= 2
     77       
     78        self.humanize_tester(test_list, result_list, 'text_fraction')
    5179
    5280if __name__ == '__main__':
    5381    unittest.main()
  • AUTHORS

     
    188188    plisk
    189189    Daniel Poelzleithner <http://poelzi.org/>
    190190    polpak@yahoo.com
     191    Max Rabkin <max.rabkin@gmail.com>
    191192    J. Rademaker
    192193    Michael Radziej <mir@noris.de>
    193194    Ramiro Morales <rm0@gmx.net>
  • docs/add_ons.txt

     
    135135
    136136You can pass in either an integer or a string representation of an integer.
    137137
     138text_fraction
     139-------------
     140
     141Converts a decimal fraction to a common fraction.
     142
     143Examples:
     144
     145    * ``0.5`` becomes ``'1/2'``
     146    * ``9.99`` becomes ``'10'``
     147    * ``-1.25`` becomes ``'-1 1/4'``
     148
     149You can pass in either an integer or a string representation of an integer.
     150
     151html_fraction
     152-------------
     153
     154Like text_fraction, but generates HTML so the fraction is rendered nicely. The
     155fractional part of the output is wrapped in ``<span class="fraction">`` tags.
     156
    138157flatpages
    139158=========
    140159
Back to Top