Compare commits

..

No commits in common. "main" and "logging_and_docs" have entirely different histories.

23 changed files with 41 additions and 429 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

View File

@ -1,70 +1 @@
# team02-AngryNerds-projekt2-turnierverwaltung
## Tournament administration
### Documentation
> Our Tournament Administration Tool is available in german and english,
> to change the language click on start in the top left corner and then select your
> desired language under 'Sprache' or 'Language' (documentation is only provided in english).
Once you start the Programm you will be presented with the possibility to either create a new tournament or load one if one has already been saved.
>To create a new tournament simply type in the name of it and press create
>To load a tournament select the tournament on the list and click open
>You can also delete a tournament by selecting it and then clicking delete
Once a tournament has been created you will be sent to the tournament screen.
Your next step should be adding a place or players you can do so vie Edit Participants or Edit Locations.
Once you have enough Players(min 4 and only number that are 2^n e.g. 4, 8, 16...), you can click the Create game schedule button and the schedule will be created
you can then input the location where a game takes place and as well the points a team scored
the winning team will then automatically advance in the tree.
The Programm automatically saves, so no worries you won't lose your progress.
# Startup and Testing
To Start the Programm use the command
>./gradlew run
To run the tests use the command
>./gradlew test
# Branching Model
We used a simple branching model, for each new functionality or working step a new branch would be created, once a segment was finished the branch would then be reviewed by peers and be pushed into the main branch via a pull request, no direct work is usually done in the main branch.
# Class Diagramm
Our class Diagramm can be found [here](https://github.zhaw.ch/PM2-IT21bWIN-ruiz-mach-krea/team02-AngryNerds-projekt2-turnierverwaltung/blob/main/ClassDiagram.png)
# Architecture
Our Model View Pattern has been set, so that the class Tournament acts as the Model.
Each View has its own unique controller assigned
For Comprehensive Reasons all Controller Classes inherit from the abstract super class FX Controller.
The Class Tournament decorator has the purpose of communication of the controller classes with the Model of the tournament, the same Decorator also communicates with the FileIO and inherits the task of saving or importing save files into the program.
The Class Tournament Decorator always stores the Tournament that is opened in a data field.
The Factory and its associated Factory Decorators are responsible to load all the views if application is started. And to switch between the views while using the application.
The Factory decorator is placed between the controllers and the factory to enable the communication.
The Model tournament saves a List of all participants, all places and all games. Participants are implemented as an interface, since we want to be able to save a team or a single player as participant (in the prototype it is not possible to create a team).
To refresh the view of the tournament tree, each game has its own game decorator with a list of listeners.
To realize the tree there are listeners placed in the game decorators of the previous round to gather the winner and calculate the new participants of a game. Listeners are only placed
Loggers are implemented in all relevant classes
Each class creates its own logger a root logger is created in the LogConfiguration class and two Handlers are specified for use, one File Handler and one Console Handler.
The setting of those handlers can be set in the file log.properties.
We choose this architecture since it gives us the advantage to add more views without having any more code duplication.
We also think it is an advantage that the saving automatically takes place and the user does not have to take into consideration to save from time to time.
It also closely resembles what we learned already in lectures we had previously, so we were able to implement it accordingly.
# Notable Pullrequests
[Number 1](https://github.zhaw.ch/PM2-IT21bWIN-ruiz-mach-krea/team02-AngryNerds-projekt2-turnierverwaltung/pull/22)
[Number 2](https://github.zhaw.ch/PM2-IT21bWIN-ruiz-mach-krea/team02-AngryNerds-projekt2-turnierverwaltung/pull/20)

View File

@ -8,6 +8,7 @@ import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import java.io.IOException;
import java.util.logging.Logger;
@ -174,6 +175,8 @@ public class Factory {
innerVbox.getChildren().add(label);
}
}
/**

View File

@ -109,7 +109,6 @@ public class FactoryDecorator implements IsObservable {
logger.warning("Failed to open tournament file Error: " + e);
printMessageToFooter("Fehler beim lesen der Datei.", true);
String msg = factory.getLanguageConfigurator().getSelectedLanguage("IOException");
logger.warning("Failed to open tournament file Error: " + e);
printMessageToFooter(msg, true);
}
}

View File

@ -2,6 +2,7 @@ package ch.zhaw.projekt2.turnierverwaltung;
import javafx.scene.control.Labeled;
import javafx.scene.control.MenuItem;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -102,9 +103,11 @@ public class LanguageConfigurator {
englishDictionary.put("participantNotExist","Participant does not exist");
germanDictionary.put("placeNotExist","Teilnehmer existiert nicht");
englishDictionary.put("placeNotExist","Participant does not exist");
//SaveMsg
germanDictionary.put("save","Zuletzt gespeichert: ");
englishDictionary.put("save","Last saved: ");
//Alert
germanDictionary.put("yes","Ja");
englishDictionary.put("yes","Yes");
@ -116,6 +119,7 @@ public class LanguageConfigurator {
englishDictionary.put("headerDelete","Delete Tournament?");
germanDictionary.put("contentDelete","Sind Sie sicher, dass sie das Turnier entfernen wollen?\nNach diesem Vorgang kann es nicht wiederhergestellt werden.");
englishDictionary.put("contentDelete","Are you shure you want to delete the tournament?\nAfter that there is no way to restore.");
germanDictionary.put("titleSchedule","Neu erstellen");
englishDictionary.put("titleSchedule","Create new");
germanDictionary.put("headerSchedule","Spielplan neu erstellen?");

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* Class Team represents a team that can be added to a tournament
* (in the prototype there is no functionality for the team)

View File

@ -1,6 +1,7 @@
package ch.zhaw.projekt2.turnierverwaltung;
import javafx.application.Platform;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

View File

@ -21,6 +21,7 @@ public class MainWindow extends Application {
private Factory factory = new Factory(fileIO, tournamentDecorator, languageConfigurator);
private static final Logger logger = Logger.getLogger(FileIO.class.getCanonicalName());
/**
* Start method used to initialize the main window and load it's needed component
* Also sets the scene and set some values like min width and height

View File

@ -7,7 +7,6 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import java.util.logging.Logger;
import java.util.logging.Logger;
@ -37,13 +36,11 @@ public class MainWindowController extends FXController {
@FXML
void changeLangToGerman() {
getLanguageConfigurator().changeLanguage(LanguageConfigurator.Language.GERMAN);
logger.fine("language setting changed to german");
}
@FXML
void changeLangToEnglish() {
getLanguageConfigurator().changeLanguage(LanguageConfigurator.Language.ENGLISH);
logger.fine("language setting changed to english");
}
/**
@ -66,9 +63,7 @@ public class MainWindowController extends FXController {
}
/**
* There is no content to load
*/
@Override
public void loadContent() {
}

View File

@ -6,9 +6,6 @@ import ch.zhaw.projekt2.turnierverwaltung.LanguageConfigurator;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
/**
* Class that is used to display the popup window to confirm a sensitive action of the user.
*/
import java.util.logging.Logger;
@ -26,9 +23,6 @@ public class AlertNewSchedule extends Alert {
private String headerDelete;
private String contentDelete;
/**
* Popup to ask the user if he is sure that he wants to reshuffle the game board.
*/
public AlertNewSchedule(LanguageConfigurator languageConfigurator){
super(Alert.AlertType.WARNING);
yes = languageConfigurator.getSelectedLanguage("yes");

View File

@ -45,7 +45,7 @@ public class GameController extends FXController{
}
@FXML
void saveGameResult() {
void saveGameResult(Event event) {
gameDecorator.saveGameResult(pointsTeamOne.getText(), pointsTeamTwo.getText());
}

View File

@ -1,51 +1,29 @@
package ch.zhaw.projekt2.turnierverwaltung.main.gameScheduleView;
import ch.zhaw.projekt2.turnierverwaltung.*;
import java.util.ArrayList;
import java.util.List;
/**
* This Class is used by the GameController for additional functionality and holds the listeners and the Model Game
*/
public class GameDecorator implements IsObservable{
private Game game;
private List<IsObserver> listener = new ArrayList<>();
/**
* Setup the GameDecorator
*
* @param game Model for the Controller
*/
public GameDecorator (Game game) {
this.game = game;
}
/**
* Method adds a new Listener to the listener list
*
* @param observer that is being added to the Listener List
*/
@Override
public void addListener(IsObserver observer) {
listener.add(observer);
}
/**
* Removes a Listener from the Listener List
*
* @param observer the Listener to be removed
*/
@Override
public void removeListener(IsObserver observer) {
listener.remove(observer);
}
/**
* Saves the Gameresult in the model
*
* @param points1 points achieved in textfield one
* @param points2 points achieved in textfield two
*/
public void saveGameResult(String points1, String points2){
if(points1.length() > 0){
game.setPoints1(Integer.parseInt(points1));
@ -60,35 +38,19 @@ public class GameDecorator implements IsObservable{
informListener();
}
/**
* Saves the Place in the Model
*
* @param place
*/
public void saveGamePlace(Place place){
game.setPlace(place);
informListener();
}
/**
* @return game points from player one
*/
public String getPoints1() {
return String.valueOf(game.getPoints1());
}
/**
*
*@return game points from player two
*/
public String getPoints2() {
return String.valueOf(game.getPoints2());
}
/**
*
* @return returns Gameparticipant one
*/
public String getParticipantOne() {
if (game.getParticipant1() != null) {
return game.getParticipant1().toString();
@ -96,10 +58,6 @@ public class GameDecorator implements IsObservable{
return "1";
}
/**
*
* @return returns Gameparticipant two
*/
public String getParticipantTwo() {
if (game.getParticipant2() != null) {
return game.getParticipant2().toString();
@ -107,25 +65,15 @@ public class GameDecorator implements IsObservable{
return "2";
}
/**
* calls method in model to refresh participant
*/
public void refreshParticipants(){
game.refreshParticipants();
informListener();
}
/**
*
* @return place
*/
public Place getPlace() {
return game.getPlace();
}
/**
* Method that informs all listeners of an update.
*/
public void informListener() {
for(IsObserver observer : listener) {
observer.update();

View File

@ -14,11 +14,6 @@ import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import java.util.logging.Logger;
/**
* Controller of Participant
*/
public class ParticipantFormularController extends FXController {
@FXML
@ -73,14 +68,8 @@ public class ParticipantFormularController extends FXController {
@FXML
private Button saveBtn;
private static final Logger logger = Logger.getLogger(ParticipantFormularController.class.getCanonicalName());
/**
* Shares GUI Elements with the LanguageConfigurator
*/
@Override
public void shareGUIElementWithLanguageConfigurator() {
logger.fine("sharing GUI Elements");
getLanguageConfigurator().recieveLabel(participantListTitle);
getLanguageConfigurator().recieveLabel(closeBtn);
getLanguageConfigurator().recieveLabel(deleteBtn);
@ -92,11 +81,8 @@ public class ParticipantFormularController extends FXController {
getLanguageConfigurator().recieveLabel(saveBtn);
}
/**
* Changes the current selection
*/
@FXML
void changedSelection(){
void changedSelection(MouseEvent event){
Player participant = (Player) participantListView.getSelectionModel().getSelectedItems().get(0);
participantNameTextField.setText(participant.getName());
firstNameTextField.setText(participant.getFirstName());
@ -104,18 +90,12 @@ public class ParticipantFormularController extends FXController {
birthDateTextField.setText(participant.getFormattedDateOfBirth());
}
/**
* Saves a new Participant and clears form
*/
@FXML
void saveParticipant() {
void saveParticipant(ActionEvent event) {
getTournamentDecorator().savePlayer(firstNameTextField.getText(), participantNameTextField.getText(), phoneNumberTextField.getText(), birthDateTextField.getText());
clearFormular();
}
/**
* Clears current form
*/
private void clearFormular() {
firstNameTextField.clear();
participantNameTextField.clear();
@ -123,30 +103,19 @@ public class ParticipantFormularController extends FXController {
birthDateTextField.clear();
}
/**
* Deletes the selected participant.
*/
@FXML
void delete() {
void delete(ActionEvent event) {
Participant participant = participantListView.getSelectionModel().getSelectedItems().get(0);
logger.fine("deleting participant:" + participant);
getTournamentDecorator().deleteParticipant(participant);
}
/**
* Closes the participant form
*/
@FXML
void close() {
void close(ActionEvent event) {
getFactoryDecorator().openScheduleView();
}
/**
* Loads the previously saved content and puts it in the list
*/
@Override
public void loadContent() {
logger.fine("loading and placing it into the list");
Tournament tournament = getTournamentDecorator().getTournament();
if(tournament != null){
participantListView.setItems(tournament.getParticipants());

View File

@ -1,38 +0,0 @@
package ch.zhaw.projekt2.turnierverwaltung;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.inOrder;
public class FactoryDecoratorTest {
private FactoryDecorator factoryDecorator;
@Test
void test() {
FileIO io = Mockito.mock(FileIO.class);
Factory fc = Mockito.mock(Factory.class);
Pane pn = Mockito.mock(BorderPane.class);
FileIO.TournamentFile tf = Mockito.mock(FileIO.TournamentFile.class);
factoryDecorator = new FactoryDecorator(io,fc,pn);
factoryDecorator.openTournament(tf);
InOrder order = inOrder(io,fc,pn);
try {
order.verify(fc).setTournament(io.loadTournament(tf));
order.verify(fc).showGameScheduler((BorderPane) pn);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
fail();
}
}
}

View File

@ -1,41 +0,0 @@
package ch.zhaw.projekt2.turnierverwaltung;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class GameTest {
private Game game;
@Test
@DisplayName("TestWinner")
void testWinnerFunction() {
Participant one = Mockito.mock(Player.class);
Participant two = Mockito.mock(Player.class);
game = new Game(one,two);
game.setPoints1(1);
Assertions.assertEquals(one,game.getWinner());
game.setPoints2(2);
Assertions.assertEquals(two,game.getWinner());
}
@Test
@DisplayName("Test refresh Participant")
void refreshTest() {
Participant one = Mockito.mock(Player.class);
Participant two = Mockito.mock(Player.class);
Game game1 = new Game(one,two);
Game game2 = new Game(one,two);
game = new Game(game1,game2);
game1.setPoints1(2);
game2.setPoints2(2);
Assertions.assertNull(game.getParticipant1());
Assertions.assertNull(game.getParticipant2());
game.refreshParticipants();
Assertions.assertEquals(one,game.getParticipant1());
Assertions.assertEquals(two,game.getParticipant2());
}
}

View File

@ -1,18 +0,0 @@
package ch.zhaw.projekt2.turnierverwaltung;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class LanguageTest {
@Test
@DisplayName("Test Dictionary")
void test() {
LanguageConfigurator languageConfigurator = new LanguageConfigurator();
Assertions.assertEquals("Ja",languageConfigurator.getSelectedLanguage("yes"));
languageConfigurator.changeLanguage(LanguageConfigurator.Language.ENGLISH);
Assertions.assertEquals("Yes",languageConfigurator.getSelectedLanguage("yes"));
}
}

View File

@ -1,25 +0,0 @@
package ch.zhaw.projekt2.turnierverwaltung;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class PersonTest {
private Person person;
@Test
@DisplayName("Test invalid Parameter")
void invalidParams() {
try {
assertThrows(Person.InvalidPhoneNumberException.class,()->new Person("A","a",".sad"));
person = new Person("A","A","");
assertEquals("A", person.getFirstName());
assertEquals("A", person.getName());
assertEquals("", person.getPhoneNumber());
} catch (InvalidNameException | Person.InvalidPhoneNumberException e) {
fail();
}
}
}

View File

@ -1,36 +0,0 @@
package ch.zhaw.projekt2.turnierverwaltung;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class PlaceTest {
private Place place;
@Test
@DisplayName("Test Params")
void paramTest() {
Assertions.assertThrows(InvalidNameException.class, () -> new Place("*"));
try {
place = new Place("placeA");
assertEquals("placeA", place.getName());
} catch (InvalidNameException e) {
fail();
}
}
@Test
@DisplayName("Place Equals Test")
void testEqual() {
try {
place = new Place("placeA");
assertTrue(place.equals(place));
assertTrue(place.equals(new Place("placeA")));
assertFalse(place.equals(new Place("nads")));
} catch (InvalidNameException e) {
fail();
}
}
}

View File

@ -1,39 +0,0 @@
package ch.zhaw.projekt2.turnierverwaltung;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class PlayerTest {
private Player player;
@Test
@DisplayName("Test invalid Parameter")
void invalidParams() {
try {
assertThrows(Person.InvalidPhoneNumberException.class,()->new Player("A","a",".sad",""));
assertThrows(Player.InvalidDateException.class,()->new Player("A","a","","a"));
player = new Player("A","A","","");
assertEquals("A",player.getFirstName());
assertEquals("A",player.getName());
assertEquals("",player.getPhoneNumber());
assertNull(player.getDateOfBirth());
} catch (InvalidNameException | Person.InvalidPhoneNumberException | Player.InvalidDateException e) {
fail();
}
}
@Test
@DisplayName("Test equals")
void equalsTest() {
try {
player = new Player("A","A","","");
assertTrue(player.equals(player));
assertFalse(player.equals(new Player("B", "D", "", "")));
assertTrue(player.equals(new Player("A", "A", "", "")));
} catch (InvalidNameException | Person.InvalidPhoneNumberException | Player.InvalidDateException e) {
e.printStackTrace();
}
}
}

View File

@ -1,31 +0,0 @@
package ch.zhaw.projekt2.turnierverwaltung;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class TeamTest {
private Team team;
@Test
@DisplayName("Team Params")
void testParams() {
Player player = Mockito.mock(Player.class);
team = new Team("Team1");
for (int i = 0; i < 3; i++) {
Assertions.assertEquals(i,team.getPlayers().size());
team.addPlayer(player);
}
}
@Test
@DisplayName("Team Equals")
void equalTeam() {
team = new Team("A");
Assertions.assertTrue(team.equals(team));
Assertions.assertTrue(team.equals(new Team("A")));
Assertions.assertFalse(team.equals(new Team("B")));
}
}

View File

@ -44,21 +44,20 @@ public class TournamentTest {
//Checks if one Participant gets added
Participant participantOne = Mockito.mock(Player.class);
when(participantOne.equals(any(Participant.class))).thenReturn(false);
try {
assertEquals(0, tournament.getParticipants().size());
tournament.saveParticipant(participantOne);
assertEquals(1, tournament.getParticipants().size());
//Checks if a second Participant gets added
Participant participantTwo = Mockito.mock(Player.class);
tournament.saveParticipant(participantTwo);
assertEquals(2, tournament.getParticipants().size());
//Checks if a allready added Particpant does not get added
when(participantOne.equals(any(Participant.class))).thenReturn(true);
tournament.saveParticipant(participantTwo);
assertEquals(2, tournament.getParticipants().size());
} catch (Person.InvalidPhoneNumberException e) {
fail();
}
}
@ -70,13 +69,13 @@ public class TournamentTest {
//Checks if Error is thrown if Participant not in list
assertThrows(Tournament.ParticipantNotExistsException.class, () -> tournament.removeParticipant(participant));
try {
//Checks if participant gets removed
tournament.saveParticipant(participant);
assertEquals(1, tournament.getParticipants().size());
try {
tournament.removeParticipant(participant);
assertEquals(0, tournament.getParticipants().size());
} catch (Tournament.ParticipantNotExistsException | Person.InvalidPhoneNumberException e) {
} catch (Tournament.ParticipantNotExistsException e) {
fail();
}
}
@ -127,18 +126,13 @@ public class TournamentTest {
tournament.createGameSchedule();
assertEquals(2, tournament.getGameList().size());
tournament.saveParticipant(participant);
} catch (Tournament.NumberOfParticipantInvalidException | Person.InvalidPhoneNumberException e) {
} catch (Tournament.NumberOfParticipantInvalidException e) {
fail();
}
} else {
assertThrows(Tournament.NumberOfParticipantInvalidException.class, () -> tournament.createGameSchedule());
try {
tournament.saveParticipant(participant);
} catch (Person.InvalidPhoneNumberException e) {
e.printStackTrace();
}
}
}
try {