bug6069: Sailor Profile Name is available as a new dimension in data mining

This commit is contained in:
Axel Uhl
2024-11-13 18:01:45 +01:00
parent 4f138c6682
commit e7ff455676
9 changed files with 150 additions and 8 deletions
@@ -285,4 +285,5 @@ RatioDurationLongVsShortTack=Ratio duration long tack / short tack
Discarded=Discarded
DurationFromRaceStartToFirstTack=Duration from race start to first tack
TypeOfFirstLeg=Type of first leg
TenthInLeg=Which tenth of the leg (1-10)
TenthInLeg=Which tenth of the leg (1-10)
SailorProfile=Sailor Profile
@@ -283,4 +283,5 @@ RatioDurationLongVsShortTack=Verhältnis gesegelte Dauer auf Streck- zu Holebug
Discarded=Gestrichen
DurationFromRaceStartToFirstTack=Dauer vom Start der Wettfahrt bis zur ersten Wende
TypeOfFirstLeg=Art des ersten Bahnschenkels
TenthInLeg=Zehntel des Bahnschenkels (1-10)
TenthInLeg=Zehntel des Bahnschenkels (1-10)
SailorProfile=Seglerprofil
@@ -34,6 +34,11 @@ public interface AbstractHasTrackedLegSliceOfCompetitorContext extends HasWindOn
@Connector(messageKey="Competitor")
public Competitor getCompetitor();
@Connector(messageKey="SailorProfile")
default public SailorProfile getSailorProfile() {
return getTrackedLegContext().getTrackedRaceContext().getLeaderboardContext().getLeaderboardGroupContext().getSailorProfiles().getProfileForCompetitor(getCompetitor());
}
@Statistic(messageKey="DistanceTraveled", resultDecimals=2, ordinal=0)
public Distance getDistanceTraveled();
@@ -0,0 +1,51 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.server.preferences.SailorProfilePreference;
import com.sap.sailing.server.preferences.SailorProfilePreferences;
import com.sap.sse.common.NamedWithUUID;
import com.sap.sse.common.Util;
/**
* A sailor profile currently mainly consists of a set of competitors that it consolidates into a single profile. It can
* be used particularly in a data mining context when there are multiple distinct records that from a user's point of
* view represent the same sailor/competitor. With this, a user can define a {@link SailorProfile} like an "equivalence
* class" that allows the user to, e.g., filter or group data mining results based on a competitor's "equivalence
* class."
* <p>
*
* A typical example would be a single-handed class, say "ILCA 6," where the same person has been participating in
* several events, but each time captured with a different technical competitor ID, maybe even competing under different
* sail numbers. Yet, during analysis, it may make sense to group the results for all of these entities under one sailor
* profile.
* <p>
*
* Technically, the sailor profiles are managed as user preferences in the user store. A preference converter can turn
* the underlying JSON string into an {@link SailorProfilePreferences} object which contains
* {@link SailorProfilePreference}. These, in turn, can be converted to instances of a type implementing this interface.
* <p>
*
* Comparability is defined such that no two profiles with different {@link #getId() IDs} will ever compare equal.
* Beyond this, {@link SailorProfile}s with a greater number of {@link #getCompetitors() competitors} than another will
* be considered "greater." In case of an equal number of competitors, the sailor profile {@link #getName() name} will
* be used for lexicographic ordering. In case of equal names, sorting will work by the stringified version of the
* {@link #getId() UUID}.
*
* @author Axel Uhl (d043530)
*
*/
public interface SailorProfile extends NamedWithUUID, Comparable<SailorProfile> {
Iterable<Competitor> getCompetitors();
@Override
default int compareTo(SailorProfile o) {
int result = Integer.compare(Util.size(getCompetitors()), Util.size(o.getCompetitors()));
if (result == 0) {
result = getName().compareTo(o.getName());
if (result == 0) {
result = getId().compareTo(o.getId());
}
}
return result;
}
}
@@ -1,13 +1,21 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.Competitor;
/**
* A "data mining" view on the current user's sailor profiles as define in the user preferences.
* When constructed, it collects all of the user's sailor profiles and builds a map from the
* {@link Competitor} objects to the {@link SailorProfile} objects that refer to that competitor.
* A "data mining" view on the current user's sailor profiles as defined in the user preferences. When an instance is
* constructed, it collects all of the user's sailor profiles and builds a map from the {@link Competitor} objects to
* the {@link SailorProfile} objects that refer to that {@link Competitor}.
* <p>
*
* When more than one of the user's {@link SailorProfile}s comprise the same {@link Competitor}, a stable algorithm
* for determining a single preferred {@link SailorProfile} for the {@link Competitor} will be used in the
* {@link #getProfileForCompetitor(Competitor)}, finding the "greatest" (as defined by the {@link Comparable} property
* of the {@link SailorProfile} interface) profile for the {@link Competitor}.
*
* @author Axel Uhl (d043530)
*
*/
public interface SailorProfiles {
SailorProfile getProfileForCompetitor(Competitor competitor);
}
@@ -1,6 +1,10 @@
package com.sap.sailing.datamining.impl.data;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.datamining.data.HasLeaderboardGroupContext;
import com.sap.sailing.datamining.data.SailorProfile;
import com.sap.sailing.datamining.data.SailorProfiles;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
@@ -51,8 +55,13 @@ public class LeaderboardGroupWithContext implements HasLeaderboardGroupContext {
public synchronized SailorProfiles getSailorProfiles() {
if (sailorProfiles == null) {
final SailorProfilePreferences sailorProfilePreferences = getSecurityService().getPreferenceObject(getSecurityService().getCurrentUser().getName(), SailorProfilePreferences.PREF_NAME);
for (final SailorProfilePreference sailorProfilePreference : sailorProfilePreferences.getSailorProfiles()) {
if (sailorProfilePreferences != null) {
final List<SailorProfile> theSailorProfiles = new ArrayList<>();
for (final SailorProfilePreference sailorProfilePreference : sailorProfilePreferences.getSailorProfiles()) {
final SailorProfile sailorProfile = new SailorProfileImpl(sailorProfilePreference.getUuid(), sailorProfilePreference.getName(), sailorProfilePreference.getCompetitors());
theSailorProfiles.add(sailorProfile);
}
sailorProfiles = new SailorProfilesImpl(theSailorProfiles);
}
}
return sailorProfiles;
@@ -0,0 +1,29 @@
package com.sap.sailing.datamining.impl.data;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import com.sap.sailing.datamining.data.SailorProfile;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.NamedWithUUIDImpl;
public class SailorProfileImpl extends NamedWithUUIDImpl implements SailorProfile {
private static final long serialVersionUID = 5851705759679719738L;
private final List<Competitor> competitors;
public SailorProfileImpl(UUID id, String name, Iterable<Competitor> competitors) {
super(name, id);
final List<Competitor> myCompetitors = new ArrayList<>();
Util.addAll(competitors, myCompetitors);
this.competitors = myCompetitors;
}
@Override
public Iterable<Competitor> getCompetitors() {
return Collections.unmodifiableList(competitors);
}
}
@@ -0,0 +1,35 @@
package com.sap.sailing.datamining.impl.data;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.datamining.data.SailorProfile;
import com.sap.sailing.datamining.data.SailorProfiles;
import com.sap.sailing.domain.base.Competitor;
public class SailorProfilesImpl implements SailorProfiles {
final Map<Competitor, SailorProfile> profilesForCompetitor;
/**
* Constructs the mapping from {@link Competitor} to {@link SailorProfile} based on the
* {@link SailorProfile#compareTo(SailorProfile) ordering} of the {@link SailorProfile}s. For each competitor,
* {@link #getProfileForCompetitor(Competitor)} will return the "greatest" profile found that
* {@link SailorProfile#getCompetitors() contains} that competitor.
*/
public SailorProfilesImpl(final Iterable<SailorProfile> sailorProfiles) {
this.profilesForCompetitor = new HashMap<>();
for (final SailorProfile sailorProfile : sailorProfiles) {
for (final Competitor competitor : sailorProfile.getCompetitors()) {
final SailorProfile existingSailorProfileForCompetitor = profilesForCompetitor.get(competitor);
if (existingSailorProfileForCompetitor == null || existingSailorProfileForCompetitor.compareTo(sailorProfile) < 0) {
profilesForCompetitor.put(competitor, sailorProfile);
}
}
}
}
@Override
public SailorProfile getProfileForCompetitor(Competitor competitor) {
return profilesForCompetitor.get(competitor);
}
}