diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java index 5e25df02..10868532 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java @@ -11,9 +11,8 @@ import javafx.stage.Stage; import ntnu.systemutvikling.team6.database.DatabaseConnection; import ntnu.systemutvikling.team6.database.DatabaseSetup; -import ntnu.systemutvikling.team6.models.Charity; import ntnu.systemutvikling.team6.models.registry.CharityRegistry; -import ntnu.systemutvikling.team6.scraper.scraperComponents.APICharityScraper; +import ntnu.systemutvikling.team6.scraper.FullCharityScrape; import ntnu.systemutvikling.team6.service.APIToDatabaseService; public class HmHApplication extends Application { @@ -49,16 +48,24 @@ public void init() { /* Test and get data from Innsamlingkontrollen API */ try { HttpClient https = HttpClient.newHttpClient(); - APICharityScraper scraper = new APICharityScraper(https); + // APICharityScraper scraper = new APICharityScraper(https); + FullCharityScrape scraper = new FullCharityScrape(); DatabaseConnection conn = new DatabaseConnection(); APIToDatabaseService db = new APIToDatabaseService(conn); - if (scraper.checkConnection()) { + if (scraper.getAPIScraper().checkConnection()) { + /* + if (scraper.checkConnection()) { CharityRegistry charityRegistry = scraper.parseJSON(scraper.getJSONData()); for (Charity charity : charityRegistry.getAllCharities()) { System.out.println(charity.getName()); } + */ + + // Comment out the two below to use already generated database. + CharityRegistry charityRegistry = scraper.getAPIAndURLCharityData(); + db.addAPIDataToTable(charityRegistry.getAllCharities()); } } catch (Exception e) { diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java index 81d6f4b8..d831b250 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java @@ -1,9 +1,20 @@ package ntnu.systemutvikling.team6.controller; +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.shape.Arc; +import javafx.scene.shape.Rectangle; import ntnu.systemutvikling.team6.controller.components.LoaderScene; import ntnu.systemutvikling.team6.models.Charity; @@ -18,6 +29,24 @@ public class CharityPageController { @FXML private Label CharityName; + @FXML private ImageView CharityLogo; + + @FXML private Hyperlink CharityURL; + + @FXML private Arc keyValueInnsamlingArc; + + @FXML private Label keyValueInnsamlingLabel; + + @FXML private Arc keyValueAdminArc; + + @FXML private Label keyValueAdminLabel; + + @FXML private Arc keyValueFormaalArc; + + @FXML private Label keyValueFormaalLabel; + + @FXML private VBox categoriesContainer; + @FXML public void initialize() {} @@ -31,7 +60,7 @@ public void initialize() {} * front page when the user clicks on a charity, to set the charity that is being displayed on the * page. * - * @param charity + * @param charity the charity to be displayed */ @FXML public void setCharity(Charity charity) { @@ -39,6 +68,40 @@ public void setCharity(Charity charity) { CharityDescription.setText(charity.getDescription()); CharityName.setText(charity.getName()); + + if (this.charity.getLogoBlob() != null) { + ByteArrayInputStream logoByteStream = new ByteArrayInputStream(this.charity.getLogoBlob()); + Image CharityLogoImage = new Image(logoByteStream); + this.CharityLogo.setImage(CharityLogoImage); + } else { + String placeholderImagePath = + Objects.requireNonNull(getClass().getResource("/images/leggTilBilde.jpg")) + .toExternalForm(); + Image placeholderImage = new Image(placeholderImagePath); + this.CharityLogo.setImage(placeholderImage); + } + + // Sets key values to a List + String input = charity.getKeyValues(); + + String[] parts = input.split(":"); + List numbers = new ArrayList<>(); + + for (String part : parts) { + part = part.replace(",", "."); + numbers.add(Double.parseDouble(part)); + } + + // Sets the value of each arc and label + setArc(keyValueInnsamlingArc, numbers.getFirst()); + keyValueInnsamlingLabel.setText(String.format("%.1f%%", numbers.getFirst())); + setArc(keyValueAdminArc, numbers.get(1)); + keyValueAdminLabel.setText(String.format("%.1f%%", numbers.get(1))); + setArc(keyValueFormaalArc, numbers.getLast()); + keyValueFormaalLabel.setText(String.format("%.1f%%", numbers.getLast())); + + // Sets the categories + setCategories(charity.getCategory()); } /** @@ -73,4 +136,73 @@ public void handleSearch(ActionEvent event) { LoaderScene.LoadScene("availableOrganization", event, null, query); } + + /** + * Opens OS default webbrowser and loads the url of the charity on click. + * + * @param event the onclick event + */ + @FXML + public void handleHomepageClick(ActionEvent event) { + try { + String url = this.charity.getURL(); + java.awt.Desktop.getDesktop().browse(java.net.URI.create(url)); + } catch (Exception e) { + System.out.println("Something went wrong when opening URL."); + e.printStackTrace(); + } + } + + /** + * Creates the labels (and the rectangle surrounding it) for the categories + * + * @param category the String for the category + * @return a fxml object used to populate the category scroll pane + */ + private StackPane createCategoryChip(String category) { + + Rectangle rect = new Rectangle(); + rect.setArcWidth(30); + rect.setArcHeight(30); + rect.setHeight(40); + rect.setWidth(200); + rect.setFill(javafx.scene.paint.Color.web("#F5F5F5")); + rect.setStroke(javafx.scene.paint.Color.web("#4F4F4F")); + + Label label = new Label(category); + label.setStyle("-fx-font-weight: bold; -fx-font-size: 14px;"); + + StackPane stack = new StackPane(rect, label); + stack.setPadding(new javafx.geometry.Insets(5)); + + return stack; + } + + /** + * Takes a list of categories for the charities and populates a scroll pane with labels containing + * the charities. + * + * @param categories the list of categories for the charity + */ + public void setCategories(List categories) { + categoriesContainer.getChildren().clear(); + + for (String category : categories) { + if (category == null || category.isEmpty()) continue; + + StackPane chip = createCategoryChip(category); + categoriesContainer.getChildren().add(chip); + } + } + + /** + * Sets the fill of the arc for the different key values. + * + * @param arc the arc for one of the 3 key values + * @param percent the percentage of the key value + */ + private void setArc(Arc arc, double percent) { + double angle = -360 * (percent / 100.0); + arc.setLength(angle); + } } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java index 752148f3..1fd15cd0 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -17,6 +18,7 @@ import ntnu.systemutvikling.team6.controller.components.NavbarFooterController; import ntnu.systemutvikling.team6.controller.components.OrganizationCardController; import ntnu.systemutvikling.team6.database.DatabaseConnection; +import ntnu.systemutvikling.team6.database.Readers.CategorySelect; import ntnu.systemutvikling.team6.database.Readers.CharitySelect; import ntnu.systemutvikling.team6.database.Readers.DonationSelect; import ntnu.systemutvikling.team6.models.Charity; @@ -41,9 +43,8 @@ public class FrontpageController extends BaseController implements NavbarFooterC @FXML private Label PreApproved_Percentage; @FXML private TextField frontSearchField; @FXML private CheckBox verifiedFilter; - @FXML private CheckBox childrenFilter; - @FXML private CheckBox healthFilter; - @FXML private CheckBox emergencyAidFilter; + @FXML private javafx.scene.layout.VBox categoryList; + private final List selectedCategories = new ArrayList<>(); private List allCharities = new ArrayList<>(); @@ -64,8 +65,27 @@ public void initialize() { DatabaseConnection conn = new DatabaseConnection(); CharitySelect cdb = new CharitySelect(conn); DonationSelect ddb = new DonationSelect(conn); + CategorySelect categoryselect = new CategorySelect(conn); CharityRegistry charities = cdb.getCharitiesFromDB(); DonationRegistry donations = ddb.getDonationFromDB(); + List categories = categoryselect.getCategoriesFromDB(); + categories.sort(String::compareToIgnoreCase); + + for (String category : categories) { + CheckBox cb = new CheckBox(category); + + cb.setOnAction( + e -> { + if (cb.isSelected()) { + selectedCategories.add(category); + } else { + selectedCategories.remove(category); + } + displayCharities(getFilteredCharities()); + }); + + categoryList.getChildren().add(cb); + } allCharities = new ArrayList<>(charities.getAllCharities()); displayCharities(allCharities); @@ -142,69 +162,42 @@ public void handleFrontSearch(ActionEvent event) { /** * This method is used to filter the charities based on the selected filters. * + *

The filters are whether the charity was pre-verified, or if it falls under one (or more) of + * the given categories.

+ * + *

The check checks whether the given charity has ALL filters. If one does not apply, + * the charity is rejected.

+ * * @return a list of filtered charities. */ private List getFilteredCharities() { - if (!verifiedFilter.isSelected() - && !childrenFilter.isSelected() - && !healthFilter.isSelected() - && !emergencyAidFilter.isSelected()) { - return allCharities; - } - List filteredCharities = new ArrayList<>(); + List filtered = new ArrayList<>(); + for (Charity charity : allCharities) { - if (matchesSelectedFilters(charity)) { - filteredCharities.add(charity); + + if (verifiedFilter.isSelected() && !charity.getPreApproved()) { + continue; } - } - return filteredCharities; - } - /** - * This method is used to check if a charity matches the selected filters. - * - * @param charity is the charity to be checked. - * @return true if the charity matches the selected filters, false otherwise. - */ - private boolean matchesSelectedFilters(Charity charity) { - return (verifiedFilter.isSelected() && charity.getPreApproved()) - || (childrenFilter.isSelected() && matchesKeywordCategory(charity, "children")) - || (healthFilter.isSelected() && matchesKeywordCategory(charity, "health")) - || (emergencyAidFilter.isSelected() && matchesKeywordCategory(charity, "emergency")); - } + if (!selectedCategories.isEmpty()) { - /** - * This method is used to check if a charity matches a specific category. - * - * @param charity is the charity to be checked. - * @param category is the category to check against. - * @return true if the charity matches the category, false otherwise. - */ - private boolean matchesKeywordCategory(Charity charity, String category) { - String text = (charity.getName() + " " + charity.getDescription()).toLowerCase(); - - return switch (category) { - case "children" -> - text.contains("child") - || text.contains("children") - || text.contains("barn") - || text.contains("youth") - || text.contains("young"); - case "health" -> - text.contains("health") - || text.contains("medical") - || text.contains("helse") - || text.contains("hospital") - || text.contains("care"); - case "emergency" -> - text.contains("emergency") - || text.contains("relief") - || text.contains("crisis") - || text.contains("aid") - || text.contains("disaster"); - default -> false; - }; + boolean hasAll = + charity.getCategory().stream() + .filter(Objects::nonNull) + .map(c -> c.toLowerCase().trim()) + .collect(java.util.stream.Collectors.toSet()) + .containsAll(selectedCategories); + + if (!hasAll) { + continue; + } + } + + filtered.add(charity); + } + + return filtered; } /** diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java index 6da6f1ca..6fdcc88d 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java @@ -198,7 +198,9 @@ FOREIGN KEY (`user_id`) CREATE TABLE IF NOT EXISTS `apbaluna`.`Categories` ( `category_id` INT NOT NULL AUTO_INCREMENT, `category` VARCHAR(255) NOT NULL, - PRIMARY KEY (`category_id`)) + PRIMARY KEY (`category_id`), + UNIQUE (`category`) + ) ENGINE = InnoDB; """; @@ -221,8 +223,8 @@ FOREIGN KEY (`Categories_category_id`) CONSTRAINT `fk_Categories_has_Charities_Charities1` FOREIGN KEY (`Charities_UUID_charities`) REFERENCES `apbaluna`.`Charities` (`UUID_charities`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) + ON DELETE CASCADE + ON UPDATE CASCADE) ENGINE = InnoDB; """; @@ -259,6 +261,7 @@ FOREIGN KEY (`User_UUID_User`) `logoURL` TEXT NULL, `key_values` TEXT NULL, `logoBLOB` MEDIUMBLOB NULL, + CONSTRAINT `unique_UUID_charity` UNIQUE (`UUID_charity`), INDEX `fk_CharityVanity_Charities1_idx` (`UUID_charity` ASC) VISIBLE, CONSTRAINT `fk_CharityVanity_Charities1` FOREIGN KEY (`UUID_charity`) diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CategorySelect.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CategorySelect.java new file mode 100644 index 00000000..5ce735e4 --- /dev/null +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CategorySelect.java @@ -0,0 +1,53 @@ +package ntnu.systemutvikling.team6.database.Readers; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import ntnu.systemutvikling.team6.database.DatabaseConnection; + +/** + * Data access class responsible for getting all the distinct categories from the database. + * + *

All queries are executed against a MySQL database via a {@link DatabaseConnection}. + */ +public class CategorySelect { + private final DatabaseConnection connection; + + /** + *Constructs a new {@code CharitySelect} with the given database connection. + * + * @param connection the {@link DatabaseConnection} to use for executing queries; must not be + * {@code null} + */ + public CategorySelect(DatabaseConnection connection) { + this.connection = connection; + } + + /** + * Retrieves all the categories listed in the Category table of the database. + * + * @return a list of strings containing the name of the categories + */ + public List getCategoriesFromDB() { + List categories = new ArrayList<>(); + + try (Connection conn = connection.getMySqlConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT category FROM Categories")) { + + while (rs.next()) { + categories.add(rs.getString("category")); + } + + } catch (SQLException e) { + e.printStackTrace(); + throw new RuntimeException( + "ERROR: Something went wrong during fetching categories from database."); + } + + return categories; + } +} diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CharitySelect.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CharitySelect.java index 5beaf2e6..e863cac8 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CharitySelect.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CharitySelect.java @@ -52,23 +52,27 @@ public CharitySelect(DatabaseConnection connection) { public CharityRegistry getCharitiesFromDB() { CharityRegistry registry = null; Connection conn = null; + try { conn = connection.getMySqlConnection(); + String sql_query = """ - SELECT - c.UUID_charities, c.org_number, c.pre_approved, c.status, - f.UUID_feedback, f.feedback_comment, f.feedback_date, f.isAnonymous, f.charity_id, f.user_id, - cv.charity_name, cv.charity_link, cv.description, cv.logoURL, cv.key_values, cv.logoBLOB, - u.UUID_user, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role, - cat.category - FROM Charities c - LEFT JOIN Feedback f ON f.charity_id = c.UUID_charities - LEFT JOIN User u ON f.user_id = u.UUID_user - LEFT JOIN Charity_Categories cc ON cc.Charities_UUID_charities = c.UUID_charities - LEFT JOIN Categories cat ON cat.category_id = cc.Categories_category_id - INNER JOIN CharityVanity cv ON cv.UUID_charity = c.UUID_charities; - """; + SELECT + c.UUID_charities, c.org_number, c.pre_approved, c.status, + f.UUID_feedback, f.feedback_comment, f.feedback_date, f.isAnonymous, f.charity_id, f.user_id, + cv.charity_name, cv.charity_link, cv.description, cv.logoURL, cv.key_values, cv.logoBLOB, + u.UUID_user, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role, + cat.category + FROM Charities c + LEFT JOIN Feedback f ON f.charity_id = c.UUID_charities + LEFT JOIN User u ON f.user_id = u.UUID_user + LEFT JOIN Charity_Categories cc ON cc.Charities_UUID_charities = c.UUID_charities + LEFT JOIN Categories cat ON cat.category_id = cc.Categories_category_id + INNER JOIN CharityVanity cv ON cv.UUID_charity = c.UUID_charities + ORDER BY c.UUID_charities; + """; + Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql_query); @@ -78,10 +82,12 @@ public CharityRegistry getCharitiesFromDB() { Set seenFeedbackIds = new HashSet<>(); registry = new CharityRegistry(); + while (rs.next()) { String currentId = rs.getString("UUID_charities"); if (lastCharity == null || !currentId.equals(lastCharity)) { + currentCharity = new Charity( rs.getString("UUID_charities"), @@ -94,19 +100,27 @@ public CharityRegistry getCharitiesFromDB() { rs.getString("logoURL"), rs.getString("key_values"), rs.getBytes("logoBLOB")); + registry.addCharity(currentCharity); lastCharity = currentId; seenFeedbackIds.clear(); } String categoryName = rs.getString("category"); - if (categoryName != null & !currentCharity.getCategory().contains(categoryName)) { - currentCharity.getCategory().add(categoryName); + + if (categoryName != null) { + categoryName = categoryName.toLowerCase().trim(); + + if (!categoryName.isEmpty() && !currentCharity.getCategory().contains(categoryName)) { + currentCharity.getCategory().add(categoryName); + } } String feedbackId = rs.getString("UUID_feedback"); + if (feedbackId != null && !seenFeedbackIds.contains(feedbackId)) { seenFeedbackIds.add(feedbackId); + User userWithMinimalSettingsAndInbox = new User( rs.getString("UUID_User"), @@ -115,6 +129,7 @@ public CharityRegistry getCharitiesFromDB() { rs.getString("user_email"), rs.getString("user_password"), rs.getString("role")); + userWithMinimalSettingsAndInbox.setSettings(new Settings(false, Language.ENGLISH, false)); Feedback feedback = @@ -132,6 +147,7 @@ public CharityRegistry getCharitiesFromDB() { e.printStackTrace(); throw new RuntimeException("ERROR: Something went wrong during updating charities table."); } + return registry; } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java index a69e3a9b..e881d424 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java @@ -103,4 +103,8 @@ public CharityRegistry getAPIAndURLCharityData() throws IOException, Interrupted } return charityRegistry; } + + public APICharityScraper getAPIScraper() { + return this.apiScraper; + } } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java index f9e98b37..33b3c529 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java @@ -70,7 +70,7 @@ public URLCharityScraper(String url, WebDriver driver) { * @return the {@code WebDriverWait} object to be used in the methods */ protected WebDriverWait createWait() { - return new WebDriverWait(driver, Duration.ofSeconds(30)); + return new WebDriverWait(driver, Duration.ofSeconds(10)); } /** @@ -118,7 +118,7 @@ protected void updateDescription() { wait.until( ExpectedConditions.numberOfElementsToBeMoreThan(By.cssSelector(".information"), 0)); - Thread.sleep(5000); + // Thread.sleep(5000); List firstDescription = findElements(By.cssSelector(".information")); for (WebElement element : firstDescription) { @@ -139,7 +139,7 @@ void updateLogo() { try { WebDriverWait wait = createWait(); wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".logo > img"))); - Thread.sleep(5000); + // Thread.sleep(5000); WebElement logo = findElement(By.cssSelector(".logo > img")); this.logoURL = logo.getAttribute("src"); @@ -155,7 +155,7 @@ void updateCategories() { WebDriverWait wait = createWait(); wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".tag-label"))); - Thread.sleep(5000); + // Thread.sleep(5000); List elements = findElements(By.cssSelector(".tag-label")); @@ -183,7 +183,7 @@ void updateKeyValues() { ExpectedConditions.visibilityOfElementLocated( By.xpath( "//li[.//h2[normalize-space()='Innsamlingsprosent']]//div[@class='graph']"))); - Thread.sleep(5000); + // Thread.sleep(5000); element = findElement( By.xpath("//li[.//h2[normalize-space()='Innsamlingsprosent']]//div[@class='graph']")); @@ -224,9 +224,9 @@ public void scrapeCharityPage() { updateLogo(); updateCategories(); updateKeyValues(); - Thread.sleep(1000); + // Thread.sleep(1000); - } catch (InterruptedException e) { + } catch (Exception e) { throw new RuntimeException(e); } finally { closeDriver(); diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/APIToDatabaseService.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/APIToDatabaseService.java index 9c50ad91..9e73c5c1 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/APIToDatabaseService.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/APIToDatabaseService.java @@ -2,6 +2,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.UUID; @@ -36,41 +37,58 @@ public APIToDatabaseService(DatabaseConnection connection) { */ public void addAPIDataToTable(List charities) { Connection conn = null; - // Everything charity related except categories try { conn = connection.getMySqlConnection(); conn.setAutoCommit(false); + String sql1 = """ - INSERT INTO Charities (UUID_charities, org_number, pre_approved, status) - VALUES (?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - pre_approved = VALUES(pre_approved), - status = VALUES(status); - """; + INSERT INTO Charities (UUID_charities, org_number, pre_approved, status) + VALUES (?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + pre_approved = VALUES(pre_approved), + status = VALUES(status); + """; String sql2 = """ - INSERT INTO CharityVanity (UUID_charity, charity_name, charity_link, description, logoURL, key_values, logoBlob) - VALUES (?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - charity_name = VALUES(charity_name), - charity_link = VALUES(charity_link), - description = VALUES(description), - logoURL = VALUES(logoURL), - key_values = VALUES(key_values), - logoBlob = VALUES(logoBlob); - """; + INSERT INTO CharityVanity (UUID_charity, charity_name, charity_link, description, logoURL, key_values, logoBlob) + VALUES (?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + charity_name = VALUES(charity_name), + charity_link = VALUES(charity_link), + description = VALUES(description), + logoURL = VALUES(logoURL), + key_values = VALUES(key_values), + logoBlob = VALUES(logoBlob); + """; + + String sql3 = "INSERT IGNORE INTO Categories (category) VALUES (?)"; + + String sql4 = "SELECT category_id FROM Categories WHERE category = ?"; + + String sql5 = + """ + INSERT IGNORE INTO Charity_Categories (Categories_category_id, Charities_UUID_charities) + VALUES (?, ?) + """; try (PreparedStatement ps1 = conn.prepareStatement(sql1); - PreparedStatement ps2 = conn.prepareStatement(sql2)) { + PreparedStatement ps2 = conn.prepareStatement(sql2); + PreparedStatement ps3 = conn.prepareStatement(sql3); + PreparedStatement ps4 = conn.prepareStatement(sql4); + PreparedStatement ps5 = conn.prepareStatement(sql5)) { for (Charity charity : charities) { String uuid; if (charity.getUUID() == null) { - uuid = UUID.randomUUID().toString(); + + uuid = + UUID.nameUUIDFromBytes( + (charity.getOrg_number() + charity.getURL() + charity.getName()).getBytes()) + .toString(); charity.setUUIDFromString(uuid); - System.out.println("API object doesnt have UUID, assigning"); + System.out.println("API object doesn't have UUID, assigning stable UUID"); } else { uuid = charity.getUUID().toString(); } @@ -89,6 +107,31 @@ INSERT INTO CharityVanity (UUID_charity, charity_name, charity_link, description ps2.setString(6, charity.getKeyValues()); ps2.setBytes(7, charity.getLogoBlob()); ps2.executeUpdate(); + + if (charity.getCategory() != null) { + for (String categoryRaw : charity.getCategory()) { + if (categoryRaw == null) continue; + + String category = categoryRaw.toLowerCase().trim(); + + if (category.isEmpty()) continue; + + ps3.setString(1, category); + ps3.executeUpdate(); + + ps4.setString(1, category); + + try (ResultSet rs = ps4.executeQuery()) { + if (rs.next()) { + int categoryId = rs.getInt("category_id"); + + ps5.setInt(1, categoryId); + ps5.setString(2, uuid); + ps5.executeUpdate(); + } + } + } + } } } catch (Exception e) { @@ -96,53 +139,63 @@ INSERT INTO CharityVanity (UUID_charity, charity_name, charity_link, description throw new RuntimeException(e); } - // -- Intergerty Check: + // Integrity Check String createTemp = """ - CREATE TEMPORARY TABLE temp_api_charities ( - org_number VARCHAR(255) PRIMARY KEY - ) - """; - + CREATE TEMPORARY TABLE temp_api_charities ( + org_number VARCHAR(255) PRIMARY KEY + ) + """; try (PreparedStatement ps = conn.prepareStatement(createTemp)) { ps.execute(); } String insertTemp = "INSERT IGNORE INTO temp_api_charities (org_number) VALUES (?)"; - try (PreparedStatement ps = conn.prepareStatement(insertTemp)) { - for (Charity charity : charities) { ps.setString(1, charity.getOrg_number().replaceAll("\\s", "")); ps.addBatch(); } - ps.executeBatch(); } String deleteSql = """ - DELETE FROM Charities c - WHERE NOT EXISTS ( - SELECT 1 FROM temp_api_charities t - WHERE t.org_number = c.org_number - ) - AND NOT EXISTS ( - SELECT 1 FROM Donations d WHERE d.charity_id = c.UUID_charities - ) - AND NOT EXISTS ( - SELECT 1 FROM Feedback f WHERE f.charity_id = c.UUID_charities - ) - AND NOT EXISTS ( - SELECT 1 FROM CharityVanity cv WHERE cv.UUID_charity = c.UUID_charities - ) - AND NOT EXISTS ( - SELECT 1 FROM CharityUsers cu WHERE cu.Charities_UUID_charities = c.UUID_charities - ); - """; - - try (PreparedStatement ps = conn.prepareStatement(deleteSql)) { - ps.executeUpdate(); + DELETE FROM Charities c + WHERE NOT EXISTS ( + SELECT 1 FROM temp_api_charities t + WHERE t.org_number = c.org_number + ) + AND NOT EXISTS ( + SELECT 1 FROM Donations d WHERE d.charity_id = c.UUID_charities + ) + AND NOT EXISTS ( + SELECT 1 FROM Feedback f WHERE f.charity_id = c.UUID_charities + ) + AND NOT EXISTS ( + SELECT 1 FROM CharityVanity cv WHERE cv.UUID_charity = c.UUID_charities + ) + AND NOT EXISTS ( + SELECT 1 FROM CharityUsers cu WHERE cu.Charities_UUID_charities = c.UUID_charities + ); + """; + + String deleteUnusedCategoriesSql = + """ + + DELETE FROM Categories c + WHERE NOT EXISTS ( + SELECT 1 + FROM Charity_Categories cc + WHERE cc.Categories_category_id = c.category_id + ); + """; + + try (PreparedStatement ps1 = conn.prepareStatement(deleteSql); + PreparedStatement ps2 = conn.prepareStatement(deleteUnusedCategoriesSql)) { + + ps1.executeUpdate(); + ps2.executeUpdate(); } conn.commit(); @@ -156,7 +209,6 @@ AND NOT EXISTS ( } } e.printStackTrace(); - throw new RuntimeException("ERROR: Something went wrong during updating charities table."); } finally { if (conn != null) { diff --git a/helpmehelpapplication/src/main/resources/fxml/charityPage.fxml b/helpmehelpapplication/src/main/resources/fxml/charityPage.fxml index e0b20e5c..d24b0911 100644 --- a/helpmehelpapplication/src/main/resources/fxml/charityPage.fxml +++ b/helpmehelpapplication/src/main/resources/fxml/charityPage.fxml @@ -25,484 +25,508 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - + - + - + + + - - - - - - - - - + + - + + - - - - - - - - - + + + + - - + + + - - + - + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + - - - - + + + + + + + + + + + + + + + + + + + diff --git a/helpmehelpapplication/src/main/resources/fxml/components/organizationCard.fxml b/helpmehelpapplication/src/main/resources/fxml/components/organizationCard.fxml index 94cf11ff..790c6793 100644 --- a/helpmehelpapplication/src/main/resources/fxml/components/organizationCard.fxml +++ b/helpmehelpapplication/src/main/resources/fxml/components/organizationCard.fxml @@ -5,6 +5,7 @@ + @@ -27,8 +28,16 @@ - + diff --git a/helpmehelpapplication/src/main/resources/fxml/donationPage.fxml b/helpmehelpapplication/src/main/resources/fxml/donationPage.fxml index 8daa36ea..34d7b3c4 100644 --- a/helpmehelpapplication/src/main/resources/fxml/donationPage.fxml +++ b/helpmehelpapplication/src/main/resources/fxml/donationPage.fxml @@ -75,14 +75,14 @@ @@ -144,11 +144,18 @@ @@ -207,8 +207,16 @@ - +