trackler 2.2.1.176 → 2.2.1.177

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/isbn-verifier/canonical-data.json +9 -9
  4. data/tracks/bash/docs/LEARNING.md +1 -1
  5. data/tracks/bash/docs/RESOURCES.md +7 -3
  6. data/tracks/clojure/docs/RESOURCES.md +1 -1
  7. data/tracks/crystal/config.json +1 -0
  8. data/tracks/crystal/docs/ABOUT.md +3 -1
  9. data/tracks/crystal/exercises/gigasecond/spec/gigasecond_spec.cr +10 -10
  10. data/tracks/delphi/exercises/isbn-verifier/uTestISBNVerifier.pas +7 -7
  11. data/tracks/erlang/config.json +36 -0
  12. data/tracks/erlang/exercises/armstrong-numbers/test/armstrong_numbers_tests.erl +1 -1
  13. data/tracks/erlang/exercises/minesweeper/README.md +68 -0
  14. data/tracks/erlang/exercises/minesweeper/rebar.config +30 -0
  15. data/tracks/erlang/exercises/minesweeper/src/example.erl +76 -0
  16. data/tracks/erlang/exercises/minesweeper/src/minesweeper.app.src +9 -0
  17. data/tracks/erlang/exercises/minesweeper/src/minesweeper.erl +8 -0
  18. data/tracks/erlang/exercises/minesweeper/test/minesweeper_tests.erl +154 -0
  19. data/tracks/erlang/exercises/perfect-numbers/README.md +59 -0
  20. data/tracks/erlang/exercises/perfect-numbers/rebar.config +30 -0
  21. data/tracks/erlang/exercises/perfect-numbers/src/example.erl +35 -0
  22. data/tracks/erlang/exercises/perfect-numbers/src/perfect_numbers.app.src +9 -0
  23. data/tracks/erlang/exercises/perfect-numbers/src/perfect_numbers.erl +8 -0
  24. data/tracks/erlang/exercises/perfect-numbers/test/perfect_numbers_tests.erl +48 -0
  25. data/tracks/erlang/exercises/secret-handshake/README.md +70 -0
  26. data/tracks/erlang/exercises/secret-handshake/rebar.config +30 -0
  27. data/tracks/erlang/exercises/secret-handshake/src/example.erl +19 -0
  28. data/tracks/erlang/exercises/secret-handshake/src/secret_handshake.app.src +9 -0
  29. data/tracks/erlang/exercises/secret-handshake/src/secret_handshake.erl +8 -0
  30. data/tracks/erlang/exercises/secret-handshake/test/secret_handshake_tests.erl +45 -0
  31. data/tracks/go/.travis.yml +2 -2
  32. data/tracks/nim/config.json +2 -2
  33. data/tracks/nim/docs/SNIPPET.txt +4 -1
  34. data/tracks/python/config.json +12 -0
  35. data/tracks/python/exercises/ledger/README.md +60 -0
  36. data/tracks/python/exercises/ledger/example.py +85 -0
  37. data/tracks/python/exercises/ledger/ledger.py +298 -0
  38. data/tracks/python/exercises/ledger/ledger_test.py +147 -0
  39. data/tracks/python/exercises/linked-list/example.py +8 -8
  40. data/tracks/python/exercises/linked-list/linked_list.py +1 -1
  41. data/tracks/python/exercises/reverse-string/example.py +2 -2
  42. data/tracks/python/exercises/reverse-string/reverse_string.py +1 -1
  43. data/tracks/reasonml/config.json +24 -0
  44. data/tracks/reasonml/exercises/armstrong-numbers/README.md +35 -0
  45. data/tracks/reasonml/exercises/armstrong-numbers/__tests__/ArmstrongNumbers_test.re +37 -0
  46. data/tracks/reasonml/exercises/armstrong-numbers/bsconfig.json +25 -0
  47. data/tracks/reasonml/exercises/armstrong-numbers/package-lock.json +5835 -0
  48. data/tracks/reasonml/exercises/armstrong-numbers/package.json +20 -0
  49. data/tracks/reasonml/exercises/armstrong-numbers/src/ArmstrongNumbers.rei +1 -0
  50. data/tracks/reasonml/exercises/armstrong-numbers/src/Example.re +17 -0
  51. data/tracks/reasonml/exercises/isogram/README.md +38 -0
  52. data/tracks/reasonml/exercises/isogram/__tests__/Isogram_test.re +41 -0
  53. data/tracks/reasonml/exercises/isogram/bsconfig.json +25 -0
  54. data/tracks/reasonml/exercises/isogram/package-lock.json +5835 -0
  55. data/tracks/reasonml/exercises/isogram/package.json +20 -0
  56. data/tracks/reasonml/exercises/isogram/src/Example.re +17 -0
  57. data/tracks/reasonml/exercises/isogram/src/Isogram.rei +1 -0
  58. data/tracks/rust/config.json +95 -94
  59. data/tracks/rust/exercises/diffie-hellman/.meta/hints.md +2 -0
  60. data/tracks/rust/exercises/diffie-hellman/README.md +4 -0
  61. data/tracks/rust/exercises/diffie-hellman/example.rs +24 -2
  62. data/tracks/rust/exercises/diffie-hellman/tests/diffie-hellman.rs +30 -0
  63. data/tracks/rust/exercises/{twofer → two-fer}/.gitignore +0 -0
  64. data/tracks/rust/exercises/{twofer → two-fer}/Cargo.toml +0 -0
  65. data/tracks/rust/exercises/{twofer → two-fer}/README.md +1 -1
  66. data/tracks/rust/exercises/{twofer → two-fer}/example.rs +0 -0
  67. data/tracks/rust/exercises/{twofer → two-fer}/src/lib.rs +0 -0
  68. data/tracks/rust/exercises/{twofer → two-fer}/tests/two-fer.rs +0 -0
  69. metadata +45 -8
@@ -0,0 +1,60 @@
1
+ # Ledger
2
+
3
+ Refactor a ledger printer.
4
+
5
+ The ledger exercise is a refactoring exercise. There is code that prints a
6
+ nicely formatted ledger, given a locale (American or Dutch) and a currency (US
7
+ dollar or euro). The code however is rather badly written, though (somewhat
8
+ surprisingly) it consistently passes the test suite.
9
+
10
+ Rewrite this code. Remember that in refactoring the trick is to make small steps
11
+ that keep the tests passing. That way you can always quickly go back to a
12
+ working version. Version control tools like git can help here as well.
13
+
14
+ Please keep a log of what changes you've made and make a comment on the exercise
15
+ containing that log, this will help reviewers.
16
+
17
+ ## Exception messages
18
+
19
+ Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to
20
+ indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not
21
+ every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include
22
+ a message.
23
+
24
+ To raise a message with an exception, just write it as an argument to the exception type. For example, instead of
25
+ `raise Exception`, you should write:
26
+
27
+ ```python
28
+ raise Exception("Meaningful message indicating the source of the error")
29
+ ```
30
+
31
+ ## Running the tests
32
+
33
+ To run the tests, run the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)):
34
+
35
+ - Python 2.7: `py.test ledger_test.py`
36
+ - Python 3.4+: `pytest ledger_test.py`
37
+
38
+ Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version):
39
+ `python -m pytest ledger_test.py`
40
+
41
+ ### Common `pytest` options
42
+
43
+ - `-v` : enable verbose output
44
+ - `-x` : stop running tests on first failure
45
+ - `--ff` : run failures from previous test before running other test cases
46
+
47
+ For other options, see `python -m pytest -h`
48
+
49
+ ## Submitting Exercises
50
+
51
+ Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/ledger` directory.
52
+
53
+ You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`.
54
+
55
+ For more detailed information about running tests, code style and linting,
56
+ please see the [help page](http://exercism.io/languages/python).
57
+
58
+ ## Submitting Incomplete Solutions
59
+
60
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.
@@ -0,0 +1,85 @@
1
+ # -*- coding: utf-8 -*-
2
+ from datetime import datetime
3
+
4
+ ROW_FMT = u'{{:<{1}}} | {{:<{2}}} | {{:{0}{3}}}'
5
+
6
+
7
+ def truncate(s, length=25):
8
+ if len(s) <= length:
9
+ return s
10
+ return s[:length - 3] + '...'
11
+
12
+
13
+ class LCInfo(object):
14
+ def __init__(self, locale, currency, columns):
15
+ self.columns = columns
16
+ if locale == 'en_US':
17
+ headers = ['Date', 'Description', 'Change']
18
+ self.datefmt = '{0.month:02}/{0.day:02}/{0.year:04}'
19
+ self.cur_fmt = u'{}{}{}{}'
20
+ self.lead_neg = '('
21
+ self.trail_neg = ')'
22
+ self.thousands = ','
23
+ self.decimal = '.'
24
+ elif locale == 'nl_NL':
25
+ headers = ['Datum', 'Omschrijving', 'Verandering']
26
+ self.datefmt = '{0.day:02}-{0.month:02}-{0.year:04}'
27
+ self.cur_fmt = u'{1} {0}{2}{3}'
28
+ self.lead_neg = '-'
29
+ self.trail_neg = ' '
30
+ self.thousands = '.'
31
+ self.decimal = ','
32
+ fmt = ROW_FMT.format('<', *columns)
33
+ self.headers = fmt.format(*headers)
34
+ self.cur_symbol = {
35
+ 'USD': '$',
36
+ 'EUR': u'€',
37
+ }.get(currency)
38
+
39
+ def number(self, n):
40
+ n_int, n_float = divmod(abs(n), 100)
41
+ n_int_parts = []
42
+ while n_int > 0:
43
+ n_int, x = divmod(n_int, 1000)
44
+ n_int_parts.insert(0, str(x))
45
+ return '{}{}{:02}'.format(
46
+ self.thousands.join(n_int_parts) or '0',
47
+ self.decimal,
48
+ n_float,
49
+ )
50
+
51
+ def currency(self, change):
52
+ return self.cur_fmt.format(
53
+ self.lead_neg if change < 0 else '',
54
+ self.cur_symbol,
55
+ self.number(change),
56
+ self.trail_neg if change < 0 else ' ',
57
+ )
58
+
59
+ def entry(self, entry):
60
+ date, change, desc = entry
61
+ fmt = ROW_FMT.format('>', *self.columns)
62
+ return fmt.format(
63
+ self.datefmt.format(date),
64
+ truncate(desc),
65
+ self.currency(change),
66
+ )
67
+
68
+ def table(self, entries):
69
+ lines = [self.headers]
70
+ lines.extend(map(self.entry, sorted(entries)))
71
+ return '\n'.join(lines)
72
+
73
+
74
+ def create_entry(date, description, change):
75
+ return (
76
+ datetime.strptime(date, '%Y-%m-%d'),
77
+ change,
78
+ description
79
+ )
80
+
81
+
82
+ def format_entries(currency, locale, entries):
83
+ columns = (10, 25, 13)
84
+ lcinfo = LCInfo(locale, currency, columns)
85
+ return lcinfo.table(entries)
@@ -0,0 +1,298 @@
1
+ # -*- coding: utf-8 -*-
2
+ from datetime import datetime
3
+
4
+
5
+ class LedgerEntry(object):
6
+ def __init__(self):
7
+ self.date = None
8
+ self.description = None
9
+ self.change = None
10
+
11
+
12
+ def create_entry(date, description, change):
13
+ entry = LedgerEntry()
14
+ entry.date = datetime.strptime(date, '%Y-%m-%d')
15
+ entry.description = description
16
+ entry.change = change
17
+ return entry
18
+
19
+
20
+ def format_entries(currency, locale, entries):
21
+ if locale == 'en_US':
22
+ # Generate Header Row
23
+ table = 'Date'
24
+ for _ in range(7):
25
+ table += ' '
26
+ table += '| Description'
27
+ for _ in range(15):
28
+ table += ' '
29
+ table += '| Change'
30
+ for _ in range(7):
31
+ table += ' '
32
+
33
+ while len(entries) > 0:
34
+ table += '\n'
35
+
36
+ # Find next entry in order
37
+ min_entry_index = -1
38
+ for i in range(len(entries)):
39
+ entry = entries[i]
40
+ if min_entry_index < 0:
41
+ min_entry_index = i
42
+ continue
43
+ min_entry = entries[min_entry_index]
44
+ if entry.date < min_entry.date:
45
+ min_entry_index = i
46
+ continue
47
+ if (
48
+ entry.date == min_entry.date and
49
+ entry.change < min_entry.change
50
+ ):
51
+ min_entry_index = i
52
+ continue
53
+ if (
54
+ entry.date == min_entry.date and
55
+ entry.change == min_entry.change and
56
+ entry.description < min_entry.description
57
+ ):
58
+ min_entry_index = i
59
+ continue
60
+ entry = entries[min_entry_index]
61
+ entries.pop(min_entry_index)
62
+
63
+ # Write entry date to table
64
+ month = entry.date.month
65
+ month = str(month)
66
+ if len(month) < 2:
67
+ month = '0' + month
68
+ date_str = month
69
+ date_str += '/'
70
+ day = entry.date.day
71
+ day = str(day)
72
+ if len(day) < 2:
73
+ day = '0' + day
74
+ date_str += day
75
+ date_str += '/'
76
+ year = entry.date.year
77
+ year = str(year)
78
+ while len(year) < 4:
79
+ year = '0' + year
80
+ date_str += year
81
+ table += date_str
82
+ table += ' | '
83
+
84
+ # Write entry description to table
85
+ # Truncate if necessary
86
+ if len(entry.description) > 25:
87
+ for i in range(22):
88
+ table += entry.description[i]
89
+ table += '...'
90
+ else:
91
+ for i in range(25):
92
+ if len(entry.description) > i:
93
+ table += entry.description[i]
94
+ else:
95
+ table += ' '
96
+ table += ' | '
97
+
98
+ # Write entry change to table
99
+ if currency == 'USD':
100
+ change_str = ''
101
+ if entry.change < 0:
102
+ change_str = '('
103
+ change_str += '$'
104
+ change_dollar = abs(int(entry.change / 100.0))
105
+ dollar_parts = []
106
+ while change_dollar > 0:
107
+ dollar_parts.insert(0, str(change_dollar % 1000))
108
+ change_dollar = change_dollar // 1000
109
+ if len(dollar_parts) == 0:
110
+ change_str += '0'
111
+ else:
112
+ while True:
113
+ change_str += dollar_parts[0]
114
+ dollar_parts.pop(0)
115
+ if len(dollar_parts) == 0:
116
+ break
117
+ change_str += ','
118
+ change_str += '.'
119
+ change_cents = abs(entry.change) % 100
120
+ change_cents = str(change_cents)
121
+ if len(change_cents) < 2:
122
+ change_cents = '0' + change_cents
123
+ change_str += change_cents
124
+ if entry.change < 0:
125
+ change_str += ')'
126
+ else:
127
+ change_str += ' '
128
+ while len(change_str) < 13:
129
+ change_str = ' ' + change_str
130
+ table += change_str
131
+ elif currency == 'EUR':
132
+ change_str = ''
133
+ if entry.change < 0:
134
+ change_str = '('
135
+ change_str += u'€'
136
+ change_euro = abs(int(entry.change / 100.0))
137
+ euro_parts = []
138
+ while change_euro > 0:
139
+ euro_parts.insert(0, str(change_euro % 1000))
140
+ change_euro = change_euro // 1000
141
+ if len(euro_parts) == 0:
142
+ change_str += '0'
143
+ else:
144
+ while True:
145
+ change_str += euro_parts[0]
146
+ euro_parts.pop(0)
147
+ if len(euro_parts) == 0:
148
+ break
149
+ change_str += ','
150
+ change_str += '.'
151
+ change_cents = abs(entry.change) % 100
152
+ change_cents = str(change_cents)
153
+ if len(change_cents) < 2:
154
+ change_cents = '0' + change_cents
155
+ change_str += change_cents
156
+ if entry.change < 0:
157
+ change_str += ')'
158
+ else:
159
+ change_str += ' '
160
+ while len(change_str) < 13:
161
+ change_str = ' ' + change_str
162
+ table += change_str
163
+ return table
164
+ elif locale == 'nl_NL':
165
+ # Generate Header Row
166
+ table = 'Datum'
167
+ for _ in range(6):
168
+ table += ' '
169
+ table += '| Omschrijving'
170
+ for _ in range(14):
171
+ table += ' '
172
+ table += '| Verandering'
173
+ for _ in range(2):
174
+ table += ' '
175
+
176
+ while len(entries) > 0:
177
+ table += '\n'
178
+
179
+ # Find next entry in order
180
+ min_entry_index = -1
181
+ for i in range(len(entries)):
182
+ entry = entries[i]
183
+ if min_entry_index < 0:
184
+ min_entry_index = i
185
+ continue
186
+ min_entry = entries[min_entry_index]
187
+ if entry.date < min_entry.date:
188
+ min_entry_index = i
189
+ continue
190
+ if (
191
+ entry.date == min_entry.date and
192
+ entry.change < min_entry.change
193
+ ):
194
+ min_entry_index = i
195
+ continue
196
+ if (
197
+ entry.date == min_entry.date and
198
+ entry.change == min_entry.change and
199
+ entry.description < min_entry.description
200
+ ):
201
+ min_entry_index = i
202
+ continue
203
+ entry = entries[min_entry_index]
204
+ entries.pop(min_entry_index)
205
+
206
+ # Write entry date to table
207
+ day = entry.date.day
208
+ day = str(day)
209
+ if len(day) < 2:
210
+ day = '0' + day
211
+ date_str = day
212
+ date_str += '-'
213
+ month = entry.date.month
214
+ month = str(month)
215
+ if len(month) < 2:
216
+ month = '0' + month
217
+ date_str += month
218
+ date_str += '-'
219
+ year = entry.date.year
220
+ year = str(year)
221
+ while len(year) < 4:
222
+ year = '0' + year
223
+ date_str += year
224
+ table += date_str
225
+ table += ' | '
226
+
227
+ # Write entry description to table
228
+ # Truncate if necessary
229
+ if len(entry.description) > 25:
230
+ for i in range(22):
231
+ table += entry.description[i]
232
+ table += '...'
233
+ else:
234
+ for i in range(25):
235
+ if len(entry.description) > i:
236
+ table += entry.description[i]
237
+ else:
238
+ table += ' '
239
+ table += ' | '
240
+
241
+ # Write entry change to table
242
+ if currency == 'USD':
243
+ change_str = '$ '
244
+ if entry.change < 0:
245
+ change_str += '-'
246
+ change_dollar = abs(int(entry.change / 100.0))
247
+ dollar_parts = []
248
+ while change_dollar > 0:
249
+ dollar_parts.insert(0, str(change_dollar % 1000))
250
+ change_dollar = change_dollar // 1000
251
+ if len(dollar_parts) == 0:
252
+ change_str += '0'
253
+ else:
254
+ while True:
255
+ change_str += dollar_parts[0]
256
+ dollar_parts.pop(0)
257
+ if len(dollar_parts) == 0:
258
+ break
259
+ change_str += '.'
260
+ change_str += ','
261
+ change_cents = abs(entry.change) % 100
262
+ change_cents = str(change_cents)
263
+ if len(change_cents) < 2:
264
+ change_cents = '0' + change_cents
265
+ change_str += change_cents
266
+ change_str += ' '
267
+ while len(change_str) < 13:
268
+ change_str = ' ' + change_str
269
+ table += change_str
270
+ elif currency == 'EUR':
271
+ change_str = u'€ '
272
+ if entry.change < 0:
273
+ change_str += '-'
274
+ change_euro = abs(int(entry.change / 100.0))
275
+ euro_parts = []
276
+ while change_euro > 0:
277
+ euro_parts.insert(0, str(change_euro % 1000))
278
+ change_euro = change_euro // 1000
279
+ if len(euro_parts) == 0:
280
+ change_str += '0'
281
+ else:
282
+ while True:
283
+ change_str += euro_parts[0]
284
+ euro_parts.pop(0)
285
+ if len(euro_parts) == 0:
286
+ break
287
+ change_str += '.'
288
+ change_str += ','
289
+ change_cents = abs(entry.change) % 100
290
+ change_cents = str(change_cents)
291
+ if len(change_cents) < 2:
292
+ change_cents = '0' + change_cents
293
+ change_str += change_cents
294
+ change_str += ' '
295
+ while len(change_str) < 13:
296
+ change_str = ' ' + change_str
297
+ table += change_str
298
+ return table
@@ -0,0 +1,147 @@
1
+ # -*- coding: utf-8 -*-
2
+ import unittest
3
+
4
+ from ledger import format_entries, create_entry
5
+
6
+
7
+ class LedgerTest(unittest.TestCase):
8
+ maxDiff = 5000
9
+
10
+ def test_empty_ledger(self):
11
+ currency = 'USD'
12
+ locale = 'en_US'
13
+ entries = []
14
+ expected = 'Date | Description | Change '
15
+ self.assertEqual(format_entries(currency, locale, entries), expected)
16
+
17
+ def test_one_entry(self):
18
+ currency = 'USD'
19
+ locale = 'en_US'
20
+ entries = [
21
+ create_entry('2015-01-01', 'Buy present', -1000),
22
+ ]
23
+ expected = '\n'.join([
24
+ 'Date | Description | Change ',
25
+ '01/01/2015 | Buy present | ($10.00)',
26
+ ])
27
+ self.assertEqual(format_entries(currency, locale, entries), expected)
28
+
29
+ def test_credit_and_debit(self):
30
+ currency = 'USD'
31
+ locale = 'en_US'
32
+ entries = [
33
+ create_entry('2015-01-02', 'Get present', 1000),
34
+ create_entry('2015-01-01', 'Buy present', -1000),
35
+ ]
36
+ expected = '\n'.join([
37
+ 'Date | Description | Change ',
38
+ '01/01/2015 | Buy present | ($10.00)',
39
+ '01/02/2015 | Get present | $10.00 ',
40
+ ])
41
+ self.assertEqual(format_entries(currency, locale, entries), expected)
42
+
43
+ def test_multiple_entries_on_same_date_ordered_by_description(self):
44
+ currency = 'USD'
45
+ locale = 'en_US'
46
+ entries = [
47
+ create_entry('2015-01-02', 'Get present', 1000),
48
+ create_entry('2015-01-01', 'Buy present', -1000),
49
+ ]
50
+ expected = '\n'.join([
51
+ 'Date | Description | Change ',
52
+ '01/01/2015 | Buy present | ($10.00)',
53
+ '01/02/2015 | Get present | $10.00 ',
54
+ ])
55
+ self.assertEqual(format_entries(currency, locale, entries), expected)
56
+
57
+ def test_final_order_tie_breaker_is_change(self):
58
+ currency = 'USD'
59
+ locale = 'en_US'
60
+ entries = [
61
+ create_entry('2015-01-01', 'Something', 0),
62
+ create_entry('2015-01-01', 'Something', -1),
63
+ create_entry('2015-01-01', 'Something', 1),
64
+ ]
65
+ expected = '\n'.join([
66
+ 'Date | Description | Change ',
67
+ '01/01/2015 | Something | ($0.01)',
68
+ '01/01/2015 | Something | $0.00 ',
69
+ '01/01/2015 | Something | $0.01 ',
70
+ ])
71
+ self.assertEqual(format_entries(currency, locale, entries), expected)
72
+
73
+ def test_overlong_description(self):
74
+ currency = 'USD'
75
+ locale = 'en_US'
76
+ entries = [
77
+ create_entry('2015-01-01', 'Freude schoner Gotterfunken', -123456),
78
+ ]
79
+ expected = '\n'.join([
80
+ 'Date | Description | Change ',
81
+ '01/01/2015 | Freude schoner Gotterf... | ($1,234.56)',
82
+ ])
83
+ self.assertEqual(format_entries(currency, locale, entries), expected)
84
+
85
+ def test_euros(self):
86
+ currency = 'EUR'
87
+ locale = 'en_US'
88
+ entries = [
89
+ create_entry('2015-01-01', 'Buy present', -1000),
90
+ ]
91
+ expected = '\n'.join([
92
+ 'Date | Description | Change ',
93
+ u'01/01/2015 | Buy present | (€10.00)',
94
+ ])
95
+ self.assertEqual(format_entries(currency, locale, entries), expected)
96
+
97
+ def test_dutch_locale(self):
98
+ currency = 'USD'
99
+ locale = 'nl_NL'
100
+ entries = [
101
+ create_entry('2015-03-12', 'Buy present', 123456),
102
+ ]
103
+ expected = '\n'.join([
104
+ 'Datum | Omschrijving | Verandering ',
105
+ '12-03-2015 | Buy present | $ 1.234,56 ',
106
+ ])
107
+ self.assertEqual(format_entries(currency, locale, entries), expected)
108
+
109
+ def test_dutch_locale_and_euros(self):
110
+ currency = 'EUR'
111
+ locale = 'nl_NL'
112
+ entries = [
113
+ create_entry('2015-03-12', 'Buy present', 123456),
114
+ ]
115
+ expected = '\n'.join([
116
+ 'Datum | Omschrijving | Verandering ',
117
+ u'12-03-2015 | Buy present | € 1.234,56 ',
118
+ ])
119
+ self.assertEqual(format_entries(currency, locale, entries), expected)
120
+
121
+ def test_dutch_negative_number_with_3_digits_before_decimal_point(self):
122
+ currency = 'USD'
123
+ locale = 'nl_NL'
124
+ entries = [
125
+ create_entry('2015-03-12', 'Buy present', -12345),
126
+ ]
127
+ expected = '\n'.join([
128
+ 'Datum | Omschrijving | Verandering ',
129
+ '12-03-2015 | Buy present | $ -123,45 ',
130
+ ])
131
+ self.assertEqual(format_entries(currency, locale, entries), expected)
132
+
133
+ def test_american_negative_number_with_3_digits_before_decimal_point(self):
134
+ currency = 'USD'
135
+ locale = 'en_US'
136
+ entries = [
137
+ create_entry('2015-03-12', 'Buy present', -12345),
138
+ ]
139
+ expected = '\n'.join([
140
+ 'Date | Description | Change ',
141
+ '03/12/2015 | Buy present | ($123.45)',
142
+ ])
143
+ self.assertEqual(format_entries(currency, locale, entries), expected)
144
+
145
+
146
+ if __name__ == '__main__':
147
+ unittest.main()