Skip to content

Merge Feat/connectfrontback 2 into Feat/connectfrontback 2 #53

Merged
merged 4 commits into from
Mar 20, 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
53 changes: 40 additions & 13 deletions src/main/java/edu/group5/app/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import edu.group5.app.control.MainController;
import edu.group5.app.control.wrapper.DbWrapper;
import edu.group5.app.control.wrapper.OrgApiWrapper;
import edu.group5.app.model.donation.Donation;
import edu.group5.app.model.donation.DonationRepository;
import edu.group5.app.model.donation.DonationService;
import edu.group5.app.model.organization.OrganizationRepository;
import edu.group5.app.model.organization.OrganizationService;
import edu.group5.app.model.user.User;
import edu.group5.app.model.user.UserRepository;
import edu.group5.app.model.user.UserService;
import javafx.application.Application;
Expand All @@ -16,19 +18,30 @@

import java.util.List;

import java.util.logging.Logger;

/**
* Main entry point for the Help-Me-Help charity donation application.
* Handles database connection, data loading, and application setup.
*/
public class App extends Application {
DbWrapper dbWrapper;
UserRepository userRepository;
DonationRepository donationRepository;
private Logger logger;
private MainController controller;
private Scene scene;

@Override
public void start(Stage stage) {
DbWrapper dbWrapper = new DbWrapper(true);
public void init() {
this.logger = Logger.getLogger(App.class.getName());
this.logger.info("Application starting");

this.dbWrapper = new DbWrapper(false);
OrgApiWrapper orgApiWrapper = new OrgApiWrapper("https://app.innsamlingskontrollen.no/api/public/v1/all");

if (!dbWrapper.connect()) {
System.err.println("Failed to connect to database");
return;
while (!dbWrapper.connect()) {
this.logger.warning("Failed to connect to database");
}

// Load data from database
Expand All @@ -47,26 +60,40 @@ public void start(Stage stage) {
}

// Create repositories with fetched data
UserRepository userRepository = new UserRepository(userData);
DonationRepository donationRepository = new DonationRepository(donationData);
this.userRepository = new UserRepository(userData);
this.donationRepository = new DonationRepository(donationData);
OrganizationRepository organizationRepository = new OrganizationRepository(organizationData);

// Create services (backend wiring)
UserService userService = new UserService(userRepository);
DonationService donationService = new DonationService(donationRepository, organizationRepository);
UserService userService = new UserService(this.userRepository);
DonationService donationService = new DonationService(this.donationRepository, organizationRepository);
OrganizationService organizationService = new OrganizationService(organizationRepository);

MainController controller = new MainController(userService, donationService, organizationService);
this.controller = new MainController(userService, donationService, organizationService);

Scene scene = controller.getMainView().getScene();
controller.showLoginPage();
this.scene = controller.getMainView().getScene();
}

@Override
public void start(Stage stage) {
this.controller.showLoginPage();

stage.getIcons().add(new Image(getClass().getResource("/header/images/hmh-logo.png").toExternalForm()));
stage.setTitle("Help-Me-Help");
stage.setScene(scene);
stage.setScene(this.scene);
stage.show();
}

@Override
public void stop() throws Exception {
super.stop();
this.logger.info("Application stopping");
this.dbWrapper.connect();
this.dbWrapper.exportUsers(this.userRepository.export());
this.dbWrapper.exportDonations(this.donationRepository.export());
this.dbWrapper.disconnect();
}

public static void main(String[] args) {
launch(args);
}
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/edu/group5/app/control/wrapper/DbWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,13 @@ public List<Object[]> importUsers() {

public int exportUsers(List<Object[]> data) {
this.importUsers();

if (data == null) {
throw new IllegalArgumentException("data can't be null");
}
if (data.isEmpty()) {
return 0;
}
if (data.get(0).length != 6) {
throw new IllegalArgumentException("data's arrays must have a length of 6");
}
Expand Down Expand Up @@ -184,10 +187,13 @@ private List<Object[]> importDonations(int user_id, boolean all) {

public int exportDonations(List<Object[]> data) {
this.fetchAllDonations();

if (data == null) {
throw new IllegalArgumentException("data can't be null");
}
if (data.isEmpty()) {
return 0;
}
if (data.get(0).length != 6) {
throw new IllegalArgumentException("data's arrays must have a length of 6");
}
Expand Down
24 changes: 13 additions & 11 deletions src/main/java/edu/group5/app/model/DBRepository.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package edu.group5.app.model;

import java.util.HashMap;
import java.util.Map;
import java.util.List;

/**
* Abstract base class for repositories that store their data
* in a database-related structure.
*
* <p>
* Extends {@link Repository} and specifies that the content
* is stored as a {@link Map}.
* Extends {@link Repository} and specifies that the content
* is stored as a {@link Map}.
* </p>
*/
public abstract class DBRepository<K, V> extends Repository<K, V> {
protected final Map<K, V> contentLock;

/**
* Constructs a DBRepository with the given content.
*
Expand All @@ -23,19 +26,18 @@ protected DBRepository(Map<K, V> content) {
this.contentLock = new HashMap<>();
}

protected void updateContentLock() {
synchronized (contentLock) {
contentLock.clear();
contentLock.putAll(this.content);
}
}
protected abstract void updateContentLock();

public abstract boolean addContent(V value);

/**
* Exports the repository content as a list of Object arrays, where each array represents a row of data.
* This method is intended for converting the repository content into a format suitable for database storage or export.
* @return a List of Object arrays representing the repository content for database export
* Exports the repository content as a list of Object arrays, where each array
* represents a row of data.
* This method is intended for converting the repository content into a format
* suitable for database storage or export.
*
* @return a List of Object arrays representing the repository content for
* database export
*/
public abstract List<Object[]> export();
}
87 changes: 53 additions & 34 deletions src/main/java/edu/group5/app/model/donation/DonationRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@
* Repository class for Donation.
*
* <p>
* Extends {@link DBRepository} and manages Donation entities.
* Extends {@link DBRepository} and manages Donation entities.
* </p>
*/
public class DonationRepository extends DBRepository<Integer, Donation> {
private final HashMap<Integer, Donation> content;

/**
* Constructs DonationRepository from a list of Object[] rows from the database.
* @param rows List of Object[] representing donations from the DB.
* Each row must have 6 elements:
* [donationId, userId, organizationId, amount, date, paymentMethod]
* @throws IllegalArgumentException if the input list is null or any row is invalid
*
* @param rows List of Object[] representing donations from the DB.
* Each row must have 6 elements:
* [donationId, userId, organizationId, amount, date, paymentMethod]
* @throws IllegalArgumentException if the input list is null or any row is
* invalid
*/
public DonationRepository(List<Object[]> rows) {
super(new HashMap<>());
Expand All @@ -35,36 +37,52 @@ public DonationRepository(List<Object[]> rows) {
}
this.content = new HashMap<>();
for (Object[] row : rows) {
if (row == null || row.length != 6 ) {
if (row == null || row.length != 6) {
throw new IllegalArgumentException("Each row must contain exactly 6 elements");
}
int donationId = (int) row[0];
int customerId = (int) row[1];
int organizationId = (int) row[2];
int organizationId = (int) row[2];
BigDecimal amount = (BigDecimal) row[3];
Timestamp date = (Timestamp) row[4];
String paymentMethod = (String) row[5];

Donation donation = new Donation(donationId, customerId, organizationId, amount, date, paymentMethod);
this.content.put(donationId, donation);
}
super.updateContentLock();
this.updateContentLock();
}

@Override
protected void updateContentLock() {
synchronized (contentLock) {
this.contentLock.clear();
this.contentLock.putAll(this.content);
}
}

@Override
public List<Object[]> export() {
return content.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> { Donation donation = entry.getValue();
return new Object[] {
donation.donationId(), donation.userId(),
donation.organizationId(), donation.amount(),
donation.date(), donation.paymentMethod()};})
.toList();
Map<Integer, Donation> output = new HashMap<>(this.content);
for (int i : super.contentLock.keySet()) {
output.remove(i);
}
this.updateContentLock();
return output.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> {
Donation donation = entry.getValue();
return new Object[] {
donation.donationId(), donation.userId(),
donation.organizationId(), donation.amount(),
donation.date(), donation.paymentMethod() };
})
.toList();
}

/**
* Retrieves a donation by its ID.
*
* @param donationId the ID of the donation to retrieve
* @return the Donation object with the specified ID, or null if not found
* @throws IllegalArgumentException if the donationId is not positive
Expand All @@ -77,10 +95,12 @@ public Donation getDonationById(int donationId) {
}

/**
* Generates the next donation ID based on the current maximum ID in the repository.
* Generates the next donation ID based on the current maximum ID in the
* repository.
*
* @return the next donation ID to be used for a new donation
*/
public int getNextDonationId() {
public int getNextDonationId() {
return content.keySet().stream().max(Integer::compareTo).orElse(0) + 1;
} /* TODO change this when data database is introduced */

Expand All @@ -91,22 +111,22 @@ public Map<Integer, Donation> getAllDonations() {
/**
* Adds a new donation to the repository
* <p>
* The donation is stored using its {@code donationId} as the key.
* If a donation with the same ID already exists, the donation
* will not be added.
* The donation is stored using its {@code donationId} as the key.
* If a donation with the same ID already exists, the donation
* will not be added.
* </p>
*
* @param donation the donation to add
* @return {@code true} if the donation was successfully added, and
* {@code false} if a donation with the same ID already exists
* {@code false} if a donation with the same ID already exists
*/
@Override
public boolean addContent(Donation donation) {
if (donation == null) {
throw new IllegalArgumentException("Donation cannot be null");
}
if (content.containsKey(donation.donationId())){
return false;
if (content.containsKey(donation.donationId())) {
return false;
}
this.content.put(donation.donationId(), donation);
return true;
Expand All @@ -116,12 +136,12 @@ public boolean addContent(Donation donation) {
* Returns all donations sorted by date (ascending).
*
* <p>
* The returned map preserves the sorted order.
* The returned map preserves the sorted order.
* </p>
*
* @return a new {@link HashMap} containing the donations sorted by date
*/
public HashMap<Integer, Donation> sortByDate(){
public HashMap<Integer, Donation> sortByDate() {
return content.entrySet().stream()
.sorted(Map.Entry.comparingByValue(
Comparator.comparing(Donation::date)))
Expand All @@ -136,7 +156,7 @@ public HashMap<Integer, Donation> sortByDate(){
* Returns all donations sorted by amount (ascending).
*
* <p>
* The returned map preserves the sorted order from lowest to highest amount.
* The returned map preserves the sorted order from lowest to highest amount.
* </p>
*
* @return a new {@link HashMap} containing the donations sorted by amount.
Expand All @@ -149,12 +169,12 @@ public HashMap<Integer, Donation> sortByAmount() {
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
LinkedHashMap::new));
}

/**
* Returns all donations associated with a specific organization.
*
* @param orgNumber the organization ID to filter by
* @return a map containing all donations that belong to the given organization
* @throws IllegalArgumentException if the orgNumber is not positive
Expand All @@ -165,17 +185,17 @@ public HashMap<Integer, Donation> filterByOrganization(int orgNumber) {
}
return content.entrySet()
.stream()
.filter(entry -> entry.getValue().organizationId() == orgNumber)
.filter(entry -> entry.getValue().organizationId() == orgNumber)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
LinkedHashMap::new));
}

/**
* Returns all donations made by a specific user.
*
* @param userId the user ID to filter by
* @return a map containing all donations that belong to the given user
* @throws IllegalArgumentException if the userId is not positive
Expand All @@ -190,7 +210,6 @@ public HashMap<Integer, Donation> filterByUser(int userId) {
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
LinkedHashMap::new));
}
}
Loading