Skip to content

Commit

Permalink
Merge pull request #36 from edeandrea/add-event-stats-win-loss
Browse files Browse the repository at this point in the history
Add additional win/loss details in event stats UI
  • Loading branch information
cescoffier committed Mar 7, 2022
2 parents e5e06b4 + 8b566ec commit 42e3e27
Show file tree
Hide file tree
Showing 16 changed files with 250 additions and 591 deletions.
13 changes: 12 additions & 1 deletion event-statistics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,18 @@ This service also has its own UI where you can see the top winners and the perce
### Team Stats
Team stats are accumulated by the number of wins by heroes vs villains. It is calculated as a percentage of hero wins to villain wins.

Team stats are then sent over the `/stats/team` [WebSocket](https://en.wikipedia.org/wiki/WebSocket) by the [`TeamStatsWebSocket`](src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java) WebSocket class. Every time a new fight event is received, the team stats are re-computed and the new hero-to-winner percentage is emitted to anyone listening on the WebSocket.
Team stats are then sent over the `/stats/team` [WebSocket](https://en.wikipedia.org/wiki/WebSocket) by the [`TeamStatsWebSocket`](src/main/java/io/quarkus/sample/superheroes/statistics/endpoint/TeamStatsWebSocket.java) WebSocket class. Every time a new fight event is received, the team stats are re-computed and a JSON structure is emitted to anyone listening on the WebSocket.

A sample payload might look like this:

```json
{
"heroWins": 15,
"villainWins": 5,
"numberOfFights": 20,
"heroWinRatio": 0.75
}
```

### Winner Stats
Winner stats are accumulated by the number of wins of each hero or villain.
Expand Down
Binary file modified event-statistics/images/event-statistics-ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.quarkus.sample.superheroes.statistics.domain;

import java.util.StringJoiner;

import io.quarkus.runtime.annotations.RegisterForReflection;

/**
* Data class for a team score
* <p>
* The {@link RegisterForReflection @RegisterForReflection} annotation instructs the native compilation to allow reflection access to the class. Without it, the serialization/deserialization would not work when running the native executable.
* </p>
*/
@RegisterForReflection
public class TeamScore {
private final int heroWins;
private final int villainWins;

public TeamScore(int heroWins, int villainWins) {
this.heroWins = heroWins;
this.villainWins = villainWins;
}

public int getHeroWins() {
return heroWins;
}

public int getVillainWins() {
return villainWins;
}

public int getNumberOfFights() {
return getHeroWins() + getVillainWins();
}

public double getHeroWinRatio() {
return ((double) this.heroWins / getNumberOfFights());
}

@Override
public String toString() {
return new StringJoiner(", ", TeamScore.class.getSimpleName() + "[", "]")
.add("heroWins=" + heroWins)
.add("villainWins=" + villainWins)
.add("numberOfFights=" + getNumberOfFights())
.add("heroWinRatio=" + getHeroWinRatio())
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import org.eclipse.microprofile.reactive.messaging.Channel;

import io.quarkus.sample.superheroes.statistics.domain.TeamScore;

import io.smallrye.mutiny.Multi;

/**
Expand All @@ -18,9 +20,9 @@
class TeamStatsChannelHolder {
@Inject
@Channel("team-stats")
Multi<Double> teamStats;
Multi<TeamScore> teamStats;

Multi<Double> getTeamStats() {
Multi<TeamScore> getTeamStats() {
return this.teamStats;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

import io.quarkus.logging.Log;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.smallrye.mutiny.subscription.Cancellable;
import io.smallrye.mutiny.unchecked.Unchecked;

/**
* WebSocket endpoint for the {@code /stats/team} endpoint. Exposes the {@code team-stats} channel over the socket to anyone listening.
Expand All @@ -29,10 +31,13 @@ public class TeamStatsWebSocket {
private final List<Session> sessions = new CopyOnWriteArrayList<>();
private Cancellable cancellable;

@Inject
ObjectMapper mapper;

@Inject
TeamStatsChannelHolder teamStatsChannelHolder;

@OnOpen
@OnOpen
public void onOpen(Session session) {
this.sessions.add(session);
}
Expand All @@ -45,8 +50,8 @@ public void onClose(Session session) {
@PostConstruct
public void subscribe() {
this.cancellable = this.teamStatsChannelHolder.getTeamStats()
.map(ratio -> Double.toString(ratio))
.subscribe().with(ratio -> this.sessions.forEach(session -> write(session, ratio)));
.map(Unchecked.function(this.mapper::writeValueAsString))
.subscribe().with(serialized -> this.sessions.forEach(session -> write(session, serialized)));
}

@PreDestroy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import io.quarkus.sample.superheroes.fight.schema.Fight;
import io.quarkus.sample.superheroes.statistics.domain.Score;
import io.quarkus.sample.superheroes.statistics.domain.TeamScore;

import io.smallrye.mutiny.Multi;

Expand All @@ -28,9 +29,9 @@ public class SuperStats {
*/
@Incoming("fights")
@Outgoing("team-stats")
public Multi<Double> computeTeamStats(Multi<Fight> results) {
public Multi<TeamScore> computeTeamStats(Multi<Fight> results) {
return results.map(this.stats::add)
.invoke(stats -> LOGGER.debugf("Fight received. Computed the team statistics: %,.010f", stats));
.invoke(score -> LOGGER.debugf("Fight received. Computed the team statistics: %s", score));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.sample.superheroes.statistics.listener;

import io.quarkus.sample.superheroes.fight.schema.Fight;
import io.quarkus.sample.superheroes.statistics.domain.TeamScore;

/**
* Object keeping track of the number of battles won by heroes and villains
Expand All @@ -12,17 +13,17 @@ class TeamStats {
/**
* Adds a {@link Fight}
* @param result The {@link Fight} received
* @return The running percentage of battles won by heroes
* @return A {@link TeamScore} containing running battle stats by team
*/
double add(Fight result) {
TeamScore add(Fight result) {
if (result.getWinnerTeam().equalsIgnoreCase("heroes")) {
this.heroes = this.heroes + 1;
}
else {
this.villains = this.villains + 1;
}

return ((double) this.heroes / (this.heroes + this.villains));
return new TeamScore(this.heroes, this.villains);
}

int getVillainsCount() {
Expand Down
18 changes: 12 additions & 6 deletions event-statistics/src/main/resources/META-INF/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,16 @@ <h2 class="card-pf-title">
<div class="card-pf-body">
<div class="progress-container progress-description-left progress-label-right">
<div class="progress-description">
Heroes
<span id="heroProgressLabel">Heroes</span>
</div>
<div class="progress">
<div id="balance" class="progress-bar" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%;">
<span>Villains</span>
<span id="villainProgressLabel" class="progress-label-right">Villains</span>
</div>
</div>
<p>
<span id="numBattles">0 battles</span>
</p>
</div>
</div>
</div>
Expand All @@ -84,7 +87,7 @@ <h2 class="card-pf-title">
var team = new WebSocket("ws://" + host + "/stats/team");
team.onmessage = function(event) {
console.log(event.data);
updateRatio(event.data);
updateTeam(JSON.parse(event.data));
};
});

Expand All @@ -96,9 +99,12 @@ <h2 class="card-pf-title">
});
}

function updateRatio(ratio) {
var percent = ratio * 100;
$("#balance").attr("aria-valuenow", ratio * 100).attr("style", "width: " + percent + "%;");
function updateTeam(teamScore) {
var percent = teamScore.heroWinRatio * 100;
$("#balance").attr("aria-valuenow", percent).attr("style", "width: " + percent + "%;");
$("#heroProgressLabel").text("Heroes (" + teamScore.heroWins + ")");
$("#villainProgressLabel").text("Villains (" + teamScore.villainWins + ")")
$("#numBattles").text(teamScore.numberOfFights + " battles");
}
</script>
</body>
Expand Down

This file was deleted.

Loading

0 comments on commit 42e3e27

Please sign in to comment.