diff --git a/database_structure.sql b/database_structure.sql index 8c1dcf7..9ec7094 100644 --- a/database_structure.sql +++ b/database_structure.sql @@ -1,11 +1,13 @@ CREATE TABLE IF NOT EXISTS User_Table( user_id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, + highest_lifetime_balance FLOAT, highest_transaction_id BIGINT, highest_category_id BIGINT, highest_category_rule_id BIGINT, highest_saving_goal_id BIGINT, - highest_payment_request_id BIGINT + highest_payment_request_id BIGINT, + highest_user_message_id BIGINT ); CREATE TABLE IF NOT EXISTS Transaction_Table( @@ -86,3 +88,14 @@ CREATE TABLE IF NOT EXISTS Payment_Request_Transaction( PRIMARY KEY(user_id, payment_request_id, transaction_id) ); +CREATE TABLE IF NOT EXISTS User_Message( + user_id INTEGER, + user_message_id BIGINT, + message TEXT, + date DATETIME, + read BOOLEAN, + type TEXT, + FOREIGN KEY(user_id) REFERENCES User_Table(user_id), + PRIMARY KEY(user_id, user_message_id) +); + diff --git a/sql_queries_and_updates.txt b/sql_queries_and_updates.txt index 9505be3..9e9312f 100644 --- a/sql_queries_and_updates.txt +++ b/sql_queries_and_updates.txt @@ -243,6 +243,57 @@ AND pr.payment_request_id = ?; INSERT INTO Payment_Request_Transaction (user_id, transaction_id, payment_request_id) VALUES (?, ?, ?); + increaseHighestUserMessageID(user_id): +UPDATE User_Table +SET highest_user_message_id = highest_user_message_id + 1 +WHERE user_id = ?; + + getHighestUserMessageID(user_id): +SELECT highest_user_message_id +FROM User_Table +WHERE user_id = ?; + + getUserMessage(user_id, user_message_id): +SELECT user_message_id, message, date, read, type +FROM User_Message +WHERE user_id = ? +AND user_message_id = ?; + + createUserMessage(user_id, user_message_id, message, date, type): +INSERT INTO User_Message (user_id, user_message_id, message, date, read, type) +VALUES (?, ?, ?, ?, 0, ?); + + getUnreadUserMessages(user_id): +SELECT user_message_id, message, date, read, type +FROM User_Message +WHERE user_id = ? +AND read = 0; + + getAllUserMessages(user_id): +SELECT user_message_id, message, date, read, type +FROM User_Message +WHERE user_id = ?; + + setUserMessageRead(user_id, user_message_id): +UPDATE User_Message +SET read = 1 +WHERE user_id = ? +AND user_message_id = ?; + + getHighestLifetimeBalance(user_id): +SELECT highest_lifetime_balance +FROM User_Table +WHERE user_id = ?; + + updateHighestLifetimeBalance(current_balance, current_balance, user_id): +UPDATE User_Table +SET highest_lifetime_balance = + CASE WHEN ? > highest_lifetime_balance + THEN ? + ELSE highest_lifetime_balance + END +WHERE user_id = ?; + linkTransactionToCategory(user_id, transaction_id, category_id): INSERT INTO Transaction_Category (user_id, transaction_id, category_id) VALUES (?, ?, ?); @@ -272,10 +323,11 @@ AND t.user_id = ? AND t.transaction_id = ?; createNewUser(session_id): -INSERT INTO User_Table (session_id, highest_transaction_id, highest_category_id, highest_category_rule_id, highest_saving_goal_id, highest_payment_request_id) -VALUES (?, 0, 0, 0, 0, 0); +INSERT INTO User_Table (session_id, highest_lifetime_balance, highest_transaction_id, highest_category_id, highest_category_rule_id, highest_saving_goal_id, highest_payment_request_id, highest_user_message_id) +VALUES (?, 0, 0, 0, 0, 0, 0, 0); getUserID(session_id): SELECT user_id FROM User_Table WHERE session_id = ?; + diff --git a/src/main/java/nl/utwente/ing/api/MainRestController.java b/src/main/java/nl/utwente/ing/api/MainRestController.java index 377ffd6..e17b9df 100644 --- a/src/main/java/nl/utwente/ing/api/MainRestController.java +++ b/src/main/java/nl/utwente/ing/api/MainRestController.java @@ -720,4 +720,50 @@ public ResponseEntity postPaymentRequest(@RequestParam(value = "session_id", def } } + /** + * Method used to retrieve the unread UserMessages belonging to the user issuing the current request. + * + * @param pSessionID The sessionID specified in the request parameters. + * @param hSessionID The sessionID specified in the HTTP header. + * @return A ResponseEntity containing a HTTP status code and either a status message or + * an ArrayList of UserMessages belonging to the user issuing the current request. + */ + @RequestMapping(method = RequestMethod.GET, + value = RestControllerConstants.URI_PREFIX + "/messages") + public ResponseEntity getUnreadUserMessages(@RequestParam(value = "session_id", defaultValue = "") String pSessionID, + @RequestHeader(value = "X-session-ID", defaultValue = "") String hSessionID) { + try { + String sessionID = this.getSessionID(pSessionID, hSessionID); + ArrayList userMessages = model.getUnreadUserMessages(sessionID); + return ResponseEntity.status(200).body(userMessages); + } catch (InvalidSessionIDException e) { + return ResponseEntity.status(401).body("Session ID is missing or invalid"); + } + } + + /** + * Method used to indicate that a certain UserMessage belonging to the user issuing the current request is read. + * + * @param pSessionID The sessionID specified in the request parameters. + * @param hSessionID The sessionID specified in the HTTP header. + * @param userMessageID The userMessageID of the UserMessage for which it is indicated that it is read. + * @return A ResponseEntity containing a HTTP status code and a status message. + */ + @RequestMapping(method = RequestMethod.PUT, + value = RestControllerConstants.URI_PREFIX + "/messages/{userMessageID}") + public ResponseEntity putUserMessageRead(@RequestParam(value = "session_id", defaultValue = "") String pSessionID, + @RequestHeader(value = "X-session-ID", defaultValue = "") String hSessionID, + @PathVariable String userMessageID) { + try { + String sessionID = this.getSessionID(pSessionID, hSessionID); + long userMessageIDLong = Long.parseLong(userMessageID); + model.setUserMessageRead(sessionID, userMessageIDLong); + return ResponseEntity.status(200).body("Successful operation"); + } catch (InvalidSessionIDException e) { + return ResponseEntity.status(401).body("Session ID is missing or invalid"); + } catch (NumberFormatException | ResourceNotFoundException e) { + return ResponseEntity.status(404).body("Resource not found"); + } + } + } diff --git a/src/main/java/nl/utwente/ing/misc/date/IntervalHelper.java b/src/main/java/nl/utwente/ing/misc/date/IntervalHelper.java index 405d7bb..f4df1f9 100644 --- a/src/main/java/nl/utwente/ing/misc/date/IntervalHelper.java +++ b/src/main/java/nl/utwente/ing/misc/date/IntervalHelper.java @@ -27,9 +27,9 @@ public class IntervalHelper { * @param amount The amount of intervals to be generated. * @return An array containing LocalDateTime objects representing the requested intervals. */ - public static LocalDateTime[] getIntervals(IntervalPeriod intervalPeriod, int amount) { + public static LocalDateTime[] getIntervals(IntervalPeriod intervalPeriod, int amount, LocalDateTime until) { LocalDateTime[] intervals = new LocalDateTime[amount + 2]; - intervals[amount + 1] = LocalDateTime.now(ZoneOffset.UTC); + intervals[amount + 1] = until; intervals[0] = LocalDateTime.parse("1970-01-01T00:00:00.000"); if (intervalPeriod == IntervalPeriod.YEAR) { @@ -112,4 +112,15 @@ public static int getMonthIdentifier(String dateString) { return (date.getYear() - 1970) * 12 + (date.getMonthValue() - 1); } + /** + * Method used to convert a String object containing a date to a LocalDateTime object containing that date. + * + * @param s The String object containing a date that will be converted to a LocalDateTime object containing that + * date. + * @return The LocalDateTime object containing the date contained in the given String object. + */ + public static LocalDateTime toLocalDateTime(String s) { + return LocalDateTime.parse(s.split("Z")[0]); + } + } diff --git a/src/main/java/nl/utwente/ing/model/Model.java b/src/main/java/nl/utwente/ing/model/Model.java index a355a93..7027031 100644 --- a/src/main/java/nl/utwente/ing/model/Model.java +++ b/src/main/java/nl/utwente/ing/model/Model.java @@ -243,10 +243,26 @@ void deleteSavingGoal(String sessionID, long savingGoalID) /** * Method used to create a new PaymentRequest for a certain user. * - * @param sessionID The sessionID of the user. + * @param sessionID The sessionID of the user. * @param paymentRequest The PaymentRequest object to be used to create the new PaymentRequest. * @return The PaymentRequest created by this method. */ PaymentRequest postPaymentRequest(String sessionID, PaymentRequest paymentRequest) throws InvalidSessionIDException; + /** + * Method used to retrieve the unread UserMessages belonging to a certain user. + * + * @param sessionID The sessionID of the user. + * @return An ArrayList of UserMessages belonging to the user with sessionID. + */ + ArrayList getUnreadUserMessages(String sessionID) throws InvalidSessionIDException; + + /** + * Method used to indicate that a certain UserMessage of a certain user is read. + * + * @param sessionID The sessionID of the user. + * @param userMessageID The ID of the UserMessage of the certain user that should be marked as read. + */ + void setUserMessageRead(String sessionID, long userMessageID) + throws InvalidSessionIDException, ResourceNotFoundException; } diff --git a/src/main/java/nl/utwente/ing/model/bean/UserMessage.java b/src/main/java/nl/utwente/ing/model/bean/UserMessage.java new file mode 100644 index 0000000..9e1daf8 --- /dev/null +++ b/src/main/java/nl/utwente/ing/model/bean/UserMessage.java @@ -0,0 +1,132 @@ +package nl.utwente.ing.model.bean; + +/** + * The UserMessage class. + * Used to store information about a UserMessage. + * + * @author Daan Kooij + */ +public class UserMessage { + + private long id; + private String message; + private String date; + private boolean read; + private String type; + + /** + * An empty constructor of PaymentRequest. + * Used by the Spring framework. + */ + public UserMessage() { + + } + + /** + * A constructor of UserMessage. + * + * @param id The ID of the to be created UserMessage. + * @param message The message of the to be created UserMessage. + * @param date The date of the to be created UserMessage. + * @param read The boolean indicating whether the to be created UserMessage is read. + * @param type The type of the to be created UserMessage. + */ + public UserMessage(long id, String message, String date, boolean read, String type) { + this.id = id; + this.message = message; + this.date = date; + this.read = read; + this.type = type; + } + + /** + * Method used to retrieve the ID of UserMessage. + * + * @return The ID of UserMessage. + */ + public long getID() { + return id; + } + + /** + * Method used to update the ID of UserMessage. + * + * @param id The new ID of UserMessage. + */ + public void setID(long id) { + this.id = id; + } + + /** + * Method used to retrieve the message of UserMessage. + * + * @return The message of UserMessage. + */ + public String getMessage() { + return message; + } + + /** + * Method used to update the message of UserMessage. + * + * @param message The new message of UserMessage. + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * Method used to retrieve the date of UserMessage. + * + * @return The date of UserMessage. + */ + public String getDate() { + return date; + } + + /** + * Method used to update the date of UserMessage. + * + * @param date The new date of UserMessage. + */ + public void setDate(String date) { + this.date = date; + } + + /** + * Method used to retrieve the boolean indicating whether UserMessage is read. + * + * @return The boolean indicating whether UserMessage is read. + */ + public boolean getRead() { + return read; + } + + /** + * Method used to update the boolean indicating whether UserMessage is read. + * + * @param read The new boolean indicating whether UserMessage is read. + */ + public void setRead(boolean read) { + this.read = read; + } + + /** + * Method used to retrieve the type of UserMessage. + * + * @return The ID of UserMessage. + */ + public String getType() { + return type; + } + + /** + * Method used to update the type of UserMessage. + * + * @param type The new type of UserMessage. + */ + public void setType(String type) { + this.type = type; + } + +} diff --git a/src/main/java/nl/utwente/ing/model/persistentmodel/CustomORM.java b/src/main/java/nl/utwente/ing/model/persistentmodel/CustomORM.java index 2e0a8fc..195079e 100644 --- a/src/main/java/nl/utwente/ing/model/persistentmodel/CustomORM.java +++ b/src/main/java/nl/utwente/ing/model/persistentmodel/CustomORM.java @@ -149,70 +149,70 @@ public class CustomORM { "AND description LIKE ?\n" + "AND external_iban LIKE ?\n" + "AND type LIKE ?;"; - public static final String GET_TRANSACTIONS_ASCENDING = + private static final String GET_TRANSACTIONS_ASCENDING = "SELECT transaction_id, date, amount, description, external_iban, type\n" + "FROM Transaction_Table\n" + "WHERE user_id = ?\n" + "ORDER BY date ASC;"; - public static final String GET_CURRENT_DATE = + private static final String GET_CURRENT_DATE = "SELECT date\n" + "FROM Transaction_Table\n" + "WHERE user_id = ?\n" + "ORDER BY date DESC\n" + "LIMIT 1"; - public static final String INCREASE_HIGHEST_SAVING_GOAL_ID = + private static final String INCREASE_HIGHEST_SAVING_GOAL_ID = "UPDATE User_Table\n" + "SET highest_saving_goal_id = highest_saving_goal_id + 1\n" + "WHERE user_id = ?;"; - public static final String GET_HIGHEST_SAVING_GOAL_ID = + private static final String GET_HIGHEST_SAVING_GOAL_ID = "SELECT highest_saving_goal_id\n" + "FROM User_Table\n" + "WHERE user_id = ?;"; - public static final String CREATE_SAVING_GOAL = + private static final String CREATE_SAVING_GOAL = "INSERT INTO Saving_Goal (user_id, saving_goal_id, creation_date, name, goal, " + "save_per_month, min_balance_required)\n" + "VALUES (?, ?, ?, ?, ?, ?, ?);"; - public static final String GET_SAVING_GOAL = + private static final String GET_SAVING_GOAL = "SELECT saving_goal_id, creation_date, deletion_date, name, goal, save_per_month, min_balance_required\n" + "FROM Saving_Goal\n" + "WHERE user_id = ?\n" + "AND saving_goal_id = ?;"; - public static final String DELETE_SAVING_GOAL = + private static final String DELETE_SAVING_GOAL = "UPDATE Saving_Goal\n" + "SET deletion_date = ?\n" + "WHERE user_id = ?\n" + "AND saving_goal_id = ?;"; - public static final String GET_SAVING_GOALS = + private static final String GET_SAVING_GOALS = "SELECT saving_goal_id, creation_date, deletion_date, name, goal, save_per_month, min_balance_required\n" + "FROM Saving_Goal\n" + "WHERE user_id = ?;"; - public static final String INCREASE_HIGHEST_PAYMENT_REQUEST_ID = + private static final String INCREASE_HIGHEST_PAYMENT_REQUEST_ID = "UPDATE User_Table\n" + "SET highest_payment_request_id = highest_payment_request_id + 1\n" + "WHERE user_id = ?;"; - public static final String GET_HIGHEST_PAYMENT_REQUEST_ID = + private static final String GET_HIGHEST_PAYMENT_REQUEST_ID = "SELECT highest_payment_request_id\n" + "FROM User_Table\n" + "WHERE user_id = ?;"; - public static final String CREATE_PAYMENT_REQUEST = + private static final String CREATE_PAYMENT_REQUEST = "INSERT INTO Payment_Request (user_id, payment_request_id, description, due_date, " + "amount, number_of_requests, filled)\n" + "VALUES (?, ?, ?, ?, ?, ?, ?);\n"; - public static final String GET_PAYMENT_REQUEST = + private static final String GET_PAYMENT_REQUEST = "SELECT payment_request_id, description, due_date, amount, number_of_requests, filled\n" + "FROM Payment_Request\n" + "WHERE user_id = ?\n" + "AND payment_request_id = ?;"; - public static final String GET_PAYMENT_REQUESTS = + private static final String GET_PAYMENT_REQUESTS = "SELECT payment_request_id, description, due_date, amount, number_of_requests, filled\n" + "FROM Payment_Request\n" + "WHERE user_id = ?;"; - public static final String SET_PAYMENT_REQUEST_FILLED = + private static final String SET_PAYMENT_REQUEST_FILLED = "UPDATE Payment_Request\n" + "SET filled = 1\n" + "WHERE user_id = ?\n" + "AND payment_request_id = ?;"; - public static final String GET_TRANSACTIONS_BY_PAYMENT_REQUEST = + private static final String GET_TRANSACTIONS_BY_PAYMENT_REQUEST = "SELECT t.transaction_id, t.date, t.amount, t.description, t.external_iban, t.type\n" + "FROM Payment_Request pr, Payment_Request_Transaction prt, Transaction_Table t\n" + "WHERE pr.payment_request_id = prt.payment_request_id\n" + @@ -221,9 +221,51 @@ public class CustomORM { "AND prt.user_id = t.user_id\n" + "AND t.user_id = ?\n" + "AND pr.payment_request_id = ?;"; - public static final String LINK_TRANSACTION_TO_PAYMENT_REQUEST = + private static final String LINK_TRANSACTION_TO_PAYMENT_REQUEST = "INSERT INTO Payment_Request_Transaction (user_id, transaction_id, payment_request_id)\n" + "VALUES (?, ?, ?);"; + private static final String INCREASE_HIGHEST_USER_MESSAGE_ID = + "UPDATE User_Table\n" + + "SET highest_user_message_id = highest_user_message_id + 1\n" + + "WHERE user_id = ?;\n"; + private static final String GET_HIGHEST_USER_MESSAGE_ID = + "SELECT highest_user_message_id\n" + + "FROM User_Table\n" + + "WHERE user_id = ?;"; + private static final String GET_USER_MESSAGE = + "SELECT user_message_id, message, date, read, type\n" + + "FROM User_Message\n" + + "WHERE user_id = ?\n" + + "AND user_message_id = ?;"; + private static final String CREATE_USER_MESSAGE = + "INSERT INTO User_Message (user_id, user_message_id, message, date, read, type)\n" + + "VALUES (?, ?, ?, ?, 0, ?);"; + private static final String GET_UNREAD_USER_MESSAGES = + "SELECT user_message_id, message, date, read, type\n" + + "FROM User_Message\n" + + "WHERE user_id = ?\n" + + "AND read = 0;"; + private static final String GET_ALL_USER_MESSAGES = + "SELECT user_message_id, message, date, read, type\n" + + "FROM User_Message\n" + + "WHERE user_id = ?;"; + private static final String SET_USER_MESSAGE_READ = + "UPDATE User_Message\n" + + "SET read = 1\n" + + "WHERE user_id = ?\n" + + "AND user_message_id = ?;"; + private static final String GET_HIGHEST_LIFETIME_BALANCE = + "SELECT highest_lifetime_balance\n" + + "FROM User_Table\n" + + "WHERE user_id = ?;"; + private static final String UPDATE_HIGHEST_LIFETIME_BALANCE = + "UPDATE User_Table\n" + + "SET highest_lifetime_balance = \n" + + " CASE WHEN ? > highest_lifetime_balance \n" + + " THEN ? \n" + + " ELSE highest_lifetime_balance \n" + + " END\n" + + "WHERE user_id = ?;"; private static final String LINK_TRANSACTION_TO_CATEGORY = "INSERT INTO Transaction_Category (user_id, transaction_id, category_id)\n" + "VALUES (?, ?, ?);"; @@ -248,9 +290,10 @@ public class CustomORM { "AND t.user_id = ?\n" + "AND t.transaction_id = ?;"; private static final String CREATE_NEW_USER = - "INSERT INTO User_Table (session_id, highest_transaction_id, highest_category_id, " + - "highest_category_rule_id, highest_saving_goal_id, highest_payment_request_id)\n" + - "VALUES (?, 0, 0, 0, 0, 0);"; + "INSERT INTO User_Table (session_id, highest_lifetime_balance, highest_transaction_id, " + + "highest_category_id, highest_category_rule_id, highest_saving_goal_id, " + + "highest_payment_request_id, highest_user_message_id)\n" + + "VALUES (?, 0, 0, 0, 0, 0, 0, 0);"; private static final String GET_USER_ID = "SELECT user_id\n" + "FROM User_Table\n" + @@ -1076,7 +1119,7 @@ public long getHighestPaymentRequestID(int userID) { /** * Method used to insert a PaymentRequest into the database. * - * @param userID The ID of the user to which this new PaymentRequest will belong. + * @param userID The ID of the user to which this new PaymentRequest will belong. * @param paymentRequest The PaymentRequest object to be inserted into the database. */ public void createPaymentRequest(int userID, PaymentRequest paymentRequest) { @@ -1098,7 +1141,7 @@ public void createPaymentRequest(int userID, PaymentRequest paymentRequest) { /** * Method used to retrieve a PaymentRequest from the database. * - * @param userID The id of the user from which a PaymentRequest should be retrieved. + * @param userID The id of the user from which a PaymentRequest should be retrieved. * @param paymentRequestID The id of the to be retrieved PaymentRequest. * @return A PaymentRequest object containing data retrieved from the database. */ @@ -1132,7 +1175,7 @@ public PaymentRequest getPaymentRequest(int userID, long paymentRequestID) { * @param userID The ID of the user to who the to be retrieved PaymentRequest objects belong. * @return An ArrayList of PaymentRequest objects. */ - public ArrayList getPaymentRequest(int userID) { + public ArrayList getPaymentRequests(int userID) { ArrayList paymentRequests = new ArrayList<>(); try { PreparedStatement statement = connection.prepareStatement(GET_PAYMENT_REQUESTS); @@ -1158,7 +1201,7 @@ public ArrayList getPaymentRequest(int userID) { /** * Method used to indicate that a certain PaymentRequest of a certain user has been filled. * - * @param userID The ID of the user to which the certain PaymentRequest belongs. + * @param userID The ID of the user to which the certain PaymentRequest belongs. * @param paymentRequestID The ID of the PaymentRequest for which it should be indicated that it is filled. */ public void setPaymentRequestFilled(int userID, long paymentRequestID) { @@ -1176,7 +1219,7 @@ public void setPaymentRequestFilled(int userID, long paymentRequestID) { * Method used to retrieve a batch of Transaction objects belonging to a certain user * and fulfilling a certain PaymentRequest from the database. * - * @param userID The id of the user to who the to be retrieved Transaction objects belong. + * @param userID The id of the user to who the to be retrieved Transaction objects belong. * @param paymentRequestID The ID of the PaymentRequest to which the retrieved Transaction objects belong. * @return An ArrayList of Transaction objects. */ @@ -1205,9 +1248,9 @@ public ArrayList getTransactionsByPaymentRequest(int userID, long p /** * Method used to link a Transaction to a PaymentRequest in the database. * - * @param userID The ID of the user to who the to be linked Transaction and PaymentRequest objects belong. - * @param transactionID The ID of the Transaction that will be linked to a PaymentRequest. - * @param paymentRequestID The ID of the PaymentRequest that will be linked to a Transaction. + * @param userID The ID of the user to who the to be linked Transaction and PaymentRequest objects belong. + * @param transactionID The ID of the Transaction that will be linked to a PaymentRequest. + * @param paymentRequestID The ID of the PaymentRequest that will be linked to a Transaction. */ public void linkTransactionToPaymentRequest(int userID, long transactionID, long paymentRequestID) { try { @@ -1221,6 +1264,203 @@ public void linkTransactionToPaymentRequest(int userID, long transactionID, long } } + /** + * Method used to increase the highestUserMessageID field of a certain user by one in the database. + * + * @param userID The ID of the user whose highestUserMessageID field should be increased. + */ + public void increaseHighestUserMessageID(int userID) { + try { + PreparedStatement statement = connection.prepareStatement(INCREASE_HIGHEST_USER_MESSAGE_ID); + statement.setInt(1, userID); + statement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * Method used to retrieve the highestUserMessageID field of a certain user from the database. + * + * @param userID The ID of the user whose highestUserMessageID field should be retrieved. + * @return The value of the highestUserMessageID field of the user with userID. + */ + public long getHighestUserMessageID(int userID) { + long highestUserMessageID = -1; + try { + PreparedStatement statement = connection.prepareStatement(GET_HIGHEST_USER_MESSAGE_ID); + statement.setInt(1, userID); + ResultSet rs = statement.executeQuery(); + highestUserMessageID = rs.getLong(1); + } catch (SQLException e) { + e.printStackTrace(); + } + return highestUserMessageID; + } + + /** + * Method used to insert a UserMessage into the database. + * + * @param userID The ID of the user to which this new UserMessage will belong. + * @param userMessage The UserMessage object to be inserted into the database. + */ + public void createUserMessage(int userID, UserMessage userMessage) { + try { + PreparedStatement statement = connection.prepareStatement(CREATE_USER_MESSAGE); + statement.setInt(1, userID); + statement.setLong(2, userMessage.getID()); + statement.setString(3, userMessage.getMessage()); + statement.setString(4, userMessage.getDate()); + statement.setString(5, userMessage.getType()); + statement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * Method used to retrieve a UserMessage from the database. + * + * @param userID The id of the user from which a UserMessage should be retrieved. + * @param userMessageID The id of the to be retrieved UserMessage. + * @return A UserMessage object containing data retrieved from the database. + */ + public UserMessage getUserMessage(int userID, long userMessageID) { + UserMessage userMessage = null; + try { + PreparedStatement statement = connection.prepareStatement(GET_USER_MESSAGE); + statement.setInt(1, userID); + statement.setLong(2, userMessageID); + ResultSet resultSet = statement.executeQuery(); + + if (resultSet.next()) { + userMessageID = resultSet.getLong(1); + String message = resultSet.getString(2); + String date = resultSet.getString(3); + boolean read = resultSet.getBoolean(4); + String type = resultSet.getString(5); + userMessage = new UserMessage(userMessageID, message, date, read, type); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return userMessage; + } + + /** + * Method used to retrieve a batch of unread UserMessage objects belonging to a certain user from the database. + * + * @param userID The ID of the user to who the to be retrieved UserMessage objects belong. + * @return An ArrayList of UserMessage objects. + */ + public ArrayList getUnreadUserMessages(int userID) { + ArrayList userMessages = new ArrayList<>(); + try { + PreparedStatement statement = connection.prepareStatement(GET_UNREAD_USER_MESSAGES); + statement.setInt(1, userID); + ResultSet resultSet = statement.executeQuery(); + + while (resultSet.next()) { + long userMessageID = resultSet.getLong(1); + String message = resultSet.getString(2); + String date = resultSet.getString(3); + boolean read = resultSet.getBoolean(4); + String type = resultSet.getString(5); + userMessages.add(new UserMessage(userMessageID, message, date, read, type)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return userMessages; + } + + /** + * Method used to retrieve a batch of UserMessage objects belonging to a certain user from the database. + * + * @param userID The ID of the user to who the to be retrieved UserMessage objects belong. + * @return An ArrayList of UserMessage objects. + */ + public ArrayList getAllUserMessages(int userID) { + ArrayList userMessages = new ArrayList<>(); + try { + PreparedStatement statement = connection.prepareStatement(GET_ALL_USER_MESSAGES); + statement.setInt(1, userID); + ResultSet resultSet = statement.executeQuery(); + + while (resultSet.next()) { + long userMessageID = resultSet.getLong(1); + String message = resultSet.getString(2); + String date = resultSet.getString(3); + boolean read = resultSet.getBoolean(4); + String type = resultSet.getString(5); + userMessages.add(new UserMessage(userMessageID, message, date, read, type)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return userMessages; + } + + /** + * Method used to indicate that a certain UserMessage of a certain user has been read. + * + * @param userID The ID of the user to which the certain UserMessage belongs. + * @param userMessageID The ID of the UserMessage for which it should be indicated that it is read. + */ + public void setUserMessageRead(int userID, long userMessageID) { + try { + PreparedStatement statement = connection.prepareStatement(SET_USER_MESSAGE_READ); + statement.setInt(1, userID); + statement.setLong(2, userMessageID); + statement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * Method user to retrieve the highest lifetime balance of a certain user. + * + * @param userID The ID of the user whose highest lifetime balance will be retrieved. + * @return The highest lifetime balance of the user. + */ + public float getHighestLifetimeBalance(int userID) { + float highestLifetimeBalance = 0; + + try { + PreparedStatement statement = connection.prepareStatement(GET_HIGHEST_LIFETIME_BALANCE); + statement.setInt(1, userID); + ResultSet resultSet = statement.executeQuery(); + if (resultSet.next()) { + highestLifetimeBalance = resultSet.getFloat(1); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return highestLifetimeBalance; + } + + /** + * Method used to potentially update the highest lifetime balance of a certain user. + * The highest lifetime balance of the user will only be updated if the current balance is higher than the current + * highest lifetime balance. + * + * @param userID The ID of the user whose highest lifetime balance may be updated. + * @param currentBalance The currentBalance of the user. + */ + public void updateHighestLifetimeBalance(int userID, float currentBalance) { + try { + PreparedStatement statement = connection.prepareStatement(UPDATE_HIGHEST_LIFETIME_BALANCE); + statement.setFloat(1, currentBalance); + statement.setFloat(2, currentBalance); + statement.setInt(3, userID); + statement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + /** * Method used to link a Transaction to a Category in the database. * diff --git a/src/main/java/nl/utwente/ing/model/persistentmodel/DatabaseConnection.java b/src/main/java/nl/utwente/ing/model/persistentmodel/DatabaseConnection.java index d866e74..a26e576 100644 --- a/src/main/java/nl/utwente/ing/model/persistentmodel/DatabaseConnection.java +++ b/src/main/java/nl/utwente/ing/model/persistentmodel/DatabaseConnection.java @@ -50,11 +50,13 @@ private static void createTables() { "CREATE TABLE IF NOT EXISTS User_Table(\n" + " user_id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + " session_id TEXT,\n" + + " highest_lifetime_balance FLOAT,\n" + " highest_transaction_id BIGINT,\n" + " highest_category_id BIGINT,\n" + " highest_category_rule_id BIGINT,\n" + " highest_saving_goal_id BIGINT,\n" + - " highest_payment_request_id BIGINT\n" + + " highest_payment_request_id BIGINT,\n" + + " highest_user_message_id BIGINT\n" + ");" ); statement.executeUpdate( @@ -136,6 +138,16 @@ private static void createTables() { " FOREIGN KEY(transaction_id) REFERENCES Transaction_Table(transaction_id),\n" + " PRIMARY KEY(user_id, payment_request_id, transaction_id)\n" + ");"); + statement.executeUpdate("CREATE TABLE IF NOT EXISTS User_Message(\n" + + " user_id INTEGER,\n" + + " user_message_id BIGINT,\n" + + " message TEXT,\n" + + " date DATETIME,\n" + + " read BOOLEAN,\n" + + " type TEXT,\n" + + " FOREIGN KEY(user_id) REFERENCES User_Table(user_id),\n" + + " PRIMARY KEY(user_id, user_message_id)\n" + + ");"); statement.close(); connection.setAutoCommit(true); } catch (SQLException e) { diff --git a/src/main/java/nl/utwente/ing/model/persistentmodel/PersistentModel.java b/src/main/java/nl/utwente/ing/model/persistentmodel/PersistentModel.java index 40bdf3a..65c71bd 100644 --- a/src/main/java/nl/utwente/ing/model/persistentmodel/PersistentModel.java +++ b/src/main/java/nl/utwente/ing/model/persistentmodel/PersistentModel.java @@ -26,6 +26,7 @@ public class PersistentModel implements Model { private Connection connection; private CustomORM customORM; + private UserMessageEmitter messageEmitter; /** * The constructor of PersistentModel. @@ -34,6 +35,7 @@ public class PersistentModel implements Model { public PersistentModel() { this.connection = DatabaseConnection.getDatabaseConnection(); this.customORM = new CustomORM(connection); + this.messageEmitter = new UserMessageEmitter(connection, customORM); } /** @@ -101,6 +103,8 @@ public Transaction postTransaction(String sessionID, String date, float amount, int userID = this.getUserID(sessionID); Transaction transaction = null; try { + float oldBalance = this.getBalance(sessionID); + connection.setAutoCommit(false); customORM.increaseHighestTransactionID(userID); long transactionID = customORM.getHighestTransactionID(userID); @@ -128,9 +132,9 @@ public Transaction postTransaction(String sessionID, String date, float amount, } this.populateCategory(userID, transaction); + List paymentRequests = customORM.getPaymentRequests(userID); if (transaction.getType().equals("deposit")) { // Check if Transaction answers some Payment Request - List paymentRequests = customORM.getPaymentRequest(userID); connection.setAutoCommit(false); for (PaymentRequest paymentRequest : paymentRequests) { if (!paymentRequest.getFilled() && transaction.getAmount() == paymentRequest.getAmount() && @@ -140,13 +144,44 @@ public Transaction postTransaction(String sessionID, String date, float amount, long numberAnswered = customORM.getTransactionsByPaymentRequest(userID, paymentRequestID).size(); if (numberAnswered >= paymentRequest.getNumber_of_requests()) { customORM.setPaymentRequestFilled(userID, paymentRequestID); + + // User Message Event: Payment Request filled + messageEmitter.eventPaymentRequestFilled(userID, + paymentRequestID, paymentRequest.getDescription()); } break; } } - connection.commit(); - connection.setAutoCommit(true); + if (!connection.getAutoCommit()) { + connection.commit(); + connection.setAutoCommit(true); + } } + + // Check if PaymentRequests are not filled on due-date + for (PaymentRequest paymentRequest : paymentRequests) { + if (!paymentRequest.getFilled() && IntervalHelper.isSmallerThan(paymentRequest.getDue_date(), date)) { + // User Message Event: Payment Request not filled + messageEmitter.eventPaymentRequestNotFilled(userID, + paymentRequest.getID(), paymentRequest.getDescription()); + } + } + + float newBalance = this.getBalance(sessionID); + + if (oldBalance >= 0 && newBalance < 0) { + // User Message Event: balance drop below zero + messageEmitter.eventBalanceBelowZero(userID); + } + + float oldHighestBalance = customORM.getHighestLifetimeBalance(userID); + customORM.updateHighestLifetimeBalance(userID, newBalance); + float newHighestBalance = customORM.getHighestLifetimeBalance(userID); + if (newHighestBalance > oldHighestBalance) { + // User Message Event: new highest lifetime balance + messageEmitter.eventBalanceNewHigh(userID); + } + } catch (SQLException e) { e.printStackTrace(); } @@ -471,16 +506,17 @@ public void deleteCategoryRule(String sessionID, long categoryRuleID) * Method used to retrieve balance history information of a certain user in the form of a list of * BalanceCandlesticks. * - * @param sessionID The sessionID of the user. + * @param sessionID The sessionID of the user. * @param intervalPeriod The IntervalPeriod specifying the span of intervals. - * @param amount The amount of intervals for which BalanceCandlesticks should be generated. + * @param amount The amount of intervals for which BalanceCandlesticks should be generated. * @return The balance history information of a certain user in the form of a list of BalanceCandlesticks. */ public ArrayList getBalanceHistory(String sessionID, IntervalPeriod intervalPeriod, int amount) throws InvalidSessionIDException { int userID = this.getUserID(sessionID); - LocalDateTime[] intervals = IntervalHelper.getIntervals(intervalPeriod, amount); + LocalDateTime[] intervals = IntervalHelper.getIntervals(intervalPeriod, amount, + IntervalHelper.toLocalDateTime(customORM.getCurrentDate(userID))); ArrayList transactions = customORM.getTransactionsAscending(userID); ArrayList savingGoals = customORM.getSavingGoals(userID); @@ -552,6 +588,14 @@ public ArrayList getBalanceHistory(String sessionID, Interva candlesticks.remove(0); + for (SavingGoal savingGoal : savingGoals) { + if (savingGoal.getDeletionDate() == null && + savingGoal.getBalance() >= savingGoal.getGoal()) { + // User Message Event: Saving Goal reached + messageEmitter.eventSavingGoalReached(userID, savingGoal.getId(), savingGoal.getName()); + } + } + return candlesticks; } @@ -661,7 +705,7 @@ public void deleteSavingGoal(String sessionID, long savingGoalID) */ public ArrayList getPaymentRequests(String sessionID) throws InvalidSessionIDException { int userID = this.getUserID(sessionID); - ArrayList paymentRequests = customORM.getPaymentRequest(userID); + ArrayList paymentRequests = customORM.getPaymentRequests(userID); for (PaymentRequest paymentRequest : paymentRequests) { ArrayList transactions = customORM.getTransactionsByPaymentRequest(userID, paymentRequest.getID()); paymentRequest.setTransactions(transactions); @@ -676,7 +720,7 @@ public ArrayList getPaymentRequests(String sessionID) throws Inv /** * Method used to create a new PaymentRequest for a certain user. * - * @param sessionID The sessionID of the user. + * @param sessionID The sessionID of the user. * @param paymentRequest The PaymentRequest object to be used to create the new PaymentRequest. * @return The PaymentRequest created by this method. */ @@ -707,6 +751,52 @@ public PaymentRequest postPaymentRequest(String sessionID, PaymentRequest paymen return createdPaymentRequest; } + /** + * Method used to retrieve the unread UserMessages belonging to a certain user. + * + * @param sessionID The sessionID of the user. + * @return An ArrayList of UserMessages belonging to the user with sessionID. + */ + public ArrayList getUnreadUserMessages(String sessionID) throws InvalidSessionIDException { + int userID = this.getUserID(sessionID); + return customORM.getUnreadUserMessages(userID); + } + + /** + * Method used to indicate that a certain UserMessage of a certain user is read. + * + * @param sessionID The sessionID of the user. + * @param userMessageID The ID of the UserMessage of the certain user that should be marked as read. + */ + public void setUserMessageRead(String sessionID, long userMessageID) + throws InvalidSessionIDException, ResourceNotFoundException { + int userID = this.getUserID(sessionID); + + UserMessage userMessage = customORM.getUserMessage(userID, userMessageID); + if (userMessage != null) { + customORM.setUserMessageRead(userID, userMessageID); + } else { + throw new ResourceNotFoundException(); + } + } + + + /** + * Method used to retrieve the current balance of a certain user. + * + * @param sessionID The sessionID of the user. + * @return The current balance of the user. + */ + private float getBalance(String sessionID) { + try { + return this.getBalanceHistory(sessionID, IntervalPeriod.HOUR, 1).get(0).getClose(); + } catch (InvalidSessionIDException e) { + // This never happens, because this method will never get called with an invalid session ID. + e.printStackTrace(); + return 0; + } + } + /** * Method used to populate a Transaction object with a Category object. * diff --git a/src/main/java/nl/utwente/ing/model/persistentmodel/UserMessageEmitter.java b/src/main/java/nl/utwente/ing/model/persistentmodel/UserMessageEmitter.java new file mode 100644 index 0000000..69b9f2a --- /dev/null +++ b/src/main/java/nl/utwente/ing/model/persistentmodel/UserMessageEmitter.java @@ -0,0 +1,154 @@ +package nl.utwente.ing.model.persistentmodel; + +import nl.utwente.ing.misc.date.IntervalHelper; +import nl.utwente.ing.model.bean.Transaction; +import nl.utwente.ing.model.bean.UserMessage; + +import java.sql.Connection; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.ArrayList; + +/** + * The UserMessageEmitter class. + * Used to create and emit UserMessages. + * + * @author Daan Kooij + */ +public class UserMessageEmitter { + + private Connection connection; + private CustomORM customORM; + + private static final String EVENT_BALANCE_DROP_BELOW_ZERO = "Balance drop below zero."; + private static final String EVENT_BALANCE_REACH_NEW_HIGH = "Balance reach new high."; + private static final String EVENT_PAYMENT_REQUEST_FILLED = "Payment request filled: "; + private static final String EVENT_PAYMENT_REQUEST_NOT_FILLED = "Payment request not filled: "; + private static final String EVENT_SAVING_GOAL_REACHED = "Saving goal reached: "; + + /** + * The constructor of UserMessageEmitter. + * + * @param connection The database connection. + * @param customORM The CustomORM. + */ + public UserMessageEmitter(Connection connection, CustomORM customORM) { + this.connection = connection; + this.customORM = customORM; + } + + /** + * Method used to emit a UserMessage for a certain user saying that his/her balance dropped below zero. + * + * @param userID The ID of the user for which the UserMessage will be emitted. + */ + public void eventBalanceBelowZero(int userID) { + this.emitUserMessage(userID, "warning", EVENT_BALANCE_DROP_BELOW_ZERO); + } + + /** + * Method used to emit a UserMessage for a certain user saying that his/her balance reached a new high. + * If such a message already exists for the user and is still unread, nothing will be emitted. + * If the Transactions of the user do not span at least three months, nothing will be emitted. + * + * @param userID The ID of the user for which the UserMessage may be emitted. + */ + public void eventBalanceNewHigh(int userID) { + ArrayList userMessages = customORM.getUnreadUserMessages(userID); + for (UserMessage userMessage : userMessages) { + if (userMessage.getMessage().equals(EVENT_BALANCE_REACH_NEW_HIGH)) { + return; + } + } + ArrayList transactions = customORM.getTransactionsAscending(userID); + if (transactions.size() == 0) { + return; + } + + LocalDateTime firstDatePlusThreeMonths = + IntervalHelper.toLocalDateTime(transactions.get(0).getDate()).plusMonths(3); + LocalDateTime currentDate = IntervalHelper.toLocalDateTime(customORM.getCurrentDate(userID)); + if (firstDatePlusThreeMonths.compareTo(currentDate) <= 0) { + this.emitUserMessage(userID, "info", EVENT_BALANCE_REACH_NEW_HIGH); + } + } + + /** + * Method used to emit a UserMessage for a certain user saying that a PaymentRequest has been filled. + * If such a message is already emitted for the same PaymentRequest, nothing will be emitted. + * + * @param userID The ID of the user for which the UserMessage may be emitted. + * @param paymentRequestID The ID of the PaymentRequest for which a UserMessage may be emitted. + * @param paymentRequestName The name of the PaymentRequest for which a UserMessage may be emitted. + */ + public void eventPaymentRequestFilled(int userID, long paymentRequestID, String paymentRequestName) { + String message = EVENT_PAYMENT_REQUEST_FILLED + paymentRequestName + " (ID = " + paymentRequestID + ")."; + ArrayList userMessages = customORM.getAllUserMessages(userID); + for (UserMessage userMessage : userMessages) { + if (userMessage.getMessage().equals(message)) { + return; + } + } + this.emitUserMessage(userID, "info", message); + } + + /** + * Method used to emit a UserMessage for a certain user saying that a PaymentRequest has not been filled in time. + * If such a message is already emitted for the same PaymentRequest, nothing will be emitted. + * + * @param userID The ID of the user for which the UserMessage may be emitted. + * @param paymentRequestID The ID of the PaymentRequest for which a UserMessage may be emitted. + * @param paymentRequestName The name of the PaymentRequest for which a UserMessage may be emitted. + */ + public void eventPaymentRequestNotFilled(int userID, long paymentRequestID, String paymentRequestName) { + String message = EVENT_PAYMENT_REQUEST_NOT_FILLED + paymentRequestName + " (ID = " + paymentRequestID + ")."; + ArrayList userMessages = customORM.getAllUserMessages(userID); + for (UserMessage userMessage : userMessages) { + if (userMessage.getMessage().equals(message)) { + return; + } + } + this.emitUserMessage(userID, "warning", message); + } + + /** + * Method used to emit a UserMessage for a certain user saying that a SavingGoal has been reached. + * If such a message is already emitted for the same SavingGoal, nothing will be emitted. + * + * @param userID The ID of the user for which the UserMessage may be emitted. + * @param savingGoalID The ID of the SavingGoal for which a UserMessage may be emitted. + * @param savingGoalName The name of the SavingGoal for which a UserMessage may be emitted. + */ + public void eventSavingGoalReached(int userID, long savingGoalID, String savingGoalName) { + String message = EVENT_SAVING_GOAL_REACHED + savingGoalName + " (ID = " + savingGoalID + ")."; + ArrayList userMessages = customORM.getAllUserMessages(userID); + for (UserMessage userMessage : userMessages) { + if (userMessage.getMessage().equals(message)) { + return; + } + } + this.emitUserMessage(userID, "info", message); + } + + /** + * Method used to emit a UserMessage for a certain user. + * + * @param userID The ID of the user for which the UserMessage will be emitted. + * @param type The type of the to be emitted UserMessage. + * @param message The message of the to be emitted UserMessage. + */ + private void emitUserMessage(int userID, String type, String message) { + try { + connection.setAutoCommit(false); + customORM.increaseHighestUserMessageID(userID); + long userMessageID = customORM.getHighestUserMessageID(userID); + connection.commit(); + connection.setAutoCommit(true); + String date = customORM.getCurrentDate(userID); + customORM.createUserMessage(userID, new UserMessage(userMessageID, message, date, false, type)); + } catch (SQLException e) { + e.printStackTrace(); + } + } + +}