Index: django/contrib/humanize/templatetags/humanize.py
===================================================================
--- django/contrib/humanize/templatetags/humanize.py (revision 5357)
+++ django/contrib/humanize/templatetags/humanize.py (working copy)
@@ -67,3 +67,101 @@
return value
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
register.filter(apnumber)
+
+def _to_frac(x, maxdenom=10):
+ """
+ Convert x to a common fraction.
+
+ Chooses the closest fraction to x with denominator <= maxdenom.
+ If x is closest to an integer, return that integer; otherwise,
+ return an (integer, numerator, denominator) tuple.
+ """
+
+ assert x >= 0, "_to_frac only works on positive numbers."
+
+ intpart = int(x)
+ x -= intpart
+
+ bestfrac = 0,1
+ mindiff = x
+
+ for denom in range(1,maxdenom+1):
+ # for each denominator, there are two numerators to consider:
+ # the one below x and the one above x
+ for num in (int(x*denom), int(x*denom+1)):
+ diff = abs(float(num)/denom - x)
+
+ # compare using '<' rather than '<=' to ensure that the
+ # fraction with the smallest denominator is preferred
+ if diff < mindiff:
+ bestfrac = num, denom
+ mindiff = diff
+
+ if bestfrac[0] == 0:
+ return intpart
+ elif mindiff >= 1-x:
+ return intpart+1
+ else:
+ return intpart, bestfrac[0], bestfrac[1]
+
+
+_frac_entities = {(1,4): "¼", (1,2): "½", (3,4): "¾",
+ (1, 3): "⅓", (2, 3): "⅔", (1, 5): "⅕",
+ (2, 5): "⅖", (3, 5): "⅗", (4, 5): "⅘",
+ (1, 6): "⅙", (5, 6): "⅚", (1, 8): "⅛",
+ (3, 8): "⅜", (5, 8): "⅝", (7, 8): "⅞"}
+
+def html_fraction (number, maxdenom=10, useUnicode=True):
+ """
+ Convert a float to a common fraction (or an integer if it is closer).
+
+ If the output is a fraction, the fraction part is wrapped in a span
+ with class "fraction" to enable styling of fractions.
+
+ If useUnicode is true, unicode entities will be used where available.
+ """
+
+ number = float(number)
+ frac = _to_frac(abs(number), maxdenom)
+
+ if type(frac) == int:
+ string = str(frac)
+ else:
+ intpart, numerator, denominator = frac
+ if useUnicode and (numerator, denominator) in _frac_entities:
+ fracpart = _frac_entities[(numerator, denominator)]
+ else:
+ fracpart = (('%i' +
+ '⁄%i') %
+ (numerator,denominator))
+ if intpart == 0:
+ string = fracpart
+ else:
+ string = str(intpart) + fracpart
+
+ if number < 0:
+ return '-'+string
+ else:
+ return string
+register.filter(html_fraction)
+
+def text_fraction (number, maxdenom=10):
+ """Convert a float to a common fraction (or integer if it is closer)."""
+
+ number = float(number)
+ frac = _to_frac(abs(number), maxdenom)
+
+ if type(frac) == int:
+ string = str(frac)
+ else:
+ intpart, numerator, denominator = frac
+ if intpart == 0:
+ string = '%i/%i' % frac[1:]
+ else:
+ string = '%i %i/%i' % frac
+
+ if number < 0:
+ return '-'+string
+ else:
+ return string
+register.filter(text_fraction)
Index: tests/regressiontests/humanize/tests.py
===================================================================
--- tests/regressiontests/humanize/tests.py (revision 5357)
+++ tests/regressiontests/humanize/tests.py (working copy)
@@ -48,6 +48,26 @@
'seven', 'eight', 'nine', '10')
self.humanize_tester(test_list, result_list, 'apnumber')
+
+ def test_html_fraction(self):
+ test_list = ['0', '0.5', '1.667', '-0.25', '-1.75', '9.99', '-3.2857']
+ result_list = ['0', '½', '1⅔',
+ '-¼', '-1¾', '10',
+ '-32⁄7']
+
+ test_list = test_list + [float(num) for num in test_list]
+ result_list *= 2
+
+ self.humanize_tester(test_list, result_list, 'html_fraction')
+
+ def test_text_fraction(self):
+ test_list = ['0', '0.5', '1.667', '-0.25', '-1.75', '9.99']
+ result_list = ['0', '1/2', '1 2/3', '-1/4', '-1 3/4', '10']
+
+ test_list = test_list + [float(num) for num in test_list]
+ result_list *= 2
+
+ self.humanize_tester(test_list, result_list, 'text_fraction')
if __name__ == '__main__':
unittest.main()
Index: AUTHORS
===================================================================
--- AUTHORS (revision 5357)
+++ AUTHORS (working copy)
@@ -188,6 +188,7 @@
plisk
Daniel Poelzleithner
polpak@yahoo.com
+ Max Rabkin
J. Rademaker
Michael Radziej
Ramiro Morales
Index: docs/add_ons.txt
===================================================================
--- docs/add_ons.txt (revision 5357)
+++ docs/add_ons.txt (working copy)
@@ -135,6 +135,25 @@
You can pass in either an integer or a string representation of an integer.
+text_fraction
+-------------
+
+Converts a decimal fraction to a common fraction.
+
+Examples:
+
+ * ``0.5`` becomes ``'1/2'``
+ * ``9.99`` becomes ``'10'``
+ * ``-1.25`` becomes ``'-1 1/4'``
+
+You can pass in either an integer or a string representation of an integer.
+
+html_fraction
+-------------
+
+Like text_fraction, but generates HTML so the fraction is rendered nicely. The
+fractional part of the output is wrapped in ```` tags.
+
flatpages
=========