diff --git a/README.md b/README.md index 8c4770018..fbb0d4a8b 100644 --- a/README.md +++ b/README.md @@ -1,181 +1,63 @@ -# Lab 5: Logout - -## Preamble - -In the current homework, you added code to the login use case to save the currently-logged-in -user by saving the user in the Data Access Layer. You also added a unit test for this. - -In this lab, you will complete a logout use case as a team. You will also begin to discuss your project -and the use cases that need to be implemented. - -We have created all the Clean Architecture classes necessary for the logout use case. - -By Friday, your team will submit: -- your completed lab code [for credit] -- a draft of your project blueprint proposal. [required, but not for credit] - -# Phase 2 [for credit] -_(recall, Phase 1 was your solo task of adding the storage of the currently-logged-in user)_ - -## Task 0: Fork this repo on GitHub -**To get started, one team member should fork this repo on GitHub and share it with the team. -All of you should then clone it.** - -* * * - -Suggested logistics: One of you should invite the others to collaborate on their fork of the -original repo on GitHub. You can do this in your repo on GitHub under `Settings -> Collaborators`. -This will allow you to push branches to a common repo and then use pull requests to contribute -your code and review. To prevent others from pushing directly to the main branch, -we recommend you set branch protection rules on GitHub. Below are how the settings might look if you -add branch protection rules: - -![image of branch protection rules for main with the -requirement of two approvers to merge in pull requests. -](images/branch_protection_rules.png) - -* * * - -Open the project in IntelliJ and make sure you can successfully run `app/Main.java`. -Note: you may need to set the Project SDK in the `Project Structure...` menu, and possibly -also manually link the Maven project, as you did in Phase 1. - -## Task 1: Understanding the Program - -You may notice that we have refactored the CA engine code _slightly_ since Phase 1, but the -way we build the engine is drastically different: we have switched from using Factories to -using the Builder design pattern, which we'll be discussing in lecture soon. - -Open up `app.Main` and read it as a team. -- What are the Views and what are the current Use Cases? -- Which Uses Cases are triggered from each View? -- Which version of the DAO is `app.Main` using? - -The major change since Phase 1 is that we have added the `app.AppBuilder` class which makes -it easier to understand how our CA engine is being constructed — it also makes `app.Main` nice and concise! -- Why do all those `addX` methods end in `return this;`? - -Run the program and make sure the signup and login Use Cases work. - -Currently, you'll notice that the "Log Out" button still doesn't actually log you out. It's time to fix -that button, which is part of the `LoggedInView`. -We have created all the classes for you, but some of the code is missing. -As a team, your task is to fill in the missing code so that the Logout Use Case is implemented. -**The next part of the readme describes how your team will do this.** - -* * * - -**Your team will know when you are done when:** - -- Clicking the "Log Out" button takes the user back to the Login View when you use the program. -- The provided `LogoutInteractorTest` test passes. - -The "Log Out" button is an instance variable in class `LoggedInVew`. Go find it. -Also look at the `interface_adapter.change_password.LoggedInViewModel`, which contains any -data showing on the `LoggedInVew`. +# CSC207 Group Project * * * -## Task 2: Dividing up the work +## Group 108 - Translator Program -There are `TODO` comments left in the files -Recall that you can use the TODO tool window to conveniently pull up a complete list. +* * * -Once the TODOs are all complete, the "Log Out" button _should_ work! - -As a team, split up the TODOs (see below) between the members of your team. - -There are TODOs in seven of the files. -Make sure each member has at least one TODO which they will be responsible for completing. -If your team prefers to work in pairs, that is fine too. Your individual branches -will not be graded for this — only the final, working version. - -The TODOs are summarized below (by file) to help your team decide how to split them up: +**Team Members:** +- Sean Yin (GitHub Username: PikachuSean) +- Haoxuan Qiu (GitHub Username: Kaguya-kyuu) +- Doran Wang (GitHub Username: Wang20030509) +- Charles Cai (GitHub Username: charles0504) * * * -- `Main.java` +**Domain:** Translation generation and management - - [ ] TODO: add the Logout Use Case to the app using the appBuilder +**Software Specification:** +The program allows users to translate between several languages. Users can create and sign in to accounts, allowing users to bookmark translations and view their translation history. Users can submit feedback on translations they think are inaccurate and can suggest a better translation. -* * * +**User Stories:** +1. Bob creates and signs in to an account for the translation app. He translates a sentence from one language to another and receives a translation from the app. (Team story) -- `LoggedInView.java` (tip: refer to the other views for similar code) +2. Bob is a student in a language class. His teacher has created a class account for all the students to use, and he wants to see the translations that his teacher displayed in class. He signs in to the class account, and he views the translation history. (Doran) - - [ ] TODO: save the logout controller in the instance variable. - - [ ] TODO: execute the logout use case through the Controller +3. Bob is a user of the translation app. He translates a sentence from one language to another. He finds the translation inaccurate and decides to submit a suggestion for a better translation. After some time, the suggestion is accepted by the developers of the app and now the app displays the new suggested translation. (Sean) -* * * - -- `LogoutController.java` (tip: refer to the other controllers for similar code) +4. Bob is studying a second language. Sometimes he wants to be able to save his translation result for future review. So he decides to bookmark it so he can see it later by accessing his bookmarked translations. (Ericka) - - [ ] TODO: Save the interactor in the instance variable. - - [ ] TODO: run the use case interactor for the logout use case +5. Bob is sick and tired of google translate so he decided to find a better translator. He found this amazing translator called (our app name). He finds out there is a create account/sign in function which is always ignored on translator apps. He decided to create an account and found out there are so many useful functions! He translated a long paragraph that he will be working on for a few days. Unfortunately, he forgot to bookmark it, but he looks through his translation history to find it and bookmarks it from there so he can come back to it within a few clicks. (Charles) -* * * +**Proposed Entities for the Domain:** -- `LogoutInputData.java` (should be done with the LogoutInteractor TODOs below) +Translator +- String inputLanguage +- String outputLanguage +- String inputText +- String translatedText - - [ ] TODO: save the current username in an instance variable and add a getter. +History +- User currentUser +- List translationHistory -- `LogoutInteractor.java` (tip: refer to `ChangePasswordInteractor.java` for similar code) +Bookmark +- User currentUser +- List bookmarkedTranslations - - [ ] TODO: save the DAO and Presenter in the instance variables. - - [ ] TODO: implement the logic of the Logout Use Case - -* * * - -- `LogoutOutputData.java` - - - [ ] TODO: save the parameters in the instance variables. - -* * * +User +- String username +- String password -- `LogoutPresenter.java` (tip: refer to `SignupPresenter.java` for similar code) +Feedback +- Translator wrongTranslation +- String inputFeedback - - [ ] TODO: assign to the three instance variables. - - [ ] TODO: have prepareSuccessView update the LoggedInState - - [ ] TODO: have prepareSuccessView update the LoginState - -* * * - -1. Make a branch named the first part of your UofT email address, everything before the `@`. -For example, if your email address is `paul.gries@mail.utoronto.ca`, then the branch name would -be `paul.gries`. - -Make sure you switch to the new branch. - -In the terminal, this would look like below, but replaced with your own information: -``` -git branch paul.gries -git switch paul.gries -``` - -2. Complete your assigned TODOs and make a pull request on GitHub. In your pull request, - briefly describe what your TODOs were and how you implemented them. If you aren't sure - about part of it, include this in your pull request so everyone knows what to look - for when reviewing — or you can of course discuss with your team before making your - pull request since you are physically working in the same space. - - **Important: don't push any changes to the `.idea` folder, as that - may cause issues for your other teammates, as some files contain - configurations specific to your individual IntelliJ projects.** - -3. Review each other's pull requests to ensure each TODO is correctly implemented and that - there are no Checkstyle issues in the files that were modified. - -4. Once all TODOs are completed, your team should debug as needed to ensure the - correctness of the code. Setting a breakpoint where the log-out use case - interactor starts its work will likely be a great place to start when debugging. - -And that's it; you now have a working Logout Use Case! Instructions for -how to submit your work on MarkUs will be posted later. - -Your team should spend the rest of the lab working on your project blueprint. - -* * * +**Proposed API to Use:** +https://www.deepl.com/en/docs-api -# Project Blueprint +**Scheduled Meeting Times + Mode of Communication:** -See Quercus for details about the project blueprint! By the end of the week, -the goal is for your team to have a fully drafted blueprint so that your team -will be ready to get started on your project after Reading Week. +Meeting time outside of lab: Meet on Friday afternoons +Mode of Communication: Discord group chat diff --git a/pom.xml b/pom.xml index e5c1e881f..6900649c0 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,11 @@ 5.8.1 test + + com.deepl.api + deepl-java + 1.6.0 + @@ -44,4 +49,4 @@ UTF-8 - \ No newline at end of file + diff --git a/src/main/java/Example.java b/src/main/java/Example.java new file mode 100644 index 000000000..36dce1d04 --- /dev/null +++ b/src/main/java/Example.java @@ -0,0 +1,23 @@ +import com.deepl.api.*; + +public class Example { + private Translator translator; + + public Example() throws Exception { + final String authKey = "59431b39-31eb-423d-9fcd-b86eb38b23e0:fx"; // Replace with your key + translator = new Translator(authKey); + final TextResult result = + translator.translateText("Hello, world!", null, "fr"); + System.out.println(result.getText()); // "Bonjour, le monde !" + } + + public static void main(String[] args) { + try { + final String authKey = "59431b39-31eb-423d-9fcd-b86eb38b23e0:fx"; // Replace with your key + final Translator translator2 = new Translator(authKey); + final TextResult result = translator2.translateText("How are you doing", null, "ja"); + System.out.println(result.getText()); + } + catch (InterruptedException e) {} + catch (DeepLException e) {} +}} diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index bef63ad7a..0d41ddf19 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -12,13 +12,13 @@ public class Main { */ public static void main(String[] args) { final AppBuilder appBuilder = new AppBuilder(); - // TODO: add the Logout Use Case to the app using the appBuilder final JFrame application = appBuilder .addLoginView() .addSignupView() .addLoggedInView() .addSignupUseCase() .addLoginUseCase() + .addLogoutUseCase() .addChangePasswordUseCase() .build(); diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java index 71f00862c..7f3787980 100644 --- a/src/main/java/data_access/InMemoryUserDataAccessObject.java +++ b/src/main/java/data_access/InMemoryUserDataAccessObject.java @@ -1,13 +1,16 @@ package data_access; import java.util.HashMap; +import java.util.List; import java.util.Map; +import entity.Translate; import entity.User; import use_case.change_password.ChangePasswordUserDataAccessInterface; import use_case.login.LoginUserDataAccessInterface; import use_case.logout.LogoutUserDataAccessInterface; import use_case.signup.SignupUserDataAccessInterface; +import use_case.translator.TranslatorUserDataAccessInterface; /** * In-memory implementation of the DAO for storing user data. This implementation does @@ -16,12 +19,17 @@ public class InMemoryUserDataAccessObject implements SignupUserDataAccessInterface, LoginUserDataAccessInterface, ChangePasswordUserDataAccessInterface, - LogoutUserDataAccessInterface { + LogoutUserDataAccessInterface, + TranslatorUserDataAccessInterface { private final Map users = new HashMap<>(); private String currentUsername; + private final Map translates = new HashMap<>(); + + private final Map> userTranslates = new HashMap<>(); + @Override public boolean existsByName(String identifier) { return users.containsKey(identifier); @@ -52,4 +60,54 @@ public void setCurrentUsername(String name) { public String getCurrentUsername() { return this.currentUsername; } + + @Override + public void saveTranslation(Translate translation) { + translates.put(translation.getInputText(), translation); + } + + @Override + public Translate getTranslation(String text) { + return translates.get(text); + } + + @Override + public void deleteTranslation(String text) { + translates.remove(text); + } + + @Override + public List getAllTranslationsByUser(User user) { + return userTranslates.get(user); + } + + @Override + public Translate getMostRecentTranslation(User user) { + return userTranslates.get(user).get(0); + } + + @Override + public boolean existsByText(String text) { + return users.containsKey(text); + } + + @Override + public void updateTranslation(User user, Translate updatedTranslation) { + translates.put(updatedTranslation.getInputText(), updatedTranslation); + + } + + public class InMemoryUserDataAccessObject implements ChangePasswordUserDataAccessInterface { + private User currentUser; + + @Override + public boolean updatePassword(String newPassword) { + if (currentUser != null) { + currentUser.setPassword(newPassword); + return true; + } + return false; + } + } + } diff --git a/src/main/java/entity/Translate.java b/src/main/java/entity/Translate.java new file mode 100644 index 000000000..9e6cd8d78 --- /dev/null +++ b/src/main/java/entity/Translate.java @@ -0,0 +1,52 @@ +package entity; + +import com.deepl.api.*; + +/** + * Translator. + */ +public class Translate { + + private final String inputLanguage; + private final String outputLanguage; + private final String inputText; + + public Translate(String inputLanguage, String outputLanguage, String inputText) { + this.inputLanguage = inputLanguage; + this.outputLanguage = outputLanguage; + this.inputText = inputText; + } + + public String getInputLanguage() { + return inputLanguage; + } + + public String getOutputLanguage() { + return outputLanguage; + } + + public String getInputText() { + return inputText; + } + + /** + * Translate the given text. + * @return The result of the translation. + */ + public static String translateText(String inputText, String inputLanguage, String outputLanguage) { + String output = null; + try { + final String authKey = "563c8dbf-539b-48c2-bc82-fae6a6622677:fx"; + final Translator translator = new Translator(authKey); + final TextResult result = translator.translateText(inputText, inputLanguage, outputLanguage); + output = result.getText(); + } + catch (DeepLException exception) { + System.out.println(exception.getMessage()); + } + catch (InterruptedException exception) { + System.out.println(exception.getMessage()); + } + return output; + } +} diff --git a/src/main/java/entity/TranslateFactory.java b/src/main/java/entity/TranslateFactory.java new file mode 100644 index 000000000..96e534110 --- /dev/null +++ b/src/main/java/entity/TranslateFactory.java @@ -0,0 +1,8 @@ +package entity; + +public class TranslateFactory { + + public Translate create(String inputLanguage, String outputLanguage, String inputText) { + return new Translate(inputLanguage, outputLanguage, inputText); + } +} diff --git a/src/main/java/interface_adapter/change_password/ChangePasswordController.java b/src/main/java/interface_adapter/change_password/ChangePasswordController.java index a7abd075f..196e3608c 100644 --- a/src/main/java/interface_adapter/change_password/ChangePasswordController.java +++ b/src/main/java/interface_adapter/change_password/ChangePasswordController.java @@ -24,3 +24,4 @@ public void execute(String password, String username) { userChangePasswordUseCaseInteractor.execute(changePasswordInputData); } } + diff --git a/src/main/java/interface_adapter/logout/LogoutController.java b/src/main/java/interface_adapter/logout/LogoutController.java index e184a3bba..7ce432838 100644 --- a/src/main/java/interface_adapter/logout/LogoutController.java +++ b/src/main/java/interface_adapter/logout/LogoutController.java @@ -1,6 +1,7 @@ package interface_adapter.logout; import use_case.logout.LogoutInputBoundary; +import use_case.logout.LogoutInputData; /** * The controller for the Logout Use Case. @@ -11,6 +12,7 @@ public class LogoutController { public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) { // TODO: Save the interactor in the instance variable. + this.logoutUseCaseInteractor = logoutUseCaseInteractor; } /** @@ -21,5 +23,11 @@ public void execute(String username) { // TODO: run the use case interactor for the logout use case // 1. instantiate the `LogoutInputData`, which should contain the username. // 2. tell the Interactor to execute. + + final LogoutInputData logoutInputData = new LogoutInputData( + username); + + logoutUseCaseInteractor.execute(logoutInputData); + } } diff --git a/src/main/java/interface_adapter/logout/LogoutPresenter.java b/src/main/java/interface_adapter/logout/LogoutPresenter.java index 78ef306a1..ecb6d8b81 100644 --- a/src/main/java/interface_adapter/logout/LogoutPresenter.java +++ b/src/main/java/interface_adapter/logout/LogoutPresenter.java @@ -1,7 +1,9 @@ package interface_adapter.logout; import interface_adapter.ViewManagerModel; +import interface_adapter.change_password.LoggedInState; import interface_adapter.change_password.LoggedInViewModel; +import interface_adapter.login.LoginState; import interface_adapter.login.LoginViewModel; import use_case.logout.LogoutOutputBoundary; import use_case.logout.LogoutOutputData; @@ -19,6 +21,9 @@ public LogoutPresenter(ViewManagerModel viewManagerModel, LoggedInViewModel loggedInViewModel, LoginViewModel loginViewModel) { // TODO: assign to the three instance variables. + this.viewManagerModel = viewManagerModel; + this.loggedInViewModel = loggedInViewModel; + this.loginViewModel = loginViewModel; } @Override @@ -28,7 +33,16 @@ public void prepareSuccessView(LogoutOutputData response) { // We also need to set the username in the LoggedInState to // the empty string. + final LoggedInState loggedInState = loggedInViewModel.getState(); + loggedInState.setUsername(""); + this.loggedInViewModel.setState(loggedInState); + this.loggedInViewModel.firePropertyChanged(); + final LoginState loginState = loginViewModel.getState(); + loginState.setUsername(""); + loginState.setPassword(""); + this.loginViewModel.setState(loginState); + this.loginViewModel.firePropertyChanged(); // TODO: have prepareSuccessView update the LoggedInState // 1. get the LoggedInState out of the appropriate View Model, // 2. set the username in the state to the empty string diff --git a/src/main/java/interface_adapter/translate/TranslateController.java b/src/main/java/interface_adapter/translate/TranslateController.java new file mode 100644 index 000000000..349bcd3dd --- /dev/null +++ b/src/main/java/interface_adapter/translate/TranslateController.java @@ -0,0 +1,30 @@ +package interface_adapter.translate; + +import use_case.translator.TranslatorOutputBoundary; +import use_case.translator.TranslatorOutputData; + +/** + * The Presenter for the Translation Use Case. + */ +public class TranslatePresenter implements TranslatorOutputBoundary { + + private final TranslateViewModel translateViewModel; + + public TranslatePresenter(TranslateViewModel translateViewModel) { + this.translateViewModel = translateViewModel; + } + + @Override + public void prepareSuccessView(TranslatorOutputData outputData) { + translateViewModel.setTranslatedText(outputData.getOutputText()); + translateViewModel.firePropertyChanged("translatedText"); + } + + @Override + public void prepareFailView(String error) { + // Retrieve the state and update the error + TranslateState translateState = translateViewModel.getState(); + translateState.setTranslateError(error); + translateViewModel.firePropertyChanged("translateError"); + } +} diff --git a/src/main/java/interface_adapter/translate/TranslatePresenter.java b/src/main/java/interface_adapter/translate/TranslatePresenter.java new file mode 100644 index 000000000..def31f0e1 --- /dev/null +++ b/src/main/java/interface_adapter/translate/TranslatePresenter.java @@ -0,0 +1,29 @@ +package interface_adapter.translate; + +import use_case.translator.TranslatorOutputBoundary; +import use_case.translator.TranslatorOutputData; + +/** + * The Presenter for the Translation Use Case. + */ +public class TranslatePresenter implements TranslatorOutputBoundary { + + private final TranslateViewModel translateViewModel; + + public TranslatePresenter(TranslateViewModel translateViewModel) { + this.translateViewModel = translateViewModel; + } + + @Override + public void prepareSuccessView(TranslatorOutputData outputData) { + translateViewModel.setTranslatedText(outputData.getTranslatedText()); + translateViewModel.firePropertyChanged("translatedText"); + } + + @Override + public void prepareFailView(String error) { + final TranslateState translateState = translateViewModel.getState(); + translateState.setTranslateError(error); + translateViewModel.firePropertyChanged("translateError"); + } +} diff --git a/src/main/java/interface_adapter/translate/TranslateState.java b/src/main/java/interface_adapter/translate/TranslateState.java new file mode 100644 index 000000000..c2460e3cc --- /dev/null +++ b/src/main/java/interface_adapter/translate/TranslateState.java @@ -0,0 +1,69 @@ +package interface_adapter.translate; + +import java.util.ArrayList; +import java.util.List; + +import entity.Translate; + +public class TranslateState { + private String inputText; + private String translatedText; + private String sourceLanguage; + private String targetLanguage; + private final List translationHistory; + + public TranslateState() { + this.translationHistory = new ArrayList<>(); + } + + public void setInputText(String inputText) { + this.inputText = inputText; + } + + public String getInputText() { + return inputText; + } + + public void setTranslatedText(String translatedText) { + this.translatedText = translatedText; + } + + public String getTranslatedText() { + return translatedText; + } + + public void setSourceLanguage(String sourceLanguage) { + this.sourceLanguage = sourceLanguage; + } + + public String getSourceLanguage() { + return sourceLanguage; + } + + public void setTargetLanguage(String targetLanguage) { + this.targetLanguage = targetLanguage; + } + + public String getTargetLanguage() { + return targetLanguage; + } + + public void addTranslationToHistory(String translation) { + translationHistory.add(translation); + } + + public List getTranslationHistory() { + return new ArrayList<>(translationHistory); + } + + public boolean performTranslation() { + if (inputText == null || sourceLanguage == null || targetLanguage == null) { + System.out.println("Translation parameters are missing."); + return false; + } + + this.translatedText = Translate.translateText(inputText, sourceLanguage, targetLanguage); + addTranslationToHistory(translatedText); + return true; + } +} diff --git a/src/main/java/interface_adapter/translate/TranslateViewModel.java b/src/main/java/interface_adapter/translate/TranslateViewModel.java new file mode 100644 index 000000000..9fbcad575 --- /dev/null +++ b/src/main/java/interface_adapter/translate/TranslateViewModel.java @@ -0,0 +1,71 @@ +package interface_adapter.translate; + +import interface_adapter.ViewModel; + +/** + * The ViewModel for Translator. + */ +public class TranslateViewModel extends ViewModel { + + public static final String TITLE_LABEL = "Translator"; + public static final String LOGOUT_LABEL = "Log out"; + + public static final String INPUT_LANGUAGE_LABEL = "Choose your input language"; + public static final String OUTPUT_LANGUAGE_LABEL = "Choose your output language"; + + public static final String INPUT_TEXT_LABEL = "Input your text here"; + public static final String OUTPUT_TEXT_LABEL = "Your translation will be here"; + + public static final String TRANSLATE_F_LABEL = "Translate"; + public static final String OTHER_TRANSLATION_F_LABEL = "Others"; + + public static final String HISTORY_LABEL = "History"; + + public static final String BOOKMARK_F_LABEL = "Bookmark current translation"; + public static final String BOOKMARKS_LABEL = "Bookmarks"; + + public static final String FEEDBACK_F_LABEL = "Submit a feedback"; + + public TranslateViewModel() { + super("translate"); + setState(new TranslateState()); + } + + /** + * Updates the input text in the state and fires a property change event. + * + * @param inputText the text to be translated + */ + public void updateInputText(String inputText) { + getState().setInputText(inputText); + firePropertyChanged("inputText"); + } + + /** + * Updates the translated text in the state and fires a property change event. + * @param translatedText translated text + */ + public void updateTranslatedText(String translatedText) { + getState().setTranslatedText(translatedText); + firePropertyChanged("translatedText"); + } + + /** + * Updates the error message in the state and fires a property change event. + * @param errorMessage error message + */ + public void updateError(String errorMessage) { + getState().setTranslateError(errorMessage); + firePropertyChanged("translateError"); + } + + /** + * Fires a property change for the specified property. + * @param propertyName fail or success + */ + public void firePropertyChanged(String propertyName) { + // Notify observers or UI components that the property has changed. + // Implementation depends on the ViewModel base class or observer pattern. + System.out.println("Property changed: " + propertyName); + } +} diff --git a/src/main/java/use_case/logout/LogoutInputData.java b/src/main/java/use_case/logout/LogoutInputData.java index 56a33b375..5d576e19e 100644 --- a/src/main/java/use_case/logout/LogoutInputData.java +++ b/src/main/java/use_case/logout/LogoutInputData.java @@ -4,9 +4,14 @@ * The Input Data for the Logout Use Case. */ public class LogoutInputData { + private String username; public LogoutInputData(String username) { // TODO: save the current username in an instance variable and add a getter. + this.username = username; } + public String getUsername() { + return this.username; + } } diff --git a/src/main/java/use_case/logout/LogoutInteractor.java b/src/main/java/use_case/logout/LogoutInteractor.java index 1ca93b44e..6a8bde97a 100644 --- a/src/main/java/use_case/logout/LogoutInteractor.java +++ b/src/main/java/use_case/logout/LogoutInteractor.java @@ -11,6 +11,8 @@ public LogoutInteractor(LogoutUserDataAccessInterface userDataAccessInterface, LogoutOutputBoundary logoutOutputBoundary) { // TODO: save the DAO and Presenter in the instance variables. // Which parameter is the DAO and which is the presenter? + this.userDataAccessObject = userDataAccessInterface; + this.logoutPresenter = logoutOutputBoundary; } @Override @@ -20,6 +22,10 @@ public void execute(LogoutInputData logoutInputData) { // * set the username to null in the DAO // * instantiate the `LogoutOutputData`, which needs to contain the username. // * tell the presenter to prepare a success view. + final String username = logoutInputData.getUsername(); + userDataAccessObject.setCurrentUsername(null); + final LogoutOutputData logoutOutputData = new LogoutOutputData(username, false); + logoutPresenter.prepareSuccessView(logoutOutputData); } } diff --git a/src/main/java/use_case/logout/LogoutOutputData.java b/src/main/java/use_case/logout/LogoutOutputData.java index 974279155..05f06f058 100644 --- a/src/main/java/use_case/logout/LogoutOutputData.java +++ b/src/main/java/use_case/logout/LogoutOutputData.java @@ -10,6 +10,8 @@ public class LogoutOutputData { public LogoutOutputData(String username, boolean useCaseFailed) { // TODO: save the parameters in the instance variables. + this.username = username; + this.useCaseFailed = useCaseFailed; } public String getUsername() { diff --git a/src/main/java/use_case/translator/TranslatorInputBoundary.java b/src/main/java/use_case/translator/TranslatorInputBoundary.java new file mode 100644 index 000000000..61e3dcef1 --- /dev/null +++ b/src/main/java/use_case/translator/TranslatorInputBoundary.java @@ -0,0 +1,13 @@ +package use_case.translator; + +/** + * Input Boundary for actions which are related to translate. + */ +public interface TranslatorInputBoundary { + + /** + * Executes the Translator use case. + * @param translatorInputData the input data. + */ + void execute(TranslatorInputData translatorInputData); +} diff --git a/src/main/java/use_case/translator/TranslatorInputData.java b/src/main/java/use_case/translator/TranslatorInputData.java new file mode 100644 index 000000000..7babc0ec6 --- /dev/null +++ b/src/main/java/use_case/translator/TranslatorInputData.java @@ -0,0 +1,32 @@ +package use_case.translator; + +/** + * The input data for the Translator Use case. + */ +public class TranslatorInputData { + private final String input; + private final String inputLanguage; + private final String outputLanguage; + +// public TranslatorInputData(String input) { +// this.input = input; +// } + + public TranslatorInputData(String input, String inputLanguage, String outputLanguage) { + this.input = input; + this.inputLanguage = inputLanguage; + this.outputLanguage = outputLanguage; + } + + public String getInput() { + return input; + } + + public String getInputLanguage() { + return inputLanguage; + } + + public String getOutputLanguage() { + return outputLanguage; + } +} diff --git a/src/main/java/use_case/translator/TranslatorInteractor.java b/src/main/java/use_case/translator/TranslatorInteractor.java new file mode 100644 index 000000000..a9b6fd820 --- /dev/null +++ b/src/main/java/use_case/translator/TranslatorInteractor.java @@ -0,0 +1,51 @@ +package use_case.translator; + +import entity.Translate; + +/** + * The Translator Interactor. + */ + +public class TranslatorInteractor implements TranslatorInputBoundary { + private final Translate translatorService; + private final TranslatorOutputBoundary translatorPresenter; + + public TranslatorInteractor(Translate translatorService, + TranslatorOutputBoundary translatorPresenter) { + this.translatorService = translatorService; + this.translatorPresenter = translatorPresenter; + } + + /** + * Executes the translation process with the given input data. + * + * @param inputData the input data containing text and language details + */ + + public void execute(TranslatorInputData inputData) { + final String inputText = inputData.getInput(); + final String inputLanguage = inputData.getInputLanguage(); + final String outputLanguage = inputData.getOutputLanguage(); + + // if (inputText == null || inputText.isBlank()) { + // translatorPresenter.prepareFailView("Input text cannot be empty."); + // return; + // + // if (inputLanguage.equals(outputLanguage)) { + // translatorPresenter.prepareFailView("Input and output languages must be different."); + // return; + // } + + try { + + final String translatedText = Translate.translateText(inputText, inputLanguage, outputLanguage); + + final TranslatorOutputData outputData = new TranslatorOutputData(translatedText); + translatorPresenter.prepareSuccessView(outputData); + } + catch (IllegalArgumentException illegalArgumentException) { + translatorPresenter.prepareFailView("An error occurred during translation: " + + illegalArgumentException.getMessage()); + } + } +} diff --git a/src/main/java/use_case/translator/TranslatorOutputBoundary.java b/src/main/java/use_case/translator/TranslatorOutputBoundary.java new file mode 100644 index 000000000..855bed32a --- /dev/null +++ b/src/main/java/use_case/translator/TranslatorOutputBoundary.java @@ -0,0 +1,19 @@ +package use_case.translator; + +/** + * The output boundary for the Translator Use Case. + */ +public interface TranslatorOutputBoundary { + + /** + * Prepares the success view for the Translator Use Case. + * @param outputData the output data + */ + void prepareSuccessView(TranslatorOutputData outputData); + + /** + * Prepares the failure view for the Translator Use Case. + * @param errorMessage the explanation of the failure + */ + void prepareFailView(String errorMessage); +} diff --git a/src/main/java/use_case/translator/TranslatorOutputData.java b/src/main/java/use_case/translator/TranslatorOutputData.java new file mode 100644 index 000000000..6ca83f68e --- /dev/null +++ b/src/main/java/use_case/translator/TranslatorOutputData.java @@ -0,0 +1,18 @@ +package use_case.translator; + +/** + * Output Data for the Translator Use Case. + */ +public class TranslatorOutputData { + + private final String outputText; + + public TranslatorOutputData(String outputText) { + this.outputText = outputText; + } + + public String getOutputText() { + return outputText; + } + +} diff --git a/src/main/java/use_case/translator/TranslatorUserDataAccessInterface.java b/src/main/java/use_case/translator/TranslatorUserDataAccessInterface.java new file mode 100644 index 000000000..ca5f317df --- /dev/null +++ b/src/main/java/use_case/translator/TranslatorUserDataAccessInterface.java @@ -0,0 +1,59 @@ +package use_case.translator; + +import java.util.List; + +import entity.Translate; +import entity.User; + +/** + * DAO for handling translation operations. + */ +public interface TranslatorUserDataAccessInterface { + + /** + * Saves a translation request. + * @param translation the translation request to save + */ + void saveTranslation(Translate translation); + + /** + * Retrieves a translation by its ID. + * @param text the unique identifier of the translation + * @return the translation request associated with the given ID + */ + Translate getTranslation(String text); + + /** + * Deletes a translation by its ID. + * @param text the unique identifier of the translation to delete + */ + void deleteTranslation(String text); + + /** + * Retrieves all translation requests made by a specific user. + * @param user the user + * @return a list of translations made by the user + */ + List getAllTranslationsByUser(User user); + + /** + * Retrieves the most recent translation request for a specific user. + * @param user the user + * @return the most recent translation request made by the user + */ + Translate getMostRecentTranslation(User user); + + /** + * Checks if a translation exists by its text. + * @param text the unique input text of the translation + * @return true if the translation exists; false otherwise + */ + boolean existsByText(String text); + + /** + * Updates an existing translation request. + * @param user the user using the translator + * @param updatedTranslation the updated translation details + */ + void updateTranslation(User user, Translate updatedTranslation); +} diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/view/LoggedInView.java index 76d40c0f1..31d17211a 100644 --- a/src/main/java/view/LoggedInView.java +++ b/src/main/java/view/LoggedInView.java @@ -1,6 +1,9 @@ package view; import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -32,37 +35,98 @@ public class LoggedInView extends JPanel implements PropertyChangeListener { private final JLabel username; private final JButton logOut; + private final JButton translate; + private final JButton history; + private final JButton changepassword; - private final JTextField passwordInputField = new JTextField(15); - private final JButton changePassword; + private final JTextField languageInputField = new JTextField(15); + private final JTextField languageOutputField = new JTextField(15); public LoggedInView(LoggedInViewModel loggedInViewModel) { this.loggedInViewModel = loggedInViewModel; this.loggedInViewModel.addPropertyChangeListener(this); - final JLabel title = new JLabel("Logged In Screen"); + final JLabel title = new JLabel("Translator"); title.setAlignmentX(Component.CENTER_ALIGNMENT); - final LabelTextPanel passwordInfo = new LabelTextPanel( - new JLabel("Password"), passwordInputField); + final LabelTextPanel languageInput = new LabelTextPanel( + new JLabel(""), languageInputField); + + final LabelTextPanel languageOutput = new LabelTextPanel( + new JLabel(""), languageOutputField); final JLabel usernameInfo = new JLabel("Currently logged in: "); username = new JLabel(); + final JLabel languageInfo = new JLabel("Language"); + final JLabel translatedLanguageInfo = new JLabel(" Language"); + + final JLabel sourceLanguageLabel = new JLabel("Source Language: "); + final JLabel translatedLanguageLabel = new JLabel("Translated Language: "); + final JPanel buttons = new JPanel(); logOut = new JButton("Log Out"); buttons.add(logOut); - changePassword = new JButton("Change Password"); - buttons.add(changePassword); + translate = new JButton("Translate"); + buttons.add(translate); + + history = new JButton("History"); + buttons.add(history); + + changepassword = new JButton("Change Password"); + buttons.add(changepassword); this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - passwordInputField.getDocument().addDocumentListener(new DocumentListener() { + final JPanel firstRow = new JPanel(new FlowLayout(FlowLayout.LEFT)); + firstRow.add(sourceLanguageLabel); + firstRow.add(buttons.getComponent(1)); + firstRow.add(translatedLanguageLabel); + + final JPanel secondRow = new JPanel(new FlowLayout(FlowLayout.LEFT)); + secondRow.add(languageInput); + secondRow.add(languageOutput); + + final JPanel thirdRow = new JPanel(new FlowLayout(FlowLayout.LEFT)); + thirdRow.add(languageInfo); + thirdRow.add(translatedLanguageInfo); + + final JPanel fourthRow = new JPanel(new FlowLayout(FlowLayout.LEFT)); + fourthRow.add(history); + fourthRow.add(bookmark); + fourthRow.add(feedback); + fourthRow.add(changepassword); + + languageInputField.getDocument().addDocumentListener(new DocumentListener() { + + private void documentListenerHelper() { + final LoggedInState currentState = loggedInViewModel.getState(); + currentState.setPassword(languageInputField.getText()); + loggedInViewModel.setState(currentState); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentListenerHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentListenerHelper(); + } + }); + + languageOutputField.getDocument().addDocumentListener(new DocumentListener() { private void documentListenerHelper() { final LoggedInState currentState = loggedInViewModel.getState(); - currentState.setPassword(passwordInputField.getText()); + currentState.setPassword(languageOutputField.getText()); loggedInViewModel.setState(currentState); } @@ -82,27 +146,46 @@ public void changedUpdate(DocumentEvent e) { } }); - changePassword.addActionListener( + translate.addActionListener( // This creates an anonymous subclass of ActionListener and instantiates it. evt -> { - if (evt.getSource().equals(changePassword)) { + if (evt.getSource().equals(translate)) { final LoggedInState currentState = loggedInViewModel.getState(); - this.changePasswordController.execute( - currentState.getUsername(), - currentState.getPassword() + this.TranslatorController.execute( + currentState.getUsername() ); } } ); logOut.addActionListener( - // This creates an anonymous subclass of ActionListener and instantiates it. - evt -> { - if (evt.getSource().equals(logOut)) { - // TODO: execute the logout use case through the Controller - // 1. get the state out of the loggedInViewModel. It contains the username. - // 2. Execute the logout Controller. + new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(logOut)) { + // 1. get the state out of the loggedInViewModel. It contains the username. + // 2. Execute the logout Controller. + final LoggedInState currentState = loggedInViewModel.getState(); + + logoutController.execute( + currentState.getUsername() + ); + } + } + } + ); + + changepassword.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource().equals(changepassword)) { + final LoggedInState currentState = loggedInViewModel.getState(); + + ChangePasswordController.execute( + currentState.getUsername(), + currentState.getPassword() + ); + } } } ); @@ -111,9 +194,13 @@ public void changedUpdate(DocumentEvent e) { this.add(usernameInfo); this.add(username); - this.add(passwordInfo); - this.add(passwordErrorField); - this.add(buttons); + this.add(firstRow); + + this.add(seconedRow); + + this.add(thiredRow); + + this.add(buttons.getComponent(0)); } @Override @@ -138,6 +225,6 @@ public void setChangePasswordController(ChangePasswordController changePasswordC } public void setLogoutController(LogoutController logoutController) { - // TODO: save the logout controller in the instance variable. + this.logoutController = logoutController; } }