126 lines
4.5 KiB
Python
126 lines
4.5 KiB
Python
|
#!/usr/bin/env python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
"""Humanizing functions for numbers."""
|
||
|
|
||
|
import re
|
||
|
from fractions import Fraction
|
||
|
from .import compat
|
||
|
from .i18n import gettext as _, gettext_noop as N_, pgettext as P_
|
||
|
|
||
|
|
||
|
def ordinal(value):
|
||
|
"""Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd',
|
||
|
3 is '3rd', etc. Works for any integer or anything int() will turn into an
|
||
|
integer. Anything other value will have nothing done to it."""
|
||
|
try:
|
||
|
value = int(value)
|
||
|
except (TypeError, ValueError):
|
||
|
return value
|
||
|
t = (P_('0', 'th'),
|
||
|
P_('1', 'st'),
|
||
|
P_('2', 'nd'),
|
||
|
P_('3', 'rd'),
|
||
|
P_('4', 'th'),
|
||
|
P_('5', 'th'),
|
||
|
P_('6', 'th'),
|
||
|
P_('7', 'th'),
|
||
|
P_('8', 'th'),
|
||
|
P_('9', 'th'))
|
||
|
if value % 100 in (11, 12, 13): # special case
|
||
|
return "%d%s" % (value, t[0])
|
||
|
return '%d%s' % (value, t[value % 10])
|
||
|
|
||
|
|
||
|
def intcomma(value):
|
||
|
"""Converts an integer to a string containing commas every three digits.
|
||
|
For example, 3000 becomes '3,000' and 45000 becomes '45,000'. To maintain
|
||
|
some compatability with Django's intcomma, this function also accepts
|
||
|
floats."""
|
||
|
try:
|
||
|
if isinstance(value, compat.string_types):
|
||
|
float(value.replace(',', ''))
|
||
|
else:
|
||
|
float(value)
|
||
|
except (TypeError, ValueError):
|
||
|
return value
|
||
|
orig = str(value)
|
||
|
new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', orig)
|
||
|
if orig == new:
|
||
|
return new
|
||
|
else:
|
||
|
return intcomma(new)
|
||
|
|
||
|
powers = [10 ** x for x in (6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 100)]
|
||
|
human_powers = (N_('million'), N_('billion'), N_('trillion'), N_('quadrillion'),
|
||
|
N_('quintillion'), N_('sextillion'), N_('septillion'),
|
||
|
N_('octillion'), N_('nonillion'), N_('decillion'), N_('googol'))
|
||
|
|
||
|
|
||
|
def intword(value, format='%.1f'):
|
||
|
"""Converts a large integer to a friendly text representation. Works best for
|
||
|
numbers over 1 million. For example, 1000000 becomes '1.0 million', 1200000
|
||
|
becomes '1.2 million' and '1200000000' becomes '1.2 billion'. Supports up to
|
||
|
decillion (33 digits) and googol (100 digits). You can pass format to change
|
||
|
the number of decimal or general format of the number portion. This function
|
||
|
returns a string unless the value passed was unable to be coaxed into an int."""
|
||
|
try:
|
||
|
value = int(value)
|
||
|
except (TypeError, ValueError):
|
||
|
return value
|
||
|
|
||
|
if value < powers[0]:
|
||
|
return str(value)
|
||
|
for ordinal, power in enumerate(powers[1:], 1):
|
||
|
if value < power:
|
||
|
chopped = value / float(powers[ordinal - 1])
|
||
|
return (' '.join([format, _(human_powers[ordinal - 1])])) % chopped
|
||
|
return str(value)
|
||
|
|
||
|
|
||
|
def apnumber(value):
|
||
|
"""For numbers 1-9, returns the number spelled out. Otherwise, returns the
|
||
|
number. This follows Associated Press style. This always returns a string
|
||
|
unless the value was not int-able, unlike the Django filter."""
|
||
|
try:
|
||
|
value = int(value)
|
||
|
except (TypeError, ValueError):
|
||
|
return value
|
||
|
if not 0 < value < 10:
|
||
|
return str(value)
|
||
|
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'),
|
||
|
_('seven'), _('eight'), _('nine'))[value - 1]
|
||
|
|
||
|
|
||
|
def fractional(value):
|
||
|
'''
|
||
|
There will be some cases where one might not want to show
|
||
|
ugly decimal places for floats and decimals.
|
||
|
This function returns a human readable fractional number
|
||
|
in form of fractions and mixed fractions.
|
||
|
Pass in a string, or a number or a float, and this function returns
|
||
|
a string representation of a fraction
|
||
|
or whole number
|
||
|
or a mixed fraction
|
||
|
Examples:
|
||
|
fractional(0.3) will return '1/3'
|
||
|
fractional(1.3) will return '1 3/10'
|
||
|
fractional(float(1/3)) will return '1/3'
|
||
|
fractional(1) will return '1'
|
||
|
This will always return a string.
|
||
|
'''
|
||
|
try:
|
||
|
number = float(value)
|
||
|
except (TypeError, ValueError):
|
||
|
return value
|
||
|
wholeNumber = int(number)
|
||
|
frac = Fraction(number - wholeNumber).limit_denominator(1000)
|
||
|
numerator = frac._numerator
|
||
|
denominator = frac._denominator
|
||
|
if wholeNumber and not numerator and denominator == 1:
|
||
|
return '%.0f' % wholeNumber # this means that an integer was passed in (or variants of that integer like 1.0000)
|
||
|
elif not wholeNumber:
|
||
|
return '%.0f/%.0f' % (numerator, denominator)
|
||
|
else:
|
||
|
return '%.0f %.0f/%.0f' % (wholeNumber, numerator, denominator)
|