Ticket #4394: fraction2.diff

File fraction2.diff, 6.0 KB (added by max.rabkin@…, 12 years ago)

Fractions for humanize, using Unicode characters where possible

  • 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
     107
     108_frac_entities = {(1,4): "&frac14;", (1,2): "&frac12;", (3,4): "&frac34;",
     109            (1, 3): "&#x2153;", (2, 3): "&#x2154;", (1, 5): "&#x2155;",
     110            (2, 5): "&#x2156;", (3, 5): "&#x2157;", (4, 5): "&#x2158;",
     111            (1, 6): "&#x2159;", (5, 6): "&#x215A;", (1, 8): "&#x215B;",
     112            (3, 8): "&#x215C;", (5, 8): "&#x215D;", (7, 8): "&#x215E;"}
     113
     114def html_fraction (number, maxdenom=10, useUnicode=True):
     115    """
     116    Convert a float to a common fraction (or an integer if it is closer).
     117   
     118    If the output is a fraction, the fraction part is wrapped in a span
     119    with class "fraction" to enable styling of fractions.
     120   
     121    If useUnicode is true, unicode entities will be used where available.
     122    """
     123   
     124    number = float(number)
     125    frac = _to_frac(abs(number), maxdenom)
     126   
     127    if type(frac) == int:
     128        string = str(frac)
     129    else:
     130        intpart, numerator, denominator = frac
     131        if useUnicode and (numerator, denominator) in _frac_entities:
     132            fracpart = _frac_entities[(numerator, denominator)]
     133        else:
     134            fracpart = (('<span class="fraction"><sup>%i</sup>' +
     135                        '&#x2044;<sub>%i</sub></span>') %
     136                        (numerator,denominator))
     137        if intpart == 0:
     138            string = fracpart
     139        else:
     140            string = str(intpart) + fracpart
     141   
     142    if number < 0:
     143        return '-'+string
     144    else:
     145        return string
     146register.filter(html_fraction)
     147
     148def text_fraction (number, maxdenom=10):
     149    """Convert a float to a common fraction (or integer if it is closer)."""
     150   
     151    number = float(number)
     152    frac = _to_frac(abs(number), maxdenom)
     153   
     154    if type(frac) == int:
     155        string = str(frac)
     156    else:
     157        intpart, numerator, denominator = frac
     158        if intpart == 0:
     159            string = '%i/%i' % frac[1:]
     160        else:
     161            string = '%i %i/%i' % frac
     162   
     163    if number < 0:
     164        return '-'+string
     165    else:
     166        return string
     167register.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', '-3.2857']
     54        result_list = ['0', '&frac12;', '1&#x2154;',
     55                       '-&frac14;', '-1&frac34;', '10',
     56                       '-3<span class="fraction"><sup>2</sup>&#x2044;<sub>7</sub></span>']
     57                       
     58        test_list = test_list + [float(num) for num in test_list]
     59        result_list *= 2
     60       
     61        self.humanize_tester(test_list, result_list, 'html_fraction')
     62   
     63    def test_text_fraction(self):
     64        test_list = ['0', '0.5', '1.667', '-0.25', '-1.75', '9.99']
     65        result_list = ['0', '1/2', '1 2/3', '-1/4', '-1 3/4', '10']
     66       
     67        test_list = test_list + [float(num) for num in test_list]
     68        result_list *= 2
     69       
     70        self.humanize_tester(test_list, result_list, 'text_fraction')
    5171
    5272if __name__ == '__main__':
    5373    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