git ssb

0+

Rômulo Alves / money-block



Commit ecf6563965cf3a734a59dfba74cd29977efbe9d6

initial commit

romuloalves committed on 10/23/2021, 12:31:09 AM

Files changed

.editorconfigadded
.gitignoreadded
README.mdadded
package-lock.jsonadded
package.jsonadded
src/domain/block.jsadded
src/domain/group.jsadded
src/domain/number.jsadded
src/domain/record.jsadded
src/domain/types.jsadded
src/helpers/block.jsadded
src/index.jsadded
tests/__mocks__/four-groups-with-balance-ignore-group.txtadded
tests/__mocks__/four-groups-with-balance.txtadded
tests/__mocks__/four-groups-with-total.txtadded
tests/__mocks__/four-groups.txtadded
tests/__mocks__/single-group.txtadded
tests/__mocks__/two-groups.txtadded
tests/helpers/mock.jsadded
tests/index.test.jsadded
.editorconfigView
@@ -1,0 +1,14 @@
1 +
2 +[*]
3 +indent_style = space
4 +indent_size = 2
5 +end_of_line = lf
6 +charset = utf-8
7 +trim_trailing_whitespace = true
8 +insert_final_newline = true
9 +
10 +[*.json]
11 +insert_final_newline = ignore
12 +
13 +[*.md]
14 +trim_trailing_whitespace = false
.gitignoreView
@@ -1,0 +1,2 @@
1 +.DS_Store
2 +node_modules/
README.mdView
@@ -1,0 +1,76 @@
1 +# money-block
2 +
3 +> Take notes of your earnings and expenses and process them over money-block to calculate everything for you
4 +
5 +## Symbols
6 +
7 +`$`: balance - Auto generated, shows your general balance
8 +
9 +`|`: group - Represents an account or card, like savings or some credit card
10 +
11 +`|-` other group - This represents groups as `|`, except that they won't be added to the general balance
12 +
13 +`+`: earning - Any more your received
14 +
15 +`-`: expense - Money you wasted
16 +
17 +`=`: total - Auto generated, balance of a group
18 +
19 +## Examples
20 +
21 +### With one group
22 +
23 +If you write this:
24 +```
25 +| savings account
26 ++ 123 first deposit
27 ++ 111 second deposit
28 +```
29 +
30 +`money-block` would return:
31 +```
32 +$ 234
33 +
34 +| savings account
35 ++ 123 first deposit
36 ++ 111 second deposit
37 += 234
38 +
39 +```
40 +
41 +### How to use
42 +
43 +```javascript
44 +const moneyBlockProcess = require('money-block');
45 +
46 +const myMoneyNotes = `
47 +| savings account
48 ++ 123 first deposit
49 ++ 111 second deposit
50 +`;
51 +
52 +console.log(moneyBlockProcess(myMoneyNotes));
53 +/* OUTPUT:
54 +$ 234
55 +
56 +| savings account
57 ++ 123 first deposit
58 ++ 111 second deposit
59 += 234
60 +
61 +*/
62 +```
63 +
64 +## Testing
65 +
66 +### With npm
67 +```
68 +npm install
69 +npm test
70 +```
71 +
72 +### With yarn
73 +```
74 +yarn install
75 +yarn test
76 +```
package-lock.jsonView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 0 bytes
New file size: 225370 bytes
package.jsonView
@@ -1,0 +1,15 @@
1 +{
2 + "name": "money-block",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "index.js",
6 + "scripts": {
7 + "test": "ava"
8 + },
9 + "keywords": [],
10 + "author": "",
11 + "license": "ISC",
12 + "devDependencies": {
13 + "ava": "^3.15.0"
14 + }
15 +}
src/domain/block.jsView
@@ -1,0 +1,72 @@
1 +const { blockIdentifiers, blockTypes } = require('./types');
2 +const { ignoreGroupInBalance, readGroup } = require('./group');
3 +const { readRecord } = require('./record');
4 +const { writeNumber } = require('./number');
5 +
6 +const GROUP_END_TYPE = 'group-end';
7 +const IGNORED = 'ignored';
8 +
9 +function getBlockType(line) {
10 + const lineFirstChar = line[0];
11 +
12 + switch (lineFirstChar) {
13 + case blockIdentifiers.GROUP:
14 + return blockTypes.GROUP;
15 + case blockIdentifiers.EARNING:
16 + return blockTypes.EARNING;
17 + case blockIdentifiers.EXPENSE:
18 + return blockTypes.EXPENSE;
19 +
20 + case '':
21 + case ' ':
22 + case undefined:
23 + return GROUP_END_TYPE;
24 +
25 + default:
26 + return IGNORED;
27 + }
28 +}
29 +
30 +module.exports.readBlock = function readBlock(data) {
31 + const lines = data.split('\n');
32 + const groupsArr = [];
33 + let currentGroup = null;
34 +
35 + for (let index = 0; index < lines.length; index++) {
36 + const line = lines[index].trim();
37 + const blockType = getBlockType(line);
38 +
39 + switch (blockType) {
40 + case blockTypes.GROUP: // Create a group
41 + currentGroup = readGroup(line);
42 + continue;
43 + case blockTypes.EARNING:
44 + case blockTypes.EXPENSE: // Add record to current group
45 + currentGroup.records.push(readRecord(line));
46 + continue;
47 + case GROUP_END_TYPE: // Push group to groups array
48 + if (currentGroup) groupsArr.push({ ...currentGroup });
49 + currentGroup = null;
50 + continue;
51 + }
52 + }
53 +
54 + return groupsArr;
55 +};
56 +
57 +module.exports.writeBlock = function writeBlock(balance, groupsArr) {
58 + function writeGroups(groups) {
59 + return groups.reduce((text, group, groupIndex) => {
60 + return `${text}${groupIndex > 0 ? `
61 +` : ''}${group.toString()}
62 +${group.records.reduce((recordsText, record, recordIndex) => `${recordsText}${recordIndex > 0 ? `
63 +` : ''}${record.toString()}`, '')}
64 +${blockIdentifiers.TOTAL} ${writeNumber(group.total)}
65 +`;
66 + }, '');
67 + }
68 +
69 + return `${blockIdentifiers.BALANCE} ${writeNumber(balance)}
70 +
71 +${writeGroups(groupsArr)}`;
72 +};
src/domain/group.jsView
@@ -1,0 +1,25 @@
1 +const { blockTypes, blockIdentifiers } = require('./types');
2 +
3 +const IGNORE_GROUP_IN_BALANCE = '-';
4 +
5 +function ignoreGroupInBalance(groupLine) {
6 + if (groupLine[0] !== blockIdentifiers.GROUP) {
7 + throw Error('Line is not a group: ' + groupLine);
8 + }
9 +
10 + return groupLine[1] === IGNORE_GROUP_IN_BALANCE;
11 +}
12 +module.exports.ignoreGroupInBalance = ignoreGroupInBalance;
13 +
14 +module.exports.readGroup = function readGroup(line) {
15 + const groupName = line.split(' ').slice(1).join(' ');
16 + const shouldIgnoreGroupInBalance = ignoreGroupInBalance(line);
17 +
18 + return {
19 + name: groupName,
20 + ignoreInBalance: shouldIgnoreGroupInBalance,
21 + records: [],
22 + total: 0,
23 + toString: () => `${blockIdentifiers.GROUP}${shouldIgnoreGroupInBalance ? IGNORE_GROUP_IN_BALANCE : ''} ${groupName}`
24 + };
25 +};
src/domain/number.jsView
@@ -1,0 +1,3 @@
1 +const isInteger = n => Number(n) === n && n % 1 === 0;
2 +
3 +module.exports.writeNumber = n => isInteger(n) ? n : Number.parseFloat(n).toFixed(2);
src/domain/record.jsView
@@ -1,0 +1,25 @@
1 +const { blockTypes, blockIdentifiers } = require('./types');
2 +const { writeNumber } = require('./number');
3 +
4 +module.exports.readRecord = function readRecord(line) {
5 + const [symbol, value, ...nameArr] = line.split(' ');
6 +
7 + if (![blockIdentifiers.EARNING, blockIdentifiers.EXPENSE].includes(symbol)) {
8 + throw Error(`Record not in right format, expected first chat to be '+' or '-': ${line}`);
9 + }
10 +
11 + if (isNaN(value)) {
12 + throw Error('Wrong monetary value to record: ' + line);
13 + }
14 +
15 + const isEarning = symbol === '+';
16 + const num = Number(value);
17 + const name = nameArr.join(' ');
18 +
19 + return {
20 + name,
21 + symbol,
22 + value: isEarning ? num : -num,
23 + toString: () => `${symbol} ${writeNumber(num)} ${name}`,
24 + };
25 +};
src/domain/types.jsView
@@ -1,0 +1,15 @@
1 +const blockTypes = {
2 + GROUP: 'group',
3 + EARNING: 'earning',
4 + EXPENSE: 'expense',
5 +};
6 +module.exports.blockTypes = blockTypes;
7 +
8 +const blockIdentifiers = {
9 + BALANCE: '$',
10 + GROUP: '|',
11 + EARNING: '+',
12 + EXPENSE: '-',
13 + TOTAL: '=',
14 +};
15 +module.exports.blockIdentifiers = blockIdentifiers;
src/helpers/block.jsView
@@ -1,0 +1,36 @@
1 +const IGNORE_GROUP_IN_BALANCE = '-';
2 +
3 +function getValue(line) {
4 + const blockType = getBlockType(line);
5 + if (![blockTypes.EARNING, blockTypes.EXPENSE].includes(blockType)) {
6 + throw Error('Line does not have value format: ' + line);
7 + }
8 +
9 + const parts = line.trim().split(' ');
10 +
11 + if (parts.length < 2) {
12 + throw Error('Line not in right format: ' + line);
13 + }
14 +
15 + const value = parts[1];
16 +
17 + if (isNaN(value)) {
18 + throw Error('Wrong value to line: ' + line);
19 + }
20 +
21 + const num = Number(value);
22 +
23 + return blockType === blockTypes.EARNING ? num : num * -1;
24 +}
25 +module.exports.getValue = getValue;
26 +
27 +function ignoreGroupInBalance(groupLine) {
28 + const blockType = getBlockType(groupLine);
29 +
30 + if (blockType !== blockTypes.GROUP) {
31 + throw Error('Line is not a group');
32 + }
33 +
34 + return groupLine[1] === IGNORE_GROUP_IN_BALANCE;
35 +}
36 +module.exports.ignoreGroupInBalance = ignoreGroupInBalance;
src/index.jsView
@@ -1,0 +1,23 @@
1 +const { readBlock, writeBlock } = require('./domain/block');
2 +
3 +/*
4 + * data {string} - Data to be parsed
5 + */
6 +module.exports = function process(lines) {
7 + const groupsArr = readBlock(lines);
8 +
9 + // Calculate total of each group
10 + const groupsWithTotals = groupsArr.map(g => ({
11 + ...g,
12 + total: g.records.reduce((total, record) => total + record.value, g.total),
13 + }));
14 +
15 + // Calculate balance
16 + const balance = groupsWithTotals.reduce((balance, g) => {
17 + if (g.ignoreInBalance) return balance;
18 +
19 + return balance + g.total;
20 + }, 0);
21 +
22 + return writeBlock(balance, groupsWithTotals);
23 +};
tests/__mocks__/four-groups-with-balance-ignore-group.txtView
@@ -1,0 +1,21 @@
1 +$ 12402867.05
2 +
3 +| savings account
4 ++ 123 first deposit
5 ++ 111 second deposit
6 +- 29.48 wasting money
7 ++ 400 saved a lot!!!
8 +
9 +| investing
10 ++ 1240 first deposit
11 ++ 02.40 second deposit
12 ++ 4.13 investing hard!
13 +
14 +| savings account 2
15 ++ 1000 first
16 +- 40 don't know
17 ++ 55.55 more deposited
18 += 1015.55
19 +
20 +|- investing hard
21 ++ 12400000.45 lot of money!!
tests/__mocks__/four-groups-with-balance.txtView
@@ -1,0 +1,21 @@
1 +$ 1234.44
2 +
3 +| savings account
4 ++ 123 first deposit
5 ++ 111 second deposit
6 +- 29.48 wasting money
7 ++ 400 saved a lot!!!
8 +
9 +| investing
10 ++ 1240 first deposit
11 ++ 02.40 second deposit
12 ++ 4.13 investing hard!
13 +
14 +| savings account 2
15 ++ 1000 first
16 +- 40 don't know
17 ++ 55.55 more deposited
18 += 1015.55
19 +
20 +| investing hard
21 ++ 12400000.45 lot of money!!
tests/__mocks__/four-groups-with-total.txtView
@@ -1,0 +1,21 @@
1 +$
2 +
3 +| savings account
4 ++ 123 first deposit
5 ++ 111 second deposit
6 +- 29.48 wasting money
7 ++ 400 saved a lot!!!
8 +
9 +| investing
10 ++ 1240 first deposit
11 ++ 02.40 second deposit
12 ++ 4.13 investing hard!
13 +
14 +| savings account 2
15 ++ 1000 first
16 +- 40 don't know
17 ++ 55.55 more deposited
18 += 1015.55
19 +
20 +| investing hard
21 ++ 12400000.45 lot of money!!
tests/__mocks__/four-groups.txtView
@@ -1,0 +1,20 @@
1 +$
2 +
3 +| savings account
4 ++ 123 first deposit
5 ++ 111 second deposit
6 +- 29.48 wasting money
7 ++ 400 saved a lot!!!
8 +
9 +| investing
10 ++ 1240 first deposit
11 ++ 02.40 second deposit
12 ++ 4.13 investing hard!
13 +
14 +| savings account 2
15 ++ 1000 first
16 +- 40 don't know
17 ++ 55.55 more deposited
18 +
19 +| investing hard
20 ++ 12400000.45 lot of money!!
tests/__mocks__/single-group.txtView
@@ -1,0 +1,5 @@
1 +$
2 +
3 +| savings account
4 ++ 123 first deposit
5 ++ 111 second deposit
tests/__mocks__/two-groups.txtView
@@ -1,0 +1,10 @@
1 +$
2 +
3 +| savings account
4 ++ 123 first deposit
5 ++ 111 second deposit
6 +
7 +| investing
8 ++ 1240 first deposit
9 ++ 02.40 second deposit
10 ++ 4.13 investing hard!
tests/helpers/mock.jsView
@@ -1,0 +1,7 @@
1 +const { readFileSync } = require('fs');
2 +const { join } = require('path');
3 +
4 +module.exports.readMock = function readMock(mockFilename) {
5 + const filePath = join(__dirname, '..', '__mocks__', mockFilename);
6 + return readFileSync(filePath, { encoding: 'utf-8' });
7 +};
tests/index.test.jsView
@@ -1,0 +1,172 @@
1 +const test = require('ava');
2 +
3 +const { readMock } = require('./helpers/mock');
4 +const moneyBlockProcess = require('../src/index');
5 +
6 +test('single group', function(t) {
7 + const data = readMock('single-group.txt');
8 + const expected = `$ 234
9 +
10 +| savings account
11 ++ 123 first deposit
12 ++ 111 second deposit
13 += 234
14 +`;
15 +
16 + const response = moneyBlockProcess(data);
17 +
18 + t.is(response, expected);
19 +});
20 +
21 +test('two groups', function(t) {
22 + const data = readMock('two-groups.txt');
23 + const expected = `$ 1480.53
24 +
25 +| savings account
26 ++ 123 first deposit
27 ++ 111 second deposit
28 += 234
29 +
30 +| investing
31 ++ 1240 first deposit
32 ++ 2.40 second deposit
33 ++ 4.13 investing hard!
34 += 1246.53
35 +`;
36 +
37 + const response = moneyBlockProcess(data);
38 +
39 + t.is(response, expected);
40 +});
41 +
42 +test('four groups', function(t) {
43 + const data = readMock('four-groups.txt');
44 + const expected = `$ 12402867.05
45 +
46 +| savings account
47 ++ 123 first deposit
48 ++ 111 second deposit
49 +- 29.48 wasting money
50 ++ 400 saved a lot!!!
51 += 604.52
52 +
53 +| investing
54 ++ 1240 first deposit
55 ++ 2.40 second deposit
56 ++ 4.13 investing hard!
57 += 1246.53
58 +
59 +| savings account 2
60 ++ 1000 first
61 +- 40 don't know
62 ++ 55.55 more deposited
63 += 1015.55
64 +
65 +| investing hard
66 ++ 12400000.45 lot of money!!
67 += 12400000.45
68 +`;
69 +
70 + const response = moneyBlockProcess(data);
71 +
72 + t.is(response, expected);
73 +});
74 +
75 +test('four groups with total', function(t) {
76 + const data = readMock('four-groups-with-total.txt');
77 + const expected = `$ 12402867.05
78 +
79 +| savings account
80 ++ 123 first deposit
81 ++ 111 second deposit
82 +- 29.48 wasting money
83 ++ 400 saved a lot!!!
84 += 604.52
85 +
86 +| investing
87 ++ 1240 first deposit
88 ++ 2.40 second deposit
89 ++ 4.13 investing hard!
90 += 1246.53
91 +
92 +| savings account 2
93 ++ 1000 first
94 +- 40 don't know
95 ++ 55.55 more deposited
96 += 1015.55
97 +
98 +| investing hard
99 ++ 12400000.45 lot of money!!
100 += 12400000.45
101 +`;
102 +
103 + const response = moneyBlockProcess(data);
104 +
105 + t.is(response, expected);
106 +});
107 +
108 +test('four groups with balance', function(t) {
109 + const data = readMock('four-groups-with-balance.txt');
110 + const expected = `$ 12402867.05
111 +
112 +| savings account
113 ++ 123 first deposit
114 ++ 111 second deposit
115 +- 29.48 wasting money
116 ++ 400 saved a lot!!!
117 += 604.52
118 +
119 +| investing
120 ++ 1240 first deposit
121 ++ 2.40 second deposit
122 ++ 4.13 investing hard!
123 += 1246.53
124 +
125 +| savings account 2
126 ++ 1000 first
127 +- 40 don't know
128 ++ 55.55 more deposited
129 += 1015.55
130 +
131 +| investing hard
132 ++ 12400000.45 lot of money!!
133 += 12400000.45
134 +`;
135 +
136 + const response = moneyBlockProcess(data);
137 +
138 + t.is(response, expected);
139 +});
140 +
141 +test('four groups with balance ignoring last group', function(t) {
142 + const data = readMock('four-groups-with-balance-ignore-group.txt');
143 + const expected = `$ 2866.60
144 +
145 +| savings account
146 ++ 123 first deposit
147 ++ 111 second deposit
148 +- 29.48 wasting money
149 ++ 400 saved a lot!!!
150 += 604.52
151 +
152 +| investing
153 ++ 1240 first deposit
154 ++ 2.40 second deposit
155 ++ 4.13 investing hard!
156 += 1246.53
157 +
158 +| savings account 2
159 ++ 1000 first
160 +- 40 don't know
161 ++ 55.55 more deposited
162 += 1015.55
163 +
164 +|- investing hard
165 ++ 12400000.45 lot of money!!
166 += 12400000.45
167 +`;
168 +
169 + const response = moneyBlockProcess(data);
170 +
171 + t.is(response, expected);
172 +});

Built with git-ssb-web