Merge lp:~stefanor/ibid/exchange-825217 into lp:ibid

Proposed by Stefano Rivera
Status: Merged
Approved by: Stefano Rivera
Approved revision: 1068
Merged at revision: 1033
Proposed branch: lp:~stefanor/ibid/exchange-825217
Merge into: lp:ibid
Diff against target: 549 lines (+258/-99)
8 files modified
docs/api/ibid.utils.rst (+8/-1)
ibid/plugins/conversions.py (+140/-68)
ibid/test/__init__.py (+26/-8)
ibid/test/plugins/test_conversions.py (+52/-4)
ibid/test/plugins/test_core.py (+6/-11)
ibid/test/plugins/test_url.py (+3/-3)
ibid/test/test_utils.py (+19/-0)
ibid/utils/__init__.py (+4/-4)
To merge this branch: bzr merge lp:~stefanor/ibid/exchange-825217
Reviewer Review Type Date Requested Status
Max Rabkin Approve
Review via email: mp+71367@code.launchpad.net

Commit message

XE.com no longer has a nice helpful country to currency list, so we build our own, based on ISO-4127. This means many heuristics, so we include a reasonable test suite.

To post a comment you must log in.
lp:~stefanor/ibid/exchange-825217 updated
1033. By Stefano Rivera

Load country_codes in _load_currencies

1034. By Stefano Rivera

Comment data-structures, remove XE.com bracketed place name search heuristics, and return to pre r586 (exchange-343775) heuristics

1035. By Stefano Rivera

Plugin name in cacheable_download location

1036. By Stefano Rivera

Add test suite for resolve_currency (and rename to a public name)

1037. By Stefano Rivera

Filter non-exchangeable currencies

Revision history for this message
Max Rabkin (max-rabkin) wrote :

Some non-currencies are missing (search http://en.wikipedia.org/wiki/ISO_4217 for "funds code" for some more -- the funds codes are in table A.2 of the standard, we may be able to use this instead of hard coding).

You should use trial's tempdir functions instead of hardcoding /tmp.

The tests are networking tesst but it don't check for IBID_NETWORKLESS_TEST.

review: Needs Fixing
lp:~stefanor/ibid/exchange-825217 updated
1038. By Stefano Rivera

Add ibid.test.TestCase, less full-stack than PluginTestCase

1039. By Stefano Rivera

use ibid.test.TestCase for conversions tests

1040. By Stefano Rivera

Use ibid.test.TestCase instead of unittest.TestCase for plugin tests

1041. By Stefano Rivera

More fund codes

Revision history for this message
Stefano Rivera (stefanor) wrote :

A.2 is of course a .doc file. I've just used it to hard-code some more fund codes.

The fund codes don't exactly hurt, they just cause confusion (and one of them matched euro before the euro currency :P )

I implemented mkdtemp, but throwing away the cache after every test is a bit silly, and seeing as our test.ini contains "cachedir = cache", I thought I'd just leave it using that.

lp:~stefanor/ibid/exchange-825217 updated
1042. By Stefano Rivera

Missing super()

1043. By Stefano Rivera

Typos

1044. By Stefano Rivera

Add a full-stack CurrencyConversionTest

1045. By Stefano Rivera

Group 'currencies for country' output by country

1046. By Stefano Rivera

Omit 'No universal currency' entries

1047. By Stefano Rivera

Don't assume we have country_currencies for every country_code

1048. By Stefano Rivera

Try to determine the country code through country_codes if the currency does not begin with a country code

1049. By Stefano Rivera

Identify all countries into country_currencies or warn. Detect non_currencies early

1050. By Stefano Rivera

Do exact search before 'in' searches, fill more entries in country_currencies (including Euro contries)

1051. By Stefano Rivera

Strip whitespace from re-ordered names

1052. By Stefano Rivera

Make sure we get countries that primarily use another country's currency in country_currencies

1053. By Stefano Rivera

Snarky comment for same-currency conversions

Revision history for this message
Max Rabkin (max-rabkin) wrote :

<Taejo> tibid: currencies for Saint Helena
<tibid> Taejo: Ascension And Tristan Da Cunha Saint Helena uses Saint Helena Pound (SHP)
<Taejo> tumbleweed: ^^
<Taejo> should be called "Saint Helena, Ascension and Tristan da Cunha"
<Taejo> but your code to invert "foo, the republic of"
<Taejo> also "Taiwan, Province of China"
<Taejo> looking through the list, one solution is to check that it ends with " of" before inverting

review: Needs Fixing
lp:~stefanor/ibid/exchange-825217 updated
1054. By Stefano Rivera

Tweak country comma-reordering to not reorder compound names.

1055. By Stefano Rivera

Document ibid.utils.get_country_codes

1056. By Stefano Rivera

Tweak country comma-reordering to not reorder compound names. in ibid.utils, add tests.

Revision history for this message
Stefano Rivera (stefanor) wrote :

Found a minutely better solution and added a test case. Also some missing documentation and stuff. Sorry the commits are rather unorganised.

lp:~stefanor/ibid/exchange-825217 updated
1057. By Stefano Rivera

etree's iter() function is only in 2.7 (even if it makes lound deprecation noises)

1058. By Stefano Rivera

Ag screw it, let's stop rotating country names with commas in them. Too messy

1059. By Stefano Rivera

Country: Currency name

1060. By Stefano Rivera

More test cases for Currency Lookup

1061. By Stefano Rivera

Don't let currencies that don't start with country-codes fall through the gaps

1062. By Stefano Rivera

s/Sint/Saint/ only works if you do it everywhere

1063. By Stefano Rivera

Drop country names, the currency table we are using disambiguates currency names sufficiently

1064. By Stefano Rivera

Don't pre-compile regexes unecessarily

1065. By Stefano Rivera

Merge nested if pair

Revision history for this message
Max Rabkin (max-rabkin) :
review: Approve
lp:~stefanor/ibid/exchange-825217 updated
1066. By Stefano Rivera

Only do the inexact currency name search if rough

1067. By Stefano Rivera

Add fallthrough test case

1068. By Stefano Rivera

Handle reversed format conversion questions, such as 'convert GBP 1 to ZAR'. Otherwise units will grab them

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'docs/api/ibid.utils.rst'
--- docs/api/ibid.utils.rst 2011-01-21 21:20:22 +0000
+++ docs/api/ibid.utils.rst 2011-10-23 20:17:24 +0000
@@ -125,9 +125,16 @@
125125
126 Returns the filename to the resource.126 Returns the filename to the resource.
127127
128.. function:: get_country_codes()
129
130 Retrieve and decode a list of ISO-3166-1 country codes.
131
132 Returns a dict of code -> country_name.
133 The codes are capitalised.
134
128.. function:: identity_name(event, identity)135.. function:: identity_name(event, identity)
129136
130 Refer to *identity* naturally in response to *event*.137 Refer to *identity* naturally in response to *event*.
131138
132URL Functions139URL Functions
133-------------140-------------
134141
=== modified file 'ibid/plugins/conversions.py'
--- ibid/plugins/conversions.py 2010-09-13 09:22:29 +0000
+++ ibid/plugins/conversions.py 2011-10-23 20:17:24 +0000
@@ -9,10 +9,10 @@
99
10import ibid10import ibid
11from ibid.plugins import Processor, handler, match11from ibid.plugins import Processor, handler, match
12from ibid.compat import any, defaultdict12from ibid.compat import any, defaultdict, ElementTree
13from ibid.config import Option13from ibid.config import Option
14from ibid.utils import file_in_path, get_country_codes, human_join, \14from ibid.utils import (cacheable_download, file_in_path, get_country_codes,
15 unicode_output, generic_webservice15 human_join, unicode_output, generic_webservice)
16from ibid.utils.html import get_html_parse_tree16from ibid.utils.html import get_html_parse_tree
1717
18features = {}18features = {}
@@ -312,86 +312,153 @@
312 country_codes = {}312 country_codes = {}
313313
314 def _load_currencies(self):314 def _load_currencies(self):
315 etree = get_html_parse_tree(315 iso4127_file = cacheable_download(
316 'http://www.xe.com/iso4217.php', headers = {316 'http://www.currency-iso.org/dl_iso_table_a1.xml',
317 'User-Agent': 'Mozilla/5.0',317 'conversions/iso4217.xml')
318 'Referer': 'http://www.xe.com/',318 document = ElementTree.parse(iso4127_file)
319 }, treetype='etree')319 # Code -> [Countries..., Currency Name]
320
321 tbl_main = [x for x in etree.getiterator('table') if x.get('class') == 'tbl_main'][0]
322
323 self.currencies = {}320 self.currencies = {}
324 for tbl_sub in tbl_main.getiterator('table'):321 # Country -> Code
325 if tbl_sub.get('class') == 'tbl_sub':322 self.country_currencies = {}
326 for tr in tbl_sub.getiterator('tr'):323 self.country_codes = get_country_codes()
327 code, place = [x.text for x in tr.getchildren()]324 # Non-currencies:
328 name = u''325 non_currencies = set(('BOV CLF COU MXV '
329 if not place:326 'UYI XSU XUA ' # Various Fund codes
330 place = u''327 'CHE CHW ' # Swiss WIR currencies
331 if u',' in place[1:-1]:328 'USN USS ' # US Dollar fund codes
332 place, name = place.split(u',', 1)329 'XAG XAU XPD XPT ' # Metals
333 place = place.strip()330 'XBA XBB XBC XBD ' # Euro Bond Market
334 if code in self.currencies:331 'XDR XTS XXX ' # Other specials
335 currency = self.currencies[code]332 ).split())
336 # Are we using another country's currency?333 no_country_codes = set(('Saint Martin',
337 if place != u'' and name != u'' and (currency[1] == u'' or currency[1].rsplit(None, 1)[0] in place334 'Virgin Islands (Us)',
338 or (u'(also called' in currency[1] and currency[1].split(u'(', 1)[0].rsplit(None, 1)[0] in place)):335 'Virgin Islands (British)',))
339 currency[0].insert(0, place)336 accociated_all_countries = True
340 currency[1] = name.strip()337 for currency in document.getiterator('ISO_CURRENCY'):
341 else:338 code = currency.findtext('ALPHABETIC_CODE').strip()
342 currency[0].append(place)339 name = currency.findtext('CURRENCY').strip()
343 else:340 place = currency.findtext('ENTITY').strip().title()
344 self.currencies[code] = [[place], name.strip()]341 if code == '' or code in non_currencies:
342 continue
343 # Fund codes
344 if re.match(r'^Zz[0-9]{2}', place, re.UNICODE):
345 continue
346 if code in self.currencies:
347 self.currencies[code][0].append(place)
348 else:
349 self.currencies[code] = [[place], name]
350 if place in no_country_codes:
351 continue
352 if (code[:2] in self.country_codes
353 and code[:2] not in self.country_currencies):
354 self.country_currencies[code[:2]] = code
355 continue
356 ascii_place = (unicodedata.normalize('NFD', unicode(place))
357 .encode('ASCII', 'ignore')
358 .replace('-', ' ')
359 .replace('Sint', 'Saint'))
360
361 # Countries with (alternative names)
362 swapped_place = None
363 m = re.match(r'^(.+?)\s+\((.+)\)$', ascii_place)
364 if m is not None:
365 swapped_place = '%s (%s)' % (m.group(2), m.group(1))
366
367 for ccode, country in self.country_codes.iteritems():
368 country = country.title()
369 ascii_country = (unicodedata.normalize('NFD', country)
370 .encode('ASCII', 'ignore')
371 .replace('-', ' ')
372 .replace('Sint', 'Saint'))
373 if ascii_country in (ascii_place, swapped_place):
374 if ccode not in self.country_currencies:
375 self.country_currencies[ccode] = code
376 break
377 else:
378 log.info(u"ISO4127 parsing: Can't identify %s as a known "
379 u"country", place)
380 accociated_all_countries = False
345381
346 # Special cases for shared currencies:382 # Special cases for shared currencies:
347 self.currencies['EUR'][0].insert(0, u'Euro Member Countries')383 self.currencies['EUR'][0].append(u'Euro Member Countries')
348 self.currencies['XOF'][0].insert(0, u'Communaut\xe9 Financi\xe8re Africaine')384 self.currencies['XAF'][0].append(u"Communaut\xe9 financi\xe8re d'Afrique")
349 self.currencies['XOF'][1] = u'Francs'385 self.currencies['XCD'][0].append(u'Organisation of Eastern Caribbean States')
386 self.currencies['XOF'][0].append(u'Coop\xe9ration financi\xe8re en Afrique centrale')
387 self.currencies['XPF'][0].append(u'Comptoirs Fran\xe7ais du Pacifique')
388 return accociated_all_countries
350389
351 def _resolve_currency(self, name, rough=True):390 def resolve_currency(self, name, rough=True, plural_recursion=False):
352 "Return the canonical name for a currency"391 "Return the canonical name for a currency"
353392
393 if not self.currencies:
394 self._load_currencies()
395
354 if name.upper() in self.currencies:396 if name.upper() in self.currencies:
355 return name.upper()397 return name.upper()
356398
357 strip_currency_re = re.compile(r'^[\.\s]*([\w\s]+?)s?$', re.UNICODE)399 # Strip leading dots (.TLD)
358 m = strip_currency_re.match(name)400 m = re.match(r'^[\.\s]*(.+)$', name, re.UNICODE)
359
360 if m is None:401 if m is None:
361 return False402 return False
362
363 name = m.group(1).lower()403 name = m.group(1).lower()
364404
365 # TLD -> country name405 # TLD:
366 if rough and len(name) == 2 and name.upper() in self.country_codes:406 if rough and len(name) == 2 and name.upper() in self.country_currencies:
367 name = self.country_codes[name.upper()].lower()407 return self.country_currencies[name.upper()]
368408
369 # Currency Name409 # Currency Name
370 if name == u'dollar':410 if name == u'dollar':
371 return "USD"411 return "USD"
372412 if name == u'pound':
373 name_re = re.compile(r'^(.+\s+)?\(?%ss?\)?(\s+.+)?$' % name, re.I | re.UNICODE)413 return "GBP"
374 for code, (places, currency) in self.currencies.iteritems():414 for code, (places, currency) in self.currencies.iteritems():
375 if name_re.match(currency) or [True for place in places if name_re.match(place)]:415 if name == currency.lower():
376 return code416 return code
377417 if name.title() in places:
418 return code
419
420 # There are also country names in country_codes:
421 for code, place in self.country_codes.iteritems():
422 if name == place.lower() and code in self.country_currencies:
423 return self.country_currencies[code]
424
425 # Second pass, not requiring exact match:
426 if rough:
427 for code, (places, currency) in self.currencies.iteritems():
428 if name in currency.lower():
429 return code
430 if any(name in place.lower() for place in places):
431 return code
432
433 for code, place in self.country_codes.iteritems():
434 if name in place.lower() and code in self.country_currencies:
435 return self.country_currencies[code]
436
437 # Maybe it's a plural?
438 if name.endswith('s') and not plural_recursion:
439 return self.resolve_currency(name[:-1], rough, True)
378 return False440 return False
379441
380 @match(r'^(exchange|convert)\s+([0-9.]+)\s+(.+)\s+(?:for|to|into)\s+(.+)$')442 @match(r'^(exchange|convert)\s+([0-9.]+)\s+(.+)\s+(?:for|to|into)\s+(.+)$')
381 def exchange(self, event, command, amount, frm, to):443 def exchange(self, event, command, amount, frm, to):
382 if not self.currencies:
383 self._load_currencies()
384
385 if not self.country_codes:
386 self.country_codes = get_country_codes()
387
388 rough = command.lower() == 'exchange'444 rough = command.lower() == 'exchange'
389445
390 canonical_frm = self._resolve_currency(frm, rough)446 canonical_frm = self.resolve_currency(frm, rough)
391 canonical_to = self._resolve_currency(to, rough)447 canonical_to = self.resolve_currency(to, rough)
392 if not canonical_frm or not canonical_to:448 if not canonical_frm or not canonical_to:
393 if rough:449 if rough:
394 event.addresponse(u"Sorry, I don't know about a currency for %s", (not canonical_frm and frm or to))450 event.addresponse(
451 u"Sorry, I don't know about a currency for %s",
452 (not canonical_frm and frm or to))
453 return
454 if canonical_frm == canonical_to:
455 event.addresponse(
456 u"Um, that's the same currency. Tell you what, "
457 u"I can offer you my special rate of 0.5 %(currency)s for "
458 u"each %(code)s you sell me.", {
459 'currency': self.currencies[canonical_frm][1],
460 'code': canonical_frm,
461 })
395 return462 return
396463
397 data = generic_webservice(464 data = generic_webservice(
@@ -407,14 +474,12 @@
407 return474 return
408475
409 event.addresponse(476 event.addresponse(
410 u'%(fresult)s %(fcode)s (%(fcountry)s %(fcurrency)s) = '477 u'%(fresult)s %(fcode)s (%(fcurrency)s) = '
411 u'%(tresult)0.2f %(tcode)s (%(tcountry)s %(tcurrency)s) '478 u'%(tresult)0.2f %(tcode)s (%(tcurrency)s) '
412 u'(Last trade rate: %(rate)s, Bid: %(bid)s, Ask: %(ask)s)', {479 u'(Last trade rate: %(rate)s, Bid: %(bid)s, Ask: %(ask)s)', {
413 'fresult': amount,480 'fresult': amount,
414 'tresult': float(amount) * float(last_trade_rate),481 'tresult': float(amount) * float(last_trade_rate),
415 'fcountry': self.currencies[canonical_frm][0][0],
416 'fcurrency': self.currencies[canonical_frm][1],482 'fcurrency': self.currencies[canonical_frm][1],
417 'tcountry': self.currencies[canonical_to][0][0],
418 'tcurrency': self.currencies[canonical_to][1],483 'tcurrency': self.currencies[canonical_to][1],
419 'fcode': canonical_frm,484 'fcode': canonical_frm,
420 'tcode': canonical_to,485 'tcode': canonical_to,
@@ -423,21 +488,28 @@
423 'ask': ask,488 'ask': ask,
424 })489 })
425490
491 @match(r'^(exchange|convert)\s+(.+)\s+([0-9.]+)\s+(?:for|to|into)\s+(.+)$')
492 def exchange_reversed(self, event, command, amount, frm, to):
493 self.exchange(event, command, frm, amount, to)
494
495
426 @match(r'^(?:currency|currencies)\s+for\s+(?:the\s+)?(.+)$')496 @match(r'^(?:currency|currencies)\s+for\s+(?:the\s+)?(.+)$')
427 def currency(self, event, place):497 def currency(self, event, place):
428 if not self.currencies:498 if not self.currencies:
429 self._load_currencies()499 self._load_currencies()
430500
431 search = re.compile(place, re.I)501 results = defaultdict(list)
432 results = []502 for code, (c_places, name) in self.currencies.iteritems():
433 for code, (places, name) in self.currencies.iteritems():503 for c_place in c_places:
434 for place in places:504 if re.search(place, c_place, re.I):
435 if search.search(place):505 results[c_place].append(u'%s (%s)' % (name, code))
436 results.append(u'%s uses %s (%s)' % (place, name, code))
437 break506 break
438507
439 if results:508 if results:
440 event.addresponse(human_join(results))509 event.addresponse(human_join(
510 u'%s uses %s' % (place, human_join(currencies))
511 for place, currencies in results.iteritems()
512 ))
441 else:513 else:
442 event.addresponse(u'No currencies found')514 event.addresponse(u'No currencies found')
443515
444516
=== modified file 'ibid/test/__init__.py'
--- ibid/test/__init__.py 2011-06-20 20:59:56 +0000
+++ ibid/test/__init__.py 2011-10-23 20:17:24 +0000
@@ -6,7 +6,7 @@
6import os6import os
7from traceback import format_exception7from traceback import format_exception
8import re8import re
9from shutil import copyfile9import shutil
10import sys10import sys
11import tempfile11import tempfile
1212
@@ -72,21 +72,37 @@
72 return None72 return None
7373
7474
75class PluginTestCase(unittest.TestCase):75class TestCase(unittest.TestCase):
76 """TestCase subclass, implementing:
77 * detection for tests using network resources
78 * basic Ibid configuration
79 """
80 network = False
81
82 def setUp(self):
83 super(TestCase, self).setUp()
84 if self.network and os.getenv('IBID_NETWORKLESS_TEST') is not None:
85 raise unittest.SkipTest('test uses network')
86 ibid.config = FileConfig(locate_resource('ibid.test', 'test.ini'))
87
88
89class PluginTestCase(TestCase):
90 """A full-stack plugin test, implementing:
91 * Loading of the specified plugins before running the tests, and cleanup
92 afterwards
93 * DB support (clean DB for each TestCase)
94 * Test events passed through the standard Ibid event dispatcher
95 """
76 load = []96 load = []
77 noload = []97 noload = []
78 load_base = True98 load_base = True
79 load_configured = None99 load_configured = None
80 username = u'user'100 username = u'user'
81 public = False101 public = False
82 network = False
83 empty_dbfile = None102 empty_dbfile = None
84103
85 def setUp(self):104 def setUp(self):
86 if self.network and os.getenv('IBID_NETWORKLESS_TEST') is not None:105 super(PluginTestCase, self).setUp()
87 raise unittest.SkipTest('test uses network')
88
89 ibid.config = FileConfig(locate_resource('ibid.test', 'test.ini'))
90106
91 if self.load_configured is None:107 if self.load_configured is None:
92 self.load_configured = not self.load108 self.load_configured = not self.load
@@ -145,7 +161,7 @@
145 if self.empty_dbfile is None:161 if self.empty_dbfile is None:
146 self._create_empty_database()162 self._create_empty_database()
147 self.dbfile = self.mktemp()163 self.dbfile = self.mktemp()
148 copyfile(self.empty_dbfile, self.dbfile)164 shutil.copyfile(self.empty_dbfile, self.dbfile)
149 ibid.config['databases']['ibid'] = 'sqlite:///' + self.dbfile165 ibid.config['databases']['ibid'] = 'sqlite:///' + self.dbfile
150166
151 def make_event(self, message=None, type=u'message'):167 def make_event(self, message=None, type=u'message'):
@@ -216,6 +232,8 @@
216 self.fail("Event was expected to fail", event)232 self.fail("Event was expected to fail", event)
217233
218 def tearDown(self):234 def tearDown(self):
235 super(PluginTestCase, self).tearDown()
236
219 for processor in ibid.processors:237 for processor in ibid.processors:
220 processor.shutdown()238 processor.shutdown()
221 del ibid.processors[:]239 del ibid.processors[:]
222240
=== modified file 'ibid/test/plugins/test_conversions.py'
--- ibid/test/plugins/test_conversions.py 2011-01-26 12:20:59 +0000
+++ ibid/test/plugins/test_conversions.py 2011-10-23 20:17:24 +0000
@@ -1,9 +1,11 @@
1# Copyright (c) 2010-2011, Max Rabkin1# Copyright (c) 2010-2011, Max Rabkin, Stefano Rivera
2# Released under terms of the MIT/X/Expat Licence. See COPYING for details.2# Released under terms of the MIT/X/Expat Licence. See COPYING for details.
33
4from ibid.test import PluginTestCase4import logging
55
6class UnihanTest(PluginTestCase):6import ibid.test
7
8class UnihanTest(ibid.test.PluginTestCase):
7 load = ['conversions']9 load = ['conversions']
8 network = True10 network = True
911
@@ -12,3 +14,49 @@
12 u'.*the traditional form is \u99AC')14 u'.*the traditional form is \u99AC')
13 self.assertResponseMatches(u'unihan \u99AC',15 self.assertResponseMatches(u'unihan \u99AC',
14 u'.*the simplified form is \u9A6C')16 u'.*the simplified form is \u9A6C')
17
18class CurrencyLookupTest(ibid.test.TestCase):
19 network = True
20
21 def setUp(self):
22 super(CurrencyLookupTest, self).setUp()
23 from ibid.plugins import conversions
24 self.processor = conversions.Currency(u'testplugin')
25
26 def test_country_association(self):
27 self.assertTrue(self.processor._load_currencies())
28
29 def test_common_currencies(self):
30 self.assertEqual(self.processor.resolve_currency('pound', True), 'GBP')
31 self.assertEqual(self.processor.resolve_currency('dollar', True), 'USD')
32 self.assertEqual(self.processor.resolve_currency('euro', True), 'EUR')
33 self.assertEqual(self.processor.resolve_currency('rand', True), 'ZAR')
34
35 def test_tld(self):
36 self.assertEqual(self.processor.resolve_currency('.za', True), 'ZAR')
37 self.assertEqual(self.processor.resolve_currency('.na', True), 'NAD')
38 self.assertEqual(self.processor.resolve_currency('.ch', True), 'CHF')
39 # Currency from a former country
40 self.assertEqual(self.processor.resolve_currency('.sx', True), 'ANG')
41 # X- Currency
42 self.assertEqual(self.processor.resolve_currency('.cm', True), 'XAF')
43
44 def test_country(self):
45 self.assertEqual(self.processor.resolve_currency('united kingdom', True), 'GBP')
46 self.assertEqual(self.processor.resolve_currency('south africa', True), 'ZAR')
47 self.assertEqual(self.processor.resolve_currency('bosnia', True), 'BAM')
48
49 def test_fallthrough(self):
50 self.assertFalse(self.processor.resolve_currency('oz', False))
51
52class CurrencyConversionTest(ibid.test.PluginTestCase):
53 load = ['conversions']
54 network = True
55
56 def test_conversion(self):
57 self.assertResponseMatches(u'exchange 1 Pound for ZAR',
58 r'1 GBP \(.+\) = [0-9.]+ ZAR \(.+\) .* Bid: [0-9.]+')
59 self.assertResponseMatches(u'exchange 1 France for Egypt',
60 r'1 EUR \(.+\) = [0-9.]+ EGP \(.+\) .* Bid: [0-9.]+')
61 self.assertResponseMatches(u'exchange 1 Virgin Islands for .tv',
62 r'1 USD \(.+\) = [0-9.]+ AUD \(.+\) .* Bid: [0-9.]+')
1563
=== modified file 'ibid/test/plugins/test_core.py'
--- ibid/test/plugins/test_core.py 2010-05-01 10:02:44 +0000
+++ ibid/test/plugins/test_core.py 2011-10-23 20:17:24 +0000
@@ -1,22 +1,17 @@
1# Copyright (c) 2009-2010, Jeremy Thurgood and Max Rabkin1# Copyright (c) 2009-2010, Jeremy Thurgood and Max Rabkin
2# Released under terms of the MIT/X/Expat Licence. See COPYING for details.2# Released under terms of the MIT/X/Expat Licence. See COPYING for details.
33
4from twisted.trial import unittest4import ibid
5
6import ibid.test5import ibid.test
7from ibid.event import Event6from ibid.event import Event
87
9class TestAddressed(unittest.TestCase):8class TestAddressed(ibid.test.TestCase):
109
11 def setUp(self):10 def setUp(self):
12 ibid.test.set_config({11 super(TestAddressed, self).setUp()
13 u'botname': u'test_ibid',12 ibid.config.botname = u'test_ibid'
14 u'plugins': {13 ibid.config.plugins['testplugin'] = ibid.test.FakeConfig(
15 u'testplugin': {14 {'names': [u'test_ibid', u'bot', u'ant']})
16 u'names': [u'test_ibid', u'bot', u'ant']
17 },
18 },
19 })
2015
21 from ibid.plugins import core16 from ibid.plugins import core
22 self.processor = core.Addressed(u'testplugin')17 self.processor = core.Addressed(u'testplugin')
2318
=== modified file 'ibid/test/plugins/test_url.py'
--- ibid/test/plugins/test_url.py 2010-01-18 22:45:15 +0000
+++ ibid/test/plugins/test_url.py 2011-10-23 20:17:24 +0000
@@ -1,13 +1,13 @@
1# Copyright (c) 2009-2010, Stefano Rivera1# Copyright (c) 2009-2010, Stefano Rivera
2# Released under terms of the MIT/X/Expat Licence. See COPYING for details.2# Released under terms of the MIT/X/Expat Licence. See COPYING for details.
33
4from twisted.trial import unittest4import ibid.test
5
6from ibid.plugins import urlgrab5from ibid.plugins import urlgrab
76
8class TestURLGrabber(unittest.TestCase):7class TestURLGrabber(ibid.test.TestCase):
98
10 def setUp(self):9 def setUp(self):
10 super(TestURLGrabber, self).setUp()
11 self.grab = urlgrab.Grab(u'testplugin')11 self.grab = urlgrab.Grab(u'testplugin')
1212
13 good_grabs = [13 good_grabs = [
1414
=== added file 'ibid/test/test_utils.py'
--- ibid/test/test_utils.py 1970-01-01 00:00:00 +0000
+++ ibid/test/test_utils.py 2011-10-23 20:17:24 +0000
@@ -0,0 +1,19 @@
1# Copyright (c) 2011, Stefano Rivera
2# Released under terms of the MIT/X/Expat Licence. See COPYING for details.
3
4import datetime
5
6import ibid.test
7import ibid.utils
8
9class TestUtils(ibid.test.TestCase):
10 def test_ago(self):
11 self.assertEqual(ibid.utils.ago(datetime.timedelta(seconds=60)), u'1 minute')
12 self.assertEqual(ibid.utils.ago(datetime.timedelta(seconds=60000), 1), u'16 hours')
13
14class TestUtilsNetwork(ibid.test.TestCase):
15 network = True
16
17 def test_get_country_codes(self):
18 codes = ibid.utils.get_country_codes()
19 self.assertIn('ZA', codes)
020
=== modified file 'ibid/utils/__init__.py'
--- ibid/utils/__init__.py 2011-01-22 23:04:00 +0000
+++ ibid/utils/__init__.py 2011-10-23 20:17:24 +0000
@@ -376,9 +376,9 @@
376 line = line.strip()376 line = line.strip()
377 if started and ';' in line:377 if started and ';' in line:
378 country, code = line.split(u';')378 country, code = line.split(u';')
379 if u',' in country:379 country = country.lower()
380 country = u' '.join(reversed(country.split(u',', 1)))380 # Hack around http://bugs.python.org/issue7008
381 country = country.title()381 country = country.title().replace(u"'S", u"'s")
382 countries[code] = country382 countries[code] = country
383 elif line == u'':383 elif line == u'':
384 started = True384 started = True
@@ -387,7 +387,7 @@
387387
388 return countries388 return countries
389389
390def identity_name (event, identity):390def identity_name(event, identity):
391 if event.identity == identity.id:391 if event.identity == identity.id:
392 return u'you'392 return u'you'
393 elif event.source == identity.source:393 elif event.source == identity.source:

Subscribers

People subscribed via source and target branches

to all changes: