diff --git a/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/DAO/CharityDAO.java b/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/DAO/CharityDAO.java new file mode 100644 index 0000000..810eca1 --- /dev/null +++ b/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/DAO/CharityDAO.java @@ -0,0 +1,273 @@ +package ntnu.sytemutvikling.team6.DAO; + +import ntnu.sytemutvikling.team6.scraper.APICharityData; + +import java.sql.*; +import java.util.List; + +/** + * Manages the charity + *

+ * This class is responsible for establishing connections using environment variables + * and maintaining the {@code charities} table based on data retrieved from the IK API. + *

+ */ + +public class CharityDAO { + +import APICharityData; + +import java.sql.*; +import java.util.List; + + + + public class DatabaseManager { + private final String databaseURL; + private final String username; + private final String password; + + /** + Constructs a new {@code DatabaseManager} using database credentials + * retrieved from system environment variables. + *

+ * Required environment variables: + *

+ *

+ * + * @throws IllegalStateException if either databaseURL, username, or password is {@code null} or blank + */ + + // Values stored in system environment variables for security (using ntnu's phpmyadmin for this project) + public DatabaseManager() { + this.databaseURL = System.getenv("HMH_DB_URL"); + this.username = System.getenv("HMH_DB_USERNAME"); + this.password = System.getenv("HMH_DB_PASSWORD"); + + if (this.databaseURL == null || this.databaseURL.isBlank()) { + throw new IllegalStateException("Database environment variable URL has not been set"); + } + + if (this.username == null || this.username.isBlank()) { + throw new IllegalStateException("Username environment variable has not been set"); + } + + if (this.password == null || this.password.isBlank()) { + throw new IllegalStateException("Password environment variable has not been set"); + } + } + + /** + * Constructs a new {@code DatabaseManager} using database credentials + *

+ * Used primarily for JUnit tests. Production should use the constructor + * using environment variables. + *

+ * + * @param databaseURL the url to the database + * @param username the username used to log in to the database + * @param password the password used to log in to the database + * @throws IllegalArgumentException if databaseURL, username, or password is + * {@code null} or blank + */ + + public DatabaseManager(String databaseURL, String username, String password) { + this.databaseURL = databaseURL; + this.username = username; + this.password = password; + + if (this.databaseURL == null || this.databaseURL.isBlank()) { + throw new IllegalArgumentException("Database environment variable URL has not been set"); + } + + if (this.username == null || this.username.isBlank()) { + throw new IllegalArgumentException("Username environment variable has not been set"); + } + + if (this.password == null || this.password.isBlank()) { + throw new IllegalArgumentException("Password environment variable has not been set"); + } + } + + /** + * Creates the {@code charities} table if it does not already exist. + *

+ * The table structure is based on fields from {@link APICharityData}. + *

+ * @throws RuntimeException if a {@link SQLException} occurs while creating the table + */ + + public void createTables() { + String sql_query = """ + -- MySQL Workbench Forward Engineering + + SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; + SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; + SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; + + -- ----------------------------------------------------- + -- Schema HelpMeHelp + -- ----------------------------------------------------- + + -- ----------------------------------------------------- + -- Schema HelpMeHelp + -- ----------------------------------------------------- + --CREATE SCHEMA IF NOT EXISTS `HelpMeHelp` DEFAULT CHARACTER SET utf8 ; + --USE `HelpMeHelp` ; + USE apbaluna; + + -- ----------------------------------------------------- + -- Table `HelpMeHelp`.`Charities` + -- ----------------------------------------------------- + CREATE TABLE IF NOT EXISTS `HelpMeHelp`.`Charities` ( + `UUID` CHAR(36) NOT NULL, + `charity_name` VARCHAR(255) NOT NULL, + `charity_description` VARCHAR(255) NOT NULL, + `isVerified` TINYINT NOT NULL, + `category` VARCHAR(45) NOT NULL, + PRIMARY KEY (`UUID`)) + ENGINE = InnoDB; + + + -- ----------------------------------------------------- + -- Table `HelpMeHelp`.`Donations` + -- ----------------------------------------------------- + CREATE TABLE IF NOT EXISTS `HelpMeHelp`.`Donations` ( + `UUID` CHAR(36) NOT NULL, + `amount` DECIMAL NOT NULL, + `date` DATE NOT NULL, + `Charities_UUID` CHAR(36) NOT NULL, + PRIMARY KEY (`UUID`), + INDEX `fk_Donations_Charities_idx` (`Charities_UUID` ASC) VISIBLE, + CONSTRAINT `fk_Donations_Charities` + FOREIGN KEY (`Charities_UUID`) + REFERENCES `HelpMeHelp`.`Charities` (`UUID`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) + ENGINE = InnoDB; + + + SET SQL_MODE=@OLD_SQL_MODE; + SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; + SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; + + """; + + try (Connection conn = DriverManager.getConnection(databaseURL, username, password); + Statement s = conn.createStatement()) { + + s.execute(sql_query); + } catch (SQLException e) { + e.printStackTrace(); + throw new RuntimeException("Error creating table."); + } + } + + /** + * Synchronizes the {@code charities} table with the provided list of APICharityData. + *

+ * Summary of function: + *

+ * A temporary table is used to determine which records should be deleted. + *

+ * + * @param charities a list of ApiCharityDaya objects + * @throws RuntimeException if a {@link SQLException} occurs during database synchronization + */ + + public void updateCharities(List charities) { + Connection conn = null; + try { + conn = DriverManager.getConnection(databaseURL, username, password); + conn.setAutoCommit(false); + // Inserts objects values into charities + String sql_query = """ + INSERT INTO charities (org_number, name, status, url, is_pre_approved) + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + name = VALUES(name), + status = VALUES(status), + url = VALUES(url), + is_pre_approved = VALUES(is_pre_approved) + """; + + try (PreparedStatement s = conn.prepareStatement(sql_query)) { + for (APICharityData charity : charities) { + s.setString(1, charity.getOrg_number().replaceAll("\\s", "")); + s.setString(2, charity.getName()); + s.setString(3, charity.getStatus()); + s.setString(4, charity.getUrl()); + s.setBoolean(5, charity.getIs_pre_approved()); + + s.addBatch(); + } + s.executeBatch(); + } + + + // Temporary table for parity check + sql_query = "CREATE TEMPORARY TABLE temp (org_number VARCHAR(100) PRIMARY KEY)"; + try (PreparedStatement temp_create = conn.prepareStatement(sql_query)) { + temp_create.execute(); + } + + String sql_update_temp_table = "INSERT INTO temp (org_number) VALUES (?)"; + try (PreparedStatement temp_update = conn.prepareStatement(sql_update_temp_table)) { + for (APICharityData charity : charities) { + temp_update.setString(1, charity.getOrg_number().replaceAll("\\s", "")); + temp_update.addBatch(); + } + temp_update.executeBatch(); + } + + + + // Removes records not found in the list from charities table + sql_query = """ + DELETE FROM charities c + WHERE NOT EXISTS ( + SELECT 1 + FROM temp t + WHERE c.org_number = t.org_number + ) + """; + + try (PreparedStatement delete_old_records = conn.prepareStatement(sql_query)){ + delete_old_records.execute(); + } + + conn.commit(); + } catch (SQLException e) { + + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + e.printStackTrace(); + throw new RuntimeException("ERROR: Something went wrong during updating charities table."); + } finally { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + } + } +} diff --git a/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/DAO/DonationDAO.java b/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/DAO/DonationDAO.java new file mode 100644 index 0000000..b902276 --- /dev/null +++ b/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/DAO/DonationDAO.java @@ -0,0 +1,4 @@ +package ntnu.sytemutvikling.team6.DAO; + +public class DonationDAO { +}