Skip to content

Merge 60 with develop #61

Merged
merged 25 commits into from
Apr 12, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion helpmehelpapplication/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.41.0</version>
<version>4.43.0</version>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package ntnu.systemutvikling.team6.database;

import java.sql.*;
import java.util.List;
import java.util.UUID;
import java.util.*;

import ntnu.systemutvikling.team6.models.Charity;
import ntnu.systemutvikling.team6.models.CharityRegistry;
import ntnu.systemutvikling.team6.models.Donation;
import ntnu.systemutvikling.team6.models.DonationRegistry;
import ntnu.systemutvikling.team6.scraper.APICharityData;
import ntnu.systemutvikling.team6.scraper.LogoDownloader;
import ntnu.systemutvikling.team6.scraper.URLCharityScraper;

/**
Expand Down Expand Up @@ -79,6 +80,7 @@ status VARCHAR(255) NOT NULL,
logoURL TEXT,
categories TEXT,
key_values TEXT,
logoBlob MEDIUMBLOB,
UNIQUE KEY unique_org_number (org_number)
) ENGINE=InnoDB;
Expand Down Expand Up @@ -117,23 +119,43 @@ REFERENCES Charities (`UUID_charities`)

/**
* This method is used to verify the integrity of the data in the {@code charities} table and to
* update it based on the data retrieved from the IK API. The param charities are retrieved from
* update it based on the data retrieved from the IK API and the charity's URL.
* The param charities are retrieved from
* the IK API through the APICharityData class. Called in initialize method in
* HmHApplication.java, which is the main class of the application, to ensure that the data is up
* to date when the application starts. Uses a a temp table to ensure that the data in the
* database is consistent with the data from the API.
* to date when the application starts. Uses a temp table to ensure that the data in the database
* is consistent with the data from the API.
* <p>Uses a URLScraper object to get data not contained in the API, and static methods from
* LogoDownloader to get the charity's logo as a blob.</p>
*
* @param charities
* @param charities a list of {@code Charity} objects to add to the database
*/
public void addAPIDataToTable(List<Charity> charities) {
Connection conn = null;
int charityCounter = 0;

// Scrapes description, logo, categories, and key values from IK
for (Charity charity : charities) {
charityCounter++;

System.out.println("Scraping charity " + charityCounter + " of " + charities.size());
URLCharityScraper urlScraper = new URLCharityScraper(charity.getURL());
urlScraper.scrapeCharityPage();

charity.setDescription(urlScraper.getDescription());
charity.setCategory(urlScraper.getCategories());
charity.setLogoURL(urlScraper.getLogoURL());
charity.setKeyValues(urlScraper.getKeyValues());
byte[] logoBlob = LogoDownloader.downloadImageAsBlob(charity.getLogoURL());
charity.setLogoBlob(logoBlob);
}
try {
conn = connection.getMySqlConnection();
conn.setAutoCommit(false);
String sql_query =
"""
INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, pre_approved, status, description, logoURL, categories, key_values)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, pre_approved, status, description, logoURL, categories, key_values, logoBlob)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
charity_name = VALUES(charity_name),
charity_link = VALUES(charity_link),
Expand All @@ -142,7 +164,8 @@ INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, p
description = VALUES(description),
logoURL = VALUES(logoURL),
categories = VALUES(categories),
key_values = VALUES(key_values)
key_values = VALUES(key_values),
logoBlob = VALUES(logoBlob)
""";

try (PreparedStatement ps = conn.prepareStatement(sql_query)) {
Expand All @@ -152,14 +175,6 @@ INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, p
} else {
ps.setString(1, charity.getUUID().toString());
}
// Scrapes description, logo, categories, and key values from IK
URLCharityScraper urlScraper = new URLCharityScraper(charity.getURL());
urlScraper.scrapeCharityPage();

charity.setDescription(urlScraper.getDescription());
charity.setCategory(urlScraper.getCategories());
charity.setLogoURL(urlScraper.getLogoURL());
charity.setKeyValues(urlScraper.getKeyValues());

ps.setString(2, charity.getOrg_number().replaceAll("\\s", ""));
ps.setString(3, charity.getName());
Expand All @@ -170,6 +185,7 @@ INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, p
ps.setString(8, charity.getLogoURL());
ps.setString(9, charity.getCategory());
ps.setString(10, charity.getKeyValues());
ps.setBytes(11, charity.getLogoBlob());

ps.addBatch();
}
Expand Down Expand Up @@ -239,13 +255,20 @@ WHERE NOT EXISTS (
}
}

/**
* Fetches the data stored in the database and converts it to a list of Charity objects
* in the form of a registry (CharityRegistry).
*
* @return a CharityRegistry of all the charities registered in the database
*/
public CharityRegistry getCharitiesFromDB() {
CharityRegistry registry = null;
Connection conn = null;
try {
conn = connection.getMySqlConnection();
String sql_query =
"SELECT UUID_charities, org_number, charity_name, charity_link, pre_approved, status FROM Charities";
"SELECT UUID_charities, org_number, charity_name, charity_link, pre_approved, status, description, logoURL, " +
"categories, key_values, logoBlob FROM Charities";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql_query);

Expand All @@ -259,6 +282,12 @@ public CharityRegistry getCharitiesFromDB() {
rs.getString("charity_name"),
rs.getBoolean("pre_approved"),
rs.getString("status"));
charity.setDescription(rs.getString("description"));
charity.setLogoURL(rs.getString("logoURL"));
charity.setCategory(rs.getString("categories"));
charity.setKeyValues(rs.getString("key_values"));
charity.setLogoBlob(rs.getBytes("logoBlob"));

registry.addCharity(charity);
}
} catch (SQLException e) {
Expand All @@ -268,6 +297,41 @@ public CharityRegistry getCharitiesFromDB() {
return registry;
}

public List<String> getCategoriesFromDB() {
Map<String, String> categoryMap = new HashMap<>();

String sql_query = "SELECT categories FROM Charities";

try (Connection conn = connection.getMySqlConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql_query)) {

while (rs.next()) {
String categoriesStr = rs.getString("categories");

if (categoriesStr != null && !categoriesStr.isEmpty()) {
String[] splitCategories = categoriesStr.split(",");

for (String category : splitCategories) {
String trimmed = category.trim();

if (!trimmed.isEmpty()) {
categoryMap.putIfAbsent(trimmed.toLowerCase(), trimmed);
}
}
}
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("ERROR: Something went wrong while fetching categories from the database.");
}

var categories = new ArrayList<>(categoryMap.values());
Collections.sort(categories);

return categories;
}

public DonationRegistry getDonationFromDB() {
DonationRegistry registry = null;
Connection conn = null;
Expand Down Expand Up @@ -317,4 +381,4 @@ public DonationRegistry getDonationFromDB() {
}
return registry;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public class Charity {
/* List that contains the charity's Feedbacks */
private List<Feedback> feedbacks;

/* Bytecode for the charity logo */
private byte[] logoBlob;

/**
* Contructor for creating a new charity. Taylored to match data given from Api. Other attributes
* will just be initialized as empty
Expand Down Expand Up @@ -91,6 +94,7 @@ public Charity(
this.logoURL = "";
this.keyValues = "";
this.feedbacks = new ArrayList<>();
this.logoBlob = null;
}

/** Getters for the charity's attributes. */
Expand Down Expand Up @@ -138,6 +142,10 @@ public String getKeyValues() {
return this.keyValues;
}

public byte[] getLogoBlob() {
return this.logoBlob;
}

/** Setter for verification status. This one sets the charity as verified. */
public void setVerified() {
this.status = "approved";
Expand Down Expand Up @@ -167,4 +175,9 @@ public void setLogoURL(String url) {
public void setKeyValues(String values) {
this.keyValues = values;
}

/** Setter for the charity's logo Blob. */
public void setLogoBlob(byte[] logoBlob) {
this.logoBlob = logoBlob;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public CharityRegistry parseJSON(String JSONData) {

CharityRegistry charityRegistry = new CharityRegistry();
for (APICharityData apiCharityData : charityData) {
if (apiCharityData.getStatus().equalsIgnoreCase("obs")) {
continue;
}
Charity charity =
new Charity(
apiCharityData.getOrg_number(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ntnu.systemutvikling.team6.scraper;

import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
* Facilitates downloading of .png images from the individual charity's page on IK, converting them
* to bytecode (blob), and then back to a .png.
*/
public class LogoDownloader {

/**
* Downloads a image from the given URL and converts it to a blob.
*
* @param imageUrl the URL of the image
* @return a blob containing the image data
*/
public static byte[] downloadImageAsBlob(String imageUrl) {
if (imageUrl == null || imageUrl.isBlank()) return null;

try (InputStream in = new URL(imageUrl).openStream()) {
return in.readAllBytes();
} catch (Exception e) {
System.out.println("Error: Something went wrong when downloading the image.");
return null;
}
}

/**
* Converts a blob of image data back to a .png image file.
*
* @param imageBytes the blob containing the image data
* @param fileName the filename of the .png image file
*/
public static void convertBlobToPNG(byte[] imageBytes, String fileName) {
if (imageBytes == null) {
return;
}
try {
Path folder = Paths.get("target", "logo");
Files.createDirectories(folder);

Path filePath = folder.resolve(fileName + ".png");

Files.write(filePath, imageBytes);

} catch (Exception e) {
System.out.println("Error: Something went wrong when converting blob to png.");
}
}
}
Loading
Loading