From e818cab839a0c6545fc578e69389daad236ffe74 Mon Sep 17 00:00:00 2001 From: Jacob Date: Tue, 20 Sep 2022 21:37:26 -0500 Subject: [PATCH] Add stock support to allocations, update parks image filter --- controllers/app-config.js | 2 +- .../app-settings/allocation-settings.tsx | 20 +++- models/app-config.js | 1 + nodejs/allocation.js | 96 +++++++++++++------ nodejs/parks.js | 20 +--- 5 files changed, 91 insertions(+), 48 deletions(-) diff --git a/controllers/app-config.js b/controllers/app-config.js index 0488ddd..548f079 100644 --- a/controllers/app-config.js +++ b/controllers/app-config.js @@ -84,7 +84,7 @@ exports.saveConfig = async (req, res, next) => { return next('No config data to save'); } - if (data.appName === 'parks' && data.parks.visited.length > 0) { + if (data.appName === 'parks' && data.parks.visited?.length > 0) { // eslint-disable-next-line no-underscore-dangle data.parks.visited = await parks.uploadAllImages(data.parks, req.user._id); } diff --git a/frontend/src/pages/app-settings/allocation-settings.tsx b/frontend/src/pages/app-settings/allocation-settings.tsx index 7b3c4f2..5532301 100644 --- a/frontend/src/pages/app-settings/allocation-settings.tsx +++ b/frontend/src/pages/app-settings/allocation-settings.tsx @@ -3,6 +3,7 @@ import makeStyles from '@mui/styles/makeStyles'; import { useForm } from 'react-hook-form'; import { Button, Form, EditableTable } from '@schulzetenberg/component-library'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; const useStyles = makeStyles(() => ({ textCenter: { @@ -17,6 +18,8 @@ type List = { label: string; value: string; isStock: boolean; + isETF: boolean; + isOther: boolean; tableData?: { id: number }; }; @@ -64,12 +67,25 @@ const AllocationSettings: React.FC<{ title="Assets" columns={[ { title: 'Name/Stock Ticker', field: 'label' }, + // TODO: change checkobxes to 1 dropdown { - title: 'Is Stock/ETF', + title: 'Is Individual Stock', field: 'isStock', type: 'boolean', - render: (x: any) => (x.isStock ? 'True' : 'False'), + render: (x: any) => (x.isStock ? : ''), }, + { + title: 'Is Mutual Fund or ETF', + field: 'isETF', + type: 'boolean', + render: (x: any) => (x.isETF ? : ''), + }, + // { + // title: 'Is Other Asset', + // field: 'isOther', + // type: 'boolean', + // render: (x: any) => (x.isOther ? : ''), + // }, { title: 'Total Value $', field: 'value', type: 'currency' }, ]} /> diff --git a/models/app-config.js b/models/app-config.js index 2c2cf28..6811c79 100644 --- a/models/app-config.js +++ b/models/app-config.js @@ -101,6 +101,7 @@ const appConfigSchema = new mongoose.Schema( }, label: '', isStock: false, + isETF: false, value: Number, }, ], diff --git a/nodejs/allocation.js b/nodejs/allocation.js index 934e995..b195bb3 100644 --- a/nodejs/allocation.js +++ b/nodejs/allocation.js @@ -1,7 +1,15 @@ +// TODO: Clean up file + const AllocationModel = require('../models/allocation-model'); const appConfig = require('./app-config'); const api = require('./api'); +const types = { + etf: 'etf', + stock: 'stock', + other: 'other', +}; + exports.save = (userId) => appConfig .app(userId, 'allocation') @@ -21,29 +29,58 @@ exports.save = (userId) => const promises = []; + const getType = (x) => { + if (x.isETF) { + return types.etf; + } + + if (x.isStock) { + return types.stock; + } + + return types.other; + }; + allocationConfig.list.forEach((x) => { - if (x.isStock && x.label) { - const ticker = x.label.toUpperCase(); + const ticker = x.label.toUpperCase(); + const type = getType(x); + + if (type === types.etf) { promises.push(getFund({ ticker, value: x.value })); } }); - return Promise.all(promises).then((data) => { - const returnData = []; + return Promise.all(promises) + .then((data) => { + const returnData = []; - data.forEach((x) => { - let parsedData = x.data.slice(21); // Remove 'angular.callbacks._0(' from start of string - parsedData = parsedData.substring(0, parsedData.length - 1); // Remove ')' from end of string - const jsonData = JSON.parse(parsedData); + data.forEach((x) => { + let parsedData = x.data.slice(21); // Remove 'angular.callbacks._0(' from start of string + parsedData = parsedData.substring(0, parsedData.length - 1); // Remove ')' from end of string + const jsonData = JSON.parse(parsedData); - returnData.push({ fund: jsonData, ticker: x.ticker, value: x.value }); - }); + returnData.push({ fund: jsonData, ticker: x.ticker, value: x.value, type: types.etf }); + }); - return returnData; - }); + return returnData; + }) + .then((fundData) => { + allocationConfig.list.forEach((x) => { + const ticker = x.label.toUpperCase(); + const type = getType(x); + + if (type === types.stock) { + fundData.push({ ticker, value: x.value, type: types.stock }); + } else if (type !== types.etf) { + fundData.push({ ticker: x.label, value: x.value, type: types.other }); + } + }); + + return fundData; + }); }) .then((data) => { - const getStocks = ({ fund, ticker, value }) => + const getStocks = ({ ticker, ...rest }) => api .get({ // eslint-disable-next-line max-len @@ -61,14 +98,13 @@ exports.save = (userId) => } return { - fund, ticker, - value, stocks: stocks.size < 1 ? [] : stocks.fund.entity, + ...rest, }; }); - const getBonds = ({ fund, ticker, value, stocks }) => + const getBonds = ({ ticker, ...rest }) => api .get({ // eslint-disable-next-line max-len @@ -86,17 +122,19 @@ exports.save = (userId) => } return { - fund, ticker, - value, - stocks, bonds: bonds.size < 1 ? [] : bonds.fund.entity, + ...rest, }; }); const promises = []; data.forEach((fundData) => { - promises.push(getStocks(fundData).then(getBonds)); + if (fundData.type === types.etf) { + promises.push(getStocks(fundData).then(getBonds)); + } else { + promises.push(fundData); + } }); return Promise.all(promises); @@ -105,7 +143,7 @@ exports.save = (userId) => const totalValue = data.reduce((partialSum, a) => partialSum + (a.value ? a.value : 0), 0); const totalPortfolio = []; - const addToArray = (x, fundValue) => { + const addToTotalsArray = (x, fundValue) => { const existingIndex = totalPortfolio.findIndex((i) => i.ticker === x.ticker); if (existingIndex !== -1) { @@ -120,13 +158,17 @@ exports.save = (userId) => }; data.forEach((x) => { - x.stocks.forEach((s) => { - addToArray(s, x.value); - }); + if (x.type === types.etf) { + x.stocks.forEach((s) => { + addToTotalsArray(s, x.value); + }); - x.bonds.forEach((b) => { - addToArray(b, x.value); - }); + x.bonds.forEach((b) => { + addToTotalsArray(b, x.value); + }); + } else { + addToTotalsArray({ percentWeight: '100', ticker: x.ticker, longName: x.label }, x.value); + } }); const sortByPercentDesc = (a, b) => { diff --git a/nodejs/parks.js b/nodejs/parks.js index 8c569f8..9e6b8ef 100644 --- a/nodejs/parks.js +++ b/nodejs/parks.js @@ -42,29 +42,13 @@ async function uploadImage(parksConfig, park, userId) { .toLowerCase() .substring(0, 100), transformation: [ - { - width: 140, - height: 140, - crop: 'lfill', - }, - { - background: '#786262', - effect: 'colorize:40', - opacity: 100, - radius: 0, - }, - { - effect: 'saturation:35', - radius: 0, - }, + { effect: 'art:sonnet', height: 140, width: 140, crop: 'lfill', quality: 'auto:good' }, + { effect: 'colorize:15' }, // TODO: Upload not visited with colorize 75 { background: '#ffffff', effect: 'hue:5', radius: 10, }, - { - effect: 'brightness:10', - }, ], });