Skip to content

Commit

Permalink
Merge pull request #314 from Arquisoft/develop
Browse files Browse the repository at this point in the history
statistics update
  • Loading branch information
Toto-hitori authored Apr 28, 2024
2 parents f08dbc6 + 536ef1c commit 3abda31
Show file tree
Hide file tree
Showing 36 changed files with 433 additions and 217 deletions.
27 changes: 7 additions & 20 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
e2e-tests:
needs: [ unit-tests ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm --prefix webapp install
- run: npm --prefix webapp run build
# - run: npm --prefix webapp run test:e2e
docker-push-api:
runs-on: ubuntu-latest
needs: [ e2e-tests ]
needs: [ unit-tests ]
steps:
- uses: actions/checkout@v4
- name: Publish to Registry
Expand All @@ -67,7 +56,7 @@ jobs:
SSL_PASSWORD
docker-push-prometheus:
runs-on: ubuntu-latest
needs: [ e2e-tests ]
needs: [ unit-tests ]
steps:
- uses: actions/checkout@v4
- name: Publish to Registry
Expand All @@ -80,7 +69,7 @@ jobs:
workdir: api/monitoring/prometheus
docker-push-grafana:
runs-on: ubuntu-latest
needs: [ e2e-tests ]
needs: [ unit-tests ]
steps:
- uses: actions/checkout@v4
- name: Publish to Registry
Expand All @@ -93,7 +82,7 @@ jobs:
workdir: api/monitoring/grafana
docker-push-reverse-proxy:
runs-on: ubuntu-latest
needs: [ e2e-tests ]
needs: [ unit-tests ]
steps:
- uses: actions/checkout@v4
- name: Publish to Registry
Expand All @@ -109,11 +98,9 @@ jobs:
permissions:
contents: read
packages: write
needs: [ e2e-tests ]
needs: [ unit-tests ]
steps:

- uses: actions/checkout@v4

- name: Create .env file
run: echo "REACT_APP_API_ENDPOINT=https://${{secrets.APP_DOMAIN}}:8443" > webapp/.env

Expand All @@ -132,7 +119,7 @@ jobs:
REACT_APP_API_ENDPOINT
docker-push-question-generator:
runs-on: ubuntu-latest
needs: [ e2e-tests ]
needs: [ unit-tests ]
steps:
- uses: actions/checkout@v4
- name: Publish to Registry
Expand Down Expand Up @@ -175,4 +162,4 @@ jobs:
echo "SSL_PASSWORD=${{ secrets.SSL_PASSWORD }}" >> .env
docker compose --profile prod down
docker compose --profile prod up -d --pull always
docker image prune -f
docker image prune -f
3 changes: 3 additions & 0 deletions api/src/main/java/lab/en2b/quizapi/auth/dtos/RegisterDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand All @@ -19,10 +20,12 @@ public class RegisterDto {
private String email;
@NonNull
@NotBlank
@Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters")
@Schema(description = "Username used for registering", example = "example user" )
private String username;
@NonNull
@NotBlank
@Size(min = 6, max = 20, message = "Password must be between 6 and 20 characters")
@Schema(description = "Password used for registering", example = "password" )
private String password;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package lab.en2b.quizapi.questions.answer;

public enum AnswerCategory {
CAPITAL_CITY, COUNTRY, SONG, STADIUM, BALLON_DOR, GAMES_PUBLISHER, PAINTING, WTPOKEMON, GAMES_COUNTRY, GAMES_GENRE, BASKETBALL_VENUE, COUNTRY_FLAG
CAPITAL_CITY, COUNTRY, SONG, STADIUM, BALLON_DOR, GAMES_PUBLISHER, PAINTING, WTPOKEMON, GAMES_COUNTRY, GAMES_GENRE, BASKETBALL_VENUE, COUNTRY_FLAG, GAMES_RELEASE
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ public class QuestionHelper {

private QuestionHelper(){} // To hide the implicit public constructor as this is static only

private static final int MAX_DISTRACTORS = 3;

public static List<Answer> getDistractors(AnswerRepository answerRepository, Question question){
return answerRepository.findDistractors(question.getAnswerCategory().toString(), question.getLanguage(), question.getCorrectAnswer().getText(), MAX_DISTRACTORS);
return answerRepository.findDistractors(question.getAnswerCategory().toString(), question.getLanguage(), question.getCorrectAnswer().getText(), 3);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,21 @@
import org.springframework.data.jpa.repository.Query;

import java.util.Optional;
import java.util.List;

public interface StatisticsRepository extends JpaRepository<Statistics, Long> {

@Query(value = "SELECT * FROM Statistics WHERE user_id = ?1", nativeQuery = true)
Optional<Statistics> findByUserId(Long userId);

//Query that gets the top ten ordered by statistics -> statistics.getCorrectRate() * statistics.getTotal() / 9L
@Query(value = "SELECT *, \n" +
" CASE \n" +
" WHEN total = 0 THEN 0 \n" +
" ELSE (correct * 100.0 / NULLIF(total, 0)) * total \n" +
" END AS points \n" +
"FROM Statistics \n" +
"ORDER BY points DESC LIMIT 10 ", nativeQuery = true)
List<Statistics> findTopTen();

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ public StatisticsResponseDto getStatisticsForUser(Authentication authentication)
}

public List<StatisticsResponseDto> getTopTenStatistics(){
List<Statistics> all = new ArrayList<>(statisticsRepository.findAll());
all.sort(Comparator.comparing(Statistics::getCorrectRate).reversed());
List<Statistics> topTen = all.stream().limit(10).toList();
return topTen.stream().map(statisticsResponseDtoMapper).collect(Collectors.toList());
return statisticsRepository.findTopTen().stream().map(statisticsResponseDtoMapper).collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ public class StatisticsResponseDto {
private Long wrong;
private Long total;
private UserResponseDto user;
@JsonProperty("correct_rate")
private Long correctRate;
@JsonProperty("percentage")
private Long percentage;
@JsonProperty("points")
private Long points;
@JsonProperty("finished_games")
private Long finishedGames;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ public StatisticsResponseDto apply(Statistics statistics) {
.right(statistics.getCorrect())
.wrong(statistics.getWrong())
.total(statistics.getTotal())
.percentage(statistics.getCorrectRate())
.user(userResponseDtoMapper.apply(statistics.getUser()))
.correctRate(statistics.getCorrectRate())
.points(statistics.getCorrectRate() * statistics.getTotal() )
.finishedGames(statistics.getFinishedGames())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public void setUp(){
.right(5L)
.wrong(5L)
.total(10L)
.correctRate(50L)
.percentage(50L)
.points(500L)
.user(defaultUserResponseDto)
.finishedGames(1L)
.build();
Expand All @@ -105,7 +106,8 @@ public void setUp(){
.right(7L)
.wrong(3L)
.total(10L)
.correctRate(70L)
.points(700L)
.percentage(70L)
.user(defaultUserResponseDto)
.finishedGames(1L)
.build();
Expand All @@ -131,7 +133,8 @@ public void getStatisticsForUserTestEmpty(){
.right(0L)
.wrong(0L)
.total(0L)
.correctRate(0L)
.points(0L)
.percentage(0L)
.finishedGames(0L)
.user(defaultUserResponseDto).build()
, result);
Expand All @@ -152,7 +155,7 @@ public void getCorrectRateTotalZero(){

@Test
public void getTopTenStatisticsTestWhenThereAreNotTen(){
when(statisticsRepository.findAll()).thenReturn(List.of(defaultStatistics2, defaultStatistics1));
when(statisticsRepository.findTopTen()).thenReturn(List.of(defaultStatistics2, defaultStatistics1));
List<StatisticsResponseDto> result = statisticsService.getTopTenStatistics();
Assertions.assertEquals(List.of(defaultStatisticsResponseDto2,defaultStatisticsResponseDto1), result);
}
Expand All @@ -172,11 +175,12 @@ public void getTopTenStatisticsTestWhenThereAreNotTenAndAreEqual(){
.right(5L)
.wrong(5L)
.total(10L)
.correctRate(50L)
.points(500L)
.percentage(50L)
.user(defaultUserResponseDto)
.finishedGames(1L)
.build();
when(statisticsRepository.findAll()).thenReturn(List.of(defaultStatistics1, defaultStatistics3));
when(statisticsRepository.findTopTen()).thenReturn(List.of(defaultStatistics1, defaultStatistics3));
List<StatisticsResponseDto> result = statisticsService.getTopTenStatistics();
Assertions.assertEquals(List.of(defaultStatisticsResponseDto1,defaultStatisticsResponseDto3), result);
}
Expand Down
Binary file removed docs/images/05_building_blocks-EN.png
Binary file not shown.
Binary file not shown.
Binary file removed docs/images/ContainerDiagram.png
Binary file not shown.
Binary file added docs/images/Gatling_10000_users.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/Gatling_1000_users.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/Gatling_100_users.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/Gatling_1_user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/images/TechnicalContextDiagram.png
Binary file not shown.
Binary file added docs/images/actuator.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/images/arc42-logo.png
Binary file not shown.
Binary file added docs/images/codescene-general.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/grafana.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/prometheus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/src/01_introduction_and_goals.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ See the complete functional requirements in the xref:#section-annex[Annex] of th
| Reliability | The system should be reliable in generating questions from Wikidata, ensuring that questions are accurate and diverse. The system shall handle user registrations, logins, and game data storage without errors.
| Availability | The system shall be available 99% of the time a user tries to access it.
| Maintainability | The system shall be designed and implemented in a way that facilitates easy maintenance and updates.
| Performance efficiency | The system shall deliver optimal performance, ensuring responsive interactions for users. The automatic generation of questions from Wikidata and the real-time gameplay shall be efficient. The system shall handle N concurrent users.
| Security | The system shall prioritize user data security. It shall implement robust authentication mechanisms for user registration and login. The API access points for user information and generated questions shall be secured with proper authorization.
| Performance efficiency | The system shall deliver optimal performance, ensuring responsive interactions for users. The automatic generation of questions from Wikidata and the real-time gameplay shall be efficient. The system shall handle 1000 concurrent users.
| Security | The system shall prioritize user data security. It shall implement robust authentication mechanisms for user registration and login. The API access points for user information and generated questions shall be secured with proper authorization.
| Usability | The system shall provide a user-friendly interface, making it easy for users to register, log in, and play the game. The system learning time for a user should be less than 4 hours.
| Compatibility | The system shall be compatible with various web browsers and devices, ensuring a seamless experience for users regardless of their choice of platform. It has to be well-optimized for different screen sizes and functionalities.
|===
Expand Down
8 changes: 7 additions & 1 deletion docs/src/09_architecture_decisions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ During the application development process, decisions had to be made as issues e

|Architecture of the Question Generator
|It has been decided to implement the QG in an independent module so it is only run once and it is not generating questions at real time, this way, we are not dependent on wikidata for having our application available.

|Template method pattern
|We chose to apply our software design knowledge and generate the questions following a template method in Java. This allows us to create new questions easily and without duplicating too much code.

|Use of JPA in the Question Generator
|We used JPA in favor of JDBC as when tables are related in a DB, JPA makes it easier to write into the DB.
|===

If needed, a more descriptive record can be seen link:https://github.com/Arquisoft/wiq_en2b/wiki[here].
If needed, more details are given in the link:https://github.com/Arquisoft/wiq_en2b/wiki[wiki].

92 changes: 63 additions & 29 deletions docs/src/10_quality_requirements.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,10 @@ ifndef::imagesdir[:imagesdir: ../images]
=== Quality Tree
This quality tree is a high-level overview of the quality goals and requirements. The Quality tree uses "quality" as a root while the rest of the quality categories will be displayed as branches.

[plantuml,"Quality Tree",png]
[plantuml,"Quality Tree",png,align="center"]
----
@startuml
title Quality attributes
agent Quality
agent Security
agent Reliability
agent Transferability
agent Usability
agent "Performance Efficiency"
agent Maintainability
agent Availability
agent Compatibility
agent "Functional Suitability"
Quality --- Security
Quality --- Reliability
Quality --- Transferability
Quality --- Usability
Quality --- "Performance Efficiency"
Quality --- Maintainability
Quality --- Availability
Quality --- Compatibility
Quality --- "Functional Suitability"
@enduml
include::../diagrams/10_Quality_Tree.puml[]
----

=== Quality Scenarios
To obtain a measurable system response to stimulus corresponding to the various quality branches outlined in the mindmap, we will use quality scenarios. Scenarios make quality requirements concrete and allow to more easily measure or decide whether they are fulfilled.

Expand All @@ -42,17 +19,74 @@ To obtain a measurable system response to stimulus corresponding to the various
| Functional suitability | Users shall be able to register, log in, play the quiz, and access historical data without encountering errors or glitches. | High, Medium
| Reliability | The system shall reliably generate accurate and diverse questions from Wikidata. User registrations, logins, and game data storage shall be handled without errors. | High, Medium
| Availability | The system shall be available 99% of the time when a user attempts to access it. | High, High
| Performance efficiency | The system shall deliver optimal performance, ensuring responsive interactions for users. It shall efficiently generate questions from Wikidata and handle real-time gameplay with up to N concurrent users. | High, High
| Performance efficiency | The system shall deliver optimal performance, ensuring responsive interactions for users. It shall efficiently generate questions from Wikidata and handle real-time gameplay with up to 1000 concurrent users. | High, High
| Usability | The system shall provide a user-friendly interface, allowing users to register, log in, and play the game with a learning time of less than 4 hours. | High, Medium
| Security | User data shall be securely handled. Robust authentication mechanisms shall be in place for user registration and login. API access points for user information and generated questions shall be secured with proper authorization. | Medium, High
| Compatibility | The system shall be compatible with various web browsers and devices, providing a seamless experience for users regardless of their choice of platform. It shall be well-optimized for different screen sizes and functionalities. | High, Medium
| Transferability | The system shall allow for easy transfer of user data and game-related information through its APIs. | Medium, High
| Testability | The unit tests shall have at least 80% coverage. | High, Medium
| Monitoring | The system shall have monitoring in place to track the performance and availability of the system. | High, Medium
|===
==== Change Scenarios
[options="header",cols="1,3,1"]
|===
|Quality attribute|Scenario|Priority
| Maintainability | The system shall be designed and implemented in a way that allows for easy maintenance and updates. | High, Medium
| Modifiability | The system shall be designed and implemented in a way that allows for easy maintenance and updates. | High, Medium
| Maintainability | The code of the system shall be well-documented, and modular, allowing for efficient troubleshooting and modifications. | High, Medium
|===
|===

==== Implementation

===== Performance efficiency
The tests were done with a 2 core and 4 GB of memory system.
This system's efficiency has been measured with Gatling. For the script that we used, a user already created, logged in and played a full game. After that, the user clicked to look the statistics.
The scripts were run a total of 4 times. One with 1 user, other with 100 users, another one with 1000 users and finally one with 10000 users.
The results of this scripts show that response times are reasonable up until 1000 users. Having 10000 users playing a game at the same time make a lot of failures.
Here are the results.

**1 user:**

image::Gatling_1_user.png[align="center", title="Gatling results with 1 user"]

**100 users:**

image::Gatling_100_users.png[align="center", title="Gatling results with 100 user"]

**1000 users:**

image::Gatling_1000_users.png[align="center", title="Gatling results with 1000 user"]

**10000 users:**

image::Gatling_10000_users.png[align="center", title="Gatling results with 10000 user"]

===== Security
The system is secured using Spring Security. The user data is stored in a database and the passwords are hashed using BCrypt. The API access points are secured with proper authorization. HTTPS is used to encrypt the data in transit.

The system is also protected against SQL injection via using JPA repositories and prepared statements.

The system is also designed in such a way that prevents cheating, by limiting the options available for the user and doing all validation in the backend, such as checking if the answer is correct, preventing request forgery.

===== Testability

===== Monitoring
The system is monitored using Spring Boot Actuator and Prometheus. The monitoring data is visualized using Grafana.

The actuator is deployed in https://kiwiq.run.place:8443/actuator/prometheus.

image::actuator.png[align="center", title="Spring Boot Actuator"]

The Prometheus server is deployed in http://20.199.84.5:9090.

image::prometheus.png[align="center", title="Prometheus"]

The Grafana is deployed in http://20.199.84.5:9091. The Grafana dashboard is available at the following link with user [email protected] and password aswgrafana.

The dashboard used is: https://grafana.com/grafana/dashboards/6756-spring-boot-statistics/
Make sure to put kiwiq.run.place:8443 as the Instance and WIQ API as the application.

image::grafana.png[align="center", title="Graphana Spring Boot dashboard"]

===== Maintainability
In our CodeScene analysis we find that our knowledge distribution is well-balanced as well as a nice code health, excepting one hotspot on a test that is not relevant.

image::codescene-general.png[align="center", title="CodeScene general view"]
Loading

0 comments on commit 3abda31

Please sign in to comment.