Skip to content

Commit

Permalink
Merge pull request #76 from cathrkri/74-featureadd-description-logo-k…
Browse files Browse the repository at this point in the history
…eyvalues-etc-to-charity-page

74 featureadd description logo keyvalues etc to charity page
  • Loading branch information
roaraf authored Apr 18, 2026
2 parents 8ed9902 + 3af1a32 commit 22c3355
Show file tree
Hide file tree
Showing 13 changed files with 958 additions and 674 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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() {}

Expand All @@ -31,14 +60,48 @@ 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) {
this.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<Double>
String input = charity.getKeyValues();

String[] parts = input.split(":");
List<Double> 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());
}

/**
Expand Down Expand Up @@ -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<String> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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<String> selectedCategories = new ArrayList<>();

private List<Charity> allCharities = new ArrayList<>();

Expand All @@ -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<String> 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);
Expand Down Expand Up @@ -142,69 +162,42 @@ public void handleFrontSearch(ActionEvent event) {
/**
* This method is used to filter the charities based on the selected filters.
*
* <p>The filters are whether the charity was pre-verified, or if it falls under one (or more) of
* the given categories.</p>
*
* <p>The check checks whether the given charity has ALL filters. If one does not apply,
* the charity is rejected.</p>
*
* @return a list of filtered charities.
*/
private List<Charity> getFilteredCharities() {
if (!verifiedFilter.isSelected()
&& !childrenFilter.isSelected()
&& !healthFilter.isSelected()
&& !emergencyAidFilter.isSelected()) {
return allCharities;
}

List<Charity> filteredCharities = new ArrayList<>();
List<Charity> 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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
""";

Expand All @@ -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;
""";

Expand Down Expand Up @@ -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`)
Expand Down
Loading

0 comments on commit 22c3355

Please sign in to comment.