Skip to content

Commit

Permalink
Closes #2992 Game, map, mod reviews average score filtering (#3013)
Browse files Browse the repository at this point in the history
Co-authored-by: Sheikah45 <[email protected]>
  • Loading branch information
bukajsytlos and Sheikah45 authored Jul 28, 2023
1 parent 65fc2f4 commit db92c61
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 25 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ dependencies {

implementation("io.projectreactor.addons:reactor-extra")

def commonsVersion = "f5a7832399"
def commonsVersion = "4a3facc824"

implementation("com.github.FAForever.faf-java-commons:faf-commons-data:${commonsVersion}") {
exclude module: 'guava'
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/faforever/client/domain/ReviewsSummaryBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public abstract class ReviewsSummaryBean {
FloatProperty positive = new SimpleFloatProperty();
FloatProperty negative = new SimpleFloatProperty();
FloatProperty score = new SimpleFloatProperty();
FloatProperty averageScore = new SimpleFloatProperty();
IntegerProperty numReviews = new SimpleIntegerProperty();
FloatProperty lowerBound = new SimpleFloatProperty();

Expand Down Expand Up @@ -64,6 +65,18 @@ public void setScore(float score) {
this.score.set(score);
}

public FloatProperty averageScoreProperty() {
return averageScore;
}

public float getAverageScore() {
return averageScore.get();
}

public void setAverageScore(float averageScore) {
this.averageScore.set(averageScore);
}

public FloatProperty scoreProperty() {
return score;
}
Expand Down
24 changes: 18 additions & 6 deletions src/main/java/com/faforever/client/fx/JavaFxUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@
import javafx.util.StringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.controlsfx.control.RangeSlider;
import org.springframework.util.Assert;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -444,6 +445,13 @@ public static void bindManagedToVisible(Node... nodes) {

public static void bindTextFieldAndRangeSlider(TextField lowValueTextField, TextField highValueTextField,
RangeSlider rangeSlider) {
DecimalFormat numberFormat = (DecimalFormat) DecimalFormat.getInstance();
numberFormat.setMaximumFractionDigits(0);
bindTextFieldAndRangeSlider(lowValueTextField, highValueTextField, rangeSlider, numberFormat);
}

public static void bindTextFieldAndRangeSlider(TextField lowValueTextField, TextField highValueTextField,
RangeSlider rangeSlider, DecimalFormat format) {
Map.of(
lowValueTextField, Pair.of(rangeSlider.lowValueProperty(), rangeSlider.getMin()),
highValueTextField, Pair.of(rangeSlider.highValueProperty(), rangeSlider.getMax())
Expand All @@ -454,18 +462,22 @@ public static void bindTextFieldAndRangeSlider(TextField lowValueTextField, Text
@Override
public String toString(Number number) {
if (!number.equals(value)) {
return String.valueOf(number.intValue());
return format.format(number);
} else {
return "";
}
}

@Override
public Number fromString(String string) {
if (NumberUtils.isParsable(string)) {
return Double.parseDouble(string);
} else {
if (!string.equals("-") && !string.equals(".")) {
String decimalSeparator = Character.toString(format.getDecimalFormatSymbols().getDecimalSeparator());
try {
Number number = format.parse(string);
String decimalSeparatorSuffix = string.endsWith(decimalSeparator) && format.getMaximumFractionDigits() > 0 ? decimalSeparator : "";
textField.setText(format.format(number) + decimalSeparatorSuffix);
return number;
} catch (ParseException e) {
if (!string.equals("-") && !string.equals(decimalSeparator)) {
textField.setText("");
}
return value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ protected void initSearchController() {

searchController.addCategoryFilter("latestVersion.width", i18n.get("map.width"), mapSizeMap);
searchController.addCategoryFilter("latestVersion.height", i18n.get("map.height"), mapSizeMap);
searchController.addRangeFilter("latestVersion.maxPlayers", i18n.get("map.maxPlayers"), 0, 16, 1, Double::intValue);
searchController.addRangeFilter("latestVersion.maxPlayers", i18n.get("map.maxPlayers"), 0, 16, 16, 0, 0);
searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"), 0, 5, 10, 4, 1);
searchController.addToggleFilter("latestVersion.ranked", i18n.get("map.onlyRanked"), "true");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ protected void initSearchController() {
searchController.addTextFilter("author", i18n.get("mod.author"), false);
searchController.addDateRangeFilter("latestVersion.updateTime", i18n.get("mod.uploadedDateTime"), 0);

searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"), 0, 5, 10, 4, 1);

searchController.addBinaryFilter("latestVersion.type", i18n.get("mod.type"),
ModType.UI.toString(), ModType.SIM.toString(), i18n.get("modType.ui"), i18n.get("modType.sim"));
searchController.addToggleFilter("latestVersion.ranked", i18n.get("mod.onlyRanked"), "true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
import javafx.scene.control.MenuButton;
import javafx.scene.control.TextField;
import lombok.Data;
import lombok.SneakyThrows;
import org.controlsfx.control.RangeSlider;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Expand All @@ -35,26 +38,30 @@ public class RangeFilterController implements FilterNodeController {

private String propertyName;
private Function<Double, ? extends Number> valueTransform;
private int numberOfFractionDigits;
private DecimalFormat numberFormat;

public void initialize() {
JavaFxUtil.bindManagedToVisible(menu);
rangeSlider.setShowTickMarks(true);
rangeSlider.setShowTickLabels(true);
rangeSlider.setMinorTickCount(0);
valueTransform = (value) -> value;
valueTransform = Function.identity();
}

public Optional<List<Condition>> getCondition() {
List<Condition> conditions = new ArrayList<>();
if (!lowValue.getText().isBlank() && rangeSlider.getLowValue() > rangeSlider.getMin()) {
double lowValueValue = normalizeToFormat(rangeSlider.getLowValue());
if (!lowValue.getText().isBlank() && lowValueValue > rangeSlider.getMin()) {
QBuilder qBuilderLow = new QBuilder<>();
DoubleProperty propertyLow = qBuilderLow.doubleNum(propertyName);
conditions.add(propertyLow.gte(valueTransform.apply(rangeSlider.getLowValue())));
conditions.add(propertyLow.gte(valueTransform.apply(lowValueValue)));
}
if (!highValue.getText().isBlank() && rangeSlider.getHighValue() < rangeSlider.getMax()) {
double highValueValue = normalizeToFormat(rangeSlider.getHighValue());
if (!highValue.getText().isBlank() && highValueValue < rangeSlider.getMax()) {
QBuilder qBuilderHigh = new QBuilder<>();
DoubleProperty propertyHigh = qBuilderHigh.doubleNum(propertyName);
conditions.add(propertyHigh.lte(valueTransform.apply(rangeSlider.getHighValue())));
conditions.add(propertyHigh.lte(valueTransform.apply(highValueValue)));
}
if (!conditions.isEmpty()) {
if (!menu.getStyleClass().contains("query-filter-selected")) {
Expand Down Expand Up @@ -84,7 +91,7 @@ public void setTitle(String title) {
menu.textProperty().bind(Bindings.createStringBinding(() -> i18n.get("query.rangeFilter", title, lowValue.getText(), highValue.getText()), lowValue.textProperty(), highValue.textProperty()));
}

public void setMinMax(double min, double max) {
public void setRange(double min, double max, int majorTickCount, int interMajorTickCount) {
rangeSlider.setMin(min);
rangeSlider.setLowValue(min);
lowValue.setText("");
Expand All @@ -93,7 +100,11 @@ public void setMinMax(double min, double max) {
rangeSlider.setHighValue(max);
highValue.setText("");

JavaFxUtil.bindTextFieldAndRangeSlider(lowValue, highValue, rangeSlider);
int numberOfTicks = majorTickCount + majorTickCount * interMajorTickCount;
double range = max - min;
rangeSlider.setBlockIncrement(range / numberOfTicks);
rangeSlider.setMajorTickUnit(range / majorTickCount);
rangeSlider.setMinorTickCount(interMajorTickCount);
}

public void setIncrement(double increment) {
Expand All @@ -112,8 +123,25 @@ public void setValueTransform(Function<Double, ? extends Number> valueTransform)
this.valueTransform = valueTransform;
}

public void setNumberOfFractionDigits(int numberOfFractionDigits) {
this.numberOfFractionDigits = numberOfFractionDigits;
}

public void bind() {
numberFormat = (DecimalFormat) DecimalFormat.getInstance();
numberFormat.setRoundingMode(RoundingMode.HALF_UP);
numberFormat.setMinimumFractionDigits(0);
numberFormat.setMaximumFractionDigits(numberOfFractionDigits);
JavaFxUtil.bindTextFieldAndRangeSlider(lowValue, highValue, rangeSlider, numberFormat);
}

@Override
public Node getRoot() {
return menu;
}

@SneakyThrows
private double normalizeToFormat(double value) {
return numberFormat.parse(numberFormat.format(value)).doubleValue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class SearchablePropertyMappings {
.put("mapVersion.height", new Property("map.heightPixels", false))
.put("mapVersion.folderName", new Property("game.map.folderName", false))
.put("mapVersion.map.author.login", new Property("game.map.author", false))
.put("reviewsSummary.averageScore", new Property("reviews.averageScore", true))

.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,12 @@ protected void initSearchController() {

//TODO: Use rating rather than estimated mean with an assumed deviation of 300 when that is available
searchController.addRangeFilter("playerStats.ratingChanges.meanBefore", i18n.get("game.rating"),
MIN_RATING, MAX_RATING, 100, (value) -> value + 300);
MIN_RATING, MAX_RATING, 10, 4, 0, value -> value + 300);

searchController.addRangeFilter("reviewsSummary.averageScore", i18n.get("reviews.averageScore"),0, 5, 10, 4, 1);

searchController.addDateRangeFilter("endTime", i18n.get("game.date"), 1);
searchController.addRangeFilter("replayTicks", i18n.get("game.duration"), 0, 60, 1, value -> (int) (value*60*10));
searchController.addRangeFilter("replayTicks", i18n.get("game.duration"), 0, 60, 12, 4, 0, value -> value*60*10);
searchController.addToggleFilter("validity", i18n.get("game.onlyRanked"), "VALID");

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,18 +303,23 @@ public CategoryFilterController addCategoryFilter(String propertyName, String ti
return categoryFilterController;
}

public RangeFilterController addRangeFilter(String propertyName, String title, double min, double max,
double tickUnit, Function<Double, ? extends Number> valueTransform) {
public void addRangeFilter(String propertyName, String title, double min, double max,
int majorTickCount, int interMajorTickCount, int numberOfFractionDigits) {
addRangeFilter(propertyName, title, min, max, majorTickCount, interMajorTickCount, numberOfFractionDigits, Function.identity());
}

public void addRangeFilter(String propertyName, String title, double min, double max,
int majorTickCount, int interMajorTickCount, int numberOfFractionDigits,
Function<Double, ? extends Number> valueTransform) {
RangeFilterController rangeFilterController = uiService.loadFxml("theme/vault/search/rangeFilter.fxml");
rangeFilterController.setTitle(title);
rangeFilterController.setPropertyName(propertyName);
rangeFilterController.setMinMax(min, max);
rangeFilterController.setIncrement(tickUnit);
rangeFilterController.setTickUnit(tickUnit);
rangeFilterController.setRange(min, max, majorTickCount, interMajorTickCount);
rangeFilterController.setSnapToTicks(true);
rangeFilterController.setNumberOfFractionDigits(numberOfFractionDigits);
rangeFilterController.setValueTransform(valueTransform);
rangeFilterController.bind();
addFilterNode(rangeFilterController);
return rangeFilterController;
}

public DateRangeFilterController addDateRangeFilter(String propertyName, String title, int initialYearsBefore) {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,7 @@ game.create.generatedMap2 = Generate the map
settings.fa.allowIpv6 = Allow the ICE adapter to use IPv6
settings.fa.allowIpv6.description = Ipv6 causes connection issues for some players. Turn this on if you do not have any IPv6 issues with connections
home.directory.warning.cyrillic = Warning\: Cyrillic characters in the home directory path may cause problems. Please, avoid using them.
reviews.averageScore = Average reviews score
ignoreWarning = Ignore warning
replay.replayRunning = Replay could not be started because another replay is already running.
teammatchmaking.queue.tmm3v3 = 3v3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ public void setUp() throws Exception {
loadFxml("theme/vault/search/rangeFilter.fxml", clazz -> instance);

instance.setPropertyName(propertyName);
instance.setMinMax(min, max);
instance.setRange(min, max, 10, 0);
instance.setIncrement(increment);
instance.setSnapToTicks(true);
instance.setTickUnit(increment);
instance.setValueTransform((value) -> value);
instance.bind();
}

@Test
Expand Down Expand Up @@ -88,7 +89,7 @@ public void testAddListener() throws Exception {
instance.rangeSlider.setHighValue(90.0);
instance.lowValue.setText("20");
instance.highValue.setText("80");
verify(queryListener, times(12)).invalidated(any());
verify(queryListener, times(20)).invalidated(any());
}

@Test
Expand Down Expand Up @@ -157,4 +158,13 @@ public void testGetConditionRange() throws Exception {
assertEquals(result.get().get(1).query(new RSQLVisitor()), new QBuilder<>().doubleNum(propertyName).lte(50.0).query(new RSQLVisitor()));
assertTrue(instance.menu.getStyleClass().contains("query-filter-selected"));
}

@Test
void testTicks() {
instance.setRange(-10.0, 90.0, 5, 1);

assertEquals(20.0, instance.rangeSlider.getMajorTickUnit());
assertEquals(1, instance.rangeSlider.getMinorTickCount());
assertEquals(10.0, instance.rangeSlider.getBlockIncrement());
}
}

0 comments on commit db92c61

Please sign in to comment.