From 033ffa7c7e834f117c3c21349cabcab07bcb64eb Mon Sep 17 00:00:00 2001 From: lukaceli Date: Tue, 10 Mar 2026 14:39:55 +0100 Subject: [PATCH 1/9] Started on donation logic/implementing. Made changes to databse system --- givehope.db | Bin 28672 -> 0 bytes .../java/persistence/OrganizationDao.java | 26 ++++++++++++++++++ src/main/resources/schema.sql | 24 +++++++--------- 3 files changed, 36 insertions(+), 14 deletions(-) delete mode 100644 givehope.db diff --git a/givehope.db b/givehope.db deleted file mode 100644 index abc4973e08f41546daeb297956b4a0eef76ac37b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28672 zcmeI)&uZgF90zbIPOQYOqn5^)r5FUuvN73BH2Pc%6Iu2}3W~3Pf zgDxFuru2m*%hJB4Ns_cBHbrdn^tsqsogavId13#q-7RVN-Bn)wSIU0&x3u}2noF>q@ayl7HSC(cpr!evghi{oedtIcWOTmany{ z-`0I?o~v!=G?6+?t2J8wE5E6|Zq^Q~%~S0=|5U4G(eTi3w4Nn3X}3S_jWp3$ zU27b*w8lxjJ}sGd*=AHMFIxUvQJAVQ9`Zr^Hqx|JNJaKS?oykVM@_%>s*#+{_N;@l zi2l-V`i%qsSc~(vJFGnIEmeKqb*g@n88^t4Z#ERASS-pvT_s>iN%E3SQ6*V3r%!;= zdbIn$3>xFZaL~DoCnsT=p1Rf_oY7wAT{3BRgJ`KCF11OGbg?WMa6HjTI%;Sy{JJkL zhJ)(yLG^{7D^DI}mEw~p@?@M?EVfmVEMsbQjOEPOkhqlP&J-$fwDf4X-Z5 z>a{)OS7Y7_`O@KP>im`px8sn{M9cD|wyG2h1^H$=+V~oZx0O`uIOmf`6*p?%HMGm~ zevh|%<8B~&j&G3Z%=31ab}rI)jCRtfy*crx6)x#;c-L+y$8UToR?L{fiI?ajY7dH#bqK!N}SAOHafKmY;| zfB*y_0D=1#7-kj8EbUG5*U=yv@ayP2_?|H~yyj+P*|zIgwnK=yPtHyAnqM(t{K^msOs5vWEv!ljP9Bul}pcW zgR(fBxe!kTH0~|j537pww6wDnZXw!z2BR58`nvRZ7Hoz{3^PGA626K}J%>kb#7wS- zrde@x?lN9skr4(ul%j3*@_|+;yD}j2i()eB#Hh< zd^qa#&i2pZ^?N)?q~bAdSQckI2p#H@h!fX#2{CLh3Y-e%oKu|`qFa+tBXTJ*#Tx1AJRc*JWS$P#Es+J1_?-LhT`O@#{*(mL50|%&TQR|Jjy&qsLphg zF=|_!2A&fTah2GvAqHeHM>o8jxT3S_drAFO{X_j-eXstb;=B3#zsu1h1Rwwb2tWV= z5P$##AOHafKwv6R$n41qbCw8 zUuMc1>E{RW_y4l`K@xvR5P$##AOHafKmY;|fB*y_009U*D1mi3Q;_4|0c7L#zxdHV z5(FRs0SG_<0uX=z1Rwwb2tWV=4_E-}{|7v|7#aj1009U<00Izz00bZa0SG`~Ca|#n n&x))yvBm!zaAj7EHwZug0uX=z1Rwwb2tWV=5P$##9)Q4q`jjEe diff --git a/src/main/java/persistence/OrganizationDao.java b/src/main/java/persistence/OrganizationDao.java index 8b45a05..966b335 100644 --- a/src/main/java/persistence/OrganizationDao.java +++ b/src/main/java/persistence/OrganizationDao.java @@ -1,4 +1,30 @@ package persistence; +import domain.organization.Organization; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import persistence.db.Database; + public class OrganizationDao { + + public void insert(Organization org) { + String sql = "INSERT INTO organization(name, org_nr, cause, description, contact_email, " + + "website, verified) VALUES(?, ? ,? ,?, ?, ? ,?) "; + + try (Connection conn = Database.getConnection()) { + PreparedStatement stmt = conn.prepareStatement(sql); + + stmt.setString(1, org.getName()); + stmt.setString(2, org.getOrgNr()); + stmt.setString(3, org.getCause().name()); + stmt.setString(4, org.getDescription()); + stmt.setString(5, org.getContactEmail()); + stmt.setString(6, org.getWebsite()); + stmt.setInt(7, org.isVerified() ? 1 : 0); + stmt.executeUpdate(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 9975777..8c2f031 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -12,24 +12,20 @@ CREATE TABLE IF NOT EXISTS user ( CREATE TABLE IF NOT EXISTS organization ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, - description TEXT -); - -CREATE TABLE IF NOT EXISTS cause ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - description TEXT, - organization_id INTEGER NOT NULL, - FOREIGN KEY (organization_id) REFERENCES organization(id) - ON DELETE CASCADE + org_nr TEXT NOT NULL UNIQUE, + cause TEXT NOT NULL, + description TEXT, + contact_email TEXT, + website TEXT, + verified INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS donation ( id INTEGER PRIMARY KEY AUTOINCREMENT, amount REAL NOT NULL, donation_date TEXT NOT NULL, - user_id INTEGER, - cause_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + organization_id INTEGER NOT NULL, FOREIGN KEY (user_id) REFERENCES user(id), - FOREIGN KEY (cause_id) REFERENCES cause(id) -); \ No newline at end of file + FOREIGN KEY (organization_id) REFERENCES organization(id) + ); \ No newline at end of file From 44a51d264ce3175f6f17ad802a39f9702c53000b Mon Sep 17 00:00:00 2001 From: lukaceli Date: Thu, 12 Mar 2026 13:47:42 +0100 Subject: [PATCH 2/9] Made some changes to organizationdao. Added id variable to organization and user --- .../domain/organization/Organization.java | 150 ++++++++++-------- src/main/java/domain/user/User.java | 9 ++ src/main/java/persistence/DonationDao.java | 18 +++ .../java/persistence/OrganizationDao.java | 53 ++++++- 4 files changed, 163 insertions(+), 67 deletions(-) diff --git a/src/main/java/domain/organization/Organization.java b/src/main/java/domain/organization/Organization.java index c4d5d3c..73e2c5d 100644 --- a/src/main/java/domain/organization/Organization.java +++ b/src/main/java/domain/organization/Organization.java @@ -5,77 +5,95 @@ import java.util.Objects; public class Organization { - private final String name; - private final String orgNr; - private final Cause cause; - private final String description; - private final String contactEmail; - private final String website; - private final boolean verified; - - - public Organization(String name, String orgNr, Cause cause, String description, String contactEmail, String website, boolean verified) { - if (name == null || name.isBlank()) { - throw new IllegalArgumentException("Name cannot be null or blank"); + private final String name; + private final String orgNr; + private final Cause cause; + private final String description; + private final String contactEmail; + private final String website; + private final boolean verified; + private Long id; + + + public Organization(String name, String orgNr, Cause cause, String description, + String contactEmail, String website, boolean verified) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("Name cannot be null or blank"); + } + if (orgNr == null || !orgNr.matches("\\d{9}")) { + throw new IllegalArgumentException("Organization number must be 9 digits"); + } + if (contactEmail != null) { + contactEmail = contactEmail.trim(); + + if (contactEmail.isBlank()) { + throw new IllegalArgumentException("Email cannot be blank"); } - if (orgNr == null || !orgNr.matches("\\d{9}")) { - throw new IllegalArgumentException("Organization number must be 9 digits"); + + if (!contactEmail.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$" + )) //Got help from ChatGPT on regex + { + throw new IllegalArgumentException("Invalid email format"); } - if (contactEmail != null) { - contactEmail = contactEmail.trim(); - - if (contactEmail.isBlank()) { - throw new IllegalArgumentException("Email cannot be blank"); - } - - if (!contactEmail.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$" - )) //Got help from ChatGPT on regex - { - throw new IllegalArgumentException("Invalid email format"); - } - } - - if (website != null) { - website = website.trim(); - - if (website.isBlank()) { - throw new IllegalArgumentException("Website cannot be blank"); - } - - if (!website.matches("^https?://.+")) { //Got help from ChatGPT for regex - throw new IllegalArgumentException("Website must start with http:// or https://"); - } + } + + if (website != null) { + website = website.trim(); + + if (website.isBlank()) { + throw new IllegalArgumentException("Website cannot be blank"); } - this.name = name.trim(); - this.orgNr = orgNr.trim(); - this.cause = Objects.requireNonNull(cause, "Cause cannot be null"); - this.description = description; - this.contactEmail = contactEmail; - this.website = website; - this.verified = verified; + if (!website.matches("^https?://.+")) { //Got help from ChatGPT for regex + throw new IllegalArgumentException("Website must start with http:// or https://"); + } } + this.name = name.trim(); + this.orgNr = orgNr.trim(); + this.cause = Objects.requireNonNull(cause, "Cause cannot be null"); + this.description = description; + this.contactEmail = contactEmail; + this.website = website; + this.verified = verified; + this.id = id; + } - public String getName() { - return name; - } - public String getOrgNr() { - return orgNr; - } - public Cause getCause() { - return cause; - } - public String getDescription() { - return description; - } - public String getContactEmail() { - return contactEmail; - } - public String getWebsite() { - return website; - } - public boolean isVerified() { - return verified; - } + + public String getName() { + return name; + } + + public String getOrgNr() { + return orgNr; + } + + public Cause getCause() { + return cause; + } + + public String getDescription() { + return description; + } + + public String getContactEmail() { + return contactEmail; + } + + public String getWebsite() { + return website; + } + + public boolean isVerified() { + return verified; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + if (this.id != null) throw new IllegalStateException("ID already set"); + this.id = id; + } } diff --git a/src/main/java/domain/user/User.java b/src/main/java/domain/user/User.java index 9b5e82f..ee6a2a2 100644 --- a/src/main/java/domain/user/User.java +++ b/src/main/java/domain/user/User.java @@ -5,6 +5,7 @@ public class User { private String phoneNumber; private final String eMail; private String password; + private Long id; public User(String userName, String phoneNumber, String password, String eMail) { @@ -28,6 +29,7 @@ public User(String userName, String phoneNumber, String password, String eMail) this.phoneNumber = phoneNumber; this.eMail = eMail.trim(); this.password = password; + this.id = id; } public String getUsername() { @@ -46,6 +48,8 @@ public String getPassword() { return password; } + public long getID() { return id; } + public void setPassword(String password) { this.password = password; } @@ -57,4 +61,9 @@ public void setUsername(String userName) { public void setPhonenumber (String phoneNumber) { this.phoneNumber = phoneNumber; } + + public void setId(Long id) { + if (this.id != null) throw new IllegalStateException("ID already set"); + this.id = id; + } } \ No newline at end of file diff --git a/src/main/java/persistence/DonationDao.java b/src/main/java/persistence/DonationDao.java index a1a0b46..acc1870 100644 --- a/src/main/java/persistence/DonationDao.java +++ b/src/main/java/persistence/DonationDao.java @@ -1,4 +1,22 @@ package persistence; +import domain.donation.Donation; +import java.sql.Connection; +import java.sql.PreparedStatement; +import persistence.db.Database; + public class DonationDao { + + public void insert(Donation donation) { + String sql = "INSERT INTO donation(amount, donation_date" + + ", user_id, organization_id) VALUES(?, ?, ?, ?)"; + + try (Connection conn = Database.getConnection()) { + PreparedStatement stmt = conn.prepareStatement(sql); + + stmt.setBigDecimal(1, donation.getAmount()); + stmt.setString(2, donation.getDateTime().toString()); + stmt.setString(3, ) + } + } } diff --git a/src/main/java/persistence/OrganizationDao.java b/src/main/java/persistence/OrganizationDao.java index 966b335..29dffb7 100644 --- a/src/main/java/persistence/OrganizationDao.java +++ b/src/main/java/persistence/OrganizationDao.java @@ -1,9 +1,15 @@ package persistence; +import domain.donation.Cause; import domain.organization.Organization; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import persistence.db.Database; public class OrganizationDao { @@ -13,7 +19,8 @@ public void insert(Organization org) { "website, verified) VALUES(?, ? ,? ,?, ?, ? ,?) "; try (Connection conn = Database.getConnection()) { - PreparedStatement stmt = conn.prepareStatement(sql); + PreparedStatement stmt = conn.prepareStatement(sql, + Statement.RETURN_GENERATED_KEYS); stmt.setString(1, org.getName()); stmt.setString(2, org.getOrgNr()); @@ -23,8 +30,52 @@ public void insert(Organization org) { stmt.setString(6, org.getWebsite()); stmt.setInt(7, org.isVerified() ? 1 : 0); stmt.executeUpdate(); + + ResultSet keys = stmt.getGeneratedKeys(); + if (keys.next()) { + org.setId(keys.getLong(1)); + } } catch (SQLException e) { throw new RuntimeException(e); } } + + public List findAll() { + String sql = "SELECT * FROM organization"; + try (Connection conn = Database.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + List list = new ArrayList<>(); + while (rs.next()) list.add(mapRow(rs)); + return list; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public Optional findById(long id) { + String sql = "SELECT * FROM organization WHERE id = ?"; + try (Connection conn = Database.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setLong(1, id); + ResultSet rs = stmt.executeQuery(); + return rs.next() ? Optional.of(mapRow(rs)) : Optional.empty(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private Organization mapRow(ResultSet rs) throws SQLException { + Organization org = new Organization( + rs.getString("name"), + rs.getString("org_nr"), + Cause.valueOf(rs.getString("cause")), + rs.getString("description"), + rs.getString("contact_email"), + rs.getString("website"), + rs.getInt("verified") == 1 + ); + org.setId(rs.getLong("id")); + return org; + } } From 7b2ff7ef670e7ea60b97d00399e2460a4f8c6b53 Mon Sep 17 00:00:00 2001 From: lukaceli Date: Thu, 19 Mar 2026 11:00:23 +0100 Subject: [PATCH 3/9] Created dao classes for donation logic and database system. Changed schema to match domain classes --- src/main/java/app/Main.java | 37 ++++++++ .../domain/organization/Organization.java | 2 +- src/main/java/persistence/DonationDao.java | 88 ++++++++++++++++++- src/main/java/persistence/UserDao.java | 18 ++-- src/main/resources/schema.sql | 12 ++- 5 files changed, 140 insertions(+), 17 deletions(-) diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 907cc4a..8a0bca2 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -3,8 +3,15 @@ import application.security.PasswordHasher; import application.user.UserLogin; import application.user.UserRegister; +import domain.donation.Cause; +import domain.donation.Donation; +import domain.organization.Organization; import domain.user.User; import integration.security.Sha256PasswordHasher; +import java.math.BigDecimal; +import java.util.List; +import persistence.DonationDao; +import persistence.OrganizationDao; import persistence.UserDao; import persistence.UserRepository; import persistence.db.Database; @@ -25,6 +32,36 @@ public static void main(String[] args) { userReg.execute("wfrfrewfhbjdddswe3f", "45586456", "Jegerkddsdwdwful", "113@1h33weqd3.com"); System.out.println("Inserted user!"); + + // Opprett DAOene + UserDao userDao = new UserDao(); + OrganizationDao orgDao = new OrganizationDao(); + DonationDao donationDao = new DonationDao(); + + // Test UserDao + userReg.execute("testuser", "12345678", "passord123", "test@example.com"); + + User user = userDao.findByEmail("test@example.com").orElseThrow(); + System.out.println("Hentet bruker: " + user.getUsername() + ", id=" + user.getID()); + + // Test OrganizationDao + Organization org = new Organization( + "Røde Kors", "123456789", Cause.HEALTH, + "En god org", "kontakt@rodekors.no", "https://rodekors.no", true + ); + orgDao.insert(org); + System.out.println("Inserted org, id=" + org.getId()); + + Organization hentetOrg = orgDao.findById(org.getId()).orElseThrow(); + System.out.println("Hentet org: " + hentetOrg.getName()); + + + Donation donation = new Donation(new BigDecimal("100.00"), user, org); + donationDao.insert(donation); + System.out.println("Inserted donation, id=" + donation.getId()); + + List donations = donationDao.findByUser(user.getID()); + System.out.println("Antall donasjoner: " + donations.size()); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/domain/organization/Organization.java b/src/main/java/domain/organization/Organization.java index 73e2c5d..403f7a0 100644 --- a/src/main/java/domain/organization/Organization.java +++ b/src/main/java/domain/organization/Organization.java @@ -56,7 +56,7 @@ public Organization(String name, String orgNr, Cause cause, String description, this.contactEmail = contactEmail; this.website = website; this.verified = verified; - this.id = id; + } diff --git a/src/main/java/persistence/DonationDao.java b/src/main/java/persistence/DonationDao.java index acc1870..db345fe 100644 --- a/src/main/java/persistence/DonationDao.java +++ b/src/main/java/persistence/DonationDao.java @@ -1,8 +1,17 @@ package persistence; +import domain.donation.Cause; import domain.donation.Donation; +import domain.organization.Organization; +import domain.user.User; +import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; import persistence.db.Database; public class DonationDao { @@ -12,11 +21,82 @@ public void insert(Donation donation) { ", user_id, organization_id) VALUES(?, ?, ?, ?)"; try (Connection conn = Database.getConnection()) { - PreparedStatement stmt = conn.prepareStatement(sql); + PreparedStatement stmt = conn.prepareStatement(sql, + Statement.RETURN_GENERATED_KEYS); - stmt.setBigDecimal(1, donation.getAmount()); + stmt.setString(1, donation.getAmount().toString()); stmt.setString(2, donation.getDateTime().toString()); - stmt.setString(3, ) + stmt.setLong(3, donation.getUser().getID()); + stmt.setString(4, donation.getOrganization().getOrgNr()); + stmt.executeUpdate(); + + ResultSet keys = stmt.getGeneratedKeys(); + if (keys.next()) { + donation.setId(keys.getLong(1)); + } + } catch (SQLException e) { + throw new RuntimeException(e); } } -} + + private Donation mapRow(ResultSet rs) throws SQLException { + User user = new User( + rs.getString("user_name"), + rs.getString("phone_number"), + rs.getString("password_hash"), + rs.getString("e_mail") + ); + user.setId(rs.getLong("user_id")); + + Organization org = new Organization( + rs.getString("name"), + rs.getString("org_nr"), + Cause.valueOf(rs.getString("cause")), + rs.getString("description"), + rs.getString("contact_email"), + rs.getString("website"), + rs.getInt("verified") == 1 + ); + org.setId(rs.getLong("org_id")); + + + Donation donation = new Donation( + new BigDecimal(rs.getString("amount")), + user, + org + ); + donation.setId(rs.getLong("id")); + return donation; + } + + public List findByUser(long userId) { + String sql = """ + SELECT d.id, d.amount, d.donation_date, + u.id AS user_id, u.user_name, u.phone_number, u.e_mail, u.password_hash, + o.id AS org_id, o.name, o.org_nr, o.cause, o.description, o.contact_email, o.website, o.verified + FROM donation d + JOIN user u ON d.user_id = u.id + JOIN organization o ON d.organization_id = o.id + WHERE d.user_id = ? + """; + + try (Connection conn = Database.getConnection()) { + PreparedStatement stmt = conn.prepareStatement(sql); + + stmt.setLong(1, userId); + ResultSet rs = stmt.executeQuery(); + List list = new ArrayList<>(); + while (rs.next()) list.add(mapRow(rs)); + return list; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + + + + + + +} \ No newline at end of file diff --git a/src/main/java/persistence/UserDao.java b/src/main/java/persistence/UserDao.java index 7e4ceaf..2fa7765 100644 --- a/src/main/java/persistence/UserDao.java +++ b/src/main/java/persistence/UserDao.java @@ -1,6 +1,7 @@ package persistence; import java.sql.ResultSet; +import java.sql.Statement; import java.util.Optional; import persistence.db.Database; import java.sql.Connection; @@ -15,7 +16,8 @@ public void insert(User user) { String sql = "INSERT INTO user(user_name, phone_number, e_mail, password_hash) VALUES(?, ?, ?, ?)"; try (Connection conn = Database.getConnection()) { - PreparedStatement stmt = conn.prepareStatement(sql); + PreparedStatement stmt = conn.prepareStatement(sql, + Statement.RETURN_GENERATED_KEYS); stmt.setString(1, user.getUsername()); stmt.setString(2, user.getPhoneNumber()); @@ -25,6 +27,11 @@ public void insert(User user) { stmt.executeUpdate(); + ResultSet keys = stmt.getGeneratedKeys(); + if (keys.next()) { + user.setId(keys.getLong(1)); + } + } catch (SQLException e) { throw new RuntimeException(e); } @@ -35,7 +42,7 @@ public void insert(User user) { @Override public Optional findByEmail(String email) { String sql = """ - SELECT user_name, phone_number, e_mail, password_hash + SELECT id, user_name, phone_number, e_mail, password_hash FROM user WHERE e_mail = ? LIMIT 1 @@ -63,7 +70,7 @@ public Optional findByEmail(String email) { @Override public Optional findByUsername(String username) { String sql = """ - SELECT user_name, phone_number, e_mail, password_hash + SELECT id, user_name, phone_number, e_mail, password_hash FROM user WHERE user_name = ? LIMIT 1 @@ -153,8 +160,9 @@ private User mapUser(ResultSet rs) throws SQLException { String phone = rs.getString("phone_number"); String email = rs.getString("e_mail"); String passwordHash = rs.getString("password_hash"); - - return new User(username, phone, passwordHash, email); + User user = new User(username, phone, passwordHash, email); + user.setId(rs.getLong("id")); + return user; } } diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8c2f031..56f577d 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,9 +1,7 @@ -PRAGMA foreign_keys = ON; - CREATE TABLE IF NOT EXISTS user ( id INTEGER PRIMARY KEY AUTOINCREMENT, - user_name TEXT NOT NULL, + user_name TEXT UNIQUE NOT NULL, phone_number TEXT, e_mail TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL @@ -22,10 +20,10 @@ CREATE TABLE IF NOT EXISTS organization ( CREATE TABLE IF NOT EXISTS donation ( id INTEGER PRIMARY KEY AUTOINCREMENT, - amount REAL NOT NULL, + amount TEXT NOT NULL, donation_date TEXT NOT NULL, user_id INTEGER NOT NULL, - organization_id INTEGER NOT NULL, - FOREIGN KEY (user_id) REFERENCES user(id), - FOREIGN KEY (organization_id) REFERENCES organization(id) + organization_id TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE RESTRICT, + FOREIGN KEY (organization_id) REFERENCES organization(id) ON DELETE RESTRICT ); \ No newline at end of file From 9c2f32c042fd96db241a6b4550c882119a3bb2da Mon Sep 17 00:00:00 2001 From: lukaceli Date: Thu, 19 Mar 2026 11:04:43 +0100 Subject: [PATCH 4/9] Changed orgDao to work with innsamlingkontollklient --- .../java/persistence/OrganizationDao.java | 99 +++++++------------ 1 file changed, 35 insertions(+), 64 deletions(-) diff --git a/src/main/java/persistence/OrganizationDao.java b/src/main/java/persistence/OrganizationDao.java index 29dffb7..a0d1346 100644 --- a/src/main/java/persistence/OrganizationDao.java +++ b/src/main/java/persistence/OrganizationDao.java @@ -1,81 +1,52 @@ package persistence; -import domain.donation.Cause; import domain.organization.Organization; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; + +import java.sql.*; import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import persistence.db.Database; public class OrganizationDao { - public void insert(Organization org) { - String sql = "INSERT INTO organization(name, org_nr, cause, description, contact_email, " + - "website, verified) VALUES(?, ? ,? ,?, ?, ? ,?) "; + private static final String URL = "jdbc:sqlite:givehope.db"; - try (Connection conn = Database.getConnection()) { - PreparedStatement stmt = conn.prepareStatement(sql, - Statement.RETURN_GENERATED_KEYS); + public void insert(Organization org) throws SQLException { + String sql = """ + INSERT OR IGNORE INTO organization + (org_number, name, status, url, is_pre_approved) + VALUES (?, ?, ?, ?, ?) + """; - stmt.setString(1, org.getName()); - stmt.setString(2, org.getOrgNr()); - stmt.setString(3, org.getCause().name()); - stmt.setString(4, org.getDescription()); - stmt.setString(5, org.getContactEmail()); - stmt.setString(6, org.getWebsite()); - stmt.setInt(7, org.isVerified() ? 1 : 0); - stmt.executeUpdate(); + try (Connection conn = DriverManager.getConnection(URL); + PreparedStatement stmt = conn.prepareStatement(sql)) { - ResultSet keys = stmt.getGeneratedKeys(); - if (keys.next()) { - org.setId(keys.getLong(1)); - } - } catch (SQLException e) { - throw new RuntimeException(e); + stmt.setString(1, org.getOrgNumber()); + stmt.setString(2, org.getName()); + stmt.setString(3, org.getStatus()); + stmt.setString(4, org.getUrl()); + stmt.setInt(5, org.isPreApproved() ? 1 : 0); + stmt.executeUpdate(); } } - public List findAll() { + public List getAll() throws SQLException { String sql = "SELECT * FROM organization"; - try (Connection conn = Database.getConnection(); - PreparedStatement stmt = conn.prepareStatement(sql); - ResultSet rs = stmt.executeQuery()) { - List list = new ArrayList<>(); - while (rs.next()) list.add(mapRow(rs)); - return list; - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - public Optional findById(long id) { - String sql = "SELECT * FROM organization WHERE id = ?"; - try (Connection conn = Database.getConnection(); - PreparedStatement stmt = conn.prepareStatement(sql)) { - stmt.setLong(1, id); - ResultSet rs = stmt.executeQuery(); - return rs.next() ? Optional.of(mapRow(rs)) : Optional.empty(); - } catch (SQLException e) { - throw new RuntimeException(e); + List list = new ArrayList<>(); + + try (Connection conn = DriverManager.getConnection(URL); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + + while (rs.next()) { + Organization org = new Organization(); + org.setOrgNumber(rs.getString("org_number")); + org.setName(rs.getString("name")); + org.setStatus(rs.getString("status")); + org.setUrl(rs.getString("url")); + org.setPreApproved(rs.getInt("is_pre_approved") == 1); + list.add(org); + } } + return list; } - - private Organization mapRow(ResultSet rs) throws SQLException { - Organization org = new Organization( - rs.getString("name"), - rs.getString("org_nr"), - Cause.valueOf(rs.getString("cause")), - rs.getString("description"), - rs.getString("contact_email"), - rs.getString("website"), - rs.getInt("verified") == 1 - ); - org.setId(rs.getLong("id")); - return org; - } -} +} \ No newline at end of file From cea0593117ac0c707cd11d3d0077b89f3e9237d8 Mon Sep 17 00:00:00 2001 From: lukaceli Date: Thu, 19 Mar 2026 14:31:55 +0100 Subject: [PATCH 5/9] Developed database logic and integrated InnsamlingskontrollClient organizationn into the database. Written test inputs in Main to check if database works. Made minor changes in domain classes --- src/main/java/app/Main.java | 52 ++++----- .../domain/organization/Organization.java | 103 ++++-------------- src/main/java/persistence/DonationDao.java | 23 ++-- src/main/resources/schema.sql | 13 +-- 4 files changed, 60 insertions(+), 131 deletions(-) diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index 8a0bca2..7cc167d 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -1,12 +1,11 @@ package app; import application.security.PasswordHasher; -import application.user.UserLogin; import application.user.UserRegister; -import domain.donation.Cause; import domain.donation.Donation; import domain.organization.Organization; import domain.user.User; +import integration.InnsamlingskontrollenClient; import integration.security.Sha256PasswordHasher; import java.math.BigDecimal; import java.util.List; @@ -17,55 +16,48 @@ import persistence.db.Database; public class Main { - UserDao userDao = new UserDao(); public static void main(String[] args) { - try{ + try { Database.getConnection(); System.out.println("Database connected"); - PasswordHasher hasher = new Sha256PasswordHasher(); - UserRepository userRepo = new UserDao(); - UserRegister userReg = new UserRegister(hasher, userRepo); - UserLogin login = new UserLogin(hasher, userRepo); - - - userReg.execute("wfrfrewfhbjdddswe3f", "45586456", "Jegerkddsdwdwful", "113@1h33weqd3.com"); - - System.out.println("Inserted user!"); - - // Opprett DAOene UserDao userDao = new UserDao(); OrganizationDao orgDao = new OrganizationDao(); DonationDao donationDao = new DonationDao(); - // Test UserDao + PasswordHasher hasher = new Sha256PasswordHasher(); + UserRepository userRepo = new UserDao(); + UserRegister userReg = new UserRegister(hasher, userRepo); + + // Test brukerregistrering userReg.execute("testuser", "12345678", "passord123", "test@example.com"); + System.out.println("Inserted user!"); User user = userDao.findByEmail("test@example.com").orElseThrow(); System.out.println("Hentet bruker: " + user.getUsername() + ", id=" + user.getID()); - // Test OrganizationDao - Organization org = new Organization( - "Røde Kors", "123456789", Cause.HEALTH, - "En god org", "kontakt@rodekors.no", "https://rodekors.no", true - ); - orgDao.insert(org); - System.out.println("Inserted org, id=" + org.getId()); + // Hent og lagre organisasjoner fra Innsamlingskontrollen + System.out.println("Henter organisasjoner fra Innsamlingskontrollen..."); + Organization[] fetchedOrgs = InnsamlingskontrollenClient.fetchOrganizations(); + for (Organization org : fetchedOrgs) { + orgDao.insert(org); + } + System.out.println("Lastet inn " + fetchedOrgs.length + " organisasjoner"); - Organization hentetOrg = orgDao.findById(org.getId()).orElseThrow(); - System.out.println("Hentet org: " + hentetOrg.getName()); + List orgs = orgDao.getAll(); + System.out.println("Organisasjoner i DB: " + orgs.size()); + System.out.println("Første org: " + orgs.get(0).getName() + ", org_number=" + orgs.get(0).getOrgNumber()); - - Donation donation = new Donation(new BigDecimal("100.00"), user, org); + // Test donasjon med første org fra DB + Donation donation = new Donation(new BigDecimal("100.00"), user, orgs.get(0)); donationDao.insert(donation); System.out.println("Inserted donation, id=" + donation.getId()); List donations = donationDao.findByUser(user.getID()); - System.out.println("Antall donasjoner: " + donations.size()); + System.out.println("Antall donasjoner for bruker: " + donations.size()); + } catch (Exception e) { e.printStackTrace(); } - - } } diff --git a/src/main/java/domain/organization/Organization.java b/src/main/java/domain/organization/Organization.java index 403f7a0..fd2cabf 100644 --- a/src/main/java/domain/organization/Organization.java +++ b/src/main/java/domain/organization/Organization.java @@ -1,99 +1,44 @@ package domain.organization; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; import domain.donation.Cause; import java.util.Objects; +@JsonIgnoreProperties(ignoreUnknown = true) public class Organization { - private final String name; - private final String orgNr; - private final Cause cause; - private final String description; - private final String contactEmail; - private final String website; - private final boolean verified; - private Long id; + @JsonProperty("org_number") + private String orgNumber; - public Organization(String name, String orgNr, Cause cause, String description, - String contactEmail, String website, boolean verified) { - if (name == null || name.isBlank()) { - throw new IllegalArgumentException("Name cannot be null or blank"); - } - if (orgNr == null || !orgNr.matches("\\d{9}")) { - throw new IllegalArgumentException("Organization number must be 9 digits"); - } - if (contactEmail != null) { - contactEmail = contactEmail.trim(); + @JsonProperty("name") + private String name; - if (contactEmail.isBlank()) { - throw new IllegalArgumentException("Email cannot be blank"); - } + @JsonProperty("status") + private String status; - if (!contactEmail.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$" - )) //Got help from ChatGPT on regex - { - throw new IllegalArgumentException("Invalid email format"); - } - } + @JsonProperty("url") + private String url; - if (website != null) { - website = website.trim(); + @JsonProperty("is_pre_approved") + private boolean isPreApproved; - if (website.isBlank()) { - throw new IllegalArgumentException("Website cannot be blank"); - } + public Organization() {} - if (!website.matches("^https?://.+")) { //Got help from ChatGPT for regex - throw new IllegalArgumentException("Website must start with http:// or https://"); - } - } - this.name = name.trim(); - this.orgNr = orgNr.trim(); - this.cause = Objects.requireNonNull(cause, "Cause cannot be null"); - this.description = description; - this.contactEmail = contactEmail; - this.website = website; - this.verified = verified; + public String getOrgNumber() { return orgNumber; } + public void setOrgNumber(String orgNumber) { this.orgNumber = orgNumber; } - } + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } - public String getName() { - return name; - } + public String getUrl() { return url; } + public void setUrl(String url) { this.url = url; } - public String getOrgNr() { - return orgNr; - } - - public Cause getCause() { - return cause; - } - - public String getDescription() { - return description; - } - - public String getContactEmail() { - return contactEmail; - } - - public String getWebsite() { - return website; - } - - public boolean isVerified() { - return verified; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - if (this.id != null) throw new IllegalStateException("ID already set"); - this.id = id; - } + public boolean isPreApproved() { return isPreApproved; } + public void setPreApproved(boolean preApproved) { isPreApproved = preApproved; } } diff --git a/src/main/java/persistence/DonationDao.java b/src/main/java/persistence/DonationDao.java index db345fe..ff8b783 100644 --- a/src/main/java/persistence/DonationDao.java +++ b/src/main/java/persistence/DonationDao.java @@ -1,6 +1,5 @@ package persistence; -import domain.donation.Cause; import domain.donation.Donation; import domain.organization.Organization; import domain.user.User; @@ -27,7 +26,7 @@ public void insert(Donation donation) { stmt.setString(1, donation.getAmount().toString()); stmt.setString(2, donation.getDateTime().toString()); stmt.setLong(3, donation.getUser().getID()); - stmt.setString(4, donation.getOrganization().getOrgNr()); + stmt.setString(4, donation.getOrganization().getOrgNumber()); stmt.executeUpdate(); ResultSet keys = stmt.getGeneratedKeys(); @@ -48,16 +47,12 @@ private Donation mapRow(ResultSet rs) throws SQLException { ); user.setId(rs.getLong("user_id")); - Organization org = new Organization( - rs.getString("name"), - rs.getString("org_nr"), - Cause.valueOf(rs.getString("cause")), - rs.getString("description"), - rs.getString("contact_email"), - rs.getString("website"), - rs.getInt("verified") == 1 - ); - org.setId(rs.getLong("org_id")); + Organization org = new Organization(); + org.setOrgNumber(rs.getString("org_number")); + org.setName(rs.getString("name")); + org.setStatus(rs.getString("status")); + org.setUrl(rs.getString("url")); + org.setPreApproved(rs.getInt("is_pre_approved") == 1); Donation donation = new Donation( @@ -73,10 +68,10 @@ public List findByUser(long userId) { String sql = """ SELECT d.id, d.amount, d.donation_date, u.id AS user_id, u.user_name, u.phone_number, u.e_mail, u.password_hash, - o.id AS org_id, o.name, o.org_nr, o.cause, o.description, o.contact_email, o.website, o.verified + o.org_number, o.name, o.status, o.url, o.is_pre_approved FROM donation d JOIN user u ON d.user_id = u.id - JOIN organization o ON d.organization_id = o.id + JOIN organization o ON d.organization_id = o.org_number WHERE d.user_id = ? """; diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 56f577d..5288a7e 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -8,14 +8,11 @@ CREATE TABLE IF NOT EXISTS user ( ); CREATE TABLE IF NOT EXISTS organization ( - id INTEGER PRIMARY KEY AUTOINCREMENT, + org_number TEXT PRIMARY KEY, name TEXT NOT NULL, - org_nr TEXT NOT NULL UNIQUE, - cause TEXT NOT NULL, - description TEXT, - contact_email TEXT, - website TEXT, - verified INTEGER NOT NULL DEFAULT 0 + status TEXT, + url TEXT, + is_pre_approved INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS donation ( @@ -25,5 +22,5 @@ CREATE TABLE IF NOT EXISTS donation ( user_id INTEGER NOT NULL, organization_id TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE RESTRICT, - FOREIGN KEY (organization_id) REFERENCES organization(id) ON DELETE RESTRICT + FOREIGN KEY (organization_id) REFERENCES organization(org_number) ON DELETE RESTRICT ); \ No newline at end of file From c072425249a2b03be10380c5e701446c6b4341b1 Mon Sep 17 00:00:00 2001 From: lukaceli Date: Tue, 24 Mar 2026 13:54:02 +0100 Subject: [PATCH 6/9] Added methods for getting user statistics. Added som simpler mapRows to use in these methods --- src/main/java/persistence/DonationDao.java | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/main/java/persistence/DonationDao.java b/src/main/java/persistence/DonationDao.java index ff8b783..338a92e 100644 --- a/src/main/java/persistence/DonationDao.java +++ b/src/main/java/persistence/DonationDao.java @@ -88,6 +88,83 @@ public List findByUser(long userId) { } } + public List getUserDonations(long userId) { + String sql = """ + SELECT amount, donation_date, organization_id + FROM donation + WHERE user_Id = ? + """; + + try (Connection conn = Database.getConnection()) { + PreparedStatement stmt = conn.prepareStatement(sql); + + stmt.setLong(1, userId); + ResultSet rs = stmt.executeQuery(); + List list = new ArrayList<>(); + while (rs.next()) list.add(mapRowSimple(rs)); + return list; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public List getFavoriteOrganizations(long userId) { + String sql = """ + SELECT organization_id, COUNT(*) AS donation_count + FROM donation + WHERE user_Id = ? + GROUP BY organization_id + ORDER BY donation_count DESC + LIMIT 3 + """; + + try (Connection conn = Database.getConnection()) { + PreparedStatement stmt = conn.prepareStatement(sql); + + stmt.setLong(1, userId); + ResultSet rs = stmt.executeQuery(); + List list = new ArrayList<>(); + while (rs.next()) list.add(mapRowFavorite(rs)); + return list; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public String getTotalDonationAmount(long userId) { + String sql = """ + SELECT SUM(CAST(amount as REAL)) + FROM donation + WHERE user_id = ? + """; + + try (Connection conn = Database.getConnection()) { + PreparedStatement stmt = conn.prepareStatement(sql); + + stmt.setLong(1, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + BigDecimal amount = rs.getBigDecimal(1); + return amount.toString(); + } + return "0"; + } catch (SQLException e) { + throw new RuntimeException(e); + } + + } + + private String mapRowSimple(ResultSet rs) throws SQLException { + return "Amount: " + rs.getString("amount") + + ", Date: " + rs.getString("donation_date") + + ", Organization: " + rs.getString("organization_id"); + } + + private String mapRowFavorite(ResultSet rs) throws SQLException { + return rs.getString("organization_id") + + " (" + rs.getInt("donation_count") + " donasjoner)"; + } + From e67f0dde99d0479b22f6190c1cbb98c9896d133f Mon Sep 17 00:00:00 2001 From: lukaceli Date: Tue, 24 Mar 2026 13:55:02 +0100 Subject: [PATCH 7/9] Made UserStatistics class, and added methods that fetch information from database through DonationDao --- .../java/application/user/UserStatistics.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/application/user/UserStatistics.java diff --git a/src/main/java/application/user/UserStatistics.java b/src/main/java/application/user/UserStatistics.java new file mode 100644 index 0000000..8f43f7d --- /dev/null +++ b/src/main/java/application/user/UserStatistics.java @@ -0,0 +1,29 @@ +package application.user; + +import domain.user.User; +import java.util.ArrayList; +import java.util.List; +import persistence.DonationDao; + +public class UserStatistics { + private DonationDao donationDao; + + public UserStatistics() { + this.donationDao = new DonationDao(); + } + + public List userFavoriteOrganizations(User user) { + List favoriteOrganizations = donationDao.getFavoriteOrganizations(user.getID()); + return favoriteOrganizations; + } + + public List userDonations(User user) { + List userDonationList = donationDao.getUserDonations(user.getID()); + return userDonationList; + } + + public String userTotalDonationAmount(User user) { + String totalDonations = donationDao.getTotalDonationAmount(user.getID()); + return totalDonations; + } +} From 05de8ff70f7805ad125876f92c3ebae300c8e2d1 Mon Sep 17 00:00:00 2001 From: lukaceli Date: Tue, 24 Mar 2026 14:26:29 +0100 Subject: [PATCH 8/9] Made an extra method for getting total donations made by a user. UserStatistics accesses database through donationdao. --- .../java/application/user/UserStatistics.java | 5 ++++ src/main/java/persistence/DonationDao.java | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/main/java/application/user/UserStatistics.java b/src/main/java/application/user/UserStatistics.java index 8f43f7d..64333f1 100644 --- a/src/main/java/application/user/UserStatistics.java +++ b/src/main/java/application/user/UserStatistics.java @@ -26,4 +26,9 @@ public String userTotalDonationAmount(User user) { String totalDonations = donationDao.getTotalDonationAmount(user.getID()); return totalDonations; } + + public String getTotalDonationsMade(User user) { + String totalDonationsMade = donationDao.getTotalDonationsMade(user.getID()); + return totalDonationsMade; + } } diff --git a/src/main/java/persistence/DonationDao.java b/src/main/java/persistence/DonationDao.java index 338a92e..70ddba6 100644 --- a/src/main/java/persistence/DonationDao.java +++ b/src/main/java/persistence/DonationDao.java @@ -151,6 +151,29 @@ SELECT SUM(CAST(amount as REAL)) } catch (SQLException e) { throw new RuntimeException(e); } + } + + public String getTotalDonationsMade(long userId) { + String sql = """ + SELECT COUNT(*) + FROM donation + WHERE user_id = ? + """; + + try (Connection conn = Database.getConnection()) { + PreparedStatement stmt = conn.prepareStatement(sql); + + stmt.setLong(1, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + int amount = rs.getInt(1); + String s = String.valueOf(amount); + return s; + } + return "0"; + } catch (SQLException e) { + throw new RuntimeException(e); + } } From ea5cf76b6b06b0eb66aba9c4ae992f5c831be7c1 Mon Sep 17 00:00:00 2001 From: lukaceli Date: Tue, 7 Apr 2026 15:11:07 +0200 Subject: [PATCH 9/9] Added javadoc to DonationDao and UserStatistics --- .../java/application/user/UserStatistics.java | 35 ++++++++ src/main/java/persistence/DonationDao.java | 87 ++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/src/main/java/application/user/UserStatistics.java b/src/main/java/application/user/UserStatistics.java index 64333f1..5fe67d4 100644 --- a/src/main/java/application/user/UserStatistics.java +++ b/src/main/java/application/user/UserStatistics.java @@ -5,28 +5,63 @@ import java.util.List; import persistence.DonationDao; +/** + * Provides statistical information about a user's donation activity. + * Acts as a service layer between the application and {@link DonationDao}, + * exposing user-friendly methods for retrieving donation history, + * total amounts, and favorite organizations. + */ public class UserStatistics { private DonationDao donationDao; + /** + * Constructs a new {@code UserStatistics} instance with a default {@link DonationDao}. + */ public UserStatistics() { this.donationDao = new DonationDao(); } + /** + * Returns the top three organizations that the user has donated to most frequently. + * + * @param user the user whose favorite organizations are to be retrieved; must not be null + * @return a list of up to three formatted strings, each representing an organization + * and its donation count; empty list if the user has no donations + */ public List userFavoriteOrganizations(User user) { List favoriteOrganizations = donationDao.getFavoriteOrganizations(user.getID()); return favoriteOrganizations; } + /** + * Returns a formatted list of all donations made by the user. + * Each entry contains the donation amount, date, and organization. + * + * @param user the user whose donation history is to be retrieved; must not be null + * @return a list of formatted donation strings; empty list if the user has no donations + */ public List userDonations(User user) { List userDonationList = donationDao.getUserDonations(user.getID()); return userDonationList; } + /** + * Returns the total amount donated by the user across all donations. + * + * @param user the user whose total donation amount is to be calculated; must not be null + * @return a string representation of the total donated amount; {@code "0"} if no donations exist + */ public String userTotalDonationAmount(User user) { String totalDonations = donationDao.getTotalDonationAmount(user.getID()); return totalDonations; } + /** + * Returns the total number of donations made by the user. + * + * @param user the user whose donation count is to be retrieved; must not be null + * @return a string representation of the total number of donations; {@code "0"} if none exist + */ public String getTotalDonationsMade(User user) { String totalDonationsMade = donationDao.getTotalDonationsMade(user.getID()); return totalDonationsMade; diff --git a/src/main/java/persistence/DonationDao.java b/src/main/java/persistence/DonationDao.java index 70ddba6..0361f2b 100644 --- a/src/main/java/persistence/DonationDao.java +++ b/src/main/java/persistence/DonationDao.java @@ -13,8 +13,20 @@ import java.util.List; import persistence.db.Database; +/** + * Data Access Object for donation-related database operations. + * Provides methods for inserting donations and retrieving donation statistics + * such as history, total amounts, and favorite organizations for a given user. + */ public class DonationDao { - + /** + * Inserts a new donation record into the database. + * Upon successful insertion, the generated database ID is set on the donation object. + * + * @param donation the {@link Donation} to insert; must not be null and must have + * a valid amount, user, and organization + * @throws RuntimeException if a database error occurs during the insert + */ public void insert(Donation donation) { String sql = "INSERT INTO donation(amount, donation_date" + ", user_id, organization_id) VALUES(?, ?, ?, ?)"; @@ -38,6 +50,15 @@ public void insert(Donation donation) { } } + /** + * Maps a single row from the provided {@code ResultSet} into a {@link Donation} object. + * The method extracts information about the donation, user, and organization + * from the {@code ResultSet} and constructs corresponding objects. + * + * @param rs the {@code ResultSet} containing the data for a single row; must not be null + * @return a {@code Donation} object populated with data from the row in the {@code ResultSet} + * @throws SQLException if a database access error occurs or if the column labels cannot be found + */ private Donation mapRow(ResultSet rs) throws SQLException { User user = new User( rs.getString("user_name"), @@ -64,6 +85,15 @@ private Donation mapRow(ResultSet rs) throws SQLException { return donation; } + /** + * Retrieves a list of donations made by a specific user, based on the user ID. + * The donations include details such as the donation amount, date, associated user, + * and organization. + * + * @param userId the unique identifier of the user whose donations are to be retrieved + * @return a list of {@code Donation} objects representing donations made by the user + * @throws RuntimeException if an error occurs during the database operation + */ public List findByUser(long userId) { String sql = """ SELECT d.id, d.amount, d.donation_date, @@ -88,6 +118,14 @@ public List findByUser(long userId) { } } + /** + * Retrieves a formatted list of donations made by a specific user. + * Each entry is a string on the form {@code "Amount: X, Date: Y, Organization: Z"}. + * + * @param userId the unique identifier of the user whose donations are to be retrieved + * @return a list of formatted strings, one per donation; empty list if none found + * @throws RuntimeException if a database error occurs during the query + */ public List getUserDonations(long userId) { String sql = """ SELECT amount, donation_date, organization_id @@ -108,6 +146,16 @@ public List getUserDonations(long userId) { } } + /** + * Retrieves a list of the user's favorite organizations based on donation history. + * The method queries the database to find the organizations the user has donated to most frequently + * and limits the result to the top three organizations. + * + * @param userId the unique identifier of the user whose favorite organizations are to be retrieved + * @return a list of strings representing the top three organizations donated to by the user, + * ordered by the number of donations in descending order + * @throws RuntimeException if an error occurs while querying the database + */ public List getFavoriteOrganizations(long userId) { String sql = """ SELECT organization_id, COUNT(*) AS donation_count @@ -131,6 +179,15 @@ SELECT organization_id, COUNT(*) AS donation_count } } + /** + * Retrieves the total donation amount made by a specific user. + * The method calculates the sum of all donations associated with the given user ID. + * + * @param userId the unique identifier of the user whose total donation amount is to be retrieved + * @return a string representing the total donation amount made by the user; if no donations exist, + * the method returns "0" + * @throws RuntimeException if an error occurs during database access + */ public String getTotalDonationAmount(long userId) { String sql = """ SELECT SUM(CAST(amount as REAL)) @@ -153,6 +210,17 @@ SELECT SUM(CAST(amount as REAL)) } } + /** + * Retrieves the total number of donations made by a specific user. + * The method counts the number of donation records associated with the provided user ID + * in the database and returns the total count as a string. + * + * @param userId the unique identifier of the user whose total number of donations + * is to be retrieved + * @return a string representing the total count of donations made by the user; + * returns "0" if no donations exist + * @throws RuntimeException if a database access error occurs + */ public String getTotalDonationsMade(long userId) { String sql = """ SELECT COUNT(*) @@ -177,12 +245,29 @@ SELECT COUNT(*) } + /** + * Maps a single row from the provided {@code ResultSet} into a string representation + * consisting of the donation amount, date, and organization ID. + * + * @param rs the {@code ResultSet} containing the data for a single row; must not be null + * @return a string representing the donation with details such as amount, date, + * and organization ID + * @throws SQLException if a database access error occurs or if the column labels cannot be found + */ private String mapRowSimple(ResultSet rs) throws SQLException { return "Amount: " + rs.getString("amount") + ", Date: " + rs.getString("donation_date") + ", Organization: " + rs.getString("organization_id"); } + /** + * Maps a single row from the provided {@code ResultSet} into a formatted string + * combining the organization ID and its donation count. + * + * @param rs the {@code ResultSet} containing the data for a single row; must not be null + * @return a string on the form {@code "organization_id (N donations)"} + * @throws SQLException if a database access error occurs or a column label cannot be found + */ private String mapRowFavorite(ResultSet rs) throws SQLException { return rs.getString("organization_id") + " (" + rs.getInt("donation_count") + " donasjoner)";