Skip to content

Commit

Permalink
add more services
Browse files Browse the repository at this point in the history
  • Loading branch information
ppapadatis committed Nov 7, 2022
1 parent 7c12f25 commit d7a300e
Show file tree
Hide file tree
Showing 18 changed files with 285 additions and 61 deletions.
4 changes: 3 additions & 1 deletion .test.env
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
ACCESS_TOKEN=window.app.userSid
EFOOD_ACCESS_TOKEN=window.app.userSid
BOX_ACCESS_TOKEN=window.localStorage.getItem('Box:token')
WOLT_ACCESS_TOKEN=JSON.parse((Object.fromEntries(document.cookie.split('; ').map(v=>v.split(/=(.*)/s).map(decodeURIComponent)))).__wtoken).accessToken
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@

# efood-lifetime-expenses
# food-order-lifetime-expenses

A simple node script that calculates the total amount spent on e-food service.
A simple node script that calculates the total amount spent on food ordering services, based in Greece.

## Before you start

At root level, there's a file named `.test.env`. Rename it to just `.env` and replace the value of `ACCESS_TOKEN` to your access token.
At root level, there's a file named `.test.env`. Rename it to just `.env` and replace the values of `*_ACCESS_TOKEN` to your access tokens.

To get your token from e-food, visit [e-food](https://www.e-food.gr/), login (if not already logged-in), open your browser's dev tools, select Console and type `window.app.userSid`.
To get a token from each service, visit the required service, login (if not already logged-in), open your browser's dev tools, select Console
and type:
- [e-food](https://www.e-food.gr/): `window.app.userSid`.
- [box](https://box.gr/): `window.localStorage.getItem('Box:token')`
- [wolt](https://wolt.com/): `JSON.parse((Object.fromEntries(document.cookie.split('; ').map(v=>v.split(/=(.*)/s).map(decodeURIComponent)))).__wtoken).accessToken`

## Installation

Expand All @@ -24,7 +28,7 @@ If you don't, then advise these files to download and install the correct versio

To calculate total expenses, run the following command
```bash
yarn calculate
yarn calculate --service=<efood/box/wolt>
```

If for any reason installation failed, run `yarn clean` and start all over again.
Expand All @@ -36,15 +40,16 @@ If for any reason installation failed, run `yarn clean` and start all over again

Number of shops: <X>.
Total amount of orders: <Y>.
Period from YYYY-MM-DD to YYYY-MM-DD.
Total amount spent in e-food is €<Z>.
Period from <YYYY-MM-DD> to <YYYY-MM-DD>.
Total amount spent in <SERVICE> is €<Z>.
```

## Roadmap

- Finish tests
- Prettier format for stats
- Add more stats
- ~Add more services~

## License

Expand Down
4 changes: 2 additions & 2 deletions __tests__/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ const { getConfig, CONFIG_MAPPER } = require('../src/config');
describe('config.js', () => {
describe('CONFIG_MAPPER', () => {
test('should return an object with configuration keys', () => {
expect(Object.keys(CONFIG_MAPPER).length).toEqual(1);
expect(Object.keys(CONFIG_MAPPER).length).toEqual(3);
});
});

describe('getConfig', () => {
test('should return the value of an existing configuration key', () => {
expect(getConfig(CONFIG_MAPPER.ACCESS_TOKEN)).toBeTruthy();
expect(getConfig(CONFIG_MAPPER.EFOOD_ACCESS_TOKEN)).toBeTruthy();
});

test('should return undefined if key does not exist', () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/constants.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { ENDPOINTS } = require('../src/constants');
describe('constants.js', () => {
describe('ENDPOINTS', () => {
test('should return an object with constants', () => {
expect(Object.keys(ENDPOINTS).length).toEqual(2);
expect(Object.keys(ENDPOINTS).length).toEqual(3);
});
});
});
2 changes: 1 addition & 1 deletion __tests__/literals.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { resolve, LITERALS_MAPPER } = require('../src/literals');
describe('literals.js', () => {
describe('LITERALS_MAPPER', () => {
test('should return an object with literals', () => {
expect(Object.keys(LITERALS_MAPPER).length).toEqual(6);
expect(Object.keys(LITERALS_MAPPER).length).toEqual(7);
});
});

Expand Down
4 changes: 2 additions & 2 deletions __tests__/parser.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { parseEfoodAsync } = require('../src/parser');
const { parseFoodOrdersAsync } = require('../src/parser');
const { getOrdersFromEfoodAsync } = require('../src/service');

jest.mock('../src/service.js', () => ({
Expand All @@ -16,7 +16,7 @@ describe('parser.js', () => {
test('it should exit if orders is empty', async () => {
getOrdersFromEfoodAsync.mockReturnValue([]);
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
await parseEfoodAsync();
await parseFoodOrdersAsync('efood');
expect(exitSpy).toHaveBeenCalledWith(0);
});
});
Expand Down
2 changes: 1 addition & 1 deletion __tests__/printer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('printer.js', () => {
sum: 1,
};
const log = jest.spyOn(console, 'log');
printStats(data);
printStats('efood', data);
expect(log).toHaveBeenCalledTimes(5);
});
});
Expand Down
9 changes: 6 additions & 3 deletions __tests__/transformer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { orderByHigherCost, getTotalExpenses, getOderDateByIndex } = require('../
const data = [
{
price: 1,
status: 'accepted',
submission_date: '2020-01-01 00:00:00',
restaurant: {
id: 1,
Expand All @@ -11,6 +12,7 @@ const data = [
},
{
price: 2,
status: 'accepted',
submission_date: '2020-01-02 00:00:00',
restaurant: {
id: 2,
Expand All @@ -19,6 +21,7 @@ const data = [
},
{
price: 4,
status: 'accepted',
submission_date: '2020-01-03 00:00:00',
restaurant: {
id: 1,
Expand All @@ -30,7 +33,7 @@ const data = [
describe('transformer.js', () => {
describe('orderByHigherCost', () => {
test('should return an array of objects from another object', () => {
const ordered = orderByHigherCost(data);
const ordered = orderByHigherCost('efood', data);
expect(ordered).toEqual([
{ id: 1, restaurant: 'name', orders: 2, price: 5 },
{ id: 2, restaurant: 'othername', orders: 1, price: 2 },
Expand All @@ -40,14 +43,14 @@ describe('transformer.js', () => {

describe('getTotalExpenses', () => {
test('should return the sum of all price attributes in an object', () => {
const sum = getTotalExpenses(data);
const sum = getTotalExpenses('efood', data);
expect(sum).toEqual('7.00');
});
});

describe('getOderDateByIndex', () => {
test('should return the date attribute for a given index from an object', () => {
const date = getOderDateByIndex(data, 0);
const date = getOderDateByIndex('efood', data, 0);
expect(date).toEqual('2020-01-01');
});
});
Expand Down
13 changes: 11 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { parseEfoodAsync } = require('./src/parser');
const { parseFoodOrdersAsync } = require('./src/parser');
const { FOOD_SERVICE } = require('./src/constants');

/**
* Subscribe to unhandledRejection event.
Expand All @@ -19,5 +20,13 @@ process.on('uncaughtException', (error) => {
* Entry point.
*/
(async () => {
await parseEfoodAsync();
const argv = require('minimist')(process.argv.slice(2));
const { service } = argv;

if (!Object.values(FOOD_SERVICE).includes(service)) {
process.exit(1);
return;
}

await parseFoodOrdersAsync(service);
})();
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "efood-lifetime-expenses",
"version": "1.1.0",
"description": "A simple node script that calculates the total amount spent on e-food service.",
"name": "food-order-lifetime-expenses",
"version": "2.0.0",
"description": "A simple node script that calculates the total amount spent on food ordering services, based in Greece.",
"main": "index.js",
"scripts": {
"node:env": "node -r dotenv/config index.js",
Expand All @@ -13,12 +13,14 @@
"license": "ISC",
"repository": {
"type": "git",
"url": "https://github.com/ppapadatis/efood-lifetime-expenses"
"url": "https://github.com/ppapadatis/food-order-lifetime-expenses"
},
"dependencies": {
"axios": "1.1.3",
"date-fns": "2.29.3",
"dotenv": "14.3.0",
"lodash": "4.17.21"
"lodash": "4.17.21",
"minimist": "1.2.7"
},
"devDependencies": {
"@babel/core": "7.20.2",
Expand Down
8 changes: 6 additions & 2 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const { get } = require('lodash');

const CONFIG = Object.freeze({
ACCESS_TOKEN: process.env.ACCESS_TOKEN,
EFOOD_ACCESS_TOKEN: process.env.EFOOD_ACCESS_TOKEN,
BOX_ACCESS_TOKEN: process.env.BOX_ACCESS_TOKEN,
WOLT_ACCESS_TOKEN: process.env.WOLT_ACCESS_TOKEN,
});

const CONFIG_MAPPER = Object.freeze({
ACCESS_TOKEN: 'ACCESS_TOKEN',
EFOOD_ACCESS_TOKEN: 'EFOOD_ACCESS_TOKEN',
BOX_ACCESS_TOKEN: 'BOX_ACCESS_TOKEN',
WOLT_ACCESS_TOKEN: 'WOLT_ACCESS_TOKEN',
});

/**
Expand Down
21 changes: 19 additions & 2 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
const FOOD_SERVICE = Object.freeze({
EFOOD: 'efood',
BOX: 'box',
WOLT: 'wolt',
});

const ENDPOINTS = Object.freeze({
BASE: 'https://api.e-food.gr/api/v1',
ORDERS: 'user/orders/history?limit=100&offset={{OFST}}&mode=extended',
[FOOD_SERVICE.EFOOD]: {
BASE: 'https://api.e-food.gr/api/v1',
ORDERS: 'user/orders/history?limit=100&offset={{OFST}}&mode=extended',
},
[FOOD_SERVICE.BOX]: {
BASE: 'https://box-client.wavecxm.com/api',
ORDERS: 'orders/get/userlist',
},
[FOOD_SERVICE.WOLT]: {
BASE: 'https://restaurant-api.wolt.com/v2',
ORDERS: 'order_details/?limit=100&skip={{OFST}}',
},
});

module.exports = {
FOOD_SERVICE,
ENDPOINTS,
};
4 changes: 3 additions & 1 deletion src/literals.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const DICTIONARY = Object.freeze({
TOTAL_SHOPS: 'Number of shops: {0}.',
TOTAL_ORDERS: 'Total amount of orders: {0}.',
SPREE_PERIOD: 'Period from {0} to {1}.',
AMOUNT_SPENT: 'Total amount spent in e-food is €{0}.',
AMOUNT_SPENT: 'Total amount spent in {0} is €{1}.',
ERRORS: {
GENERIC: 'Oops! Something went wrong!',
INVALID_SERVICE: 'Wrong food service entered! Check again your input.',
},
},
});
Expand All @@ -22,6 +23,7 @@ const LITERALS_MAPPER = Object.freeze({
MESSAGES_SPREE_PERIOD: 'MESSAGES.SPREE_PERIOD',
MESSAGES_AMOUNT_SPENT: 'MESSAGES.AMOUNT_SPENT',
MESSAGES_ERRORS_GENERIC: 'MESSAGES.ERRORS.GENERIC',
MESSAGES_ERRORS_INVALID_SERVICE: 'MESSAGES.ERRORS.INVALID_SERVICE',
});

/**
Expand Down
15 changes: 8 additions & 7 deletions src/parser.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
const { isEmpty } = require('lodash');
const { resolve, LITERALS_MAPPER } = require('./literals');
const { getOrdersFromEfoodAsync } = require('./service');
const { getOrdersFromServiceAsync } = require('./service');
const { orderByHigherCost, getTotalExpenses } = require('./transformer');
const { printStats } = require('./printer');

/**
* Prints various information on e-food stats.
* @param {string} service
* @returns {Promise<void>}
*/
const parseEfoodAsync = async () => {
const parseFoodOrdersAsync = async (service) => {
try {
const orders = await getOrdersFromEfoodAsync();
const orders = await getOrdersFromServiceAsync(service);

if (isEmpty(orders)) {
console.log(resolve(LITERALS_MAPPER.GENERIC_NO_ORDERS));
process.exit(0);
return;
}

const costs = orderByHigherCost(orders);
const sum = getTotalExpenses(orders);
printStats({ orders, costs, sum });
const costs = orderByHigherCost(service, orders);
const sum = getTotalExpenses(service, orders);
printStats(service, { orders, costs, sum });
} catch (error) {
console.error(resolve(LITERALS_MAPPER.MESSAGES_ERRORS_GENERIC));
console.error(error);
Expand All @@ -31,5 +32,5 @@ const parseEfoodAsync = async () => {
};

module.exports = {
parseEfoodAsync,
parseFoodOrdersAsync,
};
9 changes: 5 additions & 4 deletions src/printer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ const { getOderDateByIndex } = require('./transformer');

/**
* Prints a variaty of stats in console.
* @param {string} service
* @param {Array<*>} orders
* @param {Array<*>} costs
* @param {string|number} sum
*/
const printStats = ({ orders, costs, sum }) => {
const printStats = (service, { orders, costs, sum }) => {
console.log(costs);
console.log(resolve(LITERALS_MAPPER.MESSAGES_TOTAL_SHOPS, costs.length));
console.log(resolve(LITERALS_MAPPER.MESSAGES_TOTAL_ORDERS, orders.length));
console.log(
resolve(
LITERALS_MAPPER.MESSAGES_SPREE_PERIOD,
getOderDateByIndex(orders, orders.length - 1),
getOderDateByIndex(orders, 0),
getOderDateByIndex(service, orders, orders.length - 1),
getOderDateByIndex(service, orders, 0),
),
);
console.log(resolve(LITERALS_MAPPER.MESSAGES_AMOUNT_SPENT, sum));
console.log(resolve(LITERALS_MAPPER.MESSAGES_AMOUNT_SPENT, service, sum));
};

module.exports = {
Expand Down
Loading

0 comments on commit d7a300e

Please sign in to comment.