diff --git a/docs/Domenemodell/Domene Diagram 2.pdf b/docs/Domenemodell/Domene Diagram 2.pdf new file mode 100644 index 00000000..4277ec0f Binary files /dev/null and b/docs/Domenemodell/Domene Diagram 2.pdf differ diff --git a/docs/Manuals/Installation Guide HmH.pdf b/docs/Manuals/Installation Guide HmH.pdf new file mode 100644 index 00000000..e92399b1 Binary files /dev/null and b/docs/Manuals/Installation Guide HmH.pdf differ diff --git a/docs/Manuals/media/image1.png b/docs/Manuals/media/image1.png new file mode 100644 index 00000000..ccb37b6a Binary files /dev/null and b/docs/Manuals/media/image1.png differ diff --git a/docs/Manuals/media/image2.png b/docs/Manuals/media/image2.png new file mode 100644 index 00000000..22fcb9ce Binary files /dev/null and b/docs/Manuals/media/image2.png differ diff --git a/docs/Manuals/media/image3.png b/docs/Manuals/media/image3.png new file mode 100644 index 00000000..d98f38b1 Binary files /dev/null and b/docs/Manuals/media/image3.png differ diff --git a/docs/Manuals/media/image4.png b/docs/Manuals/media/image4.png new file mode 100644 index 00000000..e0a8696b Binary files /dev/null and b/docs/Manuals/media/image4.png differ diff --git a/docs/Manuals/media/image5.png b/docs/Manuals/media/image5.png new file mode 100644 index 00000000..8dcf6b34 Binary files /dev/null and b/docs/Manuals/media/image5.png differ diff --git a/docs/Manuals/media/image6.png b/docs/Manuals/media/image6.png new file mode 100644 index 00000000..4bafc2f5 Binary files /dev/null and b/docs/Manuals/media/image6.png differ diff --git "a/docs/M\303\270tedokumenter/Thursdays Meeting 2026.04.09 (With LA).pdf" "b/docs/M\303\270tedokumenter/Thursdays Meeting 2026.04.09 (With LA).pdf" new file mode 100644 index 00000000..0756fa47 Binary files /dev/null and "b/docs/M\303\270tedokumenter/Thursdays Meeting 2026.04.09 (With LA).pdf" differ diff --git a/docs/SqlDatabase/ER-Diagram v5.png b/docs/SqlDatabase/ER-Diagram v5.png new file mode 100644 index 00000000..df229943 Binary files /dev/null and b/docs/SqlDatabase/ER-Diagram v5.png differ diff --git a/docs/SqlDatabase/ER-DiagramFile.mwb b/docs/SqlDatabase/ER-DiagramFile.mwb index ee90f5e9..c43ee851 100644 Binary files a/docs/SqlDatabase/ER-DiagramFile.mwb and b/docs/SqlDatabase/ER-DiagramFile.mwb differ diff --git a/docs/SqlDatabase/ER-DiagramFile.mwb.bak b/docs/SqlDatabase/ER-DiagramFile.mwb.bak index 64d95337..c348da33 100644 Binary files a/docs/SqlDatabase/ER-DiagramFile.mwb.bak and b/docs/SqlDatabase/ER-DiagramFile.mwb.bak differ diff --git a/docs/design/Universal Design.pdf b/docs/design/Universal Design.pdf new file mode 100644 index 00000000..09aca337 Binary files /dev/null and b/docs/design/Universal Design.pdf differ diff --git a/docs/persistence/Persistence.pdf b/docs/persistence/Persistence.pdf new file mode 100644 index 00000000..ee02cf1e Binary files /dev/null and b/docs/persistence/Persistence.pdf differ diff --git a/etUUID(uuid); b/etUUID(uuid); new file mode 100644 index 00000000..74570f66 --- /dev/null +++ b/etUUID(uuid); @@ -0,0 +1,324 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + ESC-j * Forward one file line (or _N file lines). + ESC-k * Backward one file line (or _N file lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + ESC-b * Backward one window, but don't stop at beginning-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ^O^N ^On * Search forward for (_N-th) OSC8 hyperlink. + ^O^P ^Op * Search backward for (_N-th) OSC8 hyperlink. + ^O^L ^Ol Jump to the currently selected OSC8 hyperlink. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + Search is case-sensitive unless changed with -i or -I. + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^S _n Search for match in _n-th parenthesized subpattern. + ^W WRAP search if no match found. + ^L Enter next character literally into pattern. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-m_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + ^O^O Open the currently selected OSC8 hyperlink. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + #_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k _f_i_l_e ... --lesskey-file=_f_i_l_e + Use a compiled lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n ......... --line-numbers + Suppress line numbers in prompts and messages. + -N ......... --LINE-NUMBERS + Display line number at start of each line. + -o [_f_i_l_e] .. --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] .. --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p _p_a_t_t_e_r_n . --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t _t_a_g .... --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces, tabs and carriage returns. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + + --exit-follow-on-close + Exit F command on a pipe when writer closes pipe. + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --form-feed + Stop scrolling when a form feed character is reached. + --header=[_L[,_C[,_N]]] + Use _L lines (starting at line _N) and _C columns as headers. + --incsearch + Search file as each pattern character is typed in. + --intr=[_C] + Use _C instead of ^X to interrupt a read. + --lesskey-context=_t_e_x_t + Use lesskey source file contents. + --lesskey-src=_f_i_l_e + Use a lesskey source file. + --line-num-width=[_N] + Set the width of the -N line number field to _N characters. + --match-shift=[_N] + Show at least _N characters to the left of a search match. + --modelines=[_N] + Read _N lines from the input file and look for vim modelines. + --mouse + Enable mouse input. + --no-edit-warn + Don't warn when using v command on a file opened via LESSOPEN. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --no-number-headers + Don't give line numbers to header lines. + --no-paste + Ignore pasted input. + --no-search-header-lines + Searches do not include header lines. + --no-search-header-columns + Searches do not include header columns. + --no-search-headers + Searches do not include header lines or columns. + --no-vbell + Disable the terminal's visual bell. + --redraw-on-quit + Redraw final screen when quitting. + --rscroll=[_C] + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --search-options=[EFKNRW-] + Set default options for every search. + --show-preproc-errors + Display a message if preprocessor exits with an error status. + --proc-backspace + Process backspaces for bold/underline. + --PROC-BACKSPACE + Treat backspaces as control characters. + --proc-return + Delete carriage returns before newline. + --PROC-RETURN + Treat carriage returns as control characters. + --proc-tab + Expand tabs to spaces. + --PROC-TAB + Treat tabs as control characters. + --status-col-width=[_N] + Set the width of the -J status column to _N characters. + --status-line + Highlight or color the entire line containing a mark. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=[_N] + Each click of the mouse wheel moves _N lines. + --wordwrap + Wrap lines at spaces. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. diff --git a/helpmehelpapplication/.ai/mcp/mcp.json b/helpmehelpapplication/.ai/mcp/mcp.json new file mode 100644 index 00000000..e69de29b diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java index fc3588db..966cf897 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java @@ -3,19 +3,23 @@ import static javafx.application.Application.launch; import java.net.http.HttpClient; +import java.util.Objects; + import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.image.Image; 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.APICharityScraper; +import ntnu.systemutvikling.team6.scraper.scraperComponents.APICharityScraper; import ntnu.systemutvikling.team6.service.APIToDatabaseService; public class HmHApplication extends Application { @Override public void start(Stage stage) throws Exception { - /* FXMLLoader fxmlLoader = new FXMLLoader(HmHApplication.class.getResource("/fxml/frontPage.fxml")); Scene scene = new Scene(fxmlLoader.load()); @@ -30,8 +34,6 @@ public void start(Stage stage) throws Exception { stage.setFullScreen(true); stage.show(); - */ - } @Override @@ -49,6 +51,7 @@ public void init() { try { HttpClient https = HttpClient.newHttpClient(); APICharityScraper scraper = new APICharityScraper(https); + DatabaseConnection conn = new DatabaseConnection(); APIToDatabaseService db = new APIToDatabaseService(conn); diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/AvailableOrganizationController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/AvailableOrganizationController.java index 47a36eb8..ee3a19f1 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/AvailableOrganizationController.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/AvailableOrganizationController.java @@ -9,6 +9,10 @@ import javafx.scene.Parent; import javafx.scene.control.TextField; import javafx.scene.layout.FlowPane; +import ntnu.systemutvikling.team6.controller.components.BaseController; +import ntnu.systemutvikling.team6.controller.components.LoaderScene; +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.CharitySelect; import ntnu.systemutvikling.team6.models.Charity; @@ -22,7 +26,7 @@ * on the featured charity to see more details about it. The user can also switch to the charity * page or donation page for the selected charity. */ -public class AvailableOrganizationController { +public class AvailableOrganizationController extends BaseController implements NavbarFooterController { @FXML private TextField searchField; @FXML private FlowPane cardsContainer; @@ -88,7 +92,7 @@ private void displayCharities(List charities) { for (Charity charity : charities) { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/organizationCard.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/components/organizationCard.fxml")); Parent card = loader.load(); OrganizationCardController cardController = loader.getController(); @@ -115,15 +119,6 @@ public void setInitialSearch(String query) { searchField.setText(query); } - /** - * This method is used to switch to the front page. - * - * @param event action event from button click - */ - @FXML - public void switchToFrontPage(ActionEvent event) { - LoaderScene.LoadScene("FrontPage", event, charity, null); - } /** * This method is used to switch to the charity page for the selected charity. 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 b52064f6..121ad3be 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java @@ -4,6 +4,7 @@ import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TextField; +import ntnu.systemutvikling.team6.controller.components.LoaderScene; import ntnu.systemutvikling.team6.models.Charity; /** @@ -40,16 +41,6 @@ public void setCharity(Charity charity) { CharityName.setText(charity.getName()); } - /** - * This method is used to switch to the front page. - * - * @param event - */ - @FXML - public void switchToFrontPage(ActionEvent event) { - System.out.println("Click"); - LoaderScene.LoadScene("FrontPage", event, charity, null); - } /** * This method is used to switch to the donation page. diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java index 0ce757fa..4a69ed54 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java @@ -7,8 +7,11 @@ import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import javafx.scene.control.TextField; -import ntnu.systemutvikling.team6.DAO.DonationDAO; +import ntnu.systemutvikling.team6.controller.components.LoaderScene; +import ntnu.systemutvikling.team6.database.DAO.DonationDAO; +import ntnu.systemutvikling.team6.database.DatabaseConnection; import ntnu.systemutvikling.team6.models.Charity; +import ntnu.systemutvikling.team6.models.user.User; /** * This controller represents the donation page, where the user can enter a donation amount and @@ -23,6 +26,8 @@ public class DonationPageController { @FXML private TextField donationSearchField; + private DonationDAO donationSender = new DonationDAO(new DatabaseConnection()); + /** * Initialize method for the donation page. Sets the charity name label to the name of the charity * that is being donated to. The charity is set from the original page it was called from when the @@ -97,7 +102,7 @@ public void Donate(ActionEvent event) { if (result.isPresent() && result.get() == ButtonType.OK) { // Process donation - processDonation(charity, amount); + //processDonation(charity, user, amount); showAlert( Alert.AlertType.INFORMATION, "Thank you!", @@ -114,8 +119,8 @@ public void Donate(ActionEvent event) { * @param charity * @param amount */ - public void processDonation(Charity charity, double amount) { - DonationDAO.addDonation(charity, amount); + public void processDonation(Charity charity, User user, double amount) { + donationSender.addDonation(charity, user, amount); } /** 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 3dc7f4c5..b5b45226 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java @@ -12,12 +12,16 @@ import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.FlowPane; +import ntnu.systemutvikling.team6.controller.components.BaseController; +import ntnu.systemutvikling.team6.controller.components.LoaderScene; +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.CharitySelect; import ntnu.systemutvikling.team6.database.Readers.DonationSelect; import ntnu.systemutvikling.team6.models.Charity; -import ntnu.systemutvikling.team6.models.registry.CharityRegistry; import ntnu.systemutvikling.team6.models.Donation; +import ntnu.systemutvikling.team6.models.registry.CharityRegistry; import ntnu.systemutvikling.team6.models.registry.DonationRegistry; /** @@ -27,7 +31,7 @@ * it, or click on the featured charity to see more details about it. It also has buttons to switch * to the charity page and the donation page for the featured charity */ -public class FrontpageController { +public class FrontpageController extends BaseController implements NavbarFooterController { @FXML private Charity featuredCharity; @FXML private FlowPane cardsContainer; @FXML private Label Carosel_Organisasjon; @@ -43,6 +47,11 @@ public class FrontpageController { private List allCharities = new ArrayList<>(); + @Override + protected void authTokenisSet(){ + + } + /** * Initialize method for the front page. This method is called when the front page is loaded. It * retrieves the list of charities and donations from the database. The list of charities is @@ -88,6 +97,7 @@ public void initialize() { } } + /** * This method is used to switch to the charity page for the selected charity * @@ -210,7 +220,7 @@ private void displayCharities(List charities) { for (Charity charity : charities) { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/organizationCard.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/components/organizationCard.fxml")); Parent card = loader.load(); OrganizationCardController cardController = loader.getController(); cardController.setOrganization(charity); diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/BaseController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/BaseController.java new file mode 100644 index 00000000..da4af23a --- /dev/null +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/BaseController.java @@ -0,0 +1,17 @@ +package ntnu.systemutvikling.team6.controller.components; + +import ntnu.systemutvikling.team6.service.AuthenticationService; + +public abstract class BaseController { + protected AuthenticationService authToken; + + public void setAuthToken(AuthenticationService authToken){ + this.authToken = authToken; + authTokenisSet(); + }; + protected void authTokenisSet(){} // Do stuff after authtoken is set, on each controller + + protected boolean isLoggedin(){ + return authToken.isLoggedin() && authToken != null; + } +} diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/LoaderScene.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/LoaderScene.java similarity index 90% rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/LoaderScene.java rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/LoaderScene.java index 12f7f9b3..30236e9a 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/LoaderScene.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/LoaderScene.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.controller; +package ntnu.systemutvikling.team6.controller.components; import java.io.IOException; import java.util.Objects; @@ -10,6 +10,9 @@ import javafx.scene.image.Image; import javafx.stage.Stage; import ntnu.systemutvikling.team6.HmHApplication; +import ntnu.systemutvikling.team6.controller.AvailableOrganizationController; +import ntnu.systemutvikling.team6.controller.CharityPageController; +import ntnu.systemutvikling.team6.controller.DonationPageController; import ntnu.systemutvikling.team6.models.Charity; /** diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/NavbarFooterController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/NavbarFooterController.java new file mode 100644 index 00000000..0075893f --- /dev/null +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/NavbarFooterController.java @@ -0,0 +1,33 @@ +package ntnu.systemutvikling.team6.controller.components; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.TextField; + +public interface NavbarFooterController { + + default void switchToFrontPage(ActionEvent event) { + System.out.println("Click!"); + LoaderScene.LoadScene("FrontPage", event, null, null); + } + + default void switchToAboutPage(ActionEvent event) { + System.out.println("Click!"); + LoaderScene.LoadScene("aboutPage", event, null, null); + } + + default void switchToProfilePage(ActionEvent event) { + System.out.println("Click!"); + LoaderScene.LoadScene("aboutPage", event, null, null); + } + + default void handleSearch(ActionEvent event) { + System.out.println("Click!"); + LoaderScene.LoadScene("aboutPage", event, null, null); + } + + default void switchToLoginPage(ActionEvent event) { + System.out.println("Click!"); + LoaderScene.LoadScene("aboutPage", event, null, null); + } +} diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/OrganizationCardController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/OrganizationCardController.java similarity index 94% rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/OrganizationCardController.java rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/OrganizationCardController.java index b0ce278a..7644bbf8 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/OrganizationCardController.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/OrganizationCardController.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.controller; +package ntnu.systemutvikling.team6.controller.components; import javafx.event.ActionEvent; import javafx.fxml.FXML; diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/DonationDAO.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/DonationDAO.java similarity index 72% rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/DonationDAO.java rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/DonationDAO.java index c99056f9..730accac 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/DonationDAO.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/DonationDAO.java @@ -1,29 +1,34 @@ -package ntnu.systemutvikling.team6.DAO; +package ntnu.systemutvikling.team6.database.DAO; import java.sql.*; import java.util.UUID; import ntnu.systemutvikling.team6.database.DatabaseConnection; import ntnu.systemutvikling.team6.models.Charity; +import ntnu.systemutvikling.team6.models.user.User; /** * This class is responsible for sending concurrent information about the donation to the Donation * Database. Usally called from the DonationPageController, where the user confirms their donation. */ public class DonationDAO { - private static final DatabaseConnection connection = new DatabaseConnection(); + private final DatabaseConnection connection; - /** + public DonationDAO(DatabaseConnection connection) { + this.connection = connection; + } + + /** * Gets the total ammount of donations for a given charity, and sends it to the database throught * MySQL. * * @param charity * @param amount */ - public static void addDonation(Charity charity, double amount) { + public void addDonation(Charity charity, User user, double amount) { String sql_query = """ - INSERT INTO Donations (UUID_Donations, amount, date, Charities_UUID_charities) - VALUES (?, ?, ?, ?) + INSERT INTO Donations (UUID_Donations, amount, date, charity_id, user_id) + VALUES (?, ?, ?, ?, ?) """; try (Connection conn = connection.getMySqlConnection(); PreparedStatement ps = conn.prepareStatement(sql_query)) { @@ -33,6 +38,8 @@ INSERT INTO Donations (UUID_Donations, amount, date, Charities_UUID_charities) ps.setDouble(2, amount); ps.setDate(3, new Date(System.currentTimeMillis())); ps.setString(4, charity.getUUID().toString()); + ps.setString(5, user.getId().toString()); + ps.executeUpdate(); conn.commit(); diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/UserDAO.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/UserDAO.java similarity index 69% rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/UserDAO.java rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/UserDAO.java index f1ce4aa3..2125a088 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/UserDAO.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/UserDAO.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.DAO; +package ntnu.systemutvikling.team6.database.DAO; import java.sql.Connection; import java.sql.PreparedStatement; @@ -7,23 +7,28 @@ import ntnu.systemutvikling.team6.models.user.User; /** - * This class is responsible for sending concurrent information about the user to the User - * database, and user settings to the settings database. + * This class is responsible for sending concurrent information about the user to the User database, + * and user settings to the settings database. * * @author Robin Strand Prestmo */ public class UserDAO { - private static final DatabaseConnection connection = new DatabaseConnection(); + private final DatabaseConnection connection; - /** + public UserDAO(DatabaseConnection connection) { + this.connection = connection; + } + + /** * Gets the user and settings information and sends it to the database through MySQL. * + * * @param user the user to be saved in the database. * @throws RuntimeException if a database error occurs during the operation + * @return true or false based on if the register is a success or not */ - public static void registerUser(User user) { - + public boolean registerUser(User user) { String userSql = """ INSERT INTO User ( @@ -48,6 +53,9 @@ INSERT INTO Settings ( VALUES (?, ?, ?, ?) """; + int psUserRows = 0; + int psSettingsRows = 0; + try (Connection conn = connection.getMySqlConnection()) { conn.setAutoCommit(false); @@ -55,13 +63,13 @@ INSERT INTO Settings ( try (PreparedStatement psUser = conn.prepareStatement(userSql)) { psUser.setString(1, user.getId().toString()); - psUser.setString(2, user.getName()); // display name - psUser.setString(3, user.getName()); // username + psUser.setString(2, user.getDisplayName()); // display name + psUser.setString(3, user.getUsername()); // username psUser.setString(4, user.getEmail()); psUser.setString(5, user.getPasswordHash()); psUser.setString(6, user.getRole().name()); - psUser.executeUpdate(); + psUserRows = psUser.executeUpdate(); } try (PreparedStatement psSettings = conn.prepareStatement(settingsSql)) { @@ -71,13 +79,16 @@ INSERT INTO Settings ( psSettings.setString(3, user.getSettings().getLanguage().name()); psSettings.setBoolean(4, user.getSettings().isLightMode()); - psSettings.executeUpdate(); + psSettingsRows = psSettings.executeUpdate(); + } conn.commit(); + return psUserRows > 0 && psSettingsRows > 0; } catch (SQLException e) { - throw new RuntimeException("Failed to insert user into database", e); + e.printStackTrace(); + return false; } } } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java deleted file mode 100644 index 4f6d3ae2..00000000 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java +++ /dev/null @@ -1,384 +0,0 @@ -package ntnu.systemutvikling.team6.database; - -import java.sql.*; -import java.util.*; - -import ntnu.systemutvikling.team6.models.Charity; -import ntnu.systemutvikling.team6.models.registry.CharityRegistry; -import ntnu.systemutvikling.team6.models.Donation; -import ntnu.systemutvikling.team6.models.registry.DonationRegistry; -import ntnu.systemutvikling.team6.scraper.APICharityData; -import ntnu.systemutvikling.team6.scraper.LogoDownloader; -import ntnu.systemutvikling.team6.scraper.URLCharityScraper; - -/** - * Manages the Database with MySQL tables and JDBC. - * - *

This class is responsible for creating the tables needed for the application, if not done - * already and maintaining the {@code charities} table based on data retrieved from the IK API. It - * is also responsible for retrieving the data from the database and sending it to the application - * through the CharityRegistry and DonationRegistry. It is used by the FrontpageController to - * retrieve the data needed to display the charities - */ -public class DatabaseManager { - private final DatabaseConnection connection; - - /** - * Contractor for DatabaseManager. It uses a DatabaseConnection object that contains a connection - * credentials. - */ - public DatabaseManager() { - this.connection = new DatabaseConnection(); - } - - /** - * Connection test for the Database. Does a simple SELECT SQL query and returns either true og and - * Exception if failed - * - * @return true if Sucsedd or SQLExepction if failed - */ - public boolean testConnection() { - try (Connection conn = connection.getMySqlConnection(); - Statement stmt = conn.createStatement()) { - - ResultSet rs = stmt.executeQuery("SELECT 1"); - - if (rs.next()) { - System.out.println("Database connection verified."); - return true; - } - - } catch (SQLException e) { - System.out.println("Database connection failed."); - e.printStackTrace(); - } - - return false; - } - - /** - * Creates the {@code Charities} and {@code Donations} tables if they do not already exist. - * - *

The table structure for Charities is based on fields from {@link APICharityData}. - * - * @throws RuntimeException if a {@link SQLException} occurs while creating the table - */ - public void createTables() { - String sql_query1 = - """ - -- ----------------------------------------------------- - -- Table `HelpMeHelp`.`Charities` - -- ----------------------------------------------------- - CREATE TABLE IF NOT EXISTS Charities ( - UUID_charities CHAR(36) PRIMARY KEY, - org_number VARCHAR(255) NOT NULL, - charity_name VARCHAR(255) NOT NULL, - charity_link VARCHAR(255) NOT NULL, - pre_approved TINYINT NOT NULL, - status VARCHAR(255) NOT NULL, - description TEXT, - logoURL TEXT, - categories TEXT, - key_values TEXT, - logoBlob MEDIUMBLOB, - UNIQUE KEY unique_org_number (org_number) - ) ENGINE=InnoDB; - - - """; - String sql_query2 = - """ - -- ----------------------------------------------------- - -- Table `HelpMeHelp`.`Donations` - -- ----------------------------------------------------- - CREATE TABLE IF NOT EXISTS Donations ( - `UUID_Donations` CHAR(36) NOT NULL, - `amount` DECIMAL NOT NULL, - `date` DATE NOT NULL, - `Charities_UUID_charities` CHAR(36) NOT NULL, - PRIMARY KEY (`UUID_Donations`), - INDEX `fk_Donations_Charities_idx` (`Charities_UUID_charities` ASC) VISIBLE, - CONSTRAINT `fk_Donations_Charities` - FOREIGN KEY (`Charities_UUID_charities`) - REFERENCES Charities (`UUID_charities`) - ON DELETE CASCADE - ON UPDATE CASCADE) - ENGINE = InnoDB; - """; - - try (Connection conn = connection.getMySqlConnection(); - Statement s = conn.createStatement()) { - - s.execute(sql_query1); - s.execute(sql_query2); - } catch (SQLException e) { - e.printStackTrace(); - throw new RuntimeException("Error creating table."); - } - } - - /** - * 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 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 temp table to ensure that the data in the database - * is consistent with the data from the API. - *

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.

- * - * @param charities a list of {@code Charity} objects to add to the database - */ - public void addAPIDataToTable(List 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, logoBlob) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - charity_name = VALUES(charity_name), - charity_link = VALUES(charity_link), - pre_approved = VALUES(pre_approved), - status = VALUES(status), - description = VALUES(description), - logoURL = VALUES(logoURL), - categories = VALUES(categories), - key_values = VALUES(key_values), - logoBlob = VALUES(logoBlob) - """; - - try (PreparedStatement ps = conn.prepareStatement(sql_query)) { - for (Charity charity : charities) { - if (charity.getUUID() == null) { - ps.setString(1, UUID.randomUUID().toString()); - } else { - ps.setString(1, charity.getUUID().toString()); - } - - ps.setString(2, charity.getOrg_number().replaceAll("\\s", "")); - ps.setString(3, charity.getName()); - ps.setString(4, charity.getURL()); - ps.setBoolean(5, charity.getPreApproved()); - ps.setString(6, charity.getStatus()); - ps.setString(7, charity.getDescription()); - ps.setString(8, charity.getLogoURL()); - ps.setString(9, charity.getCategory()); - ps.setString(10, charity.getKeyValues()); - ps.setBytes(11, charity.getLogoBlob()); - - ps.addBatch(); - } - ps.executeBatch(); - } - - // -- Intergerty Check: - String createTemp = - """ - CREATE TEMPORARY TABLE temp_api_charities ( - org_number VARCHAR(20) 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 - ) - """; - - try (PreparedStatement ps = conn.prepareStatement(deleteSql)) { - ps.executeUpdate(); - } - - conn.commit(); - - } catch (SQLException e) { - if (conn != null) { - try { - conn.rollback(); - } catch (SQLException ex) { - ex.printStackTrace(); - } - } - e.printStackTrace(); - - throw new RuntimeException("ERROR: Something went wrong during updating charities table."); - } finally { - if (conn != null) { - try { - conn.setAutoCommit(true); - conn.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - } - } - - /** - * 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, description, logoURL, " + - "categories, key_values, logoBlob FROM Charities"; - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(sql_query); - - registry = new CharityRegistry(); - while (rs.next()) { - Charity charity = - new Charity( - rs.getString("UUID_charities"), - rs.getString("org_number"), - rs.getString("charity_link"), - 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) { - e.printStackTrace(); - throw new RuntimeException("ERROR: Something went wrong during updating charities table."); - } - return registry; - } - - public List getCategoriesFromDB() { - Map 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; - try { - conn = connection.getMySqlConnection(); - String sql_query = - """ - SELECT - d.UUID_Donations, - d.amount, - d.date, - c.UUID_charities, - c.org_number, - c.charity_name, - c.charity_link, - c.pre_approved, - c.status - FROM Donations d - JOIN Charities c - ON d.Charities_UUID_charities = c.UUID_charities - """; - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(sql_query); - - registry = new DonationRegistry(); - while (rs.next()) { - Charity charity = - new Charity( - rs.getString("UUID_charities"), - rs.getString("org_number"), - rs.getString("charity_name"), - rs.getString("charity_link"), - rs.getBoolean("pre_approved"), - rs.getString("status")); - - Donation donation = - new Donation( - rs.getString("UUID_Donations"), - rs.getDouble("amount"), - rs.getDate("date").toLocalDate(), - charity); - registry.addDonation(donation); - } - } catch (SQLException e) { - e.printStackTrace(); - throw new RuntimeException("ERROR: Something went wrong during updating charities table."); - } - return registry; - } -} \ No newline at end of file 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 62de2e89..6da6f1ca 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java @@ -42,7 +42,6 @@ public boolean testConnection() { System.out.println("Database connection failed."); e.printStackTrace(); } - return false; } @@ -57,18 +56,14 @@ public void createTables() { -- ----------------------------------------------------- -- Table `HelpMeHelp`.`Charities` -- ----------------------------------------------------- - CREATE TABLE IF NOT EXISTS `apbaluna`.`Charities` ( - `UUID_charities` CHAR(36) NOT NULL, - `org_number` VARCHAR(255) NOT NULL, - `charity_name` VARCHAR(255) NOT NULL, - `charity_description` VARCHAR(255) NOT NULL, - `charity_link` VARCHAR(255) NOT NULL, - `pre_approved` TINYINT NOT NULL, - `status` VARCHAR(255) NOT NULL, - PRIMARY KEY (`UUID_charities`), - UNIQUE INDEX `org_number_UNIQUE` (`org_number` ASC) VISIBLE) - ENGINE = InnoDB; - + CREATE TABLE IF NOT EXISTS `apbaluna`.`Charities` ( + `UUID_charities` CHAR(36) NOT NULL, + `org_number` VARCHAR(255) NOT NULL, + `pre_approved` TINYINT NOT NULL, + `status` VARCHAR(255) NOT NULL, + PRIMARY KEY (`UUID_charities`), + UNIQUE INDEX `org_number_UNIQUE` (`org_number` ASC) VISIBLE) + ENGINE = InnoDB; """; String donationsTable = """ @@ -78,6 +73,7 @@ PRIMARY KEY (`UUID_charities`), CREATE TABLE IF NOT EXISTS `apbaluna`.`Donations` ( `UUID_Donations` CHAR(36) NOT NULL, `amount` DECIMAL NOT NULL, + `isAnonymous` TINYINT NOT NULL, `date` DATE NOT NULL, `charity_id` CHAR(36) NOT NULL, `user_id` CHAR(36) NOT NULL, @@ -118,14 +114,14 @@ PRIMARY KEY (`UUID_User`)) -- Table `apbaluna`.`Settings` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `apbaluna`.`Settings` ( - `User_UUID_User` CHAR(36) NOT NULL, + `UUID_user` CHAR(36) NOT NULL, `isAnonymous` TINYINT NOT NULL, `language` VARCHAR(45) NOT NULL, `lightmode` TINYINT NOT NULL, - PRIMARY KEY (`User_UUID_User`), - INDEX `fk_Settings_User1_idx` (`User_UUID_User` ASC) VISIBLE, + PRIMARY KEY (`UUID_user`), + INDEX `fk_Settings_User1_idx` (`UUID_user` ASC) VISIBLE, CONSTRAINT `fk_Settings_User1` - FOREIGN KEY (`User_UUID_User`) + FOREIGN KEY (`UUID_user`) REFERENCES `apbaluna`.`User` (`UUID_User`) ON DELETE NO ACTION ON UPDATE NO ACTION) @@ -253,6 +249,24 @@ FOREIGN KEY (`User_UUID_User`) ON UPDATE NO ACTION) ENGINE = InnoDB; """; + String charityVanityTable = + """ + CREATE TABLE IF NOT EXISTS `apbaluna`.`CharityVanity` ( + `UUID_charity` CHAR(36) NOT NULL, + `charity_name` VARCHAR(255) NOT NULL, + `charity_link` VARCHAR(255) NOT NULL, + `description` TEXT NULL, + `logoURL` TEXT NULL, + `key_values` TEXT NULL, + `logoBLOB` MEDIUMBLOB NULL, + INDEX `fk_CharityVanity_Charities1_idx` (`UUID_charity` ASC) VISIBLE, + CONSTRAINT `fk_CharityVanity_Charities1` + FOREIGN KEY (`UUID_charity`) + REFERENCES `apbaluna`.`Charities` (`UUID_charities`) + ON DELETE CASCADE + ON UPDATE CASCADE) + ENGINE = InnoDB; + """; try (Connection conn = connection.getMySqlConnection(); Statement s = conn.createStatement()) { @@ -266,6 +280,7 @@ FOREIGN KEY (`User_UUID_User`) s.execute(categoriesTable); s.execute(charityCategoriesTable); s.execute(charityUserTable); + s.execute(charityVanityTable); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Error creating table."); 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 cdcee40f..c59db00c 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 @@ -3,10 +3,14 @@ import java.sql.*; import java.time.LocalDate; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import ntnu.systemutvikling.team6.database.DatabaseConnection; import ntnu.systemutvikling.team6.models.Charity; -import ntnu.systemutvikling.team6.models.registry.CharityRegistry; import ntnu.systemutvikling.team6.models.Feedback; +import ntnu.systemutvikling.team6.models.registry.CharityRegistry; +import ntnu.systemutvikling.team6.models.user.Language; +import ntnu.systemutvikling.team6.models.user.Settings; import ntnu.systemutvikling.team6.models.user.User; /** @@ -34,11 +38,12 @@ public CharitySelect(DatabaseConnection connection) { * Retrieves all charities from the database, including their associated feedback and the users * who submitted each piece of feedback. * - *

The query performs a LEFT JOIN between the {@code Charities}, {@code Feedback}, and {@code - * User} tables. Each unique charity is added once to the registry; any feedback rows found for - * that charity are appended to its feedback list. + *

The query performs a LEFT JOIN between the {@code Charities}, {@code Feedback}, {@code + * User}, {@code CharityVanity}, and {@code category(s)} tables. Each unique charity is added once + * to the registry; any feedback rows found for that charity are appended to its feedback list. * - *

Note: charities with no feedback are still included in the result due to the LEFT JOIN. + *

Note: charities with no feedback and categories are still included in the result due to the + * LEFT JOIN. * * @return a {@link CharityRegistry} containing all charities found in the database, each * populated with its associated {@link Feedback} objects (if any) @@ -51,20 +56,27 @@ public CharityRegistry getCharitiesFromDB() { conn = connection.getMySqlConnection(); String sql_query = """ - SELECT - c.UUID_charities, c.org_number, c.charity_name, c.charity_link, c.pre_approved, c.status, - f.UUID_feedback, f.feedback_comment, f.feedback_date, f.isAnonymous, f.charity_id, f.user_id, - u.UUID_user, u.user_name, u.user_email, u.user_password, u.role - 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 - """; + 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; + """; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql_query); Charity currentCharity = null; String lastCharity = null; + Set seenFeedbackIds = new HashSet<>(); + registry = new CharityRegistry(); while (rs.next()) { String currentId = rs.getString("UUID_charities"); @@ -74,33 +86,48 @@ public CharityRegistry getCharitiesFromDB() { new Charity( rs.getString("UUID_charities"), rs.getString("org_number"), - rs.getString("charity_link"), rs.getString("charity_name"), + rs.getString("charity_link"), + rs.getString("status"), rs.getBoolean("pre_approved"), - rs.getString("status")); + rs.getString("description"), + 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); } + String feedbackId = rs.getString("UUID_feedback"); - if (feedbackId != null) { - User userWithNoSettingsAndInbox = + if (feedbackId != null && !seenFeedbackIds.contains(feedbackId)) { + seenFeedbackIds.add(feedbackId); + User userWithMinimalSettingsAndInbox = new User( rs.getString("UUID_User"), + rs.getString("user_displayname"), rs.getString("user_name"), rs.getString("user_email"), rs.getString("user_password"), rs.getString("role")); + userWithMinimalSettingsAndInbox.setSettings(new Settings(false, Language.ENGLISH, false)); Feedback feedback = new Feedback( rs.getString("UUID_feedback"), - userWithNoSettingsAndInbox, + userWithMinimalSettingsAndInbox, rs.getString("feedback_comment"), LocalDate.parse(rs.getString("feedback_date"))); currentCharity.getFeedbacks().add(feedback); } } + } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("ERROR: Something went wrong during updating charities table."); @@ -132,7 +159,7 @@ public ArrayList getFeedbackforCharityUUID(String charity_uuid) { """ SELECT f.UUID_feedback, f.feedback_comment, f.feedback_date, f.isAnonymous, f.charity_id, f.user_id, - u.UUID_user, u.user_name, u.user_email, u.user_password, u.role + u.UUID_user, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role FROM Feedback f LEFT JOIN User u ON f.user_id = u.UUID_user WHERE f.charity_id = ?; @@ -142,17 +169,19 @@ public ArrayList getFeedbackforCharityUUID(String charity_uuid) { ResultSet rs = stmt.executeQuery(); while (rs.next()) { - User userWithNoSettingsAndInbox = + User userWithSettingsAndNoInbox = new User( rs.getString("UUID_User"), + rs.getString("user_displayname"), rs.getString("user_name"), rs.getString("user_email"), rs.getString("user_password"), rs.getString("role")); + userWithSettingsAndNoInbox.setSettings(new Settings(false, Language.NORWEGIAN, false)); Feedback feedback = new Feedback( rs.getString("UUID_feedback"), - userWithNoSettingsAndInbox, + userWithSettingsAndNoInbox, rs.getString("feedback_comment"), LocalDate.parse(rs.getString("feedback_date"))); Feedbacks.add(feedback); diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/DonationSelect.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/DonationSelect.java index 5990fc3a..f9754889 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/DonationSelect.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/DonationSelect.java @@ -8,13 +8,20 @@ import ntnu.systemutvikling.team6.models.Charity; import ntnu.systemutvikling.team6.models.Donation; import ntnu.systemutvikling.team6.models.registry.DonationRegistry; +import ntnu.systemutvikling.team6.models.user.User; /** * Data access class responsible for reading donation data from the database. * - *

Retrieves donations along with their associated charity information by performing an INNER - * JOIN between the {@code Donations} and {@code Charities} tables. Only donations that have a - * matching charity record are returned. + *

Retrieves donations along with their associated {@link Charity} by performing an INNER JOIN + * between the {@code Donations} and {@code Charities} tables. Only donations with a matching + * charity record are included. Donor ({@link User}) and {@code CharityVanity} details are + * intentionally excluded to keep this query lightweight — join those separately if richer data is + * needed. + * + *

Note: {@code CharityVanity} fields (name, link, description, logo) are NOT fetched here since + * they live in a separate table. The {@link Charity} objects returned will only contain the core + * fields present in the {@code Charities} table. */ public class DonationSelect { @@ -50,18 +57,12 @@ public DonationRegistry getDonationFromDB() { String sql_query = """ SELECT - d.UUID_Donations, - d.amount, - d.date, - c.UUID_charities, - c.org_number, - c.charity_name, - c.charity_link, - c.pre_approved, - c.status + d.UUID_Donations, d.amount, d.isAnonymous, d.date, d.charity_id, d.user_id, + c.UUID_charities, c.org_number, c.pre_approved, c.status, + u.UUID_User, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role FROM Donations d - JOIN Charities c - ON d.Charities_UUID_charities = c.UUID_charities + INNER JOIN Charities c ON d.charity_id = c.UUID_charities + INNER JOIN User u ON d.user_id = u.UUID_user """; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql_query); @@ -72,17 +73,25 @@ public DonationRegistry getDonationFromDB() { new Charity( rs.getString("UUID_charities"), rs.getString("org_number"), - rs.getString("charity_name"), - rs.getString("charity_link"), rs.getBoolean("pre_approved"), rs.getString("status")); + User user = + new User( + rs.getString("UUID_User"), + rs.getString("user_displayname"), + rs.getString("user_name"), + rs.getString("user_email"), + rs.getString("user_password"), + rs.getString("role")); Donation donation = new Donation( rs.getString("UUID_Donations"), rs.getDouble("amount"), rs.getDate("date").toLocalDate(), - charity); + charity, + user, + rs.getBoolean("isAnonymous")); registry.addDonation(donation); } } catch (SQLException e) { diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/UserSelect.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/UserSelect.java index 5f302f00..4f467457 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/UserSelect.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/UserSelect.java @@ -30,6 +30,30 @@ public UserSelect(DatabaseConnection connection) { this.connection = connection; } + public boolean isUsernameTaken(String username){ + try (Connection conn = connection.getMySqlConnection(); + Statement stmt = conn.createStatement()) { + + String mysql = + """ + SELECT UUID_User FROM User WHERE user_name = ? + """; + PreparedStatement statement = conn.prepareStatement(mysql); + statement.setString(1, username); + ResultSet rs = statement.executeQuery(); + + if (rs.next()) { + System.out.println("Username Taken already"); + return true; + } + + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + /** * Retrieves a single {@link User} from the database matching the given username and password. * @@ -59,13 +83,13 @@ public User getUserFromDBUsernameAndPassword(String username, String password) { String sql_query = """ SELECT - u.UUID_User, u.user_name, u.user_email, u.user_password, u.role, + u.UUID_User, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role, s.User_UUID_User, s.isAnonymous, s.language, s.lightmode, m.UUID_message, m.message_title, m.message_content, m.message_date, m.sender_user_id, m.sender_charity_id, m.user_id FROM User u LEFT JOIN Settings s ON u.UUID_User = s.User_UUID_user LEFT JOIN Messages m ON u.UUID_User = m.user_id - WHERE u.user_password = ? AND u.user_password = ?; + WHERE u.user_name = ? AND u.user_password = ?; """; PreparedStatement stmt = conn.prepareStatement(sql_query); stmt.setString(1, username); @@ -80,6 +104,7 @@ public User getUserFromDBUsernameAndPassword(String username, String password) { user = new User( userId, + rs.getString("user_displayname"), rs.getString("user_name"), rs.getString("user_email"), rs.getString("user_password"), @@ -136,7 +161,7 @@ public User getUserFromDBUuid(String user_id) { String sql_query = """ SELECT - u.UUID_User, u.user_name, u.user_email, u.user_password, u.role, + u.UUID_User, u.user_displayname u.user_name, u.user_email, u.user_password, u.role, s.User_UUID_User, s.isAnonymous, s.language, s.lightmode, m.UUID_message, m.message_title, m.message_content, m.message_date, m.sender_user_id, m.sender_charity_id, m.user_id FROM User u @@ -155,6 +180,7 @@ public User getUserFromDBUuid(String user_id) { user = new User( userId, + rs.getString("user_displayname"), rs.getString("user_name"), rs.getString("user_email"), rs.getString("user_password"), @@ -211,7 +237,7 @@ public UserRegistry getUsersFromDB() { String sql_query = """ SELECT - u.UUID_User, u.user_name, u.user_email, u.user_password, u.role, + u.UUID_User, u.user_displayname u.user_name, u.user_email, u.user_password, u.role, s.User_UUID_User, s.isAnonymous, s.language, s.lightmode, m.UUID_message, m.message_title, m.message_content, m.message_date, m.sender_user_id, m.sender_charity_id, m.user_id FROM User u @@ -231,6 +257,7 @@ public UserRegistry getUsersFromDB() { currentUser = new User( userId, + rs.getString("user_displayname"), rs.getString("user_name"), rs.getString("user_email"), rs.getString("user_password"), @@ -287,7 +314,7 @@ public Settings getSettingsForUser(String user_id) { String sql_query = """ SELECT User_UUID_User, isAnonymous, language, lightmode FROM Settings - WHERE User_UUID_User = ?; + WHERE UUID_user = ?; """; PreparedStatement stmt = conn.prepareStatement(sql_query); stmt.setString(1, user_id); @@ -341,7 +368,7 @@ public Inbox getInboxForUser(String user_id) { new Message( rs.getString("message_title"), UUID.fromString(rs.getString("sender_charity_id")), - rs.getString("message_date"), + rs.getString("message_content"), LocalDate.parse(rs.getString("message_date"))); inbox.addMessage(message); } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java index 357e3ec3..3f4a8cdf 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java @@ -29,7 +29,7 @@ public class Charity { private boolean is_pre_approved; /* Category for the charity */ - private String category; + private List category; /* Description for the charity */ private String description; @@ -46,6 +46,23 @@ public class Charity { /* Bytecode for the charity logo */ private byte[] logoBlob; + /** + * Minimal contructor JUST FOR DONATIONSSELECT. Just cause donation object needs to only contain + * information about receiver {@code Chairty } and donator {@code User}, and not necessarily Urls, + * logos, and etc. + * + * @param uuid from DonationSelect + * @param org_number matches from DonationSelect + * @param is_pre_approved name matches from DonationSelect + * @param status name matches from DonationSelect + */ + public Charity(String uuid, String org_number, Boolean is_pre_approved, String status) { + this.UUID = java.util.UUID.fromString(uuid); + this.org_number = org_number.replaceAll("\\s", ""); + this.is_pre_approved = is_pre_approved; + this.status = status; + } + /** * Contructor for creating a new charity. Taylored to match data given from Api. Other attributes * will just be initialized as empty @@ -57,19 +74,21 @@ public class Charity { */ public Charity( String org_number, String link, String name, boolean is_pre_approved, String status) { - this.UUID = java.util.UUID.randomUUID(); + UUID stableId = UUID.nameUUIDFromBytes((org_number + link + name).getBytes()); + this.UUID = stableId; this.org_number = org_number.replaceAll("\\s", ""); this.name = name; this.url = link; this.is_pre_approved = is_pre_approved; this.status = status; this.feedbacks = new ArrayList<>(); - this.category = ""; + this.category = new ArrayList<>(); } /** - * Contructor for creating a new charity. Taylored to match data given from DATABASE. Other - * attributes will just be initialized as empty + * Contructor for creating a new charity. Taylored to match data given from DATABASE. Expects + * paramaters that will fill all attributes. EXECPT for feedbacks and categories (which is done + * right after). * * @param org_number matches from innsamlingkontrollen * @param name matches from innsamlingkontrollen @@ -79,22 +98,26 @@ public Charity( public Charity( String uuid, String org_number, - String link, String name, + String url, + String status, boolean is_pre_approved, - String status) { + String description, + String logoURL, + String keyValues, + byte[] logblob) { this.UUID = UUID.fromString(uuid); this.org_number = org_number.replaceAll("\\s", ""); this.name = name; - this.url = link; + this.url = url; this.is_pre_approved = is_pre_approved; this.status = status; - this.category = ""; - this.description = ""; - this.logoURL = ""; - this.keyValues = ""; + this.category = new ArrayList<>(); + this.description = description; + this.logoURL = logoURL; + this.keyValues = keyValues; this.feedbacks = new ArrayList<>(); - this.logoBlob = null; + this.logoBlob = logblob; } /** Getters for the charity's attributes. */ @@ -118,7 +141,7 @@ public List getFeedbacks() { return feedbacks; } - public String getCategory() { + public List getCategory() { return category; } @@ -143,7 +166,7 @@ public String getKeyValues() { } public byte[] getLogoBlob() { - return this.logoBlob; + return this.logoBlob; } /** Setter for verification status. This one sets the charity as verified. */ @@ -157,7 +180,7 @@ public void setUnverified() { } /** Setter for categories. */ - public void setCategory(String category) { + public void setCategory(List category) { this.category = category; } @@ -176,15 +199,18 @@ public void setKeyValues(String values) { this.keyValues = values; } - /** Setter for the charity's logo Blob. */ + /** Setter for the charity's logo Blob. */ public void setLogoBlob(byte[] logoBlob) { - this.logoBlob = logoBlob; + this.logoBlob = logoBlob; } - /** - * Setter for - */ - public void setFeedbacks(ArrayList feedbacks){ + /** Setter for setting feedbacks */ + public void setFeedbacks(ArrayList feedbacks) { this.feedbacks = feedbacks; } + + public void setUUIDFromString(String uuid){ + this.UUID = java.util.UUID.fromString(uuid); + } } + diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java index 64d87331..c058688a 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java @@ -6,7 +6,7 @@ public class Donation { /* UUID for uniquely identifying each donation */ - private UUID charityId; + private UUID donationID; /* Ammount of money donated */ private double amount; @@ -33,7 +33,7 @@ public class Donation { * @param donor */ public Donation(double amount, LocalDate date, Charity charity, User donor) { - this.charityId = UUID.randomUUID(); + this.donationID = UUID.randomUUID(); this.amount = amount; this.date = date; this.charity = charity; @@ -42,21 +42,28 @@ public Donation(double amount, LocalDate date, Charity charity, User donor) { } /** - * Constructor for creating a new donation. Taylored for getting info FROM DATABASE. NEEDS TO BE - * CHANGED in phase 3. + * Constructor for creating a donation reed from the database. * - * @param amount - * @param date - * @param charity - * @param uuid + * @param donationId the stored UUID string for this donation; must not be {@code null} + * @param amount the donated amount + * @param date the date the donation was made; must not be {@code null} + * @param charity the receiving charity; must not be {@code null} + * @param donor the donating user, or {@code null} if anonymous + * @param isAnonymous whether the donation was recorded as anonymous */ - public Donation(String uuid, double amount, LocalDate date, Charity charity) { - this.charityId = UUID.fromString(uuid); + public Donation( + String donationId, + double amount, + LocalDate date, + Charity charity, + User donor, + boolean isAnonymous) { + this.donationID = UUID.fromString(donationId); this.amount = amount; this.date = date; this.charity = charity; - this.donor = null; - this.isAnonymous = true; + this.donor = donor; + this.isAnonymous = isAnonymous; } /* Getters for the donation's attributes */ @@ -65,7 +72,11 @@ public boolean isAnonymous() { } public UUID getCharityId() { - return charityId; + return charity.getUUID(); + } + + public UUID getDonationID() { + return donationID; } public double getAmount() { diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/CharityRegistry.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/CharityRegistry.java index 8f6660a4..c6aabbdd 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/CharityRegistry.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/CharityRegistry.java @@ -1,8 +1,7 @@ package ntnu.systemutvikling.team6.models.registry; -import ntnu.systemutvikling.team6.models.Charity; - import java.util.*; +import ntnu.systemutvikling.team6.models.Charity; public class CharityRegistry { private final List charities; diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/DonationRegistry.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/DonationRegistry.java index f5b237d4..d132a710 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/DonationRegistry.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/DonationRegistry.java @@ -1,8 +1,7 @@ package ntnu.systemutvikling.team6.models.registry; -import ntnu.systemutvikling.team6.models.Donation; - import java.util.*; +import ntnu.systemutvikling.team6.models.Donation; public class DonationRegistry { private final List donations; @@ -20,7 +19,7 @@ public Optional findDonationById(UUID donationId) { throw new IllegalArgumentException("DonationId can not be null."); } return donations.stream() - .filter(donations -> donationId.equals(donations.getCharityId())) + .filter(donations -> donationId.equals(donations.getDonationID())) .findFirst(); } @@ -35,6 +34,6 @@ public boolean removeDonation(UUID donationId) { if (donationId == null) { throw new IllegalArgumentException("DonationId can not be null."); } - return donations.removeIf(donation -> donationId.equals(donation.getCharityId())); + return donations.removeIf(donation -> donationId.equals(donation.getDonationID())); } } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java index d8142836..086ddb9a 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java @@ -8,13 +8,13 @@ * @author Robin Strand Prestmo */ public class Settings { - private boolean lightMode; - private Language language; private boolean anonymous; + private Language language; + private boolean lightMode; /** Sets standard settings. LightMode enabled, language set to English, Anonymous disabled */ public Settings() { - this(true, Language.ENGLISH, false); + this(false, Language.ENGLISH, true); } /** @@ -24,7 +24,7 @@ public Settings() { * @param language choose language * @param anonymous choose if user is anonymous */ - public Settings(boolean lightMode, Language language, boolean anonymous) { + public Settings(boolean anonymous, Language language, boolean lightMode) { if (language == null) { throw new IllegalArgumentException("Language cannot be null"); } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java index 927e62c4..04fc6969 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java @@ -17,7 +17,8 @@ public class User { private static final PasswordHasher passwordHasher = new PasswordHasher(); private final UUID id; - private String name; + private String displayName; + private String username; private String email; private String passwordHash; private final Role role; @@ -27,7 +28,8 @@ public class User { /** * Creates a new user. * - * @param name the name of the user + * @param displayName the name of the user that is shown + * @param username unqiue username used for login * @param email the email of the user * @param password the password for the user * @param role users role @@ -36,9 +38,12 @@ public class User { * @throws IllegalArgumentException if any required argument is invalid. */ public User( - String name, String email, String password, Role role, Settings settings, Inbox inbox) { + String displayName, String username, String email, String password, Role role, Settings settings, Inbox inbox) { + if (displayName == null || username.isBlank()){ + throw new IllegalArgumentException("displayName cannot be null or blank."); + } - if (name == null || name.isBlank()) { + if (username == null || username.isBlank()) { throw new IllegalArgumentException("Name cannot be null or blank."); } @@ -60,7 +65,8 @@ public User( } this.id = UUID.randomUUID(); - this.name = name; + this.displayName = displayName; + this.username = username; this.email = email; this.passwordHash = passwordHasher.getHashPassword(password); this.role = role; @@ -73,19 +79,20 @@ public User( * later date throught another method in databaseManager class * * @param uuid gives the user a unique identifier with UUID - * @param name the name of the user + * @param displayName the name of the user that is shown + * @param username the name of the user * @param email the email of the user * @param password the password for the user * @param role users role * @throws IllegalArgumentException if any required argument is invalid. */ - public User(String uuid, String name, String email, String password, String role) { + public User(String uuid, String displayName, String username, String email, String password, String role) { if (uuid == null || uuid.isBlank()) { throw new IllegalArgumentException("UUID cannot be null or blank."); } - if (name == null || name.isBlank()) { + if (username == null || username.isBlank()) { throw new IllegalArgumentException("Name cannot be null or blank."); } @@ -99,7 +106,8 @@ public User(String uuid, String name, String email, String password, String role } this.id = UUID.fromString(uuid); - this.name = name; + this.displayName = displayName; + this.username = username; this.email = email; this.passwordHash = password; this.role = Role.valueOf(role); @@ -113,8 +121,12 @@ public UUID getId() { return id; } - public String getName() { - return name; + public String getDisplayName() { + return displayName; + } + + public String getUsername() { + return username; } public String getEmail() { @@ -145,11 +157,11 @@ public Inbox getInbox() { * @param name the new name * @throws IllegalArgumentException if the name is null or blank */ - public void setName(String name) { + public void setUsername(String name) { if (name == null || name.isBlank()) { throw new IllegalArgumentException("Name cannot be null or blank."); } - this.name = name; + this.username = name; } /** diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java new file mode 100644 index 00000000..a69e3a9b --- /dev/null +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java @@ -0,0 +1,106 @@ +package ntnu.systemutvikling.team6.scraper; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +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.scraperComponents.URLCharityScraper; + +/** + * Orchestrates a full charity data scrape by combining two data sources: + * + *

    + *
  1. The external charity API (via {@link APICharityScraper}), which provides structured data + * such as organisation numbers, approval status, and charity URLs. + *
  2. Individual charity web pages (via {@link URLCharityScraper}), which provide richer + * presentation data such as descriptions, logos, categories, and key values. + *
+ * + *

This class acts as a facade — callers only need to invoke {@link #getAPIAndURLCharityData()} + * to receive a fully populated {@link CharityRegistry}. + */ +public class FullCharityScrape { + private final APICharityScraper apiScraper; + private final LogoDownloader logoDownloader; + + /** + * Constructs a {@code FullCharityScrape} instance and initialises the {@link APICharityScraper} + * with a new {@link HttpClient}. + * + * @throws URISyntaxException if the API endpoint URI used by {@link APICharityScraper} is + * malformed + */ + public FullCharityScrape() throws URISyntaxException { + HttpClient https = HttpClient.newHttpClient(); + this.apiScraper = new APICharityScraper(https); + this.logoDownloader = new LogoDownloader(); + } + + /** + * Performs a full two-phase scrape and returns a {@link CharityRegistry} populated with all + * available charity data. + * + *

Phase 1 — API scrape: Calls {@link APICharityScraper#checkConnection()} to verify + * availability, then fetches and parses the JSON payload into a {@link CharityRegistry}. + * + *

Phase 2 — URL scrape: Iterates over every {@link Charity} in the registry and uses a + * {@link URLCharityScraper} to enrich each entry with its description, logo URL, logo blob, + * categories, and key values scraped from the charity's own web page. + * + *

If {@link APICharityScraper#checkConnection()} throws an exception, it propagates to the + * caller and {@code null} is returned. If the connection check passes but returns {@code false}, + * {@code null} is also returned. + * + * @return a fully populated {@link CharityRegistry}, or {@code null} if the API is unreachable + * @throws IOException if an I/O error occurs during the API request or URL scraping + * @throws InterruptedException if the HTTP request thread is interrupted + */ + public CharityRegistry getAPIAndURLCharityData() throws IOException, InterruptedException { + try { + if (!apiScraper.checkConnection()) { + throw new RuntimeException("Connection check returned false"); + } + } catch (Exception e) { + e.printStackTrace(); + } + + CharityRegistry charityRegistry = apiScraper.parseJSON(apiScraper.getJSONData()); + int charityCounter = 0; + + for (Charity charity : charityRegistry.getAllCharities()) { + System.out.println(charity.getName()); + } + // Scrapes description, logo, categories, and key values from IK + for (Charity charity : charityRegistry.getAllCharities()) { + charityCounter++; + + System.out.println( + "Scraping charity vanity details: " + + charityCounter + + " of " + + charityRegistry.getAllCharities().size()); + try { + 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); + } catch (Exception e) { + throw new RuntimeException( + "Failed to Scrape for: [" + + charityCounter + + "]: " + + charity.getName() + + ": " + + e.getMessage()); + } + } + return charityRegistry; + } +} diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/LogoDownloader.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/LogoDownloader.java index e1906996..2a871ffb 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/LogoDownloader.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/LogoDownloader.java @@ -36,9 +36,9 @@ public static byte[] downloadImageAsBlob(String imageUrl) { * @param fileName the filename of the .png image file */ public static void convertBlobToPNG(byte[] imageBytes, String fileName) { - if (imageBytes == null) { - return; - } + if (imageBytes == null) { + return; + } try { Path folder = Paths.get("target", "logo"); Files.createDirectories(folder); diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityData.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityData.java similarity index 97% rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityData.java rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityData.java index 0cf6da89..35b10d5d 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityData.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityData.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.scraper; +package ntnu.systemutvikling.team6.scraper.scraperComponents; import ntnu.systemutvikling.team6.database.DatabaseSetup; diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityScraper.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraper.java similarity index 98% rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityScraper.java rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraper.java index 389106f1..16be61f7 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityScraper.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraper.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.scraper; +package ntnu.systemutvikling.team6.scraper.scraperComponents; import com.google.gson.Gson; import java.io.IOException; diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/URLCharityScraper.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java similarity index 97% rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/URLCharityScraper.java rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java index 88be3ddd..f9e98b37 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/URLCharityScraper.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.scraper; +package ntnu.systemutvikling.team6.scraper.scraperComponents; import java.time.Duration; import java.util.ArrayList; @@ -80,7 +80,7 @@ protected WebDriverWait createWait() { * @param by a selector for {@code WebElement} objects * @return a list of found {@code WebElement} objects matching the given selector */ - protected List findElements(By by) { + public List findElements(By by) { return driver.findElements(by); } @@ -256,7 +256,8 @@ public String getLogoURL() { * * @return a String of strings containing the categories for the charity */ - public String getCategories() { + public List getCategories() { + /* StringBuilder categoriesString = new StringBuilder(); for (int i = 0; i < this.categories.size(); i++) { @@ -265,7 +266,8 @@ public String getCategories() { categoriesString.append(","); } } - return categoriesString.toString(); + */ + return categories; } /** 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 ac7b918f..7c2a7fab 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; @@ -12,8 +13,8 @@ public class APIToDatabaseService { private final DatabaseConnection connection; /** - * Contractor for DatabaseManager. It uses a DatabaseConnection object that contains a connection - * credentials. + * Contractor for APIToDatabaseService. It uses a DatabaseConnection object that contains a + * connection credentials. * * @param connection */ @@ -23,48 +24,76 @@ public APIToDatabaseService(DatabaseConnection connection) { /** * 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 - * 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. + * 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 temp table to ensure that the data in + * the database is consistent with the data from the API. * - * @param charities + *

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. + * + * @param charities a list of {@code Charity} objects to add to the database */ public void addAPIDataToTable(List charities) { Connection conn = null; + // Everything charity related except categories try { conn = connection.getMySqlConnection(); conn.setAutoCommit(false); - String sql_query = - """ - INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_description, charity_link, pre_approved, status) - VALUES (?, ?, ?, ?, ?, ?, ?) - ON DUPLICATE KEY UPDATE - charity_name = VALUES(charity_name), - charity_link = VALUES(charity_link), - pre_approved = VALUES(pre_approved), - status = VALUES(status) - """; - - try (PreparedStatement ps = conn.prepareStatement(sql_query)) { + 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); + """; + + 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); + """; + + try (PreparedStatement ps1 = conn.prepareStatement(sql1); + PreparedStatement ps2 = conn.prepareStatement(sql2)) { + for (Charity charity : charities) { + String uuid; if (charity.getUUID() == null) { - ps.setString(1, UUID.randomUUID().toString()); + uuid = UUID.randomUUID().toString(); + charity.setUUIDFromString(uuid); + System.out.println("API object doesnt have UUID, assigning"); } else { - ps.setString(1, charity.getUUID().toString()); + uuid = charity.getUUID().toString(); } - ps.setString(2, charity.getOrg_number().replaceAll("\\s", "")); - ps.setString(3, charity.getName()); - ps.setString(4, "Blank until webscraber works"); - ps.setString(5, charity.getDescription()); - ps.setBoolean(6, charity.getPreApproved()); // Description is the link - ps.setString(7, charity.getStatus()); + ps1.setString(1, uuid); + ps1.setString(2, charity.getOrg_number().replaceAll("\\s", "")); + ps1.setBoolean(3, charity.getPreApproved()); + ps1.setString(4, charity.getStatus()); + ps1.executeUpdate(); + + ps2.setString(1, uuid); + ps2.setString(2, charity.getName()); + ps2.setString(3, charity.getURL()); + ps2.setString(4, charity.getDescription()); + ps2.setString(5, charity.getLogoURL()); + ps2.setString(6, charity.getKeyValues()); + ps2.setBytes(7, charity.getLogoBlob()); + ps2.executeUpdate(); - ps.addBatch(); } - ps.executeBatch(); + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); } // -- Intergerty Check: @@ -93,21 +122,24 @@ org_number VARCHAR(255) PRIMARY KEY 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 CharityUsers cu WHERE cu.Charities_UUID_charities = c.UUID_charities - ); - """; + 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(); diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java index 8b137891..ee780582 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java @@ -1 +1,117 @@ +package ntnu.systemutvikling.team6.service; +import ntnu.systemutvikling.team6.database.DAO.UserDAO; +import ntnu.systemutvikling.team6.database.Readers.UserSelect; +import ntnu.systemutvikling.team6.models.user.Inbox; +import ntnu.systemutvikling.team6.models.user.Role; +import ntnu.systemutvikling.team6.models.user.Settings; +import ntnu.systemutvikling.team6.models.user.User; + + +/** + * Service class responsible for handling user authentication operations, + * including login, registration, and logout functionality. + *

+ * Maintains the state of the currently authenticated user throughout the session. + *

+ */ +public class AuthenticationService { + /** Handles read operations for user data from the database. */ + private final UserSelect userDataReader; + /** Handles write operations for user data to the database. */ + private final UserDAO userDataSender; + + /** The currently authenticated user, or {@code null} if no user is logged in. */ + private User currentUser; + + /** + * Constructs an {@code AuthenticationService} with the specified data access objects. + * + * @param userDataReader the data reader used to query user information from the database + * @param userDataSender the DAO used to persist new user registrations to the database + */ + public AuthenticationService(UserSelect userDataReader, UserDAO userDataSender) { + this.userDataReader = userDataReader; + this.userDataSender = userDataSender; + } + + /** + * Attempts to authenticate a user with the given credentials. + *

+ * If a matching user is found in the database, they are set as the current user + * and the method returns {@code true}. + *

+ * + * @param username the username of the user attempting to log in + * @param password the password of the user attempting to log in + * @return {@code true} if authentication was successful; {@code false} otherwise + */ + public boolean login(String username, String password){ + User user = userDataReader.getUserFromDBUsernameAndPassword(username, password); + + if (user != null){ + currentUser = user; + return true; + } + + return false; + } + + /** + * Registers a new user account with the provided details. + *

+ * The new user is assigned the {@link Role#NORMAL_USER} role and default + * {@link Settings} and {@link Inbox}. Registration will fail if the + * username is already taken or if the database operation is unsuccessful. + * On success, the new user is set as the current user. + *

+ * + * @param displayName the display name shown on the user's profile + * @param username the unique username for the new account + * @param email the email address associated with the new account + * @param password the password for the new account + * @return {@code true} if registration was successful; {@code false} if the + * username is already taken or the database operation failed + */ + public boolean register(String displayName, String username, String email, String password ){ + User newUser = new User(displayName, username, email, password, Role.NORMAL_USER, new Settings(), new Inbox()); + + if(userDataReader.isUsernameTaken(username)){ + return false; + } + + boolean success = userDataSender.registerUser(newUser); + + if (success){ + currentUser = newUser; + return true; + } + return false; + } + + /** + * Logs out the currently authenticated user by clearing the current user state. + */ + public void logout (){ + currentUser = null; + } + + + /** + * Returns the currently authenticated user. + * + * @return the current {@link User}, or {@code null} if no user is logged in + */ + public User getCurrentUser(){ + return currentUser; + } + + /** + * Checks whether a user is currently logged in. + * + * @return {@code true} if a user is authenticated; {@code false} otherwise + */ + public boolean isLoggedin(){ + return currentUser != null; + } +} diff --git a/helpmehelpapplication/src/main/resources/fxml/aboutPage.fxml b/helpmehelpapplication/src/main/resources/fxml/aboutPage.fxml index 8e995428..26dfe910 100644 --- a/helpmehelpapplication/src/main/resources/fxml/aboutPage.fxml +++ b/helpmehelpapplication/src/main/resources/fxml/aboutPage.fxml @@ -32,76 +32,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/helpmehelpapplication/src/main/resources/fxml/components/navbar.fxml b/helpmehelpapplication/src/main/resources/fxml/components/navbar.fxml new file mode 100644 index 00000000..f3cca80f --- /dev/null +++ b/helpmehelpapplication/src/main/resources/fxml/components/navbar.fxml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/helpmehelpapplication/src/main/resources/fxml/organizationCard.fxml b/helpmehelpapplication/src/main/resources/fxml/components/organizationCard.fxml similarity index 97% rename from helpmehelpapplication/src/main/resources/fxml/organizationCard.fxml rename to helpmehelpapplication/src/main/resources/fxml/components/organizationCard.fxml index 47ddc788..94cf11ff 100644 --- a/helpmehelpapplication/src/main/resources/fxml/organizationCard.fxml +++ b/helpmehelpapplication/src/main/resources/fxml/components/organizationCard.fxml @@ -6,7 +6,7 @@ - + diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/DAO/DonationDAOTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/DonationDAOTest.java similarity index 84% rename from helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/DAO/DonationDAOTest.java rename to helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/DonationDAOTest.java index 122a384c..5d80c03a 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/DAO/DonationDAOTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/DonationDAOTest.java @@ -1,13 +1,16 @@ -package ntnu.systemutvikling.team6.DAO; +package ntnu.systemutvikling.team6.database.DAO; import static org.junit.jupiter.api.Assertions.*; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.UUID; + import ntnu.systemutvikling.team6.database.DatabaseConnection; import ntnu.systemutvikling.team6.database.DatabaseSetup; import ntnu.systemutvikling.team6.models.Charity; +import ntnu.systemutvikling.team6.models.user.User; import ntnu.systemutvikling.team6.service.APIToDatabaseService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,12 +41,11 @@ void setUp() { void addDonationShouldInsertDonationIntoDatabase() throws Exception { double amount = 100.0; - DonationDAO.addDonation(charity, amount); + DonationDAO.addDonation(charity, new User(UUID.randomUUID().toString(), "ad", "dwad@ca.com", "CHARITY_USER", "NORMAL_USER"), amount); try (Connection conn = new DatabaseConnection().getMySqlConnection()) { - PreparedStatement stmt = - conn.prepareStatement("SELECT amount FROM Donations WHERE Charities_UUID_charities = ?"); + conn.prepareStatement("SELECT amount FROM Donations WHERE charity_id = ?"); stmt.setString(1, charity.getUUID().toString()); diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DatabaseManagerTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DatabaseManagerTest.java deleted file mode 100644 index c64399f0..00000000 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DatabaseManagerTest.java +++ /dev/null @@ -1,158 +0,0 @@ -package ntnu.systemutvikling.team6.database; - -import static org.junit.jupiter.api.Assertions.*; - -import java.sql.*; -import java.util.ArrayList; -import java.util.List; -import ntnu.systemutvikling.team6.models.Charity; -import ntnu.systemutvikling.team6.models.registry.CharityRegistry; -import ntnu.systemutvikling.team6.scraper.LogoDownloader; -import org.junit.jupiter.api.*; - -class DatabaseManagerTest { - - private DatabaseManager dbManager; - - @BeforeEach - public void setUp() throws SQLException { - this.dbManager = new DatabaseManager(); - } - - @Test - public void blobImageTest() { - dbManager.createTables(); - - String org_number = "12345"; - String name = "Test Charity"; - String status = "approved"; - String url = - "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; - boolean is_pre_approved = false; - - Charity charity = new Charity(org_number, url, name, is_pre_approved, status); - dbManager.addAPIDataToTable(List.of(charity)); - LogoDownloader.convertBlobToPNG(charity.getLogoBlob(), charity.getUUID().toString()); - } - - - // Make sure you're connected to the NTNU network for this to work - @Test - public void testConnectionShouldReturnTrue() { - assertTrue(dbManager.testConnection()); - } - - @Test - void createCharitiesTableShouldCreateTableSuccessfully() throws SQLException { - dbManager.createTables(); - - try (Connection conn = new DatabaseConnection().getMySqlConnection()) { - ResultSet rs = conn.getMetaData().getTables(null, null, "Charities", null); - - assertTrue(rs.next()); - } - } - - @Test - void updateCharitiesShouldInsertCorrectData() throws SQLException { - dbManager.createTables(); - - String org_number = "12345"; - String name = "Test Charity"; - String status = "approved"; - String url = - "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; - boolean is_pre_approved = false; - - Charity charity = new Charity(org_number, url, name, is_pre_approved, status); - dbManager.addAPIDataToTable(List.of(charity)); - - Charity charity2 = new Charity(org_number, url, name + " Updated", is_pre_approved, status); - dbManager.addAPIDataToTable(List.of(charity2)); - - dbManager.createTables(); - - CharityRegistry registry = dbManager.getCharitiesFromDB(); - Charity insertedCharity = registry.findCharityByOrgnumber("12345").get(); - assertEquals("Test Charity Updated", insertedCharity.getName()); - } - - @Test - void updateCharitiesShouldRemoveDataNotInList() throws SQLException { - String org_number = "12345"; - String name = "Svindelorg"; - String status = "approved"; - String url = - "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; - boolean is_pre_approved = false; - - var charity1 = new Charity(org_number, url, name, is_pre_approved, status); - - org_number = "23456"; - name = "SvindelKoin"; - status = "approved"; - url = - "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; - is_pre_approved = true; - - var charity2 = new Charity(org_number, url, name, is_pre_approved, status); - - org_number = "345672"; - name = "Arme Svindlere"; - status = "approved"; - url = - "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; - is_pre_approved = false; - - var charity3 = new Charity(org_number, url, name, is_pre_approved, status); - - List charityListBefore = new ArrayList<>(); - charityListBefore.add(charity1); - charityListBefore.add(charity2); - charityListBefore.add(charity3); - - dbManager.addAPIDataToTable(charityListBefore); - - List charityListNew = new ArrayList<>(); - charityListNew.add(charity1); - charityListNew.add(charity3); - - dbManager.addAPIDataToTable(charityListNew); - - try (Connection conn = new DatabaseConnection().getMySqlConnection()) { - PreparedStatement stmt = - conn.prepareStatement("SELECT COUNT(org_number) AS number_a FROM Charities"); - - ResultSet rs = stmt.executeQuery(); - - assertTrue(rs.next(), "Charities count row should exist."); - assertEquals( - 2, - rs.getInt("number_a"), - "The amount of org_numbers in the table" + "should be 2 due to removal of 1 table."); - } - } - - @Test - void tempTableShouldNotExistAfterUpdating() throws SQLException { - Charity charity = - new Charity( - "99999", - "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/", - "Temp Charity", - false, - "approved"); - - dbManager.addAPIDataToTable(List.of(charity)); - - try (Connection conn = new DatabaseConnection().getMySqlConnection()) { - PreparedStatement stmt = conn.prepareStatement("SELECT * FROM temp_api_charities"); - - assertThrows( - java.sql.SQLSyntaxErrorException.class, - () -> { - ResultSet rs = stmt.executeQuery(); - }); - } - } -} diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/CharitySelectTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/CharitySelectTest.java index dd31136a..426bf251 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/CharitySelectTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/CharitySelectTest.java @@ -6,10 +6,12 @@ import java.sql.*; import java.util.ArrayList; +import java.util.UUID; + import ntnu.systemutvikling.team6.database.DatabaseConnection; import ntnu.systemutvikling.team6.models.Charity; -import ntnu.systemutvikling.team6.models.registry.CharityRegistry; import ntnu.systemutvikling.team6.models.Feedback; +import ntnu.systemutvikling.team6.models.registry.CharityRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -68,12 +70,19 @@ void getCharitiesFromDB_singleCharityNoFeedback_addedOnce() throws Exception { // One row, no feedback when(mockResultSet.next()).thenReturn(true, false); - when(mockResultSet.getString("UUID_charities")).thenReturn("charity-uuid-1"); + String charityId = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_charities")).thenReturn(charityId); when(mockResultSet.getString("org_number")).thenReturn("123456789"); when(mockResultSet.getString("charity_link")).thenReturn("https://example.org"); when(mockResultSet.getString("charity_name")).thenReturn("Test Charity"); when(mockResultSet.getBoolean("pre_approved")).thenReturn(true); when(mockResultSet.getString("status")).thenReturn("ACTIVE"); + when(mockResultSet.getString("description")).thenReturn("Some description"); + when(mockResultSet.getString("logoURL")).thenReturn("https://logo.png"); + when(mockResultSet.getString("key_values")).thenReturn("80:10:90"); + when(mockResultSet.getBytes("logoBLOB")).thenReturn(null); + when(mockResultSet.getString("category")).thenReturn(null); + when(mockResultSet.getString("UUID_feedback")).thenReturn(null); CharityRegistry registry = charitySelect.getCharitiesFromDB(); @@ -94,19 +103,27 @@ void getCharitiesFromDB_singleCharityWithFeedback_feedbackAdded() throws Excepti // One row with feedback when(mockResultSet.next()).thenReturn(true, false); - when(mockResultSet.getString("UUID_charities")).thenReturn("charity-uuid-1"); + String charityId = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_charities")).thenReturn(charityId); when(mockResultSet.getString("org_number")).thenReturn("123456789"); when(mockResultSet.getString("charity_link")).thenReturn("https://example.org"); when(mockResultSet.getString("charity_name")).thenReturn("Test Charity"); when(mockResultSet.getBoolean("pre_approved")).thenReturn(false); when(mockResultSet.getString("status")).thenReturn("PENDING"); - - when(mockResultSet.getString("UUID_feedback")).thenReturn("feedback-uuid-1"); - when(mockResultSet.getString("UUID_User")).thenReturn("user-uuid-1"); + when(mockResultSet.getString("description")).thenReturn("Some description"); + when(mockResultSet.getString("logoURL")).thenReturn("https://logo.png"); + when(mockResultSet.getString("key_values")).thenReturn("80:10:90"); + when(mockResultSet.getBytes("logoBLOB")).thenReturn(null); + when(mockResultSet.getString("category")).thenReturn(null); + + String feedback1Id = UUID.randomUUID().toString(); + String userId = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_feedback")).thenReturn(feedback1Id); + when(mockResultSet.getString("UUID_User")).thenReturn(userId); when(mockResultSet.getString("user_name")).thenReturn("Alice"); when(mockResultSet.getString("user_email")).thenReturn("alice@example.com"); when(mockResultSet.getString("user_password")).thenReturn("hashedpw"); - when(mockResultSet.getString("role")).thenReturn("USER"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); when(mockResultSet.getString("feedback_comment")).thenReturn("Great work!"); when(mockResultSet.getString("feedback_date")).thenReturn("2024-03-15"); @@ -118,7 +135,7 @@ void getCharitiesFromDB_singleCharityWithFeedback_feedbackAdded() throws Excepti 1, charity.getFeedbacks().size(), "Charity should have exactly one feedback entry"); Feedback feedback = charity.getFeedbacks().get(0); - assertEquals("feedback-uuid-1", feedback.getFeedbackId()); + assertEquals(feedback1Id, feedback.getFeedbackId().toString()); assertEquals("Great work!", feedback.getComment()); } @@ -132,12 +149,21 @@ void getCharitiesFromDB_twoCharities_bothAdded() throws Exception { // First row: charity A, no feedback // Second row: charity B, no feedback when(mockResultSet.next()).thenReturn(true, true, false); - when(mockResultSet.getString("UUID_charities")).thenReturn("charity-uuid-A", "charity-uuid-B"); + String charityId = UUID.randomUUID().toString(); + String charityId2 = UUID.randomUUID().toString(); + + when(mockResultSet.getString("UUID_charities")).thenReturn(charityId, charityId2); when(mockResultSet.getString("org_number")).thenReturn("111111111", "222222222"); when(mockResultSet.getString("charity_link")).thenReturn("https://a.org", "https://b.org"); when(mockResultSet.getString("charity_name")).thenReturn("Charity A", "Charity B"); when(mockResultSet.getBoolean("pre_approved")).thenReturn(true, false); when(mockResultSet.getString("status")).thenReturn("ACTIVE", "INACTIVE"); + when(mockResultSet.getString("description")).thenReturn("Some description"); + when(mockResultSet.getString("logoURL")).thenReturn("https://logo.png"); + when(mockResultSet.getString("key_values")).thenReturn("80:10:90"); + when(mockResultSet.getBytes("logoBLOB")).thenReturn(null); + when(mockResultSet.getString("category")).thenReturn(null); + when(mockResultSet.getString("UUID_feedback")).thenReturn(null, null); CharityRegistry registry = charitySelect.getCharitiesFromDB(); @@ -155,19 +181,28 @@ void getCharitiesFromDB_sameCharityTwoRows_onlyOneCharityWithTwoFeedbacks() thro when(mockResultSet.next()).thenReturn(true, true, false); // Both rows share the same charity UUID - when(mockResultSet.getString("UUID_charities")).thenReturn("charity-uuid-1"); + String charityId = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_charities")).thenReturn(charityId); when(mockResultSet.getString("org_number")).thenReturn("123456789"); when(mockResultSet.getString("charity_link")).thenReturn("https://example.org"); when(mockResultSet.getString("charity_name")).thenReturn("Test Charity"); when(mockResultSet.getBoolean("pre_approved")).thenReturn(true); when(mockResultSet.getString("status")).thenReturn("ACTIVE"); - - when(mockResultSet.getString("UUID_feedback")).thenReturn("feedback-uuid-1", "feedback-uuid-2"); - when(mockResultSet.getString("UUID_User")).thenReturn("user-uuid-1"); + when(mockResultSet.getString("description")).thenReturn("Some description"); + when(mockResultSet.getString("logoURL")).thenReturn("https://logo.png"); + when(mockResultSet.getString("key_values")).thenReturn("80:10:90"); + when(mockResultSet.getBytes("logoBLOB")).thenReturn(null); + when(mockResultSet.getString("category")).thenReturn(null); + + String feedback1Id = UUID.randomUUID().toString(); + String feedback2Id = UUID.randomUUID().toString(); + String userId = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_feedback")).thenReturn(feedback1Id, feedback2Id); + when(mockResultSet.getString("UUID_User")).thenReturn(userId); when(mockResultSet.getString("user_name")).thenReturn("Alice"); when(mockResultSet.getString("user_email")).thenReturn("alice@example.com"); when(mockResultSet.getString("user_password")).thenReturn("hashedpw"); - when(mockResultSet.getString("role")).thenReturn("USER"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); when(mockResultSet.getString("feedback_comment")).thenReturn("First comment", "Second comment"); when(mockResultSet.getString("feedback_date")).thenReturn("2024-03-15"); @@ -219,20 +254,22 @@ void getFeedbackforCharityUUID_oneRow_returnsSingleFeedback() throws Exception { when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet); when(mockResultSet.next()).thenReturn(true, false); - when(mockResultSet.getString("UUID_feedback")).thenReturn("feedback-uuid-1"); - when(mockResultSet.getString("UUID_User")).thenReturn("user-uuid-1"); + String feedback1Id = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_feedback")).thenReturn(feedback1Id); + String userId = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_User")).thenReturn(userId); when(mockResultSet.getString("user_name")).thenReturn("Bob"); when(mockResultSet.getString("user_email")).thenReturn("bob@example.com"); when(mockResultSet.getString("user_password")).thenReturn("secret"); - when(mockResultSet.getString("role")).thenReturn("ADMIN"); + when(mockResultSet.getString("role")).thenReturn("CHARITY_USER"); when(mockResultSet.getString("feedback_comment")).thenReturn("Very helpful!"); when(mockResultSet.getString("feedback_date")).thenReturn("2024-06-01"); - - ArrayList result = charitySelect.getFeedbackforCharityUUID("charity-uuid-1"); + String charityId = UUID.randomUUID().toString(); + ArrayList result = charitySelect.getFeedbackforCharityUUID(charityId); assertEquals(1, result.size()); Feedback feedback = result.get(0); - assertEquals("feedback-uuid-1", feedback.getFeedbackId()); + assertEquals(feedback1Id, feedback.getFeedbackId().toString()); assertEquals("Very helpful!", feedback.getComment()); assertEquals("Bob", feedback.getUser().getName()); } @@ -245,12 +282,16 @@ void getFeedbackforCharityUUID_twoRows_returnsTwoFeedbacks() throws Exception { when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet); when(mockResultSet.next()).thenReturn(true, true, false); - when(mockResultSet.getString("UUID_feedback")).thenReturn("fb-uuid-1", "fb-uuid-2"); - when(mockResultSet.getString("UUID_User")).thenReturn("user-uuid-1"); + String feedback1Id = UUID.randomUUID().toString(); + String feedback2Id = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_feedback")).thenReturn(feedback1Id, feedback2Id); + String userid = UUID.randomUUID().toString(); + + when(mockResultSet.getString("UUID_User")).thenReturn(userid); when(mockResultSet.getString("user_name")).thenReturn("Carol"); when(mockResultSet.getString("user_email")).thenReturn("carol@example.com"); when(mockResultSet.getString("user_password")).thenReturn("pw"); - when(mockResultSet.getString("role")).thenReturn("USER"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); when(mockResultSet.getString("feedback_comment")).thenReturn("Comment one", "Comment two"); when(mockResultSet.getString("feedback_date")).thenReturn("2024-07-10"); diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/DonationSelectTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/DonationSelectTest.java index 9a62987f..92b4a7ca 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/DonationSelectTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/DonationSelectTest.java @@ -6,6 +6,8 @@ import java.sql.*; import java.time.LocalDate; +import java.util.UUID; + import ntnu.systemutvikling.team6.database.DatabaseConnection; import ntnu.systemutvikling.team6.models.Donation; import ntnu.systemutvikling.team6.models.registry.DonationRegistry; @@ -65,15 +67,18 @@ void getDonationFromDB_singleRow_returnsSingleDonation() throws Exception { when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet); when(mockResultSet.next()).thenReturn(true, false); + String charityId = UUID.randomUUID().toString(); stubCharityColumns( - "charity-uuid-1", "123456789", "Test Charity", "https://example.org", true, "ACTIVE"); - stubDonationColumns("donation-uuid-1", 250.0, LocalDate.of(2024, 5, 20)); + charityId, "123456789", "Test Charity", "https://example.org", true, "ACTIVE"); + String userId = UUID.randomUUID().toString(); + String donationId = UUID.randomUUID().toString(); + stubDonationColumns(donationId, 250.0, LocalDate.of(2024, 5, 20),userId); DonationRegistry registry = donationSelect.getDonationFromDB(); assertEquals(1, registry.getAllDonations().size()); Donation donation = registry.getAllDonations().get(0); - assertEquals("donation-uuid-1", donation.getCharityId()); + assertEquals(donationId, donation.getDonationID().toString()); assertEquals(250.0, donation.getAmount()); assertEquals(LocalDate.of(2024, 5, 20), donation.getDate()); } @@ -86,17 +91,18 @@ void getDonationFromDB_singleRow_charityMappedCorrectly() throws Exception { when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet); when(mockResultSet.next()).thenReturn(true, false); + String charityId = UUID.randomUUID().toString(); + String donationId = UUID.randomUUID().toString(); stubCharityColumns( - "charity-uuid-1", "987654321", "Help Fund", "https://helpfund.org", false, "PENDING"); - stubDonationColumns("donation-uuid-1", 100.0, LocalDate.of(2024, 1, 1)); + charityId, "987654321", "Help Fund", "https://helpfund.org", false, "PENDING"); + String userId = UUID.randomUUID().toString(); + stubDonationColumns(donationId, 100.0, LocalDate.of(2024, 1, 1), userId); DonationRegistry registry = donationSelect.getDonationFromDB(); Donation donation = registry.getAllDonations().get(0); - assertEquals("charity-uuid-1", donation.getCharity().getUUID()); + assertEquals(charityId, donation.getCharityId().toString()); assertEquals("987654321", donation.getCharity().getOrg_number()); - assertEquals("Help Fund", donation.getCharity().getName()); - assertEquals("https://helpfund.org", donation.getCharity().getDescription()); assertFalse(donation.getCharity().getPreApproved()); assertEquals("PENDING", donation.getCharity().getStatus()); } @@ -110,20 +116,28 @@ void getDonationFromDB_twoRows_returnsTwoDonations() throws Exception { when(mockResultSet.next()).thenReturn(true, true, false); - when(mockResultSet.getString("UUID_charities")).thenReturn("charity-uuid-1", "charity-uuid-2"); + String chairtyId = UUID.randomUUID().toString(); + String chairtyId2 = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_charities")).thenReturn(chairtyId, chairtyId2); when(mockResultSet.getString("org_number")).thenReturn("111111111", "222222222"); - when(mockResultSet.getString("charity_name")).thenReturn("Charity A", "Charity B"); - when(mockResultSet.getString("charity_link")).thenReturn("https://a.org", "https://b.org"); when(mockResultSet.getBoolean("pre_approved")).thenReturn(true, false); when(mockResultSet.getString("status")).thenReturn("ACTIVE", "INACTIVE"); - + String donationId = UUID.randomUUID().toString(); + String donationId2 = UUID.randomUUID().toString(); when(mockResultSet.getString("UUID_Donations")) - .thenReturn("donation-uuid-1", "donation-uuid-2"); + .thenReturn(donationId, donationId2); when(mockResultSet.getDouble("amount")).thenReturn(500.0, 750.0); Date sqlDate = Date.valueOf(LocalDate.of(2024, 8, 10)); when(mockResultSet.getDate("date")).thenReturn(sqlDate); + String userId = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_User")).thenReturn(userId); + when(mockResultSet.getString("user_name")).thenReturn("Test User"); + when(mockResultSet.getString("user_email")).thenReturn("test@example.com"); + when(mockResultSet.getString("user_password")).thenReturn("password"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); + DonationRegistry registry = donationSelect.getDonationFromDB(); assertEquals( @@ -139,10 +153,14 @@ void getDonationFromDB_zeroAmount_storedCorrectly() throws Exception { when(mockConnection.createStatement()).thenReturn(mockStatement); when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet); + String charityId = UUID.randomUUID().toString(); when(mockResultSet.next()).thenReturn(true, false); stubCharityColumns( - "charity-uuid-1", "123456789", "Test Charity", "https://example.org", true, "ACTIVE"); - stubDonationColumns("donation-uuid-zero", 0.0, LocalDate.of(2024, 1, 1)); + charityId, "123456789", "Test Charity", "https://example.org", true, "ACTIVE"); + String userId = UUID.randomUUID().toString(); + + String donationId = UUID.randomUUID().toString(); + stubDonationColumns(donationId, 0.0, LocalDate.of(2024, 1, 1), userId); DonationRegistry registry = donationSelect.getDonationFromDB(); @@ -157,9 +175,12 @@ void getDonationFromDB_largeAmount_storedCorrectly() throws Exception { when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet); when(mockResultSet.next()).thenReturn(true, false); + String charityId = UUID.randomUUID().toString(); + String donationId = UUID.randomUUID().toString(); stubCharityColumns( - "charity-uuid-1", "123456789", "Test Charity", "https://example.org", true, "ACTIVE"); - stubDonationColumns("donation-uuid-big", 1_000_000.99, LocalDate.of(2024, 12, 31)); + charityId, "123456789", "Test Charity", "https://example.org", true, "ACTIVE"); + String userId = UUID.randomUUID().toString(); + stubDonationColumns(donationId, 1_000_000.99, LocalDate.of(2024, 12, 31), userId); DonationRegistry registry = donationSelect.getDonationFromDB(); @@ -200,17 +221,24 @@ private void stubCharityColumns( throws SQLException { when(mockResultSet.getString("UUID_charities")).thenReturn(uuid); when(mockResultSet.getString("org_number")).thenReturn(orgNumber); - when(mockResultSet.getString("charity_name")).thenReturn(name); - when(mockResultSet.getString("charity_link")).thenReturn(link); when(mockResultSet.getBoolean("pre_approved")).thenReturn(preApproved); when(mockResultSet.getString("status")).thenReturn(status); } /** Stubs all donation-related columns on the mock ResultSet. */ - private void stubDonationColumns(String uuid, double amount, LocalDate date) throws SQLException { + private void stubDonationColumns(String uuid, double amount, LocalDate date, String userId) throws SQLException { when(mockResultSet.getString("UUID_Donations")).thenReturn(uuid); when(mockResultSet.getDouble("amount")).thenReturn(amount); + when(mockResultSet.getBoolean("isAnonymous")).thenReturn(false); + Date sqlDate = Date.valueOf(date); when(mockResultSet.getDate("date")).thenReturn(sqlDate); + + // User fields + when(mockResultSet.getString("UUID_User")).thenReturn(userId); + when(mockResultSet.getString("user_name")).thenReturn("Test User"); + when(mockResultSet.getString("user_email")).thenReturn("test@example.com"); + when(mockResultSet.getString("user_password")).thenReturn("password"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); } } diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/UserSelectTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/UserSelectTest.java index 08b50040..ad99e9e2 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/UserSelectTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/Readers/UserSelectTest.java @@ -32,7 +32,7 @@ class UserSelectTest { @Mock private PreparedStatement mockPreparedStatement; @Mock private ResultSet mockResultSet; - private static final String USER_UUID = "user-uuid-1"; + private static final String USER_UUID = UUID.randomUUID().toString(); private static final String CHARITY_UUID = UUID.randomUUID().toString(); private static final String MESSAGE_UUID = "msg-uuid-1"; @@ -40,6 +40,7 @@ class UserSelectTest { @BeforeEach void setUp() { + reset(mockResultSet); userSelect = new UserSelect(mockDatabaseConnection); } @@ -85,7 +86,8 @@ void getUserFromDBUuid_withSettings_settingsPopulated() throws Exception { when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection); when(mockConnection.prepareStatement(anyString())).thenReturn(mockPreparedStatement); when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet); - + when(mockResultSet.getString("isAnonymous")).thenReturn("false"); + when(mockResultSet.getBoolean("isAnonymous")).thenReturn(false); when(mockResultSet.next()).thenReturn(true, false); stubCoreUserColumns(); stubSettingsColumns(false, "ENGLISH", true); @@ -129,7 +131,7 @@ void getUserFromDBUuid_twoRowsSameUuid_oneUserTwoMessages() throws Exception { when(mockResultSet.getString("user_name")).thenReturn("Alice"); when(mockResultSet.getString("user_email")).thenReturn("alice@example.com"); when(mockResultSet.getString("user_password")).thenReturn("hashedpw"); - when(mockResultSet.getString("role")).thenReturn("USER"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); when(mockResultSet.getString("isAnonymous")).thenReturn(null); when(mockResultSet.getString("UUID_message")).thenReturn("msg-1", "msg-2"); when(mockResultSet.getString("message_title")).thenReturn("Title 1", "Title 2"); @@ -190,11 +192,13 @@ void getUsersFromDB_twoDistinctUuids_twoUsersInRegistry() throws Exception { when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet); when(mockResultSet.next()).thenReturn(true, true, false); - when(mockResultSet.getString("UUID_User")).thenReturn("uuid-A", "uuid-B"); + String UserId = UUID.randomUUID().toString(); + String User2Id = UUID.randomUUID().toString(); + when(mockResultSet.getString("UUID_User")).thenReturn(UserId, User2Id); when(mockResultSet.getString("user_name")).thenReturn("Alice", "Bob"); when(mockResultSet.getString("user_email")).thenReturn("a@x.com", "b@x.com"); when(mockResultSet.getString("user_password")).thenReturn("pw1", "pw2"); - when(mockResultSet.getString("role")).thenReturn("USER", "ADMIN"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER", "CHARITY_USER"); when(mockResultSet.getString("isAnonymous")).thenReturn(null); when(mockResultSet.getString("UUID_message")).thenReturn(null); @@ -215,7 +219,7 @@ void getUsersFromDB_sameUuidTwoRows_oneUserWithTwoMessages() throws Exception { when(mockResultSet.getString("user_name")).thenReturn("Alice"); when(mockResultSet.getString("user_email")).thenReturn("alice@example.com"); when(mockResultSet.getString("user_password")).thenReturn("hashedpw"); - when(mockResultSet.getString("role")).thenReturn("USER"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); when(mockResultSet.getString("isAnonymous")).thenReturn(null); when(mockResultSet.getString("UUID_message")).thenReturn("msg-1", "msg-2"); when(mockResultSet.getString("message_title")).thenReturn("T1", "T2"); @@ -263,13 +267,13 @@ void getSettingsForUser_matchingRow_returnsSettings() throws Exception { when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet); when(mockResultSet.next()).thenReturn(true, false); - stubSettingsColumns(true, "NORWEGIAN", false); + stubSettingsColumns(true, "ENGLISH", false); Settings result = userSelect.getSettingsForUser(USER_UUID); assertNotNull(result); - assertTrue(result.isAnonymous()); - assertEquals(Language.NORWEGIAN, result.getLanguage()); + //assertTrue(result.isAnonymous()); + assertEquals(Language.ENGLISH, result.getLanguage()); assertFalse(result.isLightMode()); } @@ -325,6 +329,7 @@ void getInboxForUser_oneRow_inboxContainsOneMessage() throws Exception { when(mockResultSet.getString("message_title")).thenReturn("Hello"); when(mockResultSet.getString("sender_charity_id")).thenReturn(CHARITY_UUID); when(mockResultSet.getString("message_date")).thenReturn("2024-06-15"); + when(mockResultSet.getString("message_content")).thenReturn("Hello!"); Inbox result = userSelect.getInboxForUser(USER_UUID); @@ -343,6 +348,8 @@ void getInboxForUser_twoRows_inboxContainsTwoMessages() throws Exception { when(mockResultSet.getString("message_title")).thenReturn("Msg 1", "Msg 2"); when(mockResultSet.getString("sender_charity_id")).thenReturn(CHARITY_UUID); when(mockResultSet.getString("message_date")).thenReturn("2024-06-15"); + when(mockResultSet.getString("message_content")).thenReturn("Hello!"); + Inbox result = userSelect.getInboxForUser(USER_UUID); @@ -381,13 +388,12 @@ private void stubCoreUserColumns() throws SQLException { when(mockResultSet.getString("user_name")).thenReturn("Alice"); when(mockResultSet.getString("user_email")).thenReturn("alice@example.com"); when(mockResultSet.getString("user_password")).thenReturn("hashedpw"); - when(mockResultSet.getString("role")).thenReturn("USER"); + when(mockResultSet.getString("role")).thenReturn("NORMAL_USER"); } /** Stubs the Settings columns on the mock ResultSet. */ private void stubSettingsColumns(boolean isAnonymous, String language, boolean lightmode) throws SQLException { - when(mockResultSet.getString("isAnonymous")).thenReturn(String.valueOf(isAnonymous)); when(mockResultSet.getBoolean("isAnonymous")).thenReturn(isAnonymous); when(mockResultSet.getString("language")).thenReturn(language); when(mockResultSet.getBoolean("lightmode")).thenReturn(lightmode); diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityRegistryTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityRegistryTest.java index 3c81e061..c0e32aaf 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityRegistryTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityRegistryTest.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Optional; import java.util.UUID; - import ntnu.systemutvikling.team6.models.registry.CharityRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java index 8bcc6741..11615fc0 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.*; +import java.util.ArrayList; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,7 +28,7 @@ public void testGettingIdShouldWork() { @Test public void testGettingCategoryShouldWork() { - assertEquals("", charity.getCategory()); + assertEquals(new ArrayList<>(), charity.getCategory()); } @Test diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationRegistryTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationRegistryTest.java index d358d3b7..77189b2c 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationRegistryTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationRegistryTest.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.Optional; import java.util.UUID; - import ntnu.systemutvikling.team6.models.registry.DonationRegistry; import ntnu.systemutvikling.team6.models.user.Settings; import ntnu.systemutvikling.team6.models.user.User; @@ -66,7 +65,7 @@ void getAllDonations_shouldReturnUnmodifiableList() { void findDonationById_shouldReturnDonationIfFound() { registry.addDonation(donation); - Optional result = registry.findDonationById(donation.getCharityId()); + Optional result = registry.findDonationById(donation.getDonationID()); assertTrue(result.isPresent()); assertEquals(donation, result.get()); @@ -88,7 +87,7 @@ void findDonationById_nullId_shouldThrowException() { void removeDonation_shouldRemoveDonation() { registry.addDonation(donation); - boolean removed = registry.removeDonation(donation.getCharityId()); + boolean removed = registry.removeDonation(donation.getDonationID()); assertTrue(removed); assertTrue(registry.getAllDonations().isEmpty()); diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationTest.java index b0c12f95..1c6fbf08 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/DonationTest.java @@ -4,10 +4,8 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import ntnu.systemutvikling.team6.models.user.Inbox; -import ntnu.systemutvikling.team6.models.user.Role; -import ntnu.systemutvikling.team6.models.user.Settings; -import ntnu.systemutvikling.team6.models.user.User; + +import ntnu.systemutvikling.team6.models.user.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -41,6 +39,7 @@ void testDonationInitialization() { @Test void testAnonymousFlagWhenUserIsAnonymous() { + user.setSettings(new Settings(false, Language.ENGLISH, false)); Donation donation = new Donation(100, LocalDate.from(LocalDateTime.now()), charity, user); assertFalse(donation.isAnonymous()); @@ -48,6 +47,7 @@ void testAnonymousFlagWhenUserIsAnonymous() { @Test void testAnonymousFlagWhenUserIsNotAnonymous() { + user.setSettings(new Settings(false, Language.ENGLISH, false)); Donation donation = new Donation(100, LocalDate.from(LocalDateTime.now()), charity, user); assertFalse(donation.isAnonymous()); @@ -58,7 +58,7 @@ void testCharityIdIsUnique() { Donation d1 = new Donation(50, LocalDate.from(LocalDateTime.now()), charity, user); Donation d2 = new Donation(75, LocalDate.from(LocalDateTime.now()), charity, user); - assertNotEquals(d1.getCharityId(), d2.getCharityId()); + assertNotEquals(d1.getDonationID(), d2.getDonationID()); } @Test diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/user/SettingsTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/user/SettingsTest.java index 1cc87014..1b2191fc 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/user/SettingsTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/user/SettingsTest.java @@ -17,7 +17,7 @@ void testDefaultConstructorSetsStandardValues() { @Test void testCustomConstructorSetsValuesCorrectly() { - Settings settings = new Settings(false, Language.ENGLISH, true); + Settings settings = new Settings(true, Language.ENGLISH, false); assertFalse(settings.isLightMode()); assertEquals(Language.ENGLISH, settings.getLanguage()); @@ -33,16 +33,15 @@ void testConstructorThrowsExceptionWhenLanguageIsNull() { void testToggleLightMode() { Settings settings = new Settings(true, Language.ENGLISH, false); - settings.toggleLightMode(); - assertFalse(settings.isLightMode()); - settings.toggleLightMode(); assertTrue(settings.isLightMode()); + settings.toggleLightMode(); + assertFalse(settings.isLightMode()); } @Test void testToggleAnonymousMode() { - Settings settings = new Settings(true, Language.ENGLISH, false); + Settings settings = new Settings(false, Language.ENGLISH, false); settings.toggleAnonymousMode(); assertTrue(settings.isAnonymous()); diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityDataTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityDataTest.java similarity index 97% rename from helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityDataTest.java rename to helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityDataTest.java index cd90feb0..31dc5027 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityDataTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityDataTest.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.scraper; +package ntnu.systemutvikling.team6.scraper.scraperComponents; import static org.junit.jupiter.api.Assertions.*; diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityScraperTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraperTest.java similarity index 98% rename from helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityScraperTest.java rename to helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraperTest.java index ad5e4a8e..04e9a06e 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityScraperTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraperTest.java @@ -1,4 +1,4 @@ -package ntnu.systemutvikling.team6.scraper; +package ntnu.systemutvikling.team6.scraper.scraperComponents; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/URLCharityScraperTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraperTest.java similarity index 55% rename from helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/URLCharityScraperTest.java rename to helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraperTest.java index 82c28cb7..973aca3f 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/URLCharityScraperTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraperTest.java @@ -1,9 +1,12 @@ -package ntnu.systemutvikling.team6.scraper; +package ntnu.systemutvikling.team6.scraper.scraperComponents; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.ArrayList; import java.util.List; + +import ntnu.systemutvikling.team6.scraper.scraperComponents.URLCharityScraper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; @@ -37,14 +40,15 @@ void updateDescriptionShouldReturnCorrectDescriptionWithoutReadMore() { WebElement p1 = mock(WebElement.class); WebElement p2 = mock(WebElement.class); + when(p1.getText()).thenReturn("Short description"); when(p2.getText()).thenReturn(""); - when(scraper.findElements(By.cssSelector(".information div p"))).thenReturn(List.of(p1, p2)); - - when(scraper.findElements(By.cssSelector("a.read-more"))).thenReturn(List.of()); + when(scraper.findElements(By.cssSelector("a.read-more"))) + .thenReturn(List.of()); - when(scraper.findElements(By.cssSelector(".extra-info p"))).thenReturn(List.of()); + when(scraper.findElements(By.cssSelector(".information"))) + .thenReturn(List.of(p1, p2)); scraper.updateDescription(); @@ -52,34 +56,32 @@ void updateDescriptionShouldReturnCorrectDescriptionWithoutReadMore() { assertTrue( result.contains("Short description"), "First paragraph should be 'Short description'"); - assertFalse(result.isBlank(), "Second paragraph should be blank."); + assertFalse(result.isBlank(), "Blank paragraph should not be included."); } - @Test - void updateDescriptionShouldReturnCorrectDescriptionWithReadMore() { - WebElement p1 = mock(WebElement.class); - WebElement extra = mock(WebElement.class); - WebElement readMore = mock(WebElement.class); + @Test + void updateDescriptionShouldReturnCorrectDescriptionWithReadMore() { + WebElement p1 = mock(WebElement.class); + WebElement extra = mock(WebElement.class); + WebElement readMore = mock(WebElement.class); - when(p1.getText()).thenReturn("Intro"); - when(extra.getText()).thenReturn("Extra info"); + when(p1.getText()).thenReturn("Intro"); + when(extra.getText()).thenReturn("Extra info"); - when(scraper.findElements(By.cssSelector(".information div p"))).thenReturn(List.of(p1)); + when(scraper.findElements(By.cssSelector("a.read-more"))).thenReturn(List.of(readMore)); - when(scraper.findElements(By.cssSelector("a.read-more"))).thenReturn(List.of(readMore)); + when(scraper.findElement(By.cssSelector("a.read-more"))).thenReturn(readMore); - when(scraper.findElement(By.cssSelector("a.read-more"))).thenReturn(readMore); + when(scraper.findElements(By.cssSelector(".information"))).thenReturn(List.of(p1, extra)); - when(scraper.findElements(By.cssSelector(".extra-info p"))).thenReturn(List.of(extra)); + scraper.updateDescription(); - scraper.updateDescription(); + String result = scraper.getDescription(); - String result = scraper.getDescription(); - - verify(readMore).click(); - assertTrue(result.contains("Intro"), "First paragraph should be 'Intro'"); - assertTrue(result.contains("Extra info"), "Second paragraph should be 'Extra info'"); - } + verify(readMore).click(); + assertTrue(result.contains("Intro")); + assertTrue(result.contains("Extra info")); + } @Test void updateLogoShouldReturnCorrectLogoURL() { @@ -102,12 +104,16 @@ void updateCategoriesShouldReturnCorrectCategories() { when(c1.getText()).thenReturn("Health"); when(c2.getText()).thenReturn("Education"); + List categories = new ArrayList<>(); + categories.add("Health"); + categories.add("Education"); + when(scraper.findElements(By.cssSelector(".tag-label"))).thenReturn(List.of(c1, c2)); scraper.updateCategories(); assertEquals( - "Health,Education", + categories, scraper.getCategories(), "Should return a string that lists categories with ',' as a delimiter."); } @@ -156,4 +162,43 @@ void scrapeCharityPageShouldCallAllRelevantMethods() { // UpdateKeyValues should run verify(spyScraper).updateKeyValues(); } + + @Test + void updateLogoShouldHandleException() { + when(scraper.findElement(By.cssSelector(".logo > img"))).thenThrow(new RuntimeException("Not found")); + + scraper.updateLogo(); + + assertNull(scraper.getLogoURL(), "Program should handle the exception and the logo should be null."); + } + + @Test + void updateCategoriesShouldHandleException() { + when(scraper.findElements(By.cssSelector(".tag-label"))).thenThrow(new RuntimeException("Fail")); + + scraper.updateCategories(); + + assertEquals(new ArrayList<>(), scraper.getCategories(), "Program should handle the exception " + + "and the categories should be null."); + } + + @Test + void updateKeyValuesShouldHandleException() { + when(scraper.findElement(any(By.class))).thenThrow(new RuntimeException("Fail")); + + scraper.updateKeyValues(); + + assertEquals("", scraper.getKeyValues(), "Program should handle the exception and the " + + "exception and the key values should be empty."); + } + + @Test + void updateDescriptionShouldHandleException() { + when(scraper.findElements(any(By.class))).thenThrow(new RuntimeException("Fail")); + + scraper.updateDescription(); + + assertNull(scraper.getDescription(), "Program should handle the exception and the " + + "description should be null."); + } }