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);
}
}
@@ -25,6 +25,9 @@
leg's end waypoint (e.g., the windward mark for an upwind leg). The metrics are again
available in signed/unsigned as well as in absolute and relative (to half the leg distance)
variants.</li>
<li>Introduced a new Data Mining dimension "Sailor Profile Name." With this, if a user in their "User Details"
creates one or more "Sailor Profiles" to group multiple occurrences of what is to be considered "the same sailor"
into one profile, these profiles can be used for filtering and grouping in data mining.</li>
</ul>
<h5 class="articleSubheadline">October 2024</h5>
<ul class="bulletList">