Skip to content

Commit

Permalink
Add proper time filtering to rating graph and display year
Browse files Browse the repository at this point in the history
Fixes FAForever#935
Fixes FAForever#1220

Co-authored-by: axel1200 <alexander.von.trostorff@gmail.com>
  • Loading branch information
2 people authored and Chris Haggan committed Apr 15, 2022
1 parent 838d26b commit 5f1856f
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 53 deletions.
21 changes: 21 additions & 0 deletions src/main/java/com/faforever/client/chat/TimePeriod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.faforever.client.chat;

import lombok.Getter;

import java.time.LocalDateTime;

public enum TimePeriod {
ALL_TIME("userInfo.ratingHistory.allTime", LocalDateTime.MIN),
LAST_YEAR("userInfo.ratingHistory.lastYear", LocalDateTime.now().minusYears(1)),
LAST_MONTH("userInfo.ratingHistory.lastMonth", LocalDateTime.now().minusMonths(1));

@Getter
private final String i18nKey;
@Getter
private final LocalDateTime date;

TimePeriod(String i18nKey, LocalDateTime date) {
this.i18nKey = i18nKey;
this.date = date;
}
}
111 changes: 78 additions & 33 deletions src/main/java/com/faforever/client/chat/UserInfoWindowController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.faforever.client.api.dto.PlayerEvent;
import com.faforever.client.domain.RatingHistoryDataPoint;
import com.faforever.client.events.EventService;
import com.faforever.client.fa.RatingMode;
import com.faforever.client.fx.Controller;
import com.faforever.client.fx.OffsetDateTimeCell;
import com.faforever.client.game.KnownFeaturedMod;
Expand All @@ -35,11 +36,11 @@
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
Expand All @@ -54,8 +55,11 @@
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -85,7 +89,6 @@
@Slf4j
@RequiredArgsConstructor
public class UserInfoWindowController implements Controller<Node> {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("d MMM");

private final StatisticsService statisticsService;
private final CountryFlagService countryFlagService;
Expand Down Expand Up @@ -116,11 +119,11 @@ public class UserInfoWindowController implements Controller<Node> {
public Label mostRecentAchievementNameLabel;
public Pane lockedAchievementsContainer;
public Pane unlockedAchievementsContainer;
public ToggleButton globalButton;
public ToggleButton ladder1v1Button;
public NumberAxis yAxis;
public NumberAxis xAxis;
public LineChart<Integer, Integer> ratingHistoryChart;
public LineChart<Long, Integer> ratingHistoryChart;
public ComboBox<TimePeriod> timePeriodComboBox;
public ComboBox<RatingMode> ratingTypeComboBox;
public Label usernameLabel;
public Label countryLabel;
public ImageView countryImageView;
Expand All @@ -129,9 +132,10 @@ public class UserInfoWindowController implements Controller<Node> {
public TableColumn<NameRecord, OffsetDateTime> changeDateColumn;
public TableColumn<NameRecord, String> nameColumn;
private Player player;
private Map<String, AchievementItemController> achievementItemById = new HashMap<>();
private Map<String, AchievementDefinition> achievementDefinitionById = new HashMap<>();
private final Map<String, AchievementItemController> achievementItemById = new HashMap<>();
private final Map<String, AchievementDefinition> achievementDefinitionById = new HashMap<>();
private Window ownerWindow;
private List<RatingHistoryDataPoint> ratingData;

private static boolean isUnlocked(PlayerAchievement playerAchievement) {
return UNLOCKED == AchievementState.valueOf(playerAchievement.getState().name());
Expand Down Expand Up @@ -164,6 +168,20 @@ public void initialize() {
nameColumn.setCellValueFactory(param -> param.getValue().nameProperty());
changeDateColumn.setCellValueFactory(param -> param.getValue().changeDateProperty());
changeDateColumn.setCellFactory(param -> new OffsetDateTimeCell<>(timeService));

timePeriodComboBox.setConverter(timePeriodStringConverter());

timePeriodComboBox.getItems().addAll(TimePeriod.values());
timePeriodComboBox.setValue(TimePeriod.ALL_TIME);

ratingTypeComboBox.setConverter(ratingModeStringConverter());

ArrayList<RatingMode> modes = new ArrayList<>(Arrays.asList(RatingMode.values()));
modes.removeIf(mode -> mode == RatingMode.NONE);
ratingTypeComboBox.getItems().addAll(modes);
ratingTypeComboBox.setValue(RatingMode.GLOBAL);

ratingData = Collections.emptyList();
}

public Region getRoot() {
Expand Down Expand Up @@ -195,8 +213,7 @@ public void setPlayer(Player player) {
updateNameHistory(player);
countryLabel.setText(i18n.getCountryNameLocalized(player.getCountry()));

globalButton.fire();
globalButton.setSelected(true);
onRatingTypeChange();

loadAchievements();
eventService.getPlayerEvents(player.getId())
Expand Down Expand Up @@ -358,56 +375,84 @@ private void enterAchievementsLoadedState() {
achievementsPane.setVisible(true);
}

public void ladder1v1ButtonClicked() {
loadStatistics(KnownFeaturedMod.LADDER_1V1);
public void onRatingTypeChange() {
CompletableFuture<Void> statisticsFuture = loadStatistics(ratingTypeComboBox.getValue().getFeaturedMod());
statisticsFuture.thenRun(() -> Platform.runLater(this::plotPlayerRatingGraph));
}

private CompletableFuture<Void> loadStatistics(KnownFeaturedMod featuredMod) {
return statisticsService.getRatingHistory(featuredMod, player.getId())
.thenAccept(ratingHistory -> Platform.runLater(() -> plotPlayerRatingGraph(ratingHistory)))
.thenAccept(ratingHistory -> ratingData = ratingHistory)
.exceptionally(throwable -> {
// FIXME display to user
log.warn("Statistics could not be loaded", throwable);
return null;
});
}

@SuppressWarnings("unchecked")
private void plotPlayerRatingGraph(List<RatingHistoryDataPoint> dataPoints) {
List<XYChart.Data<Integer, Integer>> values = dataPoints.stream()
.map(datapoint -> new Data<>(dataPoints.indexOf(datapoint), RatingUtil.getRating(datapoint)))
public void plotPlayerRatingGraph() {
OffsetDateTime afterDate = OffsetDateTime.of(timePeriodComboBox.getValue().getDate(), ZoneOffset.UTC);
List<XYChart.Data<Long, Integer>> values = ratingData.stream()
.filter(dataPoint -> dataPoint.getInstant().isAfter(afterDate))
.map(dataPoint -> new Data<>(dataPoint.getInstant().toEpochSecond(), RatingUtil.getRating(dataPoint)))
.collect(Collectors.toList());

xAxis.setTickLabelFormatter(ratingLabelFormatter(dataPoints));
xAxis.setTickLabelFormatter(ratingLabelFormatter());
if (values.size() > 0) {
xAxis.setLowerBound(values.get(0).getXValue());
xAxis.setUpperBound(values.get(values.size() - 1).getXValue());
}
xAxis.setTickUnit((xAxis.getUpperBound() - xAxis.getLowerBound()) / 10);

XYChart.Series<Integer, Integer> series = new XYChart.Series<>(observableList(values));
XYChart.Series<Long, Integer> series = new XYChart.Series<>(observableList(values));
series.setName(i18n.get("userInfo.ratingOverTime"));
ratingHistoryChart.getData().setAll(series);
ratingHistoryChart.setData(FXCollections.observableList(Collections.singletonList(series)));
}

@NotNull
private StringConverter<Number> ratingLabelFormatter(final List<RatingHistoryDataPoint> dataPoints) {
return new StringConverter<Number>() {
private StringConverter<RatingMode> ratingModeStringConverter() {
return new StringConverter<>() {
@Override
public String toString(Number object) {
int number = object.intValue();
int numberOfDataPoints = dataPoints.size();
int dataPointIndex = number >= numberOfDataPoints ? numberOfDataPoints - 1 : number;
if (dataPointIndex >= dataPoints.size() || dataPointIndex < 0) {
return "";
}
return DATE_FORMATTER.format(dataPoints.get(dataPointIndex).getInstant());
public String toString(RatingMode mode) {
return i18n.get(mode.getI18nKey());
}

@Override
public Number fromString(String string) {
public RatingMode fromString(String string) {
return null;
}
};
}

public void globalButtonClicked() {
loadStatistics(KnownFeaturedMod.FAF);
@NotNull
private StringConverter<TimePeriod> timePeriodStringConverter() {
return new StringConverter<>() {
@Override
public String toString(TimePeriod period) {
return i18n.get(period.getI18nKey());
}

@Override
public TimePeriod fromString(String string) {
return null;
}
};
}

@NotNull
private StringConverter<Number> ratingLabelFormatter() {
return new StringConverter<>() {
@Override
public String toString(Number object) {
long number = object.longValue();
return timeService.asDate(Instant.ofEpochSecond(number));
}

@Override
public Number fromString(String string) {
return null;
}
};
}

public void show() {
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/faforever/client/fa/RatingMode.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
package com.faforever.client.fa;

import com.faforever.client.game.KnownFeaturedMod;
import lombok.Getter;

public enum RatingMode {
GLOBAL,
LADDER_1V1,
NONE
GLOBAL("userInfo.ratingHistory.global", KnownFeaturedMod.FAF),
LADDER_1V1("userInfo.ratingHistory.1v1", KnownFeaturedMod.LADDER_1V1),
NONE("", null);

@Getter
private final String i18nKey;
@Getter
private final KnownFeaturedMod featuredMod;

RatingMode(String i18nKey, KnownFeaturedMod featuredMod) {
this.i18nKey = i18nKey;
this.featuredMod = featuredMod;
}
}
3 changes: 3 additions & 0 deletions src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,9 @@ userInfo.losses=Losses
userInfo.ratingOverTime=Rating over time
userInfo.ratingHistory.1v1=1v1
userInfo.ratingHistory.global=Global
userInfo.ratingHistory.allTime=All Time
userInfo.ratingHistory.lastYear=Last Year
userInfo.ratingHistory.lastMonth=Last Month
serInfo.statistics.errorLoading=Could not load statistics
userInfo.achievements=Achievements
userInfo.achievements.errorLoading=Could not load achievements
Expand Down
24 changes: 12 additions & 12 deletions src/main/resources/theme/user_info_window.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>

<?import javafx.scene.control.ComboBox?>
<VBox fx:id="userInfoRoot" prefHeight="700.0" prefWidth="900.0" styleClass="user-info-window" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.faforever.client.chat.UserInfoWindowController">
<children>
<HBox alignment="CENTER_LEFT" spacing="20.0" styleClass="user-info-header-area">
Expand Down Expand Up @@ -108,29 +109,28 @@
</padding>
</GridPane>
<VBox styleClass="card" GridPane.columnSpan="2147483647" GridPane.rowIndex="1">
<padding>
<Insets right="15.0" />
</padding>
<children>
<HBox alignment="TOP_RIGHT" spacing="10.0">
<HBox alignment="CENTER_RIGHT" spacing="10.0">
<padding>
<Insets top="10.0" />
</padding>
<children>
<ToggleButton fx:id="ladder1v1Button" mnemonicParsing="false" onAction="#ladder1v1ButtonClicked" selected="true" text="%userInfo.ratingHistory.1v1">
<toggleGroup>
<ToggleGroup fx:id="ratingType" />
</toggleGroup>
</ToggleButton>
<ToggleButton fx:id="globalButton" mnemonicParsing="false" onAction="#globalButtonClicked" text="%userInfo.ratingHistory.global" toggleGroup="$ratingType" />
<ComboBox fx:id="timePeriodComboBox" onAction="#plotPlayerRatingGraph"/>
<ComboBox fx:id="ratingTypeComboBox" onAction="#onRatingTypeChange"/>
</children>
</HBox>
<LineChart fx:id="ratingHistoryChart" createSymbols="false" legendVisible="false" prefHeight="300.0" VBox.vgrow="ALWAYS">
<xAxis>
<NumberAxis forceZeroInRange="false" side="BOTTOM" fx:id="xAxis" />
<NumberAxis forceZeroInRange="false" autoRanging="false" side="BOTTOM" fx:id="xAxis" />
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" forceZeroInRange="false" side="LEFT" />
</yAxis>
</LineChart>
</LineChart>
</children>
<padding>
<Insets top="10.0" />
</padding>
</VBox>
</children>
</GridPane>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.faforever.client.api.dto.AchievementState;
import com.faforever.client.domain.RatingHistoryDataPoint;
import com.faforever.client.events.EventService;
import com.faforever.client.fa.RatingMode;
import com.faforever.client.game.KnownFeaturedMod;
import com.faforever.client.i18n.I18n;
import com.faforever.client.leaderboard.LeaderboardEntry;
Expand Down Expand Up @@ -79,7 +80,6 @@ public void setUp() throws Exception {
when(uiService.loadFxml("theme/achievement_item.fxml")).thenReturn(achievementItemController);
when(achievementItemController.getRoot()).thenReturn(new HBox());
when(playerService.getPlayersByIds(any())).thenReturn(CompletableFuture.completedFuture(Collections.emptyList()));

when(leaderboardService.getEntryForPlayer(eq(PLAYER_ID))).thenReturn(CompletableFuture.completedFuture(new LeaderboardEntry()));
when(statisticsService.getRatingHistory(any(), eq(PLAYER_ID))).thenReturn(CompletableFuture.completedFuture(Arrays.asList(
new RatingHistoryDataPoint(OffsetDateTime.now(), 1500f, 50f),
Expand Down Expand Up @@ -138,16 +138,18 @@ public void testSetPlayerInfoBean() throws Exception {
}

@Test
public void testOnGlobalRatingButtonClicked() throws Exception {
public void testOnRatingTypeChangeGlobal() throws Exception {
testSetPlayerInfoBean();
instance.globalButtonClicked();
instance.ratingTypeComboBox.setValue(RatingMode.GLOBAL);
instance.onRatingTypeChange();
verify(statisticsService, times(2)).getRatingHistory(KnownFeaturedMod.FAF, PLAYER_ID);
}

@Test
public void testOn1v1RatingButtonClicked() throws Exception {
public void testOnRatingTypeChange1v1() throws Exception {
testSetPlayerInfoBean();
instance.ladder1v1ButtonClicked();
instance.ratingTypeComboBox.setValue(RatingMode.LADDER_1V1);
instance.onRatingTypeChange();
verify(statisticsService).getRatingHistory(KnownFeaturedMod.LADDER_1V1, PLAYER_ID);
}
}

0 comments on commit 5f1856f

Please sign in to comment.