api: re-impl histories (muchhhhhhhhhh better now)
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 37s
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 37s
This commit is contained in:
parent
29f5d5983a
commit
7b0c9f54ff
@ -3,17 +3,36 @@ package cc.fascinated.common;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@UtilityClass
|
||||
public class DateUtils {
|
||||
private static final ZoneId ZONE_ID = ZoneId.of("Europe/London");
|
||||
private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_INSTANT
|
||||
.withLocale(Locale.UK)
|
||||
.withZone(ZONE_ID);
|
||||
private static final DateTimeFormatter SIMPLE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||
.withLocale(Locale.UK)
|
||||
.withZone(ZONE_ID);
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT;
|
||||
/**
|
||||
* Gets the date from a string.
|
||||
*
|
||||
* @param date The date string.
|
||||
* @return The date.
|
||||
*/
|
||||
public static Date getDateFromIsoString(String date) {
|
||||
return Date.from(Instant.from(ISO_FORMATTER.parse(date)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date from a string.
|
||||
@ -22,13 +41,25 @@ public class DateUtils {
|
||||
* @return The date.
|
||||
*/
|
||||
public static Date getDateFromString(String date) {
|
||||
return Date.from(Instant.from(FORMATTER.parse(date)));
|
||||
LocalDate localDate = LocalDate.parse(date, SIMPLE_FORMATTER);
|
||||
ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZONE_ID);
|
||||
return Date.from(zonedDateTime.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date to a string.
|
||||
*
|
||||
* @param date The date to format.
|
||||
* @return The formatted date.
|
||||
*/
|
||||
public String formatDate(Date date) {
|
||||
return SIMPLE_FORMATTER.format(date.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the date to the current hour.
|
||||
* <p>
|
||||
* eg: 00:05 -> 00:00
|
||||
* eg: 00:05 -> 00:00
|
||||
* </p>
|
||||
*
|
||||
* @param date The date to align.
|
||||
@ -47,4 +78,13 @@ public class DateUtils {
|
||||
public static Date getDaysAgo(int days) {
|
||||
return Date.from(Instant.now().minus(days, ChronoUnit.DAYS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date for midnight today.
|
||||
*
|
||||
* @return The date.
|
||||
*/
|
||||
public static Date getMidnightToday() {
|
||||
return Date.from(Instant.now().truncatedTo(ChronoUnit.DAYS));
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ public class UserController {
|
||||
/**
|
||||
* The user service to use
|
||||
*/
|
||||
@NonNull private final UserService userService;
|
||||
@NonNull
|
||||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
public UserController(@NonNull UserService userService) {
|
||||
@ -36,4 +37,18 @@ public class UserController {
|
||||
public ResponseEntity<UserDTO> getUser(@PathVariable String id) {
|
||||
return ResponseEntity.ok(userService.getUser(id).getAsDTO());
|
||||
}
|
||||
|
||||
/**
|
||||
* A GET mapping to retrieve a user's statistic
|
||||
* history using the users steam id.
|
||||
*
|
||||
* @param id the id of the user
|
||||
* @return the user's statistic history
|
||||
* @throws BadRequestException if the user is not found
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/histories/{id}")
|
||||
public ResponseEntity<?> getUserHistories(@PathVariable String id) {
|
||||
return ResponseEntity.ok(userService.getUser(id).getHistory().getPreviousHistories(30));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
package cc.fascinated.model.score;
|
||||
|
||||
import cc.fascinated.model.user.hmd.DeviceController;
|
||||
import cc.fascinated.model.user.hmd.DeviceHeadset;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class DeviceInformation {
|
||||
/**
|
||||
* The headset that was used to set the score.
|
||||
*/
|
||||
private final DeviceHeadset headset;
|
||||
|
||||
/**
|
||||
* The left controller that was used to set the score.
|
||||
*/
|
||||
private final DeviceController leftController;
|
||||
|
||||
/**
|
||||
* The right controller that was used to set the score.
|
||||
*/
|
||||
private final DeviceController rightController;
|
||||
|
||||
/**
|
||||
* Checks if the device information contains unknown values.
|
||||
*
|
||||
* @return if the device information contains unknown values
|
||||
*/
|
||||
public boolean containsUnknownDevices() {
|
||||
return headset == DeviceHeadset.UNKNOWN
|
||||
|| leftController == DeviceController.UNKNOWN
|
||||
|| rightController == DeviceController.UNKNOWN;
|
||||
}
|
||||
}
|
@ -36,7 +36,8 @@ public class Score {
|
||||
/**
|
||||
* The ID of the player that set the score.
|
||||
*/
|
||||
@Indexed @JsonIgnore
|
||||
@Indexed
|
||||
@JsonIgnore
|
||||
private final String playerId;
|
||||
|
||||
/**
|
||||
@ -59,7 +60,8 @@ public class Score {
|
||||
/**
|
||||
* The ID of the leaderboard the score was set on.
|
||||
*/
|
||||
@Indexed @JsonIgnore
|
||||
@Indexed
|
||||
@JsonIgnore
|
||||
private final String leaderboardId;
|
||||
|
||||
/**
|
||||
@ -100,6 +102,14 @@ public class Score {
|
||||
*/
|
||||
private final Integer badCuts;
|
||||
|
||||
/**
|
||||
* The device information that was used to set the score.
|
||||
* <p>
|
||||
* Headset and controllers information.
|
||||
* </p>
|
||||
*/
|
||||
private final DeviceInformation deviceInformation;
|
||||
|
||||
/**
|
||||
* The score history for map the score was set on.
|
||||
*/
|
||||
@ -155,4 +165,13 @@ public class Score {
|
||||
public List<Score> getPreviousScores() {
|
||||
return previousScores == null ? List.of() : previousScores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the score is ranked.
|
||||
*
|
||||
* @return true if the score is ranked, false otherwise
|
||||
*/
|
||||
public boolean isRanked() {
|
||||
return pp != null;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cc.fascinated.model.score.impl.scoresaber;
|
||||
|
||||
import cc.fascinated.model.score.DeviceInformation;
|
||||
import cc.fascinated.model.score.Score;
|
||||
import cc.fascinated.platform.Platform;
|
||||
import lombok.Getter;
|
||||
@ -28,10 +29,10 @@ public class ScoreSaberScore extends Score {
|
||||
private final int maxCombo;
|
||||
|
||||
public ScoreSaberScore(long id, String playerId, Platform.Platforms platform, String platformScoreId, String leaderboardId, int rank,
|
||||
double accuracy, Double pp, int score, String[] modifiers, Integer misses, Integer badCuts, List<Score> previousScores,
|
||||
Date timestamp, Double weight, double multiplier, int maxCombo) {
|
||||
double accuracy, Double pp, int score, String[] modifiers, Integer misses, Integer badCuts, DeviceInformation deviceInformation,
|
||||
List<Score> previousScores, Date timestamp, Double weight, double multiplier, int maxCombo) {
|
||||
super(id, playerId, platform, platformScoreId, leaderboardId, rank, accuracy, pp, score, modifiers, misses,
|
||||
badCuts, previousScores, timestamp);
|
||||
badCuts, deviceInformation, previousScores, timestamp);
|
||||
this.weight = weight;
|
||||
this.multiplier = multiplier;
|
||||
this.maxCombo = maxCombo;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cc.fascinated.model.score.impl.scoresaber;
|
||||
|
||||
import cc.fascinated.model.leaderboard.Leaderboard;
|
||||
import cc.fascinated.model.score.DeviceInformation;
|
||||
import cc.fascinated.model.score.Score;
|
||||
import cc.fascinated.model.user.UserDTO;
|
||||
import cc.fascinated.platform.Platform;
|
||||
@ -25,10 +26,11 @@ public class ScoreSaberScoreResponse extends ScoreSaberScore {
|
||||
private final Leaderboard leaderboard;
|
||||
|
||||
public ScoreSaberScoreResponse(long id, String playerId, Platform.Platforms platform, String platformScoreId, String leaderboardId, int rank,
|
||||
double accuracy, double pp, int score, String[] modifiers, int misses, int badCuts, List<Score> previousScores,
|
||||
Date timestamp, double weight, double multiplier, int maxCombo, UserDTO user, Leaderboard leaderboard) {
|
||||
double accuracy, double pp, int score, String[] modifiers, int misses, int badCuts, DeviceInformation deviceInformation,
|
||||
List<Score> previousScores, Date timestamp, double weight, double multiplier, int maxCombo, UserDTO user,
|
||||
Leaderboard leaderboard) {
|
||||
super(id, playerId, platform, platformScoreId, leaderboardId, rank, accuracy, pp, score, modifiers, misses, badCuts,
|
||||
previousScores, timestamp, weight, multiplier, maxCombo);
|
||||
deviceInformation, previousScores, timestamp, weight, multiplier, maxCombo);
|
||||
this.user = user;
|
||||
this.leaderboard = leaderboard;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public class ScoreSaberAccount {
|
||||
token.getCountry(),
|
||||
token.getRank(),
|
||||
token.getCountryRank(),
|
||||
DateUtils.getDateFromString(token.getFirstSeen()),
|
||||
DateUtils.getDateFromIsoString(token.getFirstSeen()),
|
||||
new Date()
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,17 @@
|
||||
package cc.fascinated.model.user;
|
||||
|
||||
import cc.fascinated.model.user.statistic.Statistic;
|
||||
import cc.fascinated.platform.Platform;
|
||||
import cc.fascinated.model.user.history.History;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.*;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.index.Indexed;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -21,11 +20,10 @@ import java.util.UUID;
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Log4j2
|
||||
@ToString
|
||||
@Document("user")
|
||||
public class User {
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy");
|
||||
|
||||
/**
|
||||
* The ID of the user.
|
||||
*/
|
||||
@ -67,55 +65,16 @@ public class User {
|
||||
/**
|
||||
* The user's statistic history.
|
||||
*/
|
||||
public Map<Platform.Platforms, Map<String, Statistic>> histories;
|
||||
public History history;
|
||||
|
||||
/**
|
||||
* The user's history points history.
|
||||
* Gets the user's statistic history
|
||||
*/
|
||||
@JsonIgnore
|
||||
public Map<Platform.Platforms, Map<Date, Statistic>> getHistories() {
|
||||
if (this.histories == null) {
|
||||
this.histories = new HashMap<>();
|
||||
public History getHistory() {
|
||||
if (this.history == null) {
|
||||
this.history = new History();
|
||||
}
|
||||
Map<Platform.Platforms, Map<Date, Statistic>> toReturn = new HashMap<>();
|
||||
for (Platform.Platforms platform : histories.keySet()) {
|
||||
toReturn.put(platform, getHistory(platform));
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the history points for a platform.
|
||||
*
|
||||
* @param platform the platform to get the statistics for
|
||||
* @return the statistics
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<Date, Statistic> getHistory(@NonNull Platform.Platforms platform) {
|
||||
if (this.histories == null) {
|
||||
this.histories = new HashMap<>();
|
||||
}
|
||||
Map<String, Statistic> statisticMap = this.histories.computeIfAbsent(platform, k -> new HashMap<>());
|
||||
Map<Date, Statistic> statistics = new HashMap<>();
|
||||
for (Map.Entry<String, Statistic> entry : statisticMap.entrySet()) {
|
||||
statistics.put(DATE_FORMAT.parse(entry.getKey()), entry.getValue());
|
||||
}
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a history point to the user's history.
|
||||
*
|
||||
* @param platform the platform to add the statistic for
|
||||
* @param date the date of the statistic
|
||||
* @param statistic the statistic to add
|
||||
*/
|
||||
public void addHistory(@NonNull Platform.Platforms platform, @NonNull Date date, @NonNull Statistic statistic) {
|
||||
if (this.histories == null) {
|
||||
this.histories = new HashMap<>();
|
||||
}
|
||||
Map<String, Statistic> statisticMap = this.histories.computeIfAbsent(platform, k -> new HashMap<>());
|
||||
statisticMap.put(String.valueOf(date.toString()), statistic);
|
||||
return this.history;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,78 @@
|
||||
package cc.fascinated.model.user.history;
|
||||
|
||||
import cc.fascinated.common.DateUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
public class History {
|
||||
/**
|
||||
* The user's history points in time.
|
||||
*/
|
||||
private Map<String, HistoryPoint> histories;
|
||||
|
||||
/**
|
||||
* The user's history points history.
|
||||
*/
|
||||
@JsonIgnore
|
||||
public Map<Date, HistoryPoint> getHistories() {
|
||||
if (this.histories == null) {
|
||||
this.histories = new HashMap<>();
|
||||
}
|
||||
Map<Date, HistoryPoint> toReturn = new HashMap<>();
|
||||
this.histories.forEach((key, value) -> toReturn.put(DateUtils.getDateFromString(key), value));
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's history for today.
|
||||
*
|
||||
* @return the user's history for today
|
||||
*/
|
||||
@JsonIgnore
|
||||
public HistoryPoint getTodayHistory() {
|
||||
if (this.histories == null) {
|
||||
this.histories = new HashMap<>();
|
||||
}
|
||||
Date midnight = DateUtils.getMidnightToday();
|
||||
return this.histories.computeIfAbsent(DateUtils.formatDate(midnight), key -> new HistoryPoint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's history for a specific date.
|
||||
*
|
||||
* @param date the date to get the history for
|
||||
* @return the user's history for the date
|
||||
*/
|
||||
public HistoryPoint getHistoryForDate(Date date) {
|
||||
if (this.histories == null) {
|
||||
this.histories = new HashMap<>();
|
||||
}
|
||||
return this.histories.get(DateUtils.formatDate(date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's HistoryPoint history for
|
||||
* an amount of days ago.
|
||||
*
|
||||
* @param days the amount of days ago
|
||||
* @return the user's HistoryPoint history
|
||||
*/
|
||||
public TreeMap<String, HistoryPoint> getPreviousHistories(int days) {
|
||||
Date date = DateUtils.getDaysAgo(days);
|
||||
Map<String, HistoryPoint> toReturn = new HashMap<>();
|
||||
for (Map.Entry<Date, HistoryPoint> history : getHistories().entrySet()) {
|
||||
if (history.getKey().after(date)) {
|
||||
toReturn.put(DateUtils.formatDate(history.getKey()), history.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the history by date (newest > oldest)
|
||||
TreeMap<String, HistoryPoint> sorted = new TreeMap<>(Comparator.comparing(DateUtils::getDateFromString).reversed());
|
||||
sorted.putAll(toReturn);
|
||||
return sorted;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package cc.fascinated.model.user.history;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class HistoryPoint {
|
||||
/**
|
||||
* The rank of the player.
|
||||
*/
|
||||
private Integer rank;
|
||||
|
||||
/**
|
||||
* The pp of the player.
|
||||
*/
|
||||
private Integer countryRank;
|
||||
|
||||
/**
|
||||
* The pp of the player.
|
||||
*/
|
||||
private Double pp;
|
||||
|
||||
/**
|
||||
* Play count of all the player's scores.
|
||||
*/
|
||||
private Integer totalPlayCount;
|
||||
|
||||
/**
|
||||
* Play count of all the player's ranked scores.
|
||||
*/
|
||||
private Integer totalRankedPlayCount;
|
||||
|
||||
/**
|
||||
* Play count for this day's unranked scores.
|
||||
*/
|
||||
private Integer unrankedPlayCount = 0;
|
||||
|
||||
/**
|
||||
* Play count for this day's ranked scores.
|
||||
*/
|
||||
private Integer rankedPlayCount = 0;
|
||||
|
||||
/**
|
||||
* Increment the total ranked play count for this day.
|
||||
*/
|
||||
public void incrementRankedPlayCount() {
|
||||
rankedPlayCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the total unranked play count for this day.
|
||||
*/
|
||||
public void incrementUnrankedPlayCount() {
|
||||
unrankedPlayCount++;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package cc.fascinated.model.user.hmd;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum DeviceController {
|
||||
UNKNOWN("Unknown"),
|
||||
|
||||
/**
|
||||
* Oculus Controllers
|
||||
*/
|
||||
OCULUS_QUEST_TOUCH("Touch"),
|
||||
OCULUS_QUEST_2_TOUCH("Quest 2 Touch"),
|
||||
OCULUS_QUEST_3_TOUCH("Quest 3 Touch"),
|
||||
|
||||
/**
|
||||
* HP Controllers
|
||||
*/
|
||||
HP_REVERB("HP Reverb"),
|
||||
|
||||
/**
|
||||
* Valve Controllers
|
||||
*/
|
||||
VALVE_KNUCKLES("Knuckles");
|
||||
|
||||
/**
|
||||
* The controller name
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Gets a controller by its name.
|
||||
*
|
||||
* @param name the name of the controller
|
||||
* @return the controller
|
||||
*/
|
||||
public static DeviceController getByName(String name) {
|
||||
for (DeviceController deviceController : values()) {
|
||||
if (deviceController.getName().equalsIgnoreCase(name)) {
|
||||
return deviceController;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package cc.fascinated.model.user.hmd;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum DeviceHeadset {
|
||||
UNKNOWN("Unknown"),
|
||||
|
||||
/**
|
||||
* Oculus HMDs
|
||||
*/
|
||||
OCULUS_CV1("Rift"),
|
||||
OCULUS_QUEST("Quest"),
|
||||
OCULUS_QUEST_2("Quest 2"),
|
||||
OCULUS_QUEST_3("Quest 3"),
|
||||
OCULUS_RIFT_S("Rift S"),
|
||||
|
||||
/**
|
||||
* HTC HMDs
|
||||
*/
|
||||
HTC_VIVE("Vive"),
|
||||
|
||||
/**
|
||||
* HP HMDs
|
||||
*/
|
||||
HP_REVERB("HP Reverb"),
|
||||
|
||||
/**
|
||||
* Valve HMDs
|
||||
*/
|
||||
VALVE_INDEX("Valve Index");
|
||||
|
||||
/**
|
||||
* The name of the headset.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Gets a headset by its name.
|
||||
*
|
||||
* @param name the name of the headset
|
||||
* @return the headset
|
||||
*/
|
||||
public static DeviceHeadset getByName(String name) {
|
||||
for (DeviceHeadset deviceHeadset : values()) {
|
||||
if (deviceHeadset.getName().equalsIgnoreCase(name)) {
|
||||
return deviceHeadset;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package cc.fascinated.model.user.statistic;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Document("player_histories")
|
||||
public class Statistic {
|
||||
/**
|
||||
* The rank of the player.
|
||||
*/
|
||||
private final int rank;
|
||||
|
||||
/**
|
||||
* The pp of the player.
|
||||
*/
|
||||
private final int countryRank;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package cc.fascinated.model.user.statistic.impl;
|
||||
|
||||
import cc.fascinated.model.user.statistic.Statistic;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@Getter
|
||||
public class ScoreSaberStatistic extends Statistic {
|
||||
/**
|
||||
* The total score of the player.
|
||||
*/
|
||||
private final long totalScore;
|
||||
|
||||
/**
|
||||
* The total ranked score of the player.
|
||||
*/
|
||||
private final long totalRankedScore;
|
||||
|
||||
/**
|
||||
* The average ranked accuracy of the player.
|
||||
*/
|
||||
private final double averageRankedAccuracy;
|
||||
|
||||
/**
|
||||
* The total play count of the player.
|
||||
*/
|
||||
private final int totalPlayCount;
|
||||
|
||||
public ScoreSaberStatistic(int rank, int countryRank, long totalScore, long totalRankedScore, double averageRankedAccuracy, int totalPlayCount) {
|
||||
super(rank, countryRank);
|
||||
this.totalScore = totalScore;
|
||||
this.totalRankedScore = totalRankedScore;
|
||||
this.averageRankedAccuracy = averageRankedAccuracy;
|
||||
this.totalPlayCount = totalPlayCount;
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ public abstract class Platform {
|
||||
* Called to update the players
|
||||
* data in QuestDB.
|
||||
*/
|
||||
public abstract void updatePlayers();
|
||||
public abstract void trackPlayerMetrics();
|
||||
|
||||
/**
|
||||
* Called to update the metrics
|
||||
|
@ -7,7 +7,7 @@ import cc.fascinated.model.token.ScoreSaberAccountToken;
|
||||
import cc.fascinated.model.token.ScoreSaberLeaderboardPageToken;
|
||||
import cc.fascinated.model.token.ScoreSaberLeaderboardToken;
|
||||
import cc.fascinated.model.user.User;
|
||||
import cc.fascinated.model.user.statistic.impl.ScoreSaberStatistic;
|
||||
import cc.fascinated.model.user.history.HistoryPoint;
|
||||
import cc.fascinated.platform.CurvePoint;
|
||||
import cc.fascinated.platform.Platform;
|
||||
import cc.fascinated.services.ScoreSaberService;
|
||||
@ -137,21 +137,19 @@ public class ScoreSaberPlatform extends Platform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePlayers() {
|
||||
Date date = DateUtils.alignToCurrentHour(new Date());
|
||||
public void trackPlayerMetrics() {
|
||||
Date date = DateUtils.getMidnightToday();
|
||||
for (User user : this.userService.getUsers(false)) {
|
||||
if (!user.isLinkedAccount()) { // Check if the user has linked their account
|
||||
continue;
|
||||
}
|
||||
ScoreSaberAccountToken account = scoreSaberService.getAccount(user); // Get the account from the ScoreSaber API
|
||||
user.addHistory(this.getPlatform(), date, new ScoreSaberStatistic(
|
||||
account.getRank(),
|
||||
account.getCountryRank(),
|
||||
account.getScoreStats().getTotalScore(),
|
||||
account.getScoreStats().getTotalRankedScore(),
|
||||
account.getScoreStats().getAverageRankedAccuracy(),
|
||||
account.getScoreStats().getTotalPlayCount()
|
||||
)); // Add the statistic to the user's history
|
||||
HistoryPoint history = user.getHistory().getHistoryForDate(date);
|
||||
history.setPp(account.getPp());
|
||||
history.setRank(account.getRank());
|
||||
history.setCountryRank(account.getCountryRank());
|
||||
history.setTotalPlayCount(account.getScoreStats().getTotalPlayCount());
|
||||
history.setTotalRankedPlayCount((int) account.getScoreStats().getTotalRankedScore());
|
||||
this.userService.saveUser(user); // Save the user
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ public class PlatformService {
|
||||
/**
|
||||
* Updates the platform metrics.
|
||||
* <p>
|
||||
* This method is scheduled to run every minute.
|
||||
* This method is scheduled to run every 5 minutes.
|
||||
* </p>
|
||||
*/
|
||||
@Scheduled(cron = "0 */5 * * * *")
|
||||
@ -65,7 +65,7 @@ public class PlatformService {
|
||||
public void updatePlayerMetrics() {
|
||||
log.info("Updating %s platform player metrics...".formatted(this.platforms.size()));
|
||||
for (Platform platform : this.platforms) {
|
||||
platform.updatePlayers();
|
||||
platform.trackPlayerMetrics();
|
||||
}
|
||||
log.info("Finished updating platform player metrics.");
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import cc.fascinated.common.EnumUtils;
|
||||
import cc.fascinated.common.MathUtils;
|
||||
import cc.fascinated.common.Tuple;
|
||||
import cc.fascinated.model.leaderboard.Leaderboard;
|
||||
import cc.fascinated.model.score.DeviceInformation;
|
||||
import cc.fascinated.model.score.Score;
|
||||
import cc.fascinated.model.score.TotalScoresResponse;
|
||||
import cc.fascinated.model.score.impl.scoresaber.ScoreSaberScore;
|
||||
@ -14,6 +15,9 @@ import cc.fascinated.model.token.ScoreSaberPlayerScoreToken;
|
||||
import cc.fascinated.model.token.ScoreSaberScoreToken;
|
||||
import cc.fascinated.model.user.User;
|
||||
import cc.fascinated.model.user.UserDTO;
|
||||
import cc.fascinated.model.user.history.HistoryPoint;
|
||||
import cc.fascinated.model.user.hmd.DeviceController;
|
||||
import cc.fascinated.model.user.hmd.DeviceHeadset;
|
||||
import cc.fascinated.platform.Platform;
|
||||
import cc.fascinated.repository.ScoreRepository;
|
||||
import lombok.NonNull;
|
||||
@ -96,6 +100,7 @@ public class ScoreService {
|
||||
score.getModifiers(),
|
||||
score.getMisses(),
|
||||
score.getBadCuts(),
|
||||
score.getDeviceInformation(),
|
||||
score.getPreviousScores(),
|
||||
score.getTimestamp(),
|
||||
scoreSaberScore.getWeight(),
|
||||
@ -130,6 +135,12 @@ public class ScoreService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total scores for the platform.
|
||||
*
|
||||
* @param platform The platform to get the scores from.
|
||||
* @return The total scores.
|
||||
*/
|
||||
public TotalScoresResponse getTotalScores(Platform.Platforms platform) {
|
||||
return new TotalScoresResponse(
|
||||
scoreRepository.getTotalScores(platform),
|
||||
@ -179,15 +190,27 @@ public class ScoreService {
|
||||
modifiers.length == 0 ? null : modifiers, // no modifiers, set to null to save data
|
||||
score.getMissedNotes() == 0 ? null : score.getMissedNotes(), // no misses, set to null to save data
|
||||
score.getBadCuts() == 0 ? null : score.getBadCuts(), // no bad cuts, set to null to save data
|
||||
new DeviceInformation(
|
||||
score.getDeviceHmd() == null ? DeviceHeadset.UNKNOWN : DeviceHeadset.getByName(score.getDeviceHmd()),
|
||||
score.getDeviceControllerLeft() == null ? DeviceController.UNKNOWN : DeviceController.getByName(score.getDeviceControllerLeft()),
|
||||
score.getDeviceControllerRight() == null ? DeviceController.UNKNOWN : DeviceController.getByName(score.getDeviceControllerRight())
|
||||
),
|
||||
previousScores,
|
||||
DateUtils.getDateFromString(score.getTimeSet()),
|
||||
DateUtils.getDateFromIsoString(score.getTimeSet()),
|
||||
score.getWeight() == 0 ? null : score.getWeight(), // no weight, set to null to save data
|
||||
score.getMultiplier(),
|
||||
score.getMaxCombo()
|
||||
);
|
||||
scoreRepository.save(scoreSaberScore);
|
||||
this.saveScore(user, scoreSaberScore);
|
||||
this.logScore(Platform.Platforms.SCORESABER, Leaderboard.getFromScoreSaberToken(leaderboard), scoreSaberScore, user,
|
||||
previousScoreExists && previousScore.getScore() < scoreSaberScore.getScore());
|
||||
if (scoreSaberScore.getDeviceInformation().containsUnknownDevices()) {
|
||||
log.warn(" - Score contains unknown device: hmd: {}, controller left: {}, controller right: {}",
|
||||
score.getDeviceHmd(),
|
||||
score.getDeviceControllerLeft(),
|
||||
score.getDeviceControllerRight()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,6 +262,22 @@ public class ScoreService {
|
||||
return scoreRepository.getBestImprovedScores(platform, DateUtils.getDaysAgo(days));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a score.
|
||||
*
|
||||
* @param score The score to save.
|
||||
*/
|
||||
public void saveScore(User user, Score score) {
|
||||
HistoryPoint todayHistory = user.getHistory().getTodayHistory();
|
||||
if (score.isRanked()) {
|
||||
todayHistory.incrementRankedPlayCount();
|
||||
} else {
|
||||
todayHistory.incrementUnrankedPlayCount();
|
||||
}
|
||||
userService.saveUser(user); // Save the user
|
||||
scoreRepository.save(score); // Save the score
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a score.
|
||||
*
|
||||
@ -250,6 +289,7 @@ public class ScoreService {
|
||||
@NonNull User user, boolean improvedScore) {
|
||||
String platformName = EnumUtils.getEnumName(platform);
|
||||
boolean isRanked = score.getPp() != 0;
|
||||
|
||||
log.info("[{}] {}Tracked{} Score! id: {}, acc: {}%, {} score id: {},{} leaderboard: {}, difficulty: {}, player: {} ({})",
|
||||
platformName,
|
||||
improvedScore ? "Improved " : "",
|
||||
|
Reference in New Issue
Block a user