diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6188241385..b891b6c17d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -338,30 +338,90 @@ Example: `budget view` ## Product scope ### Target user profile -{Describe the target user profile} +Our target user profile is ... +- a working adult with a source of income +- someone who dislike navigating graphic user interface +- someone who can type fast +- someone who cannot manage their finances such as income and expenses properly +- unable to reach their financial goals +- is slightly interested in the equity market +- needs reminders for tasks + ### Value proposition -{Describe the value proposition: what problem does it solve?} +Our financial planner application can help individuals manage their finances effectively and achieve their financial +goals. The purpose of such an application is to provide users with a range of tools and features to help them better +understand their financial situation. This will enable them to make more informed decisions, and plan for their future +financial well-being. The application will allow the user to keep track of their income, expenses and overall balance. +It also lets the user view their income and expenses using visualization tool to have a better view of their cash flow +based on categories. It also allows the user to set the budget for the month. It also allows users to add their financial +goals to the wishlist. Furthermore, it allows users to track the stock market if they have interest in investing in +equities. ## User Stories -|Version| As a ... | I want to ... | So that I can ...| -|--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| +| Version | As a ... | I want to ... | So that I can ... | +|---------|-----------------------|-------------------------------|--------------------------------------------------------------------------------------| +| v1.0 | user | Add my income | Store my income information and view/track them later | +| v1.0 | user | Delete my income | Remove the income entry that I have mistakenly added or do not keep track | +| v1.0 | user | Add my expense | Store my expense information and view/track them later | +| v1.0 | user | Delete my expense | Remove the expense entry that I have mistakenly added or do not keep track | +| v2.0 | user | set my expense type | Break down my expenses into different categories | +| v1.0 | user | set my income type | Break down my income into different categories | +| v2.0 | user | Add recurring cash flows | add a regular expense or income (salary, rent) easily | +| v2.0 | user | Delete recurring cash flows | delete a regular expense of income easily | +| v1.0 | user | list all cash flow entries | view all my income and expenses in a comprehensive list | +| v1.0 | user | list all expenses entries | view all my expenses in a comprehensive list | +| v1.0 | user | list all income entries | view all my income in a comprehensive list | +| v2.0 | user | list all recurring cash flows | view all my recurring income or expenses in a comprehensive list | +| v2.0 | new user | see usage instructions | refer to them when I forget how to use the application | +| v1.0 | user | set a budget | keep track of a budget together with my cash flow and ensure I do not exceed it | +| v1.0 | user | update the budget | make changes to the budget according to my needs | +| v1.0 | user | reset the budget | return to my initial budget easily | +| v1.0 | user | delete budget | remove the budget that I no longer want to keep track of | +| v1.0 | user | view budget | keep track of the amount of budget I have left | +| v1.0 | user | see overview of the app | see the overall view of all income, expense and overall balance as well as reminders | +| v1.0 | user | view balance | see my overall balance according to the income and expenses I am keeping track | +| v1.0 | investment enthusiast | view my watchlist | keep track of stocks that I am interested in | +| v2.0 | investment enthusiast | add new stocks to watchlist | add new stock that I am interested in investing in | +| v2.0 | investment enthusiast | delete stocks from watchlist | remove stocks that I am no longer interested in | +| v1.0 | user | add reminder | add reminders (eg to pay loans) so I will not forget | +| v1.0 | user | delete reminder | delete reminders that I no longer want to keep track | +| v1.0 | user | mark reminder | set the reminder as completed | +| v1.0 | user | view wishlist | keep track of my goals easily | +| v1.0 | user | set goals | add a new goal to my that I think of | +| v1.0 | user | delete goals | remove goals that I can no longer achieve | +| v1.0 | user | mark goal | that I have achieved | +| v1.0 | user | visualize my cash flow | easily see where the distribution for my spending and earnings | ## Non-Functional Requirements * Should work on main OS (Windows, Linux, Mac) that has Java 11 installed. * This app is meant for a single user. * This app is targeted towards users with an above-average typing speed. +* Watchlist should work reliably and not crash the application when the 3rd party dependencies are down (API is down) ## Glossary * *Cashflow* - Refers to an income or expense. * *WishList* - A list containing goals/targets. +* *Watchlist* - A list of stocks that the financial planner is currently tracking ## Instructions for manual testing -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +Given below are instructions to test the app manually + +- Note: These instructions only provide a starting point for testers to work on + +### Launch and shutdown + +1. Initial Launch + 1. Download the jar file and copy into an empty folder + 2. Open up the terminal and run java -jar tp.jar Expected: +shows you the welcome screen for the financial planner app +2. Closing the application + 1. Type `exit` into the terminal. + 2. Expected: the financial planner will exit with a goodbye message. +Under the data newly created data directory, a watchlist.json and a data.txt file will be created + diff --git a/docs/team/wwweert123.md b/docs/team/wwweert123.md index d54d3fa8d7..94d84a4a31 100644 --- a/docs/team/wwweert123.md +++ b/docs/team/wwweert123.md @@ -36,6 +36,10 @@ you a one-stop interface to access a plethora of features to manage your finance * Implementation * Class Diagram * Sequence Diagrams +* Value Proposition +* User Profile +* USer Stories +* Manual Testing ### Contributions to team-based tasks: diff --git a/src/main/java/seedu/financialplanner/commands/AddStockCommand.java b/src/main/java/seedu/financialplanner/commands/AddStockCommand.java index 37328afa6a..8d4f68837f 100644 --- a/src/main/java/seedu/financialplanner/commands/AddStockCommand.java +++ b/src/main/java/seedu/financialplanner/commands/AddStockCommand.java @@ -8,10 +8,20 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Command that inherits from the Command abstract class + * Represents that command that add stock to watchlist + */ public class AddStockCommand extends Command { private static final Logger logger = Logger.getLogger("Financial Planner Logger"); private final String stockCode; + /** + * Constructor for the command add stock to watchlist + * + * @param rawCommand + * @throws IllegalArgumentException + */ public AddStockCommand(RawCommand rawCommand) throws IllegalArgumentException { if (!rawCommand.extraArgs.containsKey("s")) { throw new IllegalArgumentException("Stock code cannot be empty"); @@ -28,6 +38,9 @@ public AddStockCommand(RawCommand rawCommand) throws IllegalArgumentException { } } + /** + * Executes the command to add stock to watchlist + */ @Override public void execute() { Ui ui = Ui.getInstance(); diff --git a/src/main/java/seedu/financialplanner/commands/DeleteStockCommand.java b/src/main/java/seedu/financialplanner/commands/DeleteStockCommand.java index 1ffbbb0aed..d09453b973 100644 --- a/src/main/java/seedu/financialplanner/commands/DeleteStockCommand.java +++ b/src/main/java/seedu/financialplanner/commands/DeleteStockCommand.java @@ -8,10 +8,20 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Command that inherits from the Command abstract class + * Represents the command to delete stock from watchlist + */ public class DeleteStockCommand extends Command { private static final Logger logger = Logger.getLogger("Financial Planner Logger"); private final String stockCode; + /** + * Constructor for the command to delete stock from watchlist + * + * @param rawCommand + * @throws IllegalArgumentException + */ public DeleteStockCommand(RawCommand rawCommand) throws IllegalArgumentException { if (!rawCommand.extraArgs.containsKey("s")) { throw new IllegalArgumentException("Stock code cannot be empty"); @@ -28,6 +38,11 @@ public DeleteStockCommand(RawCommand rawCommand) throws IllegalArgumentException } } + /** + * Executes the command to delete stock from watchlist + * + * @throws Exception + */ @Override public void execute() throws Exception { Ui ui = Ui.getInstance(); diff --git a/src/main/java/seedu/financialplanner/commands/VisCommand.java b/src/main/java/seedu/financialplanner/commands/VisCommand.java index 4e9762ffc2..dffdbd2dcb 100644 --- a/src/main/java/seedu/financialplanner/commands/VisCommand.java +++ b/src/main/java/seedu/financialplanner/commands/VisCommand.java @@ -11,11 +11,21 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Command class that inherit from Command abstract class + * Represents the command to visualize your cash flow + */ public class VisCommand extends Command { private static final Logger logger = Logger.getLogger("Financial Planner Logger"); private String type; private String chart; + /** + * Constructor for the command to visualize cash flow + * + * @param rawCommand + * @throws IllegalArgumentException + */ public VisCommand(RawCommand rawCommand) throws IllegalArgumentException { if (!rawCommand.extraArgs.containsKey("t")) { throw new IllegalArgumentException("Entry type must be defined"); @@ -35,6 +45,11 @@ public VisCommand(RawCommand rawCommand) throws IllegalArgumentException { } } + /** + * Executes the command to visualize cash flow + * + * @throws FinancialPlannerException + */ @Override public void execute() throws FinancialPlannerException { Ui ui = Ui.getInstance(); diff --git a/src/main/java/seedu/financialplanner/commands/WatchListCommand.java b/src/main/java/seedu/financialplanner/commands/WatchListCommand.java index 16ac46c619..2c1e997ce7 100644 --- a/src/main/java/seedu/financialplanner/commands/WatchListCommand.java +++ b/src/main/java/seedu/financialplanner/commands/WatchListCommand.java @@ -8,9 +8,19 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Command that inherits from Command abstract class + * Represents the command to fetch and display watchlist data + */ public class WatchListCommand extends Command { private static final Logger logger = Logger.getLogger("Financial Planner Logger"); + /** + * Constructor for the command to fetch and display watchlist data + * + * @param rawCommand + * @throws IllegalArgumentException + */ public WatchListCommand(RawCommand rawCommand) throws IllegalArgumentException{ if (!rawCommand.extraArgs.isEmpty()) { logger.log(Level.WARNING, "Invalid extra arguments found"); @@ -20,6 +30,9 @@ public WatchListCommand(RawCommand rawCommand) throws IllegalArgumentException{ } } + /** + * Executes the command to fetch and display watchlist data + */ @Override public void execute() { Ui ui = Ui.getInstance(); diff --git a/src/main/java/seedu/financialplanner/investments/Stock.java b/src/main/java/seedu/financialplanner/investments/Stock.java index 7f7f086d68..8379e6a42c 100644 --- a/src/main/java/seedu/financialplanner/investments/Stock.java +++ b/src/main/java/seedu/financialplanner/investments/Stock.java @@ -16,6 +16,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Represents a stock within the financial planner app + */ public class Stock { private static final Logger logger = Logger.getLogger("Financial Planner Logger"); private static final String API_ENDPOINT = "https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords="; @@ -30,11 +33,24 @@ public class Stock { private Date lastUpdated = null; private long lastFetched = 0; + /** + * Constructor for stock that sets the symbol and searches the api + * for stock name for it + * + * @param symbol + * @throws FinancialPlannerException + */ public Stock(String symbol) throws FinancialPlannerException { this.symbol = symbol; this.stockName = getStockNameFromAPI(symbol); } + /** + * Constructor that sets the symbol and stock name directly + * + * @param symbol + * @param stockName + */ public Stock(String symbol, String stockName) { this.symbol = symbol; this.stockName = stockName; @@ -44,6 +60,14 @@ public String getStockName() { return stockName; } + /** + * Method that gets the stock name from the Alpha vantage api. Will throw financial planner exception for any errors + * when attempting this. If succuessful, will return the stock name for the symbol provided + * + * @param symbol + * @return stock name + * @throws FinancialPlannerException + */ public String getStockNameFromAPI(String symbol) throws FinancialPlannerException { String requestURI = String.format("%s%s&apikey=%s", API_ENDPOINT,symbol,API_KEY); HttpClient client = HttpClient.newHttpClient(); @@ -102,6 +126,11 @@ public void setSymbol(String symbol) { this.symbol = symbol; } + /** + * toString method is override to output its symbol appended with a comma + * + * @return + */ @Override public String toString() { return symbol + ","; diff --git a/src/main/java/seedu/financialplanner/investments/WatchList.java b/src/main/java/seedu/financialplanner/investments/WatchList.java index e18d7db483..b2d44a67a8 100644 --- a/src/main/java/seedu/financialplanner/investments/WatchList.java +++ b/src/main/java/seedu/financialplanner/investments/WatchList.java @@ -20,6 +20,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Class that represents the watchlist in the financial planner app + */ public class WatchList { private static WatchList watchlist = null; private static Logger logger = Logger.getLogger("Financial Planner Logger"); @@ -27,11 +30,20 @@ public class WatchList { private static final String API_KEY = "iFumtYryBCbHpS3sDqLdVKi2SdP63vSV"; private HashMap stocks; + /** + * Constructor for the watchlist. It will load up the watchlist data from watchlist.json file and clean up + * any erroneous inputs + */ private WatchList() { stocks = LoadData.loadWatchList(); cleanUpLoadedWatchList(); } + /** + * Method that helps to clean up the watchlist loaded up after application start up + * If watchlist stocks is null, it will help to initialize a new base watchlist. + * It will call another method checkValidStock to clean up the watchlist stocks loaded if it is not empty + */ private void cleanUpLoadedWatchList() { if (stocks == null) { stocks = initalizeNewWatchlist(); @@ -43,6 +55,14 @@ private void cleanUpLoadedWatchList() { } } + /** + * Checks the validity of the stock key, and the Stock class value in the watchlist stocks hashmap + * If it is not valid, it will return false + * + * @param key + * @param stockToCheck + * @return + */ private boolean checkValidStock(String key, Stock stockToCheck) { if (stockToCheck.getStockName() == null || stockToCheck.getSymbol() == null) { return false; @@ -50,6 +70,11 @@ private boolean checkValidStock(String key, Stock stockToCheck) { return key.equals(stockToCheck.getSymbol()); } + /** + * Initialize a new watchlist stocks hashmap with base stocks (AAPL and GOOGL) + * + * @return + */ public HashMap initalizeNewWatchlist() { HashMap baseStocks = new HashMap<>(); Ui.getInstance().showMessage("Initializing New watchlist.. adding AAPL and GOOGL for your reference"); @@ -65,6 +90,11 @@ public HashMap initalizeNewWatchlist() { return baseStocks; } + /** + * Method to get the watchlist singleton or create one if it does not exist and returns it + * + * @return + */ public static WatchList getInstance() { if (watchlist == null) { watchlist = new WatchList(); @@ -72,11 +102,22 @@ public static WatchList getInstance() { return watchlist; } + /** + * Method to get the latest watchlist info such as price and daily lowest + * + * @throws FinancialPlannerException + */ public void getLatestWatchlistInfo() throws FinancialPlannerException { StringBuilder queryStocks = getExpiredStocks(); fetchFMPStockPrices(queryStocks); } + /** + * Checks the watchlist stocks hashmap for stocks that are expired meaning their data should be refreshed using + * the api. Returns a string of stocks that are expired separated by a comma + * + * @return + */ public StringBuilder getExpiredStocks() { StringBuilder queryStocks = new StringBuilder(); long currentTime = System.currentTimeMillis(); @@ -91,6 +132,14 @@ public StringBuilder getExpiredStocks() { return queryStocks; } + /** + * Request the Financial Modeling prep API for the latest stock info using the query stocks parameter passed in + * by the getExpiredStocks method. Will throw a FinancialPlannerException if an error is encountered in the attempt + * If the attempt is successful, it will update the stock information in the watchlist stocks. + * + * @param queryStocks + * @throws FinancialPlannerException + */ public void fetchFMPStockPrices(StringBuilder queryStocks) throws FinancialPlannerException { if (stocks.isEmpty()) { throw new FinancialPlannerException("Empty Watchlist. Nothing to display..."); @@ -132,6 +181,12 @@ public void fetchFMPStockPrices(StringBuilder queryStocks) throws FinancialPlann } } + /** + * Method to extract out required information from the full JSON array received from the API + * + * @param jsonstocks + * @throws FinancialPlannerException + */ public void extractWatchlistInfoFromJSONArray(JSONArray jsonstocks) throws FinancialPlannerException { if (jsonstocks == null) { throw new FinancialPlannerException("Incorrect API Response Received. Please try again"); @@ -149,6 +204,14 @@ public void extractWatchlistInfoFromJSONArray(JSONArray jsonstocks) throws Finan } } + /** + * Method called by extractWatchlistInfoFromJSONArray to set stock info for individual stocks using information + * obtained from the API (eg. setting latest price) + * + * @param stock + * @param stockLocal + * @param fetchTime + */ public void extractStockInfoFromJSONObject(JSONObject stock, Stock stockLocal, long fetchTime) { stockLocal.setLastFetched(fetchTime); @@ -173,6 +236,13 @@ public void extractStockInfoFromJSONObject(JSONObject stock, Stock stockLocal, l stockLocal.setLastUpdated(new Date(lastUpdated)); } + /** + * Method used to add a new stock to the watchlist that has the stockCode given by the parameter provided + * + * @param stockCode + * @return stockName + * @throws FinancialPlannerException + */ public String addStock(String stockCode) throws FinancialPlannerException { if (stocks.size() >= 5) { throw new FinancialPlannerException("Watchlist is full (max 5). Delete a stock to add a new one"); @@ -192,6 +262,13 @@ public String addStock(String stockCode) throws FinancialPlannerException { return newStock.getStockName(); } + /** + * Method for deleting a stock from the watchlist that has the stockCode given by the parameter provided + * + * @param stockCode + * @return deleted stock name + * @throws FinancialPlannerException + */ public String deleteStock(String stockCode) throws FinancialPlannerException { if (stocks.isEmpty()) { throw new FinancialPlannerException("No stock in watchlist!"); diff --git a/src/main/java/seedu/financialplanner/storage/LoadData.java b/src/main/java/seedu/financialplanner/storage/LoadData.java index dce3a6781e..b58c626b2f 100644 --- a/src/main/java/seedu/financialplanner/storage/LoadData.java +++ b/src/main/java/seedu/financialplanner/storage/LoadData.java @@ -321,6 +321,11 @@ private static String getDescription(String[] split, int index) { return description; } + /** + * Load the watchlist.json file into the application on startup as a hashmap. + * + * @return + */ public static HashMap loadWatchList() { Ui ui = Ui.getInstance(); Gson gson = new Gson(); diff --git a/src/main/java/seedu/financialplanner/storage/SaveData.java b/src/main/java/seedu/financialplanner/storage/SaveData.java index ccba165c0a..84d1ab9230 100644 --- a/src/main/java/seedu/financialplanner/storage/SaveData.java +++ b/src/main/java/seedu/financialplanner/storage/SaveData.java @@ -44,6 +44,10 @@ public static void save(String filePath) throws FinancialPlannerException { } } + /** + * Method to save the current watchlist to watchlist.json file + * + */ public static void saveWatchList() { Ui ui = Ui.getInstance(); WatchList wl = WatchList.getInstance(); diff --git a/src/main/java/seedu/financialplanner/visualisations/Categorizer.java b/src/main/java/seedu/financialplanner/visualisations/Categorizer.java index dc532b84d5..72e9885ec8 100644 --- a/src/main/java/seedu/financialplanner/visualisations/Categorizer.java +++ b/src/main/java/seedu/financialplanner/visualisations/Categorizer.java @@ -10,9 +10,23 @@ import java.util.logging.Level; import java.util.logging.Logger; + +/** + * Class that is used to sort the cash flow list into different categories according to the type they are + * (Income, Expense) + */ public class Categorizer { private static final Logger logger = Logger.getLogger("Financial Planner Logger"); + /** + * Method that calls the required methods to sort the cash flow based on the type is specified by the user + * (Income, Expense) + * + * @param cashflowList + * @param type + * @return hashmap of sorted income/expense according to category + * @throws FinancialPlannerException + */ public static HashMap sortType(CashflowList cashflowList, String type) throws FinancialPlannerException { switch (type) { @@ -27,6 +41,12 @@ public static HashMap sortType(CashflowList cashflowList, String } } + /** + * Method to sort the expenses of the cash flow list into different categories and return the sorted hashmap + * + * @param cashflowList + * @return + */ public static HashMap sortExpenses(CashflowList cashflowList) { HashMap expensesByCat = new HashMap<>(); for (Cashflow e: cashflowList.list) { @@ -40,6 +60,12 @@ public static HashMap sortExpenses(CashflowList cashflowList) { return expensesByCat; } + /** + * Method to sort the incomes of the cash flow list into different categories and return the sorted hashmap + * + * @param cashflowList + * @return + */ public static HashMap sortIncome(CashflowList cashflowList) { HashMap incomeByCat = new HashMap<>(); for (Cashflow e: cashflowList.list) { diff --git a/src/main/java/seedu/financialplanner/visualisations/Visualizer.java b/src/main/java/seedu/financialplanner/visualisations/Visualizer.java index 682dbc0e8e..10c9a0a3f9 100644 --- a/src/main/java/seedu/financialplanner/visualisations/Visualizer.java +++ b/src/main/java/seedu/financialplanner/visualisations/Visualizer.java @@ -24,9 +24,22 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Class the helps to output the visualization of the cash flow to the user so that the user can easily view their + * expense/income based on types + */ public class Visualizer { private static final Logger logger = Logger.getLogger("Financial Planner Logger"); + /** + * Method that calls the appropriate method for printing the different visualizations tools based on the + * preference of the user (pie,bar,radar) + * + * @param chart + * @param cashFlowByCat + * @param type + * @throws FinancialPlannerException + */ public static void displayChart(String chart, HashMap cashFlowByCat, String type) throws FinancialPlannerException { switch (chart) { @@ -44,6 +57,12 @@ public static void displayChart(String chart, HashMap cashFlowBy } } + /** + * Method to display the pier chart to the screen + * + * @param cashflowByCat + * @param type + */ public static void displayPieChart (HashMap cashflowByCat, String type) { PieChart chart = new PieChartBuilder().width(800).height(600) .title(StringUtils.capitalize(type) + " Chart") @@ -72,6 +91,12 @@ public static void displayPieChart (HashMap cashflowByCat, Strin ); } + /** + * Method to display the bar chart to the screen + * + * @param cashflowByCat + * @param type + */ public static void displayBarChart (HashMap cashflowByCat, String type) { CategoryChart chart = new CategoryChartBuilder().width(800).height(600) .title(StringUtils.capitalize(type) + " Chart") @@ -99,6 +124,13 @@ public static void displayBarChart (HashMap cashflowByCat, Strin } + /** + * Method to display the radar chart to the screen + * + * @param cashflowByCat + * @param type + * @throws FinancialPlannerException + */ public static void displayRadarChart (HashMap cashflowByCat, String type) throws FinancialPlannerException { RadarChart radarChart = new RadarChartBuilder().width(800).height(600)