Merge lp:~stefanor/ibid/exchange-825217 into lp:ibid
- exchange-825217
- Merge into trunk
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 | ||||
Related bugs: |
|
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.
Description of the change
- 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
- 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
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.
- 1042. By Stefano Rivera
-
Missing super()
- 1043. By Stefano Rivera
-
Typos
- 1044. By Stefano Rivera
-
Add a full-stack CurrencyConvers
ionTest - 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
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
- 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.
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.
- 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
Max Rabkin (max-rabkin) : | # |
- 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
1 | === modified file 'docs/api/ibid.utils.rst' |
2 | --- docs/api/ibid.utils.rst 2011-01-21 21:20:22 +0000 |
3 | +++ docs/api/ibid.utils.rst 2011-10-23 20:17:24 +0000 |
4 | @@ -125,9 +125,16 @@ |
5 | |
6 | Returns the filename to the resource. |
7 | |
8 | +.. function:: get_country_codes() |
9 | + |
10 | + Retrieve and decode a list of ISO-3166-1 country codes. |
11 | + |
12 | + Returns a dict of code -> country_name. |
13 | + The codes are capitalised. |
14 | + |
15 | .. function:: identity_name(event, identity) |
16 | |
17 | - Refer to *identity* naturally in response to *event*. |
18 | + Refer to *identity* naturally in response to *event*. |
19 | |
20 | URL Functions |
21 | ------------- |
22 | |
23 | === modified file 'ibid/plugins/conversions.py' |
24 | --- ibid/plugins/conversions.py 2010-09-13 09:22:29 +0000 |
25 | +++ ibid/plugins/conversions.py 2011-10-23 20:17:24 +0000 |
26 | @@ -9,10 +9,10 @@ |
27 | |
28 | import ibid |
29 | from ibid.plugins import Processor, handler, match |
30 | -from ibid.compat import any, defaultdict |
31 | +from ibid.compat import any, defaultdict, ElementTree |
32 | from ibid.config import Option |
33 | -from ibid.utils import file_in_path, get_country_codes, human_join, \ |
34 | - unicode_output, generic_webservice |
35 | +from ibid.utils import (cacheable_download, file_in_path, get_country_codes, |
36 | + human_join, unicode_output, generic_webservice) |
37 | from ibid.utils.html import get_html_parse_tree |
38 | |
39 | features = {} |
40 | @@ -312,86 +312,153 @@ |
41 | country_codes = {} |
42 | |
43 | def _load_currencies(self): |
44 | - etree = get_html_parse_tree( |
45 | - 'http://www.xe.com/iso4217.php', headers = { |
46 | - 'User-Agent': 'Mozilla/5.0', |
47 | - 'Referer': 'http://www.xe.com/', |
48 | - }, treetype='etree') |
49 | - |
50 | - tbl_main = [x for x in etree.getiterator('table') if x.get('class') == 'tbl_main'][0] |
51 | - |
52 | + iso4127_file = cacheable_download( |
53 | + 'http://www.currency-iso.org/dl_iso_table_a1.xml', |
54 | + 'conversions/iso4217.xml') |
55 | + document = ElementTree.parse(iso4127_file) |
56 | + # Code -> [Countries..., Currency Name] |
57 | self.currencies = {} |
58 | - for tbl_sub in tbl_main.getiterator('table'): |
59 | - if tbl_sub.get('class') == 'tbl_sub': |
60 | - for tr in tbl_sub.getiterator('tr'): |
61 | - code, place = [x.text for x in tr.getchildren()] |
62 | - name = u'' |
63 | - if not place: |
64 | - place = u'' |
65 | - if u',' in place[1:-1]: |
66 | - place, name = place.split(u',', 1) |
67 | - place = place.strip() |
68 | - if code in self.currencies: |
69 | - currency = self.currencies[code] |
70 | - # Are we using another country's currency? |
71 | - if place != u'' and name != u'' and (currency[1] == u'' or currency[1].rsplit(None, 1)[0] in place |
72 | - or (u'(also called' in currency[1] and currency[1].split(u'(', 1)[0].rsplit(None, 1)[0] in place)): |
73 | - currency[0].insert(0, place) |
74 | - currency[1] = name.strip() |
75 | - else: |
76 | - currency[0].append(place) |
77 | - else: |
78 | - self.currencies[code] = [[place], name.strip()] |
79 | + # Country -> Code |
80 | + self.country_currencies = {} |
81 | + self.country_codes = get_country_codes() |
82 | + # Non-currencies: |
83 | + non_currencies = set(('BOV CLF COU MXV ' |
84 | + 'UYI XSU XUA ' # Various Fund codes |
85 | + 'CHE CHW ' # Swiss WIR currencies |
86 | + 'USN USS ' # US Dollar fund codes |
87 | + 'XAG XAU XPD XPT ' # Metals |
88 | + 'XBA XBB XBC XBD ' # Euro Bond Market |
89 | + 'XDR XTS XXX ' # Other specials |
90 | + ).split()) |
91 | + no_country_codes = set(('Saint Martin', |
92 | + 'Virgin Islands (Us)', |
93 | + 'Virgin Islands (British)',)) |
94 | + accociated_all_countries = True |
95 | + for currency in document.getiterator('ISO_CURRENCY'): |
96 | + code = currency.findtext('ALPHABETIC_CODE').strip() |
97 | + name = currency.findtext('CURRENCY').strip() |
98 | + place = currency.findtext('ENTITY').strip().title() |
99 | + if code == '' or code in non_currencies: |
100 | + continue |
101 | + # Fund codes |
102 | + if re.match(r'^Zz[0-9]{2}', place, re.UNICODE): |
103 | + continue |
104 | + if code in self.currencies: |
105 | + self.currencies[code][0].append(place) |
106 | + else: |
107 | + self.currencies[code] = [[place], name] |
108 | + if place in no_country_codes: |
109 | + continue |
110 | + if (code[:2] in self.country_codes |
111 | + and code[:2] not in self.country_currencies): |
112 | + self.country_currencies[code[:2]] = code |
113 | + continue |
114 | + ascii_place = (unicodedata.normalize('NFD', unicode(place)) |
115 | + .encode('ASCII', 'ignore') |
116 | + .replace('-', ' ') |
117 | + .replace('Sint', 'Saint')) |
118 | + |
119 | + # Countries with (alternative names) |
120 | + swapped_place = None |
121 | + m = re.match(r'^(.+?)\s+\((.+)\)$', ascii_place) |
122 | + if m is not None: |
123 | + swapped_place = '%s (%s)' % (m.group(2), m.group(1)) |
124 | + |
125 | + for ccode, country in self.country_codes.iteritems(): |
126 | + country = country.title() |
127 | + ascii_country = (unicodedata.normalize('NFD', country) |
128 | + .encode('ASCII', 'ignore') |
129 | + .replace('-', ' ') |
130 | + .replace('Sint', 'Saint')) |
131 | + if ascii_country in (ascii_place, swapped_place): |
132 | + if ccode not in self.country_currencies: |
133 | + self.country_currencies[ccode] = code |
134 | + break |
135 | + else: |
136 | + log.info(u"ISO4127 parsing: Can't identify %s as a known " |
137 | + u"country", place) |
138 | + accociated_all_countries = False |
139 | |
140 | # Special cases for shared currencies: |
141 | - self.currencies['EUR'][0].insert(0, u'Euro Member Countries') |
142 | - self.currencies['XOF'][0].insert(0, u'Communaut\xe9 Financi\xe8re Africaine') |
143 | - self.currencies['XOF'][1] = u'Francs' |
144 | + self.currencies['EUR'][0].append(u'Euro Member Countries') |
145 | + self.currencies['XAF'][0].append(u"Communaut\xe9 financi\xe8re d'Afrique") |
146 | + self.currencies['XCD'][0].append(u'Organisation of Eastern Caribbean States') |
147 | + self.currencies['XOF'][0].append(u'Coop\xe9ration financi\xe8re en Afrique centrale') |
148 | + self.currencies['XPF'][0].append(u'Comptoirs Fran\xe7ais du Pacifique') |
149 | + return accociated_all_countries |
150 | |
151 | - def _resolve_currency(self, name, rough=True): |
152 | + def resolve_currency(self, name, rough=True, plural_recursion=False): |
153 | "Return the canonical name for a currency" |
154 | |
155 | + if not self.currencies: |
156 | + self._load_currencies() |
157 | + |
158 | if name.upper() in self.currencies: |
159 | return name.upper() |
160 | |
161 | - strip_currency_re = re.compile(r'^[\.\s]*([\w\s]+?)s?$', re.UNICODE) |
162 | - m = strip_currency_re.match(name) |
163 | - |
164 | + # Strip leading dots (.TLD) |
165 | + m = re.match(r'^[\.\s]*(.+)$', name, re.UNICODE) |
166 | if m is None: |
167 | return False |
168 | - |
169 | name = m.group(1).lower() |
170 | |
171 | - # TLD -> country name |
172 | - if rough and len(name) == 2 and name.upper() in self.country_codes: |
173 | - name = self.country_codes[name.upper()].lower() |
174 | + # TLD: |
175 | + if rough and len(name) == 2 and name.upper() in self.country_currencies: |
176 | + return self.country_currencies[name.upper()] |
177 | |
178 | # Currency Name |
179 | if name == u'dollar': |
180 | return "USD" |
181 | - |
182 | - name_re = re.compile(r'^(.+\s+)?\(?%ss?\)?(\s+.+)?$' % name, re.I | re.UNICODE) |
183 | + if name == u'pound': |
184 | + return "GBP" |
185 | for code, (places, currency) in self.currencies.iteritems(): |
186 | - if name_re.match(currency) or [True for place in places if name_re.match(place)]: |
187 | - return code |
188 | - |
189 | + if name == currency.lower(): |
190 | + return code |
191 | + if name.title() in places: |
192 | + return code |
193 | + |
194 | + # There are also country names in country_codes: |
195 | + for code, place in self.country_codes.iteritems(): |
196 | + if name == place.lower() and code in self.country_currencies: |
197 | + return self.country_currencies[code] |
198 | + |
199 | + # Second pass, not requiring exact match: |
200 | + if rough: |
201 | + for code, (places, currency) in self.currencies.iteritems(): |
202 | + if name in currency.lower(): |
203 | + return code |
204 | + if any(name in place.lower() for place in places): |
205 | + return code |
206 | + |
207 | + for code, place in self.country_codes.iteritems(): |
208 | + if name in place.lower() and code in self.country_currencies: |
209 | + return self.country_currencies[code] |
210 | + |
211 | + # Maybe it's a plural? |
212 | + if name.endswith('s') and not plural_recursion: |
213 | + return self.resolve_currency(name[:-1], rough, True) |
214 | return False |
215 | |
216 | @match(r'^(exchange|convert)\s+([0-9.]+)\s+(.+)\s+(?:for|to|into)\s+(.+)$') |
217 | def exchange(self, event, command, amount, frm, to): |
218 | - if not self.currencies: |
219 | - self._load_currencies() |
220 | - |
221 | - if not self.country_codes: |
222 | - self.country_codes = get_country_codes() |
223 | - |
224 | rough = command.lower() == 'exchange' |
225 | |
226 | - canonical_frm = self._resolve_currency(frm, rough) |
227 | - canonical_to = self._resolve_currency(to, rough) |
228 | + canonical_frm = self.resolve_currency(frm, rough) |
229 | + canonical_to = self.resolve_currency(to, rough) |
230 | if not canonical_frm or not canonical_to: |
231 | if rough: |
232 | - event.addresponse(u"Sorry, I don't know about a currency for %s", (not canonical_frm and frm or to)) |
233 | + event.addresponse( |
234 | + u"Sorry, I don't know about a currency for %s", |
235 | + (not canonical_frm and frm or to)) |
236 | + return |
237 | + if canonical_frm == canonical_to: |
238 | + event.addresponse( |
239 | + u"Um, that's the same currency. Tell you what, " |
240 | + u"I can offer you my special rate of 0.5 %(currency)s for " |
241 | + u"each %(code)s you sell me.", { |
242 | + 'currency': self.currencies[canonical_frm][1], |
243 | + 'code': canonical_frm, |
244 | + }) |
245 | return |
246 | |
247 | data = generic_webservice( |
248 | @@ -407,14 +474,12 @@ |
249 | return |
250 | |
251 | event.addresponse( |
252 | - u'%(fresult)s %(fcode)s (%(fcountry)s %(fcurrency)s) = ' |
253 | - u'%(tresult)0.2f %(tcode)s (%(tcountry)s %(tcurrency)s) ' |
254 | + u'%(fresult)s %(fcode)s (%(fcurrency)s) = ' |
255 | + u'%(tresult)0.2f %(tcode)s (%(tcurrency)s) ' |
256 | u'(Last trade rate: %(rate)s, Bid: %(bid)s, Ask: %(ask)s)', { |
257 | 'fresult': amount, |
258 | 'tresult': float(amount) * float(last_trade_rate), |
259 | - 'fcountry': self.currencies[canonical_frm][0][0], |
260 | 'fcurrency': self.currencies[canonical_frm][1], |
261 | - 'tcountry': self.currencies[canonical_to][0][0], |
262 | 'tcurrency': self.currencies[canonical_to][1], |
263 | 'fcode': canonical_frm, |
264 | 'tcode': canonical_to, |
265 | @@ -423,21 +488,28 @@ |
266 | 'ask': ask, |
267 | }) |
268 | |
269 | + @match(r'^(exchange|convert)\s+(.+)\s+([0-9.]+)\s+(?:for|to|into)\s+(.+)$') |
270 | + def exchange_reversed(self, event, command, amount, frm, to): |
271 | + self.exchange(event, command, frm, amount, to) |
272 | + |
273 | + |
274 | @match(r'^(?:currency|currencies)\s+for\s+(?:the\s+)?(.+)$') |
275 | def currency(self, event, place): |
276 | if not self.currencies: |
277 | self._load_currencies() |
278 | |
279 | - search = re.compile(place, re.I) |
280 | - results = [] |
281 | - for code, (places, name) in self.currencies.iteritems(): |
282 | - for place in places: |
283 | - if search.search(place): |
284 | - results.append(u'%s uses %s (%s)' % (place, name, code)) |
285 | + results = defaultdict(list) |
286 | + for code, (c_places, name) in self.currencies.iteritems(): |
287 | + for c_place in c_places: |
288 | + if re.search(place, c_place, re.I): |
289 | + results[c_place].append(u'%s (%s)' % (name, code)) |
290 | break |
291 | |
292 | if results: |
293 | - event.addresponse(human_join(results)) |
294 | + event.addresponse(human_join( |
295 | + u'%s uses %s' % (place, human_join(currencies)) |
296 | + for place, currencies in results.iteritems() |
297 | + )) |
298 | else: |
299 | event.addresponse(u'No currencies found') |
300 | |
301 | |
302 | === modified file 'ibid/test/__init__.py' |
303 | --- ibid/test/__init__.py 2011-06-20 20:59:56 +0000 |
304 | +++ ibid/test/__init__.py 2011-10-23 20:17:24 +0000 |
305 | @@ -6,7 +6,7 @@ |
306 | import os |
307 | from traceback import format_exception |
308 | import re |
309 | -from shutil import copyfile |
310 | +import shutil |
311 | import sys |
312 | import tempfile |
313 | |
314 | @@ -72,21 +72,37 @@ |
315 | return None |
316 | |
317 | |
318 | -class PluginTestCase(unittest.TestCase): |
319 | +class TestCase(unittest.TestCase): |
320 | + """TestCase subclass, implementing: |
321 | + * detection for tests using network resources |
322 | + * basic Ibid configuration |
323 | + """ |
324 | + network = False |
325 | + |
326 | + def setUp(self): |
327 | + super(TestCase, self).setUp() |
328 | + if self.network and os.getenv('IBID_NETWORKLESS_TEST') is not None: |
329 | + raise unittest.SkipTest('test uses network') |
330 | + ibid.config = FileConfig(locate_resource('ibid.test', 'test.ini')) |
331 | + |
332 | + |
333 | +class PluginTestCase(TestCase): |
334 | + """A full-stack plugin test, implementing: |
335 | + * Loading of the specified plugins before running the tests, and cleanup |
336 | + afterwards |
337 | + * DB support (clean DB for each TestCase) |
338 | + * Test events passed through the standard Ibid event dispatcher |
339 | + """ |
340 | load = [] |
341 | noload = [] |
342 | load_base = True |
343 | load_configured = None |
344 | username = u'user' |
345 | public = False |
346 | - network = False |
347 | empty_dbfile = None |
348 | |
349 | def setUp(self): |
350 | - if self.network and os.getenv('IBID_NETWORKLESS_TEST') is not None: |
351 | - raise unittest.SkipTest('test uses network') |
352 | - |
353 | - ibid.config = FileConfig(locate_resource('ibid.test', 'test.ini')) |
354 | + super(PluginTestCase, self).setUp() |
355 | |
356 | if self.load_configured is None: |
357 | self.load_configured = not self.load |
358 | @@ -145,7 +161,7 @@ |
359 | if self.empty_dbfile is None: |
360 | self._create_empty_database() |
361 | self.dbfile = self.mktemp() |
362 | - copyfile(self.empty_dbfile, self.dbfile) |
363 | + shutil.copyfile(self.empty_dbfile, self.dbfile) |
364 | ibid.config['databases']['ibid'] = 'sqlite:///' + self.dbfile |
365 | |
366 | def make_event(self, message=None, type=u'message'): |
367 | @@ -216,6 +232,8 @@ |
368 | self.fail("Event was expected to fail", event) |
369 | |
370 | def tearDown(self): |
371 | + super(PluginTestCase, self).tearDown() |
372 | + |
373 | for processor in ibid.processors: |
374 | processor.shutdown() |
375 | del ibid.processors[:] |
376 | |
377 | === modified file 'ibid/test/plugins/test_conversions.py' |
378 | --- ibid/test/plugins/test_conversions.py 2011-01-26 12:20:59 +0000 |
379 | +++ ibid/test/plugins/test_conversions.py 2011-10-23 20:17:24 +0000 |
380 | @@ -1,9 +1,11 @@ |
381 | -# Copyright (c) 2010-2011, Max Rabkin |
382 | +# Copyright (c) 2010-2011, Max Rabkin, Stefano Rivera |
383 | # Released under terms of the MIT/X/Expat Licence. See COPYING for details. |
384 | |
385 | -from ibid.test import PluginTestCase |
386 | - |
387 | -class UnihanTest(PluginTestCase): |
388 | +import logging |
389 | + |
390 | +import ibid.test |
391 | + |
392 | +class UnihanTest(ibid.test.PluginTestCase): |
393 | load = ['conversions'] |
394 | network = True |
395 | |
396 | @@ -12,3 +14,49 @@ |
397 | u'.*the traditional form is \u99AC') |
398 | self.assertResponseMatches(u'unihan \u99AC', |
399 | u'.*the simplified form is \u9A6C') |
400 | + |
401 | +class CurrencyLookupTest(ibid.test.TestCase): |
402 | + network = True |
403 | + |
404 | + def setUp(self): |
405 | + super(CurrencyLookupTest, self).setUp() |
406 | + from ibid.plugins import conversions |
407 | + self.processor = conversions.Currency(u'testplugin') |
408 | + |
409 | + def test_country_association(self): |
410 | + self.assertTrue(self.processor._load_currencies()) |
411 | + |
412 | + def test_common_currencies(self): |
413 | + self.assertEqual(self.processor.resolve_currency('pound', True), 'GBP') |
414 | + self.assertEqual(self.processor.resolve_currency('dollar', True), 'USD') |
415 | + self.assertEqual(self.processor.resolve_currency('euro', True), 'EUR') |
416 | + self.assertEqual(self.processor.resolve_currency('rand', True), 'ZAR') |
417 | + |
418 | + def test_tld(self): |
419 | + self.assertEqual(self.processor.resolve_currency('.za', True), 'ZAR') |
420 | + self.assertEqual(self.processor.resolve_currency('.na', True), 'NAD') |
421 | + self.assertEqual(self.processor.resolve_currency('.ch', True), 'CHF') |
422 | + # Currency from a former country |
423 | + self.assertEqual(self.processor.resolve_currency('.sx', True), 'ANG') |
424 | + # X- Currency |
425 | + self.assertEqual(self.processor.resolve_currency('.cm', True), 'XAF') |
426 | + |
427 | + def test_country(self): |
428 | + self.assertEqual(self.processor.resolve_currency('united kingdom', True), 'GBP') |
429 | + self.assertEqual(self.processor.resolve_currency('south africa', True), 'ZAR') |
430 | + self.assertEqual(self.processor.resolve_currency('bosnia', True), 'BAM') |
431 | + |
432 | + def test_fallthrough(self): |
433 | + self.assertFalse(self.processor.resolve_currency('oz', False)) |
434 | + |
435 | +class CurrencyConversionTest(ibid.test.PluginTestCase): |
436 | + load = ['conversions'] |
437 | + network = True |
438 | + |
439 | + def test_conversion(self): |
440 | + self.assertResponseMatches(u'exchange 1 Pound for ZAR', |
441 | + r'1 GBP \(.+\) = [0-9.]+ ZAR \(.+\) .* Bid: [0-9.]+') |
442 | + self.assertResponseMatches(u'exchange 1 France for Egypt', |
443 | + r'1 EUR \(.+\) = [0-9.]+ EGP \(.+\) .* Bid: [0-9.]+') |
444 | + self.assertResponseMatches(u'exchange 1 Virgin Islands for .tv', |
445 | + r'1 USD \(.+\) = [0-9.]+ AUD \(.+\) .* Bid: [0-9.]+') |
446 | |
447 | === modified file 'ibid/test/plugins/test_core.py' |
448 | --- ibid/test/plugins/test_core.py 2010-05-01 10:02:44 +0000 |
449 | +++ ibid/test/plugins/test_core.py 2011-10-23 20:17:24 +0000 |
450 | @@ -1,22 +1,17 @@ |
451 | # Copyright (c) 2009-2010, Jeremy Thurgood and Max Rabkin |
452 | # Released under terms of the MIT/X/Expat Licence. See COPYING for details. |
453 | |
454 | -from twisted.trial import unittest |
455 | - |
456 | +import ibid |
457 | import ibid.test |
458 | from ibid.event import Event |
459 | |
460 | -class TestAddressed(unittest.TestCase): |
461 | +class TestAddressed(ibid.test.TestCase): |
462 | |
463 | def setUp(self): |
464 | - ibid.test.set_config({ |
465 | - u'botname': u'test_ibid', |
466 | - u'plugins': { |
467 | - u'testplugin': { |
468 | - u'names': [u'test_ibid', u'bot', u'ant'] |
469 | - }, |
470 | - }, |
471 | - }) |
472 | + super(TestAddressed, self).setUp() |
473 | + ibid.config.botname = u'test_ibid' |
474 | + ibid.config.plugins['testplugin'] = ibid.test.FakeConfig( |
475 | + {'names': [u'test_ibid', u'bot', u'ant']}) |
476 | |
477 | from ibid.plugins import core |
478 | self.processor = core.Addressed(u'testplugin') |
479 | |
480 | === modified file 'ibid/test/plugins/test_url.py' |
481 | --- ibid/test/plugins/test_url.py 2010-01-18 22:45:15 +0000 |
482 | +++ ibid/test/plugins/test_url.py 2011-10-23 20:17:24 +0000 |
483 | @@ -1,13 +1,13 @@ |
484 | # Copyright (c) 2009-2010, Stefano Rivera |
485 | # Released under terms of the MIT/X/Expat Licence. See COPYING for details. |
486 | |
487 | -from twisted.trial import unittest |
488 | - |
489 | +import ibid.test |
490 | from ibid.plugins import urlgrab |
491 | |
492 | -class TestURLGrabber(unittest.TestCase): |
493 | +class TestURLGrabber(ibid.test.TestCase): |
494 | |
495 | def setUp(self): |
496 | + super(TestURLGrabber, self).setUp() |
497 | self.grab = urlgrab.Grab(u'testplugin') |
498 | |
499 | good_grabs = [ |
500 | |
501 | === added file 'ibid/test/test_utils.py' |
502 | --- ibid/test/test_utils.py 1970-01-01 00:00:00 +0000 |
503 | +++ ibid/test/test_utils.py 2011-10-23 20:17:24 +0000 |
504 | @@ -0,0 +1,19 @@ |
505 | +# Copyright (c) 2011, Stefano Rivera |
506 | +# Released under terms of the MIT/X/Expat Licence. See COPYING for details. |
507 | + |
508 | +import datetime |
509 | + |
510 | +import ibid.test |
511 | +import ibid.utils |
512 | + |
513 | +class TestUtils(ibid.test.TestCase): |
514 | + def test_ago(self): |
515 | + self.assertEqual(ibid.utils.ago(datetime.timedelta(seconds=60)), u'1 minute') |
516 | + self.assertEqual(ibid.utils.ago(datetime.timedelta(seconds=60000), 1), u'16 hours') |
517 | + |
518 | +class TestUtilsNetwork(ibid.test.TestCase): |
519 | + network = True |
520 | + |
521 | + def test_get_country_codes(self): |
522 | + codes = ibid.utils.get_country_codes() |
523 | + self.assertIn('ZA', codes) |
524 | |
525 | === modified file 'ibid/utils/__init__.py' |
526 | --- ibid/utils/__init__.py 2011-01-22 23:04:00 +0000 |
527 | +++ ibid/utils/__init__.py 2011-10-23 20:17:24 +0000 |
528 | @@ -376,9 +376,9 @@ |
529 | line = line.strip() |
530 | if started and ';' in line: |
531 | country, code = line.split(u';') |
532 | - if u',' in country: |
533 | - country = u' '.join(reversed(country.split(u',', 1))) |
534 | - country = country.title() |
535 | + country = country.lower() |
536 | + # Hack around http://bugs.python.org/issue7008 |
537 | + country = country.title().replace(u"'S", u"'s") |
538 | countries[code] = country |
539 | elif line == u'': |
540 | started = True |
541 | @@ -387,7 +387,7 @@ |
542 | |
543 | return countries |
544 | |
545 | -def identity_name (event, identity): |
546 | +def identity_name(event, identity): |
547 | if event.identity == identity.id: |
548 | return u'you' |
549 | elif event.source == identity.source: |
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_NETWORKLES S_TEST.