]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Added support for territory currency lookups.
authorArmin Ronacher <armin.ronacher@active-4.com>
Tue, 30 Jul 2013 00:13:45 +0000 (02:13 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Tue, 30 Jul 2013 00:13:45 +0000 (02:13 +0200)
The main usecase of this is to figure out at what point in time did
a country use a certain currency.  The default behavior is to use
the current date.

This fixes #42

CHANGES
babel/numbers.py
docs/api/numbers.rst
scripts/import_cldr.py
tests/test_numbers.py

diff --git a/CHANGES b/CHANGES
index 7dbaf25cc10f167e1c0987ab35f1df8ff4950166..147124e4f4d5cf672ea7753620942b3679fc2e9e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,10 @@ Version 2.0
 
 (release date to be decided, codename to be selected)
 
+- Added support for looking up currencies that belong to a territory
+  through the :func:`babel.numbers.get_territory_currencies`
+  function.
+
 Version 1.4
 -----------
 
index 0f387190a7a2c697701d3b85f582813e4b03e8a0..344625e193b47a468fccbe3e02e1cd793f1eafcd 100644 (file)
@@ -21,8 +21,9 @@
 from decimal import Decimal, InvalidOperation
 import math
 import re
+from datetime import date as date_, datetime as datetime_
 
-from babel.core import default_locale, Locale
+from babel.core import default_locale, Locale, get_global
 from babel._compat import range_type
 
 
@@ -63,6 +64,91 @@ def get_currency_symbol(currency, locale=LC_NUMERIC):
     return Locale.parse(locale).currency_symbols.get(currency, currency)
 
 
+def get_territory_currencies(territory, start_date=None, end_date=None,
+                             tender=True, non_tender=False,
+                             include_details=False):
+    """Returns the list of currencies for the given territory that are valid at
+    the given date range.  In addition to that the currency database
+    distinguishes between tender and non-tender currencies.  By default only
+    tender currencies are returned.
+
+    The return value is a list of all currencies roughly ordered by the time
+    of when the currency became active.  The longer the currency is being in
+    use the more to the left of the list it will be.
+
+    The start date defaults to today.  If no end date is given it will be the
+    same as the start date.  Otherwise a range can be defined.  For instance
+    this can be used to find the currencies in use in Austria between 1995 and
+    2011:
+
+    >>> from datetime import date
+    >>> get_territory_currencies('AT', date(1995, 1, 1), date(2011, 1, 1))
+    ['ATS', 'EUR']
+
+    Likewise it's also possible to find all the currencies in use on a
+    single date:
+
+    >>> get_territory_currencies('AT', date(1995, 1, 1))
+    ['ATS']
+    >>> get_territory_currencies('AT', date(2011, 1, 1))
+    ['EUR']
+
+    By default the return value only includes tender currencies.  This
+    however can be changed:
+
+    >>> get_territory_currencies('US')
+    ['USD']
+    >>> get_territory_currencies('US', tender=False, non_tender=True)
+    ['USN', 'USS']
+
+    .. versionadded:: 2.0
+
+    :param territory: the name of the territory to find the currency fo
+    :param start_date: the start date.  If not given today is assumed.
+    :param end_date: the end date.  If not given the start date is assumed.
+    :param tender: controls whether tender currencies should be included.
+    :param non_tender: controls whether non-tender currencies should be
+                       included.
+    :param include_details: if set to `True`, instead of returning currency
+                            codes the return value will be dictionaries
+                            with detail information.  In that case each
+                            dictionary will have the keys ``'currency'``,
+                            ``'from'``, ``'to'``, and ``'tender'``.
+    """
+    currencies = get_global('territory_currencies')
+    if start_date is None:
+        start_date = date_.today()
+    elif isinstance(start_date, datetime_):
+        start_date = start_date.date()
+    if end_date is None:
+        end_date = start_date
+    elif isinstance(end_date, datetime_):
+        end_date = end_date.date()
+
+    curs = currencies.get(territory.upper(), ())
+    # TODO: validate that the territory exists
+
+    def _is_active(start, end):
+        return (start is None or start <= end_date) and \
+               (end is None or end >= start_date)
+
+    result = []
+    for currency_code, start, end, is_tender in curs:
+        if ((is_tender and tender) or \
+            (not is_tender and non_tender)) and _is_active(start, end):
+            if include_details:
+                result.append({
+                    'currency': currency_code,
+                    'from': start,
+                    'to': end,
+                    'tender': is_tender,
+                })
+            else:
+                result.append(currency_code)
+
+    return result
+
+
 def get_decimal_symbol(locale=LC_NUMERIC):
     """Return the symbol used by the locale to separate decimal fractions.
 
index de3573e71466808bfba66a531e146550d36efc48..207ae0bca5fdcf193a015d13cdcdd0b7b783e81d 100644 (file)
@@ -43,3 +43,5 @@ Data Access
 .. autofunction:: get_plus_sign_symbol
 
 .. autofunction:: get_minus_sign_symbol
+
+.. autofunction:: get_territory_currencies
index 84b2b1ddf6ef223aa432f4da67d6e47c041fb433..c189e4f3e74121fff904cd68c90b27a3c5145848 100755 (executable)
@@ -21,6 +21,8 @@ try:
 except ImportError:
     from xml.etree import ElementTree
 
+from datetime import date
+
 # Make sure we're using Babel source, and not some previously installed version
 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..'))
 
@@ -95,6 +97,18 @@ def _translate_alias(ctxt, path):
     return keys
 
 
+def _parse_currency_date(s):
+    if not s:
+        return None
+    parts = s.split('-', 2)
+    return date(*map(int, parts + [1] * (3 - len(parts))))
+
+
+def _currency_sort_key(tup):
+    code, start, end, tender = tup
+    return int(not tender), start or date(1, 1, 1)
+
+
 def main():
     parser = OptionParser(usage='%prog path/to/cldr')
     options, args = parser.parse_args()
@@ -128,6 +142,7 @@ def main():
         script_aliases = global_data.setdefault('script_aliases', {})
         variant_aliases = global_data.setdefault('variant_aliases', {})
         likely_subtags = global_data.setdefault('likely_subtags', {})
+        territory_currencies = global_data.setdefault('territory_currencies', {})
 
         # create auxiliary zone->territory map from the windows zones (we don't set
         # the 'zones_territories' map directly here, because there are some zones
@@ -186,6 +201,19 @@ def main():
         for likely_subtag in sup_likely.findall('.//likelySubtags/likelySubtag'):
             likely_subtags[likely_subtag.attrib['from']] = likely_subtag.attrib['to']
 
+        # Currencies in territories
+        for region in sup.findall('.//currencyData/region'):
+            region_code = region.attrib['iso3166']
+            region_currencies = []
+            for currency in region.findall('./currency'):
+                cur_start = _parse_currency_date(currency.attrib.get('from'))
+                cur_end = _parse_currency_date(currency.attrib.get('to'))
+                region_currencies.append((currency.attrib['iso4217'],
+                                          cur_start, cur_end,
+                                          currency.attrib.get('tender', 'true') == 'true'))
+            region_currencies.sort(key=_currency_sort_key)
+            territory_currencies[region_code] = region_currencies
+
         outfile = open(global_path, 'wb')
         try:
             pickle.dump(global_data, outfile, 2)
index 6db4b6795e4250eea762e0555480f0ae80c12d1b..99e0d1bda1dfc296b1060ce3f4dd498cfef95abe 100644 (file)
@@ -15,6 +15,8 @@ from decimal import Decimal
 import unittest
 import pytest
 
+from datetime import date
+
 from babel import numbers
 
 
@@ -180,6 +182,27 @@ def test_get_currency_symbol():
     assert numbers.get_currency_symbol('USD', 'en_US') == u'$'
 
 
+def test_get_territory_currencies():
+    assert numbers.get_territory_currencies('AT', date(1995, 1, 1)) == ['ATS']
+    assert numbers.get_territory_currencies('AT', date(2011, 1, 1)) == ['EUR']
+
+    assert numbers.get_territory_currencies('US', date(2013, 1, 1)) == ['USD']
+    assert sorted(numbers.get_territory_currencies('US', date(2013, 1, 1),
+        non_tender=True)) == ['USD', 'USN', 'USS']
+
+    assert numbers.get_territory_currencies('US', date(2013, 1, 1),
+        include_details=True) == [{
+            'currency': 'USD',
+            'from': date(1792, 1, 1),
+            'to': None,
+            'tender': True
+        }]
+
+    assert numbers.get_territory_currencies('LS', date(2013, 1, 1)) == ['ZAR', 'LSL']
+
+    assert numbers.get_territory_currencies('QO', date(2013, 1, 1)) == []
+
+
 def test_get_decimal_symbol():
     assert numbers.get_decimal_symbol('en_US') == u'.'