Merge branch 'master' into racecommittee_integrated

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
This commit is contained in:
Armin Zamani committed 2012-12-09 20:36:22 +01:00
commit b05d0b9198
66 files changed
+1078 -403

No files matched your search

@@ -15,7 +15,8 @@ public enum DetailType implements Serializable {
DISTANCE_TRAVELED(0, ASCENDING), AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_RANK(0, ASCENDING),
RANK_GAIN(0, DESCENDING), NUMBER_OF_MANEUVERS(0, ASCENDING), CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING),
ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS(1, ASCENDING), VELOCITY_MADE_GOOD_IN_KNOTS(2, DESCENDING),
GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING),
GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), GAP_CHANGE_SINCE_LEG_START_IN_SECONDS(0, ASCENDING),
SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING),
AVERAGE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), RACE_DISTANCE_TRAVELED(0, ASCENDING),
RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_GAP_TO_LEADER_IN_SECONDS(0, ASCENDING),
RACE_DISTANCE_TO_LEADER_IN_METERS(0, ASCENDING), RACE_AVERAGE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING),
@@ -10,43 +10,55 @@ package com.sap.sailing.domain.common;
public enum MaxPointsReason {
/** The competitor finished the race properly */
NONE(true),
NONE(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
/** Did Not Start */
DNS(true),
DNS(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** Did Not Finish */
DNF(true),
DNF(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** DiSQualified */
DSQ(true),
DSQ(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** On Course Side (jumped the gun) */
OCS(true),
OCS(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** Disqualified, non-discardable */
DND(false),
DND(/* discardable */ false, /* advanceCompetitorsTrackedWorse */ true),
/** 20 % penalty under rule 30.2 */
ZFP(true),
ZFP(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
/** Took a Scoring penalty under rule 44.3 (a) */
SCP(true),
SCP(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
/** Disqualification not excludable under rule 90.3 (b) */
DNE(false),
DNE(/* discardable */ false, /* advanceCompetitorsTrackedWorse */ true),
/** Disqualification for gross misconduct not excludable under rule 90.3 (b) */
DGM(false),
DGM(/* discardable */ false, /* advanceCompetitorsTrackedWorse */ true),
/** Redress given */
RDG(true),
RDG(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
/** Black Flag Disqualified */
BFD(true),
BFD(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** Did Not Compete */
DNC(true),
DNC(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** Retired After Finishing */
RAF(true),
RAF(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** Discretionary Penalty Imposed by the race committee */
DPI(true);
DPI(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false);
private final boolean discardable;
private final boolean advanceCompetitorsTrackedWorse;
private MaxPointsReason(boolean discardable) {
private MaxPointsReason(boolean discardable, boolean advanceCompetitorsTrackedWorse) {
this.discardable = discardable;
this.advanceCompetitorsTrackedWorse = advanceCompetitorsTrackedWorse;
}
public boolean isDiscardable() {
return discardable;
}
/**
* Most "max points reasons" are a penalty. If the competitor has finished the race and by the penalty is ranked to the "bottom,"
* those competitors tracked worse usually will advance by one rank. However, "max points reasons" such as a redress given (RDG)
* are different. In this case, only the points awarded to the competitor change without any effect on other competitors.
*/
public boolean isAdvanceCompetitorsTrackedWorse() {
return advanceCompetitorsTrackedWorse;
}
}
@@ -90,6 +90,7 @@ public class MarkPassingReceiver extends AbstractReceiverWithQueue<RaceCompetito
markPassings.add(passing);
}
}
logger.fine("Received mark passings in race "+trackedRace.getRace().getName()+": "+markPassings);
if (getSimulator() != null) {
getSimulator().delayMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()), markPassings);
} else {
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Pair;
/**
* A leaderboard has a scoring scheme that decides how race ranks map to scores, how penalties are to be scored,
@@ -67,7 +68,8 @@ public interface ScoringScheme extends Serializable {
* @param competitor1Scores scores of the first competitor, in the order of race columns in the leaderboard
* @param competitor2Scores scores of the second competitor, in the order of race columns in the leaderboard
*/
int compareByBetterScore(List<Double> competitor1Scores, List<Double> competitor2Scores, boolean nullScoresAreBetter);
int compareByBetterScore(List<Pair<RaceColumn, Double>> competitor1Scores,
List<Pair<RaceColumn, Double>> competitor2Scores, boolean nullScoresAreBetter);
/**
* In case two competitors scored in different numbers of races, this scoring scheme decides whether this
@@ -81,7 +83,7 @@ public interface ScoringScheme extends Serializable {
/**
* Usually, when all other sorting criteria end up in a tie, the last race sailed is used to decide.
*/
int compareByLastRace(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter);
int compareByLastRace(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter);
/**
* Under certain circumstances, a scoring scheme may decide that the scores of a column are not (yet) to be used
@@ -107,8 +107,9 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
}
/**
* Per competitor disqualified ({@link ScoreCorrection} has a {@link MaxPointsReason} for the competitor), all
* competitors ranked worse by the tracking system need to have their rank corrected by one.
* Per competitor disqualified ({@link ScoreCorrection} has a {@link MaxPointsReason} for the competitor that has
* <code>{@link MaxPointsReason#isAdvanceCompetitorsTrackedWorse()}==true</code>), all competitors ranked worse by
* the tracking system need to have their rank corrected by one.
*
* @param trackedRace
* the race to which the rank refers; look for disqualifications / max points reasons in this column
@@ -131,7 +132,8 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
while (betterCompetitorRank < rank && ci.hasNext()) {
Competitor betterTrackedCompetitor = ci.next();
MaxPointsReason maxPointsReasonForBetterCompetitor = getScoreCorrection().getMaxPointsReason(betterTrackedCompetitor, raceColumn);
if (maxPointsReasonForBetterCompetitor != null && maxPointsReasonForBetterCompetitor != MaxPointsReason.NONE) {
if (maxPointsReasonForBetterCompetitor != null && maxPointsReasonForBetterCompetitor != MaxPointsReason.NONE &&
maxPointsReasonForBetterCompetitor.isAdvanceCompetitorsTrackedWorse()) {
correctedRank--;
}
betterCompetitorRank++;
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -120,16 +121,22 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
* Assuming both competitors scored in the same number of races, compares the sorted scores.
*/
@Override
public int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
public int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
assert o1Scores.size() == o2Scores.size();
Comparator<Double> scoreComparator = getScoreComparator(nullScoresAreBetter);
List<Double> o1ScoresCopy = new ArrayList<Double>(o1Scores);
List<Double> o2ScoresCopy = new ArrayList<Double>(o2Scores);
final Comparator<Double> pureScoreComparator = getScoreComparator(nullScoresAreBetter);
Comparator<Pair<RaceColumn, Double>> scoreComparator = new Comparator<Pair<RaceColumn, Double>>() {
@Override
public int compare(Pair<RaceColumn, Double> o1, Pair<RaceColumn, Double> o2) {
return pureScoreComparator.compare(o1.getB(), o2.getB());
}
};
List<Pair<RaceColumn, Double>> o1ScoresCopy = new ArrayList<Pair<RaceColumn, Double>>(o1Scores);
List<Pair<RaceColumn, Double>> o2ScoresCopy = new ArrayList<Pair<RaceColumn, Double>>(o2Scores);
Collections.sort(o1ScoresCopy, scoreComparator);
Collections.sort(o2ScoresCopy, scoreComparator);
// now both lists are sorted from best to worst score
Iterator<Double> o1Iter = o1ScoresCopy.iterator();
Iterator<Double> o2Iter = o2ScoresCopy.iterator();
Iterator<Pair<RaceColumn, Double>> o1Iter = o1ScoresCopy.iterator();
Iterator<Pair<RaceColumn, Double>> o2Iter = o2ScoresCopy.iterator();
int result = 0;
while (result == 0 && o1Iter.hasNext() && o2Iter.hasNext()) {
result = scoreComparator.compare(o1Iter.next(), o2Iter.next());
@@ -147,10 +154,11 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
}
@Override
public int compareByLastRace(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
public int compareByLastRace(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
int result = 0;
if (!o1Scores.isEmpty() && !o2Scores.isEmpty()) {
result = getScoreComparator(nullScoresAreBetter).compare(o1Scores.get(o1Scores.size()-1), o2Scores.get(o2Scores.size()-1));
result = getScoreComparator(nullScoresAreBetter).compare(o1Scores.get(o1Scores.size()-1).getB(),
o2Scores.get(o2Scores.size()-1).getB());
}
return result;
}
@@ -5,6 +5,7 @@ import java.util.List;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.impl.Util.Pair;
/**
* A variant of the {@link HighPoint} scoring scheme which breaks ties differently and which assigns a score of 10 to
@@ -42,13 +43,13 @@ public class HighPointExtremeSailingSeriesOverall extends HighPoint {
* Implements rule 13.5 of the Extreme Sailing Series notice of race as of August 2012.
*/
@Override
public int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
public int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
assert o1Scores.size() == o2Scores.size();
int o1Wins = getWins(o1Scores);
int o2Wins = getWins(o2Scores);
int result = o1Wins - o2Wins;
int result = o2Wins - o1Wins;
if (result == 0 && o1Scores.size() >= 1 && o2Scores.size() >= 1) {
result = o1Scores.get(o1Scores.size()-1).compareTo(o2Scores.get(o2Scores.size()-1));
result = o1Scores.get(o1Scores.size()-1).getB().compareTo(o2Scores.get(o2Scores.size()-1).getB());
}
return result;
}
@@ -57,10 +58,10 @@ public class HighPointExtremeSailingSeriesOverall extends HighPoint {
* Counts a competitor's wins by comparing the scores to {@link #MAX_POINTS} which is the score attributed to a race
* won
*/
private int getWins(List<Double> scores) {
private int getWins(List<Pair<RaceColumn, Double>> scores) {
int wins = 0;
for (Double score : scores) {
if (score == MAX_POINTS) {
for (Pair<RaceColumn, Double> score : scores) {
if (score.getB() == MAX_POINTS * score.getA().getFactor()) {
wins++;
}
}
@@ -2,7 +2,9 @@ package com.sap.sailing.domain.leaderboard.impl;
import java.util.List;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.impl.Util.Pair;
/**
* In this variant of the {@link HighPoint} scoring scheme, scoring by "better race" (sometimes referred to as a
@@ -16,7 +18,7 @@ public class HighPointLastBreaksTie extends HighPoint {
private static final long serialVersionUID = -5338636946886101669L;
@Override
public int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
public int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
return 0;
}
@@ -93,8 +93,8 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
@Override
public int compare(Competitor o1, Competitor o2) {
List<Double> o1Scores = new ArrayList<Double>();
List<Double> o2Scores = new ArrayList<Double>();
List<Pair<RaceColumn, Double>> o1Scores = new ArrayList<Pair<RaceColumn, Double>>();
List<Pair<RaceColumn, Double>> o2Scores = new ArrayList<Pair<RaceColumn, Double>>();
double o1ScoreSum = getLeaderboard().getCarriedPoints(o1);
double o2ScoreSum = getLeaderboard().getCarriedPoints(o2);
Double o1MedalRaceScore = 0.0;
@@ -104,12 +104,12 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
int preemptiveColumnResult = 0;
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
if (o1Score != null) {
o1Scores.add(o1Score);
o1Scores.add(new Pair<RaceColumn, Double>(raceColumn, o1Score));
o1ScoreSum += o1Score;
}
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
if (o2Score != null) {
o2Scores.add(o2Score);
o2Scores.add(new Pair<RaceColumn, Double>(raceColumn, o2Score));
o2ScoreSum += o2Score;
}
if (raceColumn.isMedalRace()) {
@@ -237,7 +237,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
*
* @see ScoringScheme#compareByBetterScore(List, List, boolean)
*/
protected int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores) {
protected int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores) {
return scoringScheme.compareByBetterScore(o1Scores, o2Scores, nullScoresAreBetter);
}
@@ -0,0 +1,10 @@
package com.sap.sailing.domain.tracking;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Tack;
public interface MarkPassingManeuver extends Maneuver {
Waypoint getWaypointPassed();
Tack getSide();
}
@@ -78,11 +78,14 @@ public interface TrackedLegOfCompetitor extends Serializable {
/**
* Infers the maneuvers of the competitor up to <code>timePoint</code> on this leg. If the competitor hasn't started
* the leg at the time point specified, <code>null</code> is returned. Otherwise, the list will be valid. If the
* time point is after the competitor has finished this leg, all of the competitor's maneuvers during this leg will
* be reported in chronological order. The list may be empty if no maneuvers happened between the point in time when
* the competitor started the leg and <code>timePoint</code>.
* @param waitForLatest TODO
* the leg at the time point specified, an empty list is returned. If the time point is after the competitor has
* finished this leg, all of the competitor's maneuvers during this leg will be reported in chronological order. The
* list may be empty if no maneuvers happened between the point in time when the competitor started the leg and
* <code>timePoint</code>.<p>
*
* Note that the mark passing maneuver at leg start and finish are not guaranteed to be part of this leg's maneuvers. They
* may be part of the respective adjacent leg, depending on the maneuver's time point which may be slightly before, at, or
* after the corresponding mark passing event.
*/
List<Maneuver> getManeuvers(TimePoint timePoint, boolean waitForLatest) throws NoWindException;
@@ -190,7 +193,8 @@ public interface TrackedLegOfCompetitor extends Serializable {
* as defined by <code>timePointBeforeManeuver</code> is extrapolated until <code>timePointAfterManeuver</code>,
* and the resulting extrapolated position's "windward distance" is computed to the competitor's actual position
* at that time. This distance is returned as the result of this method.
* @param maneuverTimePoint TODO
*/
Distance getManeuverLoss(TimePoint timePointBeforeManeuver, TimePoint maneuverTimePoint, TimePoint timePointAfterManeuver) throws NoWindException;
TrackedLeg getTrackedLeg();
}
@@ -0,0 +1,43 @@
package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
public class MarkPassingManeuverImpl extends ManeuverImpl implements MarkPassingManeuver {
private static final long serialVersionUID = 8935348908557191614L;
private final Waypoint waypointPassed;
private final Tack side;
public MarkPassingManeuverImpl(ManeuverType type, Tack newTack, Position position, TimePoint timePoint,
SpeedWithBearing speedWithBearingBefore, SpeedWithBearing speedWithBearingAfter,
double directionChangeInDegrees, Distance maneuverLoss, Waypoint waypointPassed, Tack side) {
super(type, newTack, position, timePoint, speedWithBearingBefore, speedWithBearingAfter,
directionChangeInDegrees, maneuverLoss);
this.waypointPassed = waypointPassed;
this.side = side;
}
@Override
public Waypoint getWaypointPassed() {
return waypointPassed;
}
@Override
public Tack getSide() {
return side;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(super.toString());
result.append(", passed waypoint "+getWaypointPassed()+" to "+getSide().name());
return result.toString();
}
}
@@ -46,7 +46,8 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
this.competitor = competitor;
}
protected TrackedLegImpl getTrackedLeg() {
@Override
public TrackedLegImpl getTrackedLeg() {
return trackedLeg;
}
@@ -430,7 +431,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
return getGapToLeaderInSeconds(timePoint, new LeaderGetter() {
@Override
public Competitor getLeader() {
return getTrackedLeg().getLeader(timePoint);
return getTrackedLeg().getLeader(hasFinishedLeg(timePoint) ? getFinishTime() : timePoint);
}
});
}
@@ -1915,12 +1915,16 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
// the TrackedLegOfCompetitor variables may be null, e.g., in case the time points are before or after the race
TrackedLegOfCompetitor legBeforeManeuver = getTrackedLeg(competitor, timePointBeforeManeuver);
TrackedLegOfCompetitor legAfterManeuver = getTrackedLeg(competitor, timePointAfterManeuver);
Waypoint waypointPassed = null; // set for MARK_PASSING maneuvers only
Tack sideToWhichWaypointWasPassed = null; // set for MARK_PASSING maneuvers only
// check for mask passing first; a tacking / jibe-setting mark rounding thus takes precedence over being detected as a penalty circle
if (legBeforeManeuver != legAfterManeuver
// a maneuver at the start line is not to be considered a MARK_PASSING maneuver; show a tack as a tack
&& legAfterManeuver != null
&& legAfterManeuver.getLeg().getFrom() != getRace().getCourse().getFirstWaypoint()) {
maneuverType = ManeuverType.MARK_PASSING;
waypointPassed = legAfterManeuver.getLeg().getFrom();
sideToWhichWaypointWasPassed = totalCourseChangeInDegrees < 0 ? Tack.PORT : Tack.STARBOARD;
} else if (Math.abs(totalCourseChangeInDegrees) > PENALTY_CIRCLE_DEGREES_THRESHOLD) {
maneuverType = ManeuverType.PENALTY_CIRCLE;
if (legBeforeManeuver != null) {
@@ -1973,9 +1977,16 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
: ManeuverType.BEAR_AWAY;
}
}
Maneuver maneuver = new ManeuverImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverTimePoint,
speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd,
totalCourseChangeInDegrees, maneuverLoss);
final Maneuver maneuver;
if (maneuverType == ManeuverType.MARK_PASSING) {
maneuver = new MarkPassingManeuverImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverTimePoint,
speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd,
totalCourseChangeInDegrees, maneuverLoss, waypointPassed, sideToWhichWaypointWasPassed);
} else {
maneuver = new ManeuverImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverTimePoint,
speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd,
totalCourseChangeInDegrees, maneuverLoss);
}
return maneuver;
}
@@ -18,8 +18,9 @@ public class LeaderboardPanelMock extends LeaderboardPanel {
String leaderboardName, ErrorReporter errorReporter,
StringMessages stringConstants) {
super(sailingService, null, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */false), new CompetitorSelectionModel(
/* hasMultiSelection */true), leaderboardName, null, errorReporter, stringConstants, null, /* showRaceDetails */ true);
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false,
/* showMetaLeaderboardsOnSamePage */ false), new CompetitorSelectionModel(
/* hasMultiSelection */true), leaderboardName, errorReporter, stringConstants, null, /* showRaceDetails */ true);
}
@Override
+13 -3
View File
@@ -181,9 +181,19 @@ body ::-webkit-scrollbar-track {
font-family: 'UbuntuMedium', Arial, Verdana, sans-serif;
font-size: 18px;
color: #fff;
margin: 13px 0 0 0;
margin-top: 13px;
}
.titleLabelRight {
text-align: right;
font-family: 'UbuntuMedium', Arial, Verdana, sans-serif;
font-size: 18px;
color: #fff;
margin-top: 13px;
margin-right: 12px;
}
.subTitleLabelWrapper {
position: absolute;
min-width: 200px;
@@ -444,7 +454,8 @@ body ::-webkit-scrollbar-track {
.sapLogo {
float: left;
margin: 13px 0 0 12px;
margin-top: 13px;
margin-left: 12px;
}
.sailingAnalyticsLabelPanel {
@@ -546,7 +557,6 @@ body ::-webkit-scrollbar-track {
cursor: default;
color: #888;
background: #F1F1F1;
border: 1px solid #919090;
top: 0;
}
+10 -1
View File
@@ -47,8 +47,11 @@ h1 {
table {
border-collapse: separate; }
.leaderboardMargin {
margin: 5px;
}
.leaderboardContent {
margin: 5px;
}
.leaderboardContent td, .leaderboardContent th {
@@ -119,6 +122,12 @@ body, table td, select, button {
margin-top: 22px;
}
.leaderboardHeading {
font-size: 16px;
font-family: 'UbuntuRegular', Arial, Verdana, sans-serif;
font-weight: bold;
}
.gwt-DialogBox {
z-index: 20;
}
@@ -19,8 +19,7 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter {
public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter, WindowSizeDetector {
private DialogBox errorDialogBox;
private HTML serverResponseLabel;
private Button dialogCloseButton;
@@ -75,9 +74,15 @@ public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter {
reportError(message);
}
}
@Override
public boolean isSmallWidth() {
int width = Window.getClientWidth();
return width <= 720;
}
public void createErrorPage(String message) {
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages);
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages, this);
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
RootPanel.get().add(logoAndTitlePanel);
RootPanel.get().add(new Label(message));
@@ -25,6 +25,10 @@ public class DetailTypeFormatter {
return stringMessages.velocityMadeGoodInKnots();
case GAP_TO_LEADER_IN_SECONDS:
return stringMessages.gapToLeaderInSeconds();
case GAP_CHANGE_SINCE_LEG_START_IN_SECONDS:
return stringMessages.gapChangeSinceLegStartInSeconds();
case SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED:
return stringMessages.sideToWhichMarkAtLegStartWasRounded();
case WINDWARD_DISTANCE_TO_GO_IN_METERS:
return stringMessages.windwardDistanceToGoInMeters();
case RACE_DISTANCE_TRAVELED:
@@ -4,47 +4,72 @@ import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RequiresResize;
public class LogoAndTitlePanel extends FlowPanel {
protected Label titleLabel;
protected Label subTitleLabel;
public class LogoAndTitlePanel extends FlowPanel implements RequiresResize {
private final String title;
private final String subtitle;
private Label titleLabel;
private Label subTitleLabel;
private final Anchor sapLogo;
private boolean lastIsSmallWidth;
private final WindowSizeDetector windowSizeDetector;
private final StringMessages stringMessages;
public LogoAndTitlePanel(StringMessages stringConstants) {
this(null, null, stringConstants);
public LogoAndTitlePanel(StringMessages stringConstants, WindowSizeDetector windowSizeDetector) {
this(null, null, stringConstants, windowSizeDetector);
}
public LogoAndTitlePanel(String title, StringMessages stringConstants) {
this(title, null, stringConstants);
public LogoAndTitlePanel(String title, StringMessages stringConstants, WindowSizeDetector windowSizeDetector) {
this(title, null, stringConstants, windowSizeDetector);
}
public LogoAndTitlePanel(String title, String subTitle, StringMessages stringConstants) {
Anchor sapLogo = new Anchor(new SafeHtmlBuilder().appendHtmlConstant(
public LogoAndTitlePanel(String title, String subtitle, StringMessages stringMessages, WindowSizeDetector windowSizeDetector) {
this.title = title;
this.subtitle = subtitle;
this.stringMessages = stringMessages;
this.windowSizeDetector = windowSizeDetector;
lastIsSmallWidth = windowSizeDetector.isSmallWidth();
sapLogo = new Anchor(new SafeHtmlBuilder().appendHtmlConstant(
"<img class=\"linkNoBorder\" src=\"/gwt/images/sap_66_transparent.png\"/>").toSafeHtml());
sapLogo.setHref("http://www.sap.com");
sapLogo.addStyleName("sapLogo");
this.add(sapLogo);
FlowPanel sailingAnalyticsLabelPanel = new FlowPanel();
Label sailingAnalyticsLabel = new Label(stringConstants.sapSailingAnalytics());
sailingAnalyticsLabelPanel.add(sailingAnalyticsLabel);
sailingAnalyticsLabelPanel.addStyleName("sailingAnalyticsLabelPanel");
sailingAnalyticsLabel.addStyleName("sailingAnalyticsLabel boldLabel");
this.add(sailingAnalyticsLabelPanel);
if (title != null) {
FlowPanel subTitleLabelWrapper = new FlowPanel();
subTitleLabelWrapper.addStyleName("titleLabelWrapper");
renderUI();
}
private void renderUI() {
for (int i=getWidgetCount()-1; i>=0; i--) {
if (getWidget(i) != sapLogo) {
remove(i);
}
}
if (!lastIsSmallWidth) {
FlowPanel sailingAnalyticsLabelPanel = new FlowPanel();
Label sailingAnalyticsLabel = new Label(stringMessages.sapSailingAnalytics());
sailingAnalyticsLabelPanel.add(sailingAnalyticsLabel);
sailingAnalyticsLabelPanel.addStyleName("sailingAnalyticsLabelPanel");
sailingAnalyticsLabel.addStyleName("sailingAnalyticsLabel boldLabel");
this.add(sailingAnalyticsLabelPanel);
}
if (!lastIsSmallWidth && title != null) {
FlowPanel titleLabelWrapper = new FlowPanel();
titleLabelWrapper.addStyleName("titleLabelWrapper");
titleLabel = new Label(title);
titleLabel.addStyleName("titleLabel");
subTitleLabelWrapper.add(titleLabel);
this.add(subTitleLabelWrapper);
titleLabelWrapper.add(titleLabel);
this.add(titleLabelWrapper);
}
if (subTitle != null) {
if (subtitle != null) {
subTitleLabel = new Label(subtitle);
FlowPanel subTitleLabelWrapper = new FlowPanel();
subTitleLabelWrapper.addStyleName("subTitleLabelWrapper");
subTitleLabel = new Label(subTitle);
subTitleLabel.addStyleName("subTitleLabel");
if (lastIsSmallWidth) {
subTitleLabelWrapper.addStyleName("titleLabelWrapper");
subTitleLabel.addStyleName("titleLabelRight");
} else {
subTitleLabelWrapper.addStyleName("subTitleLabelWrapper");
subTitleLabel.addStyleName("subTitleLabel");
}
subTitleLabelWrapper.add(subTitleLabel);
this.add(subTitleLabelWrapper);
}
@@ -68,5 +93,14 @@ public class LogoAndTitlePanel extends FlowPanel {
if(subTitleLabel != null) {
subTitleLabel.setText(subTitle);
}
}
@Override
public void onResize() {
if (windowSizeDetector.isSmallWidth() != lastIsSmallWidth) {
lastIsSmallWidth = windowSizeDetector.isSmallWidth();
renderUI();
}
}
}
@@ -18,7 +18,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
private boolean autoAdjustPlayMode;
private RaceTimesInfoDTO lastRaceTimesInfo;
public RaceTimePanel(Timer timer, TimeRangeProvider timeRangeProvider, StringMessages stringMessages, RaceTimesInfoProvider raceTimesInfoProvider) {
public RaceTimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages, RaceTimesInfoProvider raceTimesInfoProvider) {
super(timer, timeRangeProvider, stringMessages);
this.raceTimesInfoProvider = raceTimesInfoProvider;
selectedRace = null;
@@ -67,7 +67,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
}
boolean timerAlreadyInitialized = getFromTime() != null && getToTime() != null && timeSlider.getCurrentValue() != null;
if (!isTimeZoomed) {
if (!timeRangeProvider.isZoomed()) {
updateMinMax(raceTimesInfo);
if (!timerAlreadyInitialized) {
initTimerPosition(raceTimesInfo);
@@ -85,9 +85,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
@Override
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
super.onTimeZoomChanged(zoomStartTimepoint, zoomEndTimepoint);
isTimeZoomed = true;
timeSlider.setZoomed(true);
// timer.setAutoAdvance(false);
setMinMax(zoomStartTimepoint, zoomEndTimepoint, false);
timeSlider.clearMarkersAndLabelsAndTicks();
redrawAllMarkers(lastRaceTimesInfo);
@@ -96,10 +94,13 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
@Override
public void onTimeZoomReset() {
super.onTimeZoomReset();
isTimeZoomed = false;
timeSlider.setZoomed(false);
// timer.setAutoAdvance(true);
updateMinMax(this.lastRaceTimesInfo);
timeSlider.setMinValue(new Double(timeRangeProvider.getFromTime().getTime()), false);
timeSlider.setMaxValue(new Double(timeRangeProvider.getToTime().getTime()), false);
timeSlider.setCurrentValue(new Double(timer.getTime().getTime()), true);
// updateMinMax(this.lastRaceTimesInfo);
timeSlider.clearMarkersAndLabelsAndTicks();
redrawAllMarkers(lastRaceTimesInfo);
}
@@ -269,6 +269,8 @@ public interface SailingService extends RemoteService {
RaceEventLogDTO getRaceEventLog();
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor);
List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException;
List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException;
List<String> getOverallLeaderboardNamesContaining(String leaderboardName);
}
@@ -421,6 +421,14 @@ public interface SailingServiceAsync {
void getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date,
AsyncCallback<List<Pair<String, List<CompetitorDTO>>>> callback);
void getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
long stepSize, DetailType detailType, AsyncCallback<CompetitorsRaceDataDTO> callback);
void getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
long stepSize, DetailType detailType, AsyncCallback<CompetitorsRaceDataDTO> callback);
/**
* Finds out the names of all {@link MetaLeaderboard}s managed by this server that
* {@link MetaLeaderboard#getLeaderboards() contain} the leaderboard identified by <code>leaderboardName</code>. The
* names of those meta-leaderboards are returned. The list returned is never <code>null</code> but may be empty if no such
* leaderboard is found.
*/
void getOverallLeaderboardNamesContaining(String leaderboardName, AsyncCallback<List<String>> asyncCallback);
}
@@ -439,4 +439,13 @@ public interface StringMessages extends Messages {
String rankNAfterRace(long l, String string);
String afterRace();
String showRankChart();
String overallLeaderboardSelection();
String showOverallLeaderboardOnSamePage();
String errorTryingToObtainOverallLeaderboards(String message);
String finished();
String passedTo(String name);
String gapChangeSinceLegStartInSeconds();
String sideToWhichMarkAtLegStartWasRounded();
String raceIsLive(String raceName);
String racesAreLive(String raceNames);
}
@@ -439,4 +439,13 @@ errorFetchingRankingChartData=Error fetching ranking chart data: {0}
loadingCompetitorData=Loading competitor data...
rankNAfterRace=Rank {0} after race {1}
afterRace=after race
showRankChart=Show rank chart
showRankChart=Show rank chart
overallLeaderboardSelection=Overall Leaderboard
showOverallLeaderboardOnSamePage=Show overall leaderboard on same page
errorTryingToObtainOverallLeaderboards=Error trying to obtain overall leaderboards: {0}
finished=finished
passedTo=passed to {0}
gapChangeSinceLegStartInSeconds=Gap Change
sideToWhichMarkAtLegStartWasRounded=Mark rounded to
raceIsLive=Race {0} is live
racesAreLive=Races {0} are live
@@ -440,4 +440,13 @@ errorFetchingRankingChartData=Fehler beim Laden der Daten für das Ranglisten-Ch
loadingCompetitorData=Lade Teilnehmer-Daten
rankNAfterRace=Rang {0} nach Rennen {1}
afterRace=nach Rennen
showRankChart=Rang-Diagramm anzeigen
showRankChart=Rang-Diagramm anzeigen
overallLeaderboardSelection=Gesamtergebnisse
showOverallLeaderboardOnSamePage=Gesamtergebnisse auf derselben Seite anzeigen
errorTryingToObtainOverallLeaderboards=Fehler beim Laden der Gesamtergebnisse: {0}
finished=beendet
passedTo=an {0} gerundet
gapChangeSinceLegStartInSeconds=Gap Änderung
sideToWhichMarkAtLegStartWasRounded=Tonne gerundet an
raceIsLive=Rennen {0} ist live
racesAreLive=Rennen {0} sind live
@@ -33,8 +33,7 @@ import com.sap.sailing.gwt.ui.shared.controls.slider.TimeSlider;
public class TimePanel<T extends TimePanelSettings> extends FormPanel implements Component<T>, TimeListener, TimeZoomChangeListener,
TimeRangeChangeListener, PlayStateListener, RequiresResize {
protected final Timer timer;
protected boolean isTimeZoomed;
protected final TimeRangeProvider timeRangeProvider;
protected final TimeRangeWithZoomProvider timeRangeProvider;
private final IntegerBox playSpeedBox;
private final Label timeDelayLabel;
@@ -83,11 +82,10 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
}
}
public TimePanel(Timer timer, TimeRangeProvider timeRangeProvider, StringMessages stringMessages) {
public TimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages) {
this.timer = timer;
this.timeRangeProvider = timeRangeProvider;
this.stringMessages = stringMessages;
isTimeZoomed = false;
timer.addTimeListener(this);
timer.addPlayStateListener(this);
timeRangeProvider.addTimeRangeChangeListener(this);
@@ -285,30 +283,32 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
@Override
public void timeChanged(Date time) {
if (getFromTime() != null && getToTime() != null) {
// handle the case where time advances beyond slider's end.
if (time.after(getToTime())) {
switch (timer.getPlayMode()) {
case Live:
Date newMaxTime = new Date(time.getTime());
if (newMaxTime.getTime() - getToTime().getTime() < MINIMUM_AUTO_ADVANCE_TIME_IN_MS) {
newMaxTime.setTime(getToTime().getTime() + MINIMUM_AUTO_ADVANCE_TIME_IN_MS);
if(timeRangeProvider.isZoomed()) {
} else {
if (getFromTime() != null && getToTime() != null) {
// handle the case where time advances beyond slider's end.
if (time.after(getToTime())) {
switch (timer.getPlayMode()) {
case Live:
Date newMaxTime = new Date(time.getTime());
if (newMaxTime.getTime() - getToTime().getTime() < MINIMUM_AUTO_ADVANCE_TIME_IN_MS) {
newMaxTime.setTime(getToTime().getTime() + MINIMUM_AUTO_ADVANCE_TIME_IN_MS);
}
setMinMax(getFromTime(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max
break;
case Replay:
timer.stop();
break;
}
setMinMax(getFromTime(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max
break;
case Replay:
timer.stop();
break;
}
timeSlider.setCurrentValue(new Double(time.getTime()), false);
}
long t = time.getTime();
timeSlider.setCurrentValue(new Double(t), false);
dateLabel.setText(dateFormatter.format(time));
if (lastReceivedDataTimepoint == null) {
timeLabel.setText(timeFormatter.format(time));
} else {
timeLabel.setText(timeFormatter.format(time) + " (" + timeFormatter.format(lastReceivedDataTimepoint) + ")");
}
}
dateLabel.setText(dateFormatter.format(time));
if (lastReceivedDataTimepoint == null) {
timeLabel.setText(timeFormatter.format(time));
} else {
timeLabel.setText(timeFormatter.format(time) + " (" + timeFormatter.format(lastReceivedDataTimepoint) + ")");
}
}
@@ -340,7 +340,9 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
}
}
if (changed) {
timeRangeProvider.setTimeRange(min, max, this);
if(!timeRangeProvider.isZoomed()) {
timeRangeProvider.setTimeRange(min, max, this);
}
int numSteps = timeSlider.getElement().getClientWidth();
if (numSteps > 0) {
@@ -57,4 +57,9 @@ public class TimeRangeModel implements TimeRangeProvider {
public Date getToTime() {
return toTime;
}
@Override
public String toString() {
return "TimeRangeModel [fromTime=" + fromTime + ", toTime=" + toTime + "]";
}
}
@@ -42,6 +42,8 @@ public class TimeRangeWithZoomModel extends TimeRangeModel implements TimeRangeW
public void resetTimeZoom(TimeZoomChangeListener... listenersNotToNotify) {
isTimeZoomed = false;
this.zoomStartTimepoint = null;
this.zoomEndTimepoint = null;
for (TimeZoomChangeListener listener : listeners) {
if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) {
listener.onTimeZoomReset();
@@ -56,4 +58,10 @@ public class TimeRangeWithZoomModel extends TimeRangeModel implements TimeRangeW
public boolean isZoomed() {
return isTimeZoomed;
}
@Override
public String toString() {
return "TimeRangeWithZoomModel [fromTime=" + fromTime + ", toTime=" + toTime + ", zoomStartTimepoint=" + zoomStartTimepoint + ", zoomEndTimepoint="
+ zoomEndTimepoint + ", isTimeZoomed=" + isTimeZoomed + "]";
}
}
@@ -0,0 +1,7 @@
package com.sap.sailing.gwt.ui.client;
public interface WindowSizeDetector {
boolean isSmallWidth();
}
@@ -16,10 +16,10 @@ import com.google.gwt.user.client.ui.Widget;
*/
public class DialogBoxExt extends DialogBox {
private HorizontalPanel captionPanel = new HorizontalPanel();
private final HorizontalPanel captionPanel;
/** widget which will be use to close the dialog box */
private Widget closeWidget = null;
private final Widget closeWidget;
/**
* You have to provide a widget here. If click on the widget the dialog box will be closed.
@@ -27,21 +27,17 @@ public class DialogBoxExt extends DialogBox {
*/
public DialogBoxExt(Widget closeDialogBox) {
super();
assert closeDialogBox != null;
captionPanel = new HorizontalPanel();
closeWidget = closeDialogBox;
closeWidget.getElement().addClassName("chartCloseButton");
// an empty header could case a problem!
// an empty header could cause a problem!
setHTML("&nbsp;");
}
@Override
public void setHTML(String html) {
if (closeWidget != null) {
setCaption(html, closeWidget);
} else {
super.setHTML(html);
}
setCaption(html, closeWidget);
}
@Override
@@ -10,7 +10,6 @@ import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
@@ -46,6 +45,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
private static final String PARAM_AUTO_EXPAND_LAST_RACE_COLUMN = "autoExpandLastRaceColumn";
private static final String PARAM_REGATTA_NAME = "regattaName";
private static final String PARAM_REFRESH_INTERVAL_MILLIS = "refreshIntervalMillis";
private static final String PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE = "showOverallLeaderboardsOnSamePage";
private static final String PARAM_DELAY_TO_LIVE_MILLIS = "delayToLiveMillis";
/**
@@ -55,6 +55,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
private static final String PARAM_NAME_LAST_N = "lastN";
private String leaderboardName;
private String leaderboardGroupName;
private GlobalNavigationPanel globalNavigationPanel;
@Override
public void onModuleLoad() {
@@ -91,15 +92,25 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
if (!embedded) {
// Hack to shorten the leaderboardName in case of overall leaderboards
String leaderboardDisplayName = Window.Location.getParameter("displayName");
if(leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
if (leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
leaderboardDisplayName = leaderboardName;
}
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages);
globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, null, leaderboardGroupName);
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages, this) {
@Override
public void onResize() {
super.onResize();
if (isSmallWidth()) {
remove(globalNavigationPanel);
} else {
add(globalNavigationPanel);
}
}
};
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, null, leaderboardGroupName);
logoAndTitlePanel.add(globalNavigationPanel);
if (!isSmallWidth()) {
logoAndTitlePanel.add(globalNavigationPanel);
}
mainPanel.addNorth(logoAndTitlePanel, 68);
}
ScrollPanel contentScrollPanel = new ScrollPanel();
@@ -114,10 +125,10 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(),
leaderboardSettings,
preselectedRace, new CompetitorSelectionModel(
/* hasMultiSelection */true), timer, leaderboardName, leaderboardGroupName,
LeaderboardEntryPoint.this, stringMessages, userAgent, showRaceDetails, /* raceTimesInfoProvider */ null,
Window.Location.getParameterMap().containsKey(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN) ?
/* hasMultiSelection */true), timer, leaderboardName, LeaderboardEntryPoint.this,
stringMessages, userAgent, showRaceDetails, /* raceTimesInfoProvider */ null, Window.Location.getParameterMap().containsKey(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN) ?
Boolean.valueOf(Window.Location.getParameterMap().get(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN).get(0)) : false);
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
contentScrollPanel.setWidget(leaderboardPanel);
mainPanel.add(contentScrollPanel);
@@ -153,6 +164,9 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
} else {
numberOfLastRacesToShow = null;
}
boolean showOverallLeaderboardsOnSamePage = parameterMap.containsKey(PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE) ?
Boolean.valueOf(parameterMap.get(PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE).get(0)) :
false;
if (parameterMap.containsKey(PARAM_RACE_NAME) || parameterMap.containsKey(PARAM_RACE_DETAIL) ||
parameterMap.containsKey(PARAM_LEG_DETAIL) || parameterMap.containsKey(PARAM_MANEUVER_DETAIL) ||
parameterMap.containsKey(PARAM_OVERALL_DETAIL)) {
@@ -171,12 +185,13 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
/* sort by column */ (namesOfRacesToShow != null && !namesOfRacesToShow.isEmpty()) ?
namesOfRacesToShow.get(0) : null, /* ascending */ true,
/* updateUponPlayStateChange */ raceDetails.isEmpty() && legDetails.isEmpty(),
raceColumnSelectionStrategy);
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
} else {
final List<DetailType> overallDetails = Collections.emptyList();
result = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, /* overallDetails */
overallDetails, null,
/* autoExpandFirstRace */ false, refreshIntervalMillis, numberOfLastRacesToShow, raceColumnSelectionStrategy);
result = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null,
/* overallDetails */ overallDetails, null,
/* autoExpandFirstRace */false, refreshIntervalMillis, numberOfLastRacesToShow,
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
}
return result;
}
@@ -285,7 +300,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
StringBuilder maneuverDetails = new StringBuilder();
for (DetailType maneuverDetail : settings.getLeaderboardSettings().getManeuverDetailsToShow()) {
maneuverDetails.append('&');
maneuverDetails.append(PARAM_RACE_DETAIL);
maneuverDetails.append(PARAM_MANEUVER_DETAIL);
maneuverDetails.append('=');
maneuverDetails.append(maneuverDetail.name());
}
@@ -293,6 +308,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
String link = URLFactory.INSTANCE.encode("/gwt/Leaderboard.html?name=" + leaderboardName
+ (settings.isShowRaceDetails() ? "&"+PARAM_SHOW_RACE_DETAILS+"=true" : "")
+ (settings.isEmbedded() ? "&"+PARAM_EMBEDDED+"=true" : "")
+ (settings.getLeaderboardSettings().isShowOverallLeaderboardsOnSamePage() ? "&"+PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE+"=true" : "")
+ (settings.getLeaderboardSettings().getDelayInMilliseconds() == null &&
settings.getLeaderboardSettings().getDelayInMilliseconds() != 0 ? "" :
"&"+PARAM_DELAY_TO_LIVE_MILLIS+"="+settings.getLeaderboardSettings().getDelayInMilliseconds())
@@ -302,6 +318,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
+ legDetails.toString()
+ raceDetails.toString()
+ overallDetails.toString()
+ maneuverDetails.toString()
+ (settings.isAutoExpandLastRaceColumn() ? "&"+PARAM_AUTO_EXPAND_LAST_RACE_COLUMN+"=true" : "")
+ (settings.getLeaderboardSettings().getNumberOfLastRacesToShow() == null ? "" :
"&"+PARAM_NAME_LAST_N+"="+settings.getLeaderboardSettings().getNumberOfLastRacesToShow())
@@ -16,11 +16,13 @@ import com.google.gwt.cell.client.CompositeCell;
import com.google.gwt.cell.client.EditTextCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.Column;
@@ -53,10 +55,12 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.SortingOrder;
import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction;
import com.sap.sailing.gwt.ui.client.Collator;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.FlagImageResolver;
@@ -105,6 +109,24 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
private final SailingServiceAsync sailingService;
private static String IS_LIVE_TEXT_COLOR = "#1876B3";
private static String DEFAULT_TEXT_COLOR = "#000000";
private static final String STYLE_LEADERBOARD_CONTENT = "leaderboardContent";
private static final String STYLE_LEADERBOARD_INFO = "leaderboardInfo";
private static final String STYLE_LEADERBOARD_TOOLBAR = "leaderboardContent-toolbar";
private static final String STYLE_LEADERBOARD_LIVE_RACE = "leaderboardContent-liverace";
interface RaceColumnTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<div style=\"color:{0}; border-bottom: 3px solid {1}\">")
SafeHtml cellFrameWithTextColorAndFleetBorder(String textColor, String borderStyle);
@SafeHtmlTemplates.Template("<div style=\"color:{0};\">")
SafeHtml cellFrameWithTextColor(String textColor);
}
private static RaceColumnTemplates raceColumnTemplate = GWT.create(RaceColumnTemplates.class);
/**
* The leaderboard name is used to
* {@link SailingServiceAsync#getLeaderboardByName(String, java.util.Date, String[], com.google.gwt.user.client.rpc.AsyncCallback)
@@ -162,11 +184,15 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
protected final String TOTAL_COLUMN_STYLE;
public static final String LEADERBOARD_MARGIN_STYLE = "leaderboardMargin";
private final Timer timer;
private boolean autoExpandPreSelectedRace;
private boolean autoExpandLastRaceColumn;
private boolean showOverallLeaderboardsOnSamePage;
/**
* Remembers whether the auto-expand of the pre-selected race (see {@link #autoExpandPreSelectedRace}) or last
@@ -191,12 +217,26 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
private final RaceIdentifier preSelectedRace;
private final VerticalPanel contentPanel;
/**
* Used to display one or more overall leaderboards if selected. See also
* {@link LeaderboardSettings#isShowOverallLeaderboardsOnSamePage()}. Always contained by the {@link #contentPanel} at the
* right position, but may be empty.
*/
private final VerticalPanel overallLeaderboardsPanel;
/**
* The leaderboard panels displayed in {@link #overallLeaderboardsPanel}.
*/
private final List<LeaderboardPanel> overallLeaderboardPanels;
private final HorizontalPanel refreshAndSettingsPanel;
private final FlowPanel informationPanel;
private final Label scoreCorrectionLastUpdateTimeLabel;
private final Label scoreCorrectionCommentLabel;
private final Label liveRaceLabel;
private final DateTimeFormat dateFormatter = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_LONG);
private final DateTimeFormat timeFormatter = DateTimeFormat.getFormat("HH:mm:ss");
@@ -247,6 +287,8 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
*/
private RaceTimesInfoProvider raceTimesInfoProvider;
private final UserAgentDetails userAgent;
private class SettingsClickHandler implements ClickHandler {
private final StringMessages stringMessages;
@@ -284,6 +326,11 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
return null;
}
@Override
public Widget getLegendWidget() {
return null;
}
@Override
public boolean isEmbedded() {
return isEmbedded;
@@ -390,6 +437,67 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
getLeaderboardTable().sortColumn(raceColumnByRaceName, /* ascending */true);
}
}
if (newSettings.isShowOverallLeaderboardsOnSamePage() != showOverallLeaderboardsOnSamePage) {
showOverallLeaderboardsOnSamePage = newSettings.isShowOverallLeaderboardsOnSamePage();
if (showOverallLeaderboardsOnSamePage) {
showOverallLeaderboards();
} else {
hideOverallLeaderboards();
}
}
}
private void showOverallLeaderboards() {
overallLeaderboardsPanel.clear();
final Label overallStandingsLabel = new Label(stringMessages.overallStandings());
overallStandingsLabel.setStyleName("leaderboardHeading");
overallLeaderboardsPanel.add(overallStandingsLabel);
updateOverallLeaderboardsAndAddToPanel();
}
/**
* Fetches from the server the list of overall ("meta") leaderboard descriptions in which this main leaderboard occurs and
* creates one {@link LeaderboardPanel} for each such overall leaderboard. Those panels are then added to
* {@link #overallLeaderboardPanels}, registered with the {@link #timer} for common timing with the main leaderboard panel,
* and the panels are made visible by adding them to the {@link #overallLeaderboardsPanel} panel.
*/
private void updateOverallLeaderboardsAndAddToPanel() {
getSailingService().getOverallLeaderboardNamesContaining(leaderboardName, new AsyncCallback<List<String>>() {
@Override
public void onFailure(Throwable caught) {
getErrorReporter().reportError(
stringMessages.errorTryingToObtainOverallLeaderboards(caught.getMessage()));
}
@Override
public void onSuccess(List<String> overallLeaderboardNames) {
for (String overallLeaderboardName : overallLeaderboardNames) {
LeaderboardPanel overallLeaderboardPanel = new LeaderboardPanel(
getSailingService(), asyncActionsExecutor, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
/* namesOfRaceColumnsToShow */ null, /* namesOfRacesToShow */ null, /* nameOfRaceToSort */ null,
/* autoExpandPreSelectedRace */ false, /* showOverallLeaderboardsOnSamePage */ false),
/* preSelectedRace */ null, new CompetitorSelectionModel(/* hasMultiSelection */ true),
timer, overallLeaderboardName, errorReporter, stringMessages, userAgent, showRaceDetails,
/* optionalRaceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false);
overallLeaderboardPanels.add(overallLeaderboardPanel);
overallLeaderboardsPanel.add(overallLeaderboardPanel);
}
}
});
}
/**
* Clears the panel used to display the overall leaderboards, removes all overall leaderboard panels from their listener
* relationship with the {@link #timer} and then clears the {@link #overallLeaderboardPanels} list.
*/
private void hideOverallLeaderboards() {
overallLeaderboardsPanel.clear();
for (LeaderboardPanel overallLeaderboardPanel : overallLeaderboardPanels) {
timer.removeTimeListener(overallLeaderboardPanel);
timer.removePlayStateListener(overallLeaderboardPanel);
}
overallLeaderboardPanels.clear();
}
private void setRaceColumnSelectionToLastNStrategy(final Integer numberOfLastRacesToShow) {
@@ -554,6 +662,10 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
public RaceColumnDTO getRace() {
return race;
}
public void setRace(RaceColumnDTO race) {
this.race = race;
}
public String getRaceColumnName() {
return race.getRaceColumnName();
@@ -563,6 +675,10 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
return race.isMedalRace();
}
public boolean isLive(FleetDTO fleetDTO) {
return race.isLive(fleetDTO);
}
@Override
public String getColumnStyle() {
return columnStyle;
@@ -585,15 +701,18 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder html) {
LeaderboardEntryDTO entry = object.fieldsByRaceColumnName.get(getRaceColumnName());
if (entry != null) {
String fleetColorBarStyle = "";
boolean isLive = isLive(entry.fleet);
String textColor = isLive ? IS_LIVE_TEXT_COLOR : DEFAULT_TEXT_COLOR;
String totalPointsAsText = entry.totalPoints == null ? "" : scoreFormat.format(entry.totalPoints);
String netPointsAsText = entry.netPoints == null ? "" : scoreFormat.format(entry.netPoints);
if (entry.fleet != null && entry.fleet.getColor() != null) {
fleetColorBarStyle = " style=\"border-bottom: 3px solid " + entry.fleet.getColor().getAsHtml()
+ ";\"";
html.append(raceColumnTemplate.cellFrameWithTextColorAndFleetBorder(textColor, entry.fleet.getColor().getAsHtml()));
} else {
html.append(raceColumnTemplate.cellFrameWithTextColor(textColor));
}
html.appendHtmlConstant("<div" + fleetColorBarStyle + ">");
// don't show points if max points / penalty
if (entry.reasonForMaxPoints == null || entry.reasonForMaxPoints == MaxPointsReason.NONE) {
if (!entry.discarded) {
@@ -611,8 +730,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
if (entry.discarded) {
html.appendHtmlConstant("<del>");
}
html.appendEscaped(entry.reasonForMaxPoints == MaxPointsReason.NONE ? "" : entry.reasonForMaxPoints
.name());
html.appendEscaped(entry.reasonForMaxPoints == MaxPointsReason.NONE ? "" : entry.reasonForMaxPoints.name());
if (entry.discarded) {
html.appendHtmlConstant("</del>");
}
@@ -653,8 +771,9 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
}
public static DetailType[] getAvailableRaceDetailColumnTypes() {
return new DetailType[] { DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS,
DetailType.RACE_DISTANCE_TRAVELED, DetailType.RACE_GAP_TO_LEADER_IN_SECONDS,
return new DetailType[] { DetailType.RACE_GAP_TO_LEADER_IN_SECONDS,
DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS,
DetailType.RACE_DISTANCE_TRAVELED,
DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TO_LEADER_IN_METERS,
DetailType.NUMBER_OF_MANEUVERS, DetailType.DISPLAY_LEGS, DetailType.CURRENT_LEG,
DetailType.RACE_AVERAGE_CROSS_TRACK_ERROR_IN_METERS };
@@ -996,7 +1115,9 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
@Override
public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<span style=\"font-weight: bold;\">");
String textColor = getLeaderboard().hasLiveRace() ? IS_LIVE_TEXT_COLOR : DEFAULT_TEXT_COLOR;
sb.appendHtmlConstant("<span style=\"font-weight: bold; color:" + textColor + "\">");
sb.appendEscaped(getValue(object));
sb.appendHtmlConstant("</span>");
}
@@ -1096,29 +1217,28 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
public LeaderboardPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
LeaderboardSettings settings, CompetitorSelectionProvider competitorSelectionProvider,
String leaderboardName, String leaderboardGroupName, ErrorReporter errorReporter,
final StringMessages stringMessages, final UserAgentDetails userAgent, boolean showRaceDetails) {
String leaderboardName, ErrorReporter errorReporter, final StringMessages stringMessages,
final UserAgentDetails userAgent, boolean showRaceDetails) {
this(sailingService, asyncActionsExecutor, settings, /* preSelectedRace */null, competitorSelectionProvider,
leaderboardName, leaderboardGroupName, errorReporter, stringMessages, userAgent, showRaceDetails);
leaderboardName, errorReporter, stringMessages, userAgent, showRaceDetails);
}
public LeaderboardPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
LeaderboardSettings settings, RaceIdentifier preSelectedRace,
CompetitorSelectionProvider competitorSelectionProvider, String leaderboardName,
String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages,
final UserAgentDetails userAgent, boolean showRaceDetails) {
ErrorReporter errorReporter, final StringMessages stringMessages, final UserAgentDetails userAgent,
boolean showRaceDetails) {
this(sailingService, asyncActionsExecutor, settings, preSelectedRace, competitorSelectionProvider, new Timer(
PlayModes.Replay, /* delayBetweenAutoAdvancesInMilliseconds */3000l), leaderboardName,
leaderboardGroupName, errorReporter, stringMessages, userAgent, showRaceDetails,
/* optionalRaceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false);
errorReporter, stringMessages, userAgent, showRaceDetails, /* optionalRaceTimesInfoProvider */ null,
/* autoExpandLastRaceColumn */ false);
}
public LeaderboardPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
LeaderboardSettings settings, RaceIdentifier preSelectedRace,
CompetitorSelectionProvider competitorSelectionProvider, Timer timer, String leaderboardName,
String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages,
final UserAgentDetails userAgent, boolean showRaceDetails, RaceTimesInfoProvider optionalRaceTimesInfoProvider,
boolean autoExpandLastRaceColumn) {
ErrorReporter errorReporter, final StringMessages stringMessages, final UserAgentDetails userAgent,
boolean showRaceDetails, RaceTimesInfoProvider optionalRaceTimesInfoProvider, boolean autoExpandLastRaceColumn) {
this.showRaceDetails = showRaceDetails;
this.sailingService = sailingService;
this.asyncActionsExecutor = asyncActionsExecutor;
@@ -1133,6 +1253,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
this.selectedOverallDetailColumns = new ArrayList<DetailType>();
this.raceTimesInfoProvider = optionalRaceTimesInfoProvider;
this.selectedManeuverDetails = new ArrayList<DetailType>();
this.showOverallLeaderboardsOnSamePage = settings.isShowOverallLeaderboardsOnSamePage();
overallDetailColumnMap = createOverallDetailColumnMap();
settingsUpdatedExplicitly = !settings.updateUponPlayStateChange();
if (settings.getLegDetailsToShow() != null) {
@@ -1178,6 +1299,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
leaderboardTable = new SortedCellTableWithStylableHeaders<LeaderboardRowDTO>(
/* pageSize */10000, tableResources);
getLeaderboardTable().setWidth("100%");
this.userAgent = userAgent;
if (userAgent.isMobile() == UserAgentDetails.PlatformTypes.MOBILE) {
leaderboardSelectionModel = new ToggleSelectionModel<LeaderboardRowDTO>();
} else {
@@ -1203,16 +1325,28 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
isEmbedded = true;
}
contentPanel = new VerticalPanel();
contentPanel.setStyleName("leaderboardContent");
contentPanel.setStyleName(STYLE_LEADERBOARD_CONTENT);
overallLeaderboardsPanel = new VerticalPanel();
overallLeaderboardPanels = new ArrayList<LeaderboardPanel>();
// the information panel
informationPanel = new FlowPanel();
informationPanel.setStyleName("leaderboardInfo");
informationPanel.setStyleName(STYLE_LEADERBOARD_INFO);
scoreCorrectionLastUpdateTimeLabel = new Label("");
scoreCorrectionCommentLabel = new Label("");
informationPanel.add(scoreCorrectionCommentLabel);
informationPanel.add(scoreCorrectionLastUpdateTimeLabel);
liveRaceLabel = new Label(stringMessages.live());
liveRaceLabel.setStyleName(STYLE_LEADERBOARD_LIVE_RACE);
liveRaceLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD);
liveRaceLabel.getElement().getStyle().setColor(IS_LIVE_TEXT_COLOR);
liveRaceLabel.setVisible(false);
informationPanel.add(liveRaceLabel);
// the toolbar panel
DockPanel toolbarPanel = new DockPanel();
toolbarPanel.setStyleName("leaderboardContent-toolbar");
toolbarPanel.setStyleName(STYLE_LEADERBOARD_TOOLBAR);
busyIndicator = new SimpleBusyIndicator(false, 0.8f);
if (!isEmbedded) {
toolbarPanel.add(informationPanel, DockPanel.WEST);
@@ -1276,6 +1410,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
contentPanel.add(toolbarPanel);
}
contentPanel.add(getLeaderboardTable());
contentPanel.add(overallLeaderboardsPanel);
setWidget(contentPanel);
raceNameForDefaultSorting = settings.getNameOfRaceToSort();
}
@@ -1523,6 +1658,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
raceColumnSelection.autoUpdateRaceColumnSelectionForUpdatedLeaderboard(getLeaderboard(), leaderboard);
setLeaderboard(leaderboard);
adjustColumnLayout(leaderboard);
updateRaceColumnDTOsToRaceColumns(leaderboard);
for (RaceColumn<?> columnToCollapseAndExpandAgain : columnsToCollapseAndExpandAgain) {
columnToCollapseAndExpandAgain.toggleExpansion();
}
@@ -1563,19 +1699,55 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
leaderboardSelectionModel.setSelected(row, true);
}
}
scoreCorrectionCommentLabel.setText(leaderboard.getComment() != null ? leaderboard.getComment() : "");
if (leaderboard.getTimePointOfLastCorrectionsValidity() != null) {
Date lastCorrectionDate = leaderboard.getTimePointOfLastCorrectionsValidity();
String lastUpdate = dateFormatter.format(lastCorrectionDate) + " "
String lastUpdate = dateFormatter.format(lastCorrectionDate) + ", "
+ timeFormatter.format(lastCorrectionDate);
scoreCorrectionLastUpdateTimeLabel.setText(stringMessages.lastScoreUpdate() + ": " + lastUpdate);
} else {
scoreCorrectionLastUpdateTimeLabel.setText("");
}
List<Pair<RaceColumnDTO, FleetDTO>> liveRaces = leaderboard.getLiveRaces();
boolean hasLiveRace = !liveRaces.isEmpty();
if (hasLiveRace) {
String liveRaceText = "";
if(liveRaces.size() == 1) {
Pair<RaceColumnDTO, FleetDTO> liveRace = liveRaces.get(0);
liveRaceText = stringMessages.raceIsLive("'" + liveRace.getA().getRaceColumnName() + "'");
} else {
String raceNames = "";
for(Pair<RaceColumnDTO, FleetDTO> liveRace: liveRaces) {
raceNames += "'" + liveRace.getA().getRaceColumnName() + "', ";
}
// remove last ", "
raceNames = raceNames.substring(0, raceNames.length()-2);
liveRaceText = stringMessages.racesAreLive(raceNames);
}
liveRaceLabel.setText(liveRaceText);
} else {
liveRaceLabel.setText("");
}
scoreCorrectionLastUpdateTimeLabel.setVisible(!hasLiveRace);
liveRaceLabel.setVisible(hasLiveRace);
}
}
/**
* The race columns hold a now outdated copy of a {@link RaceColumnDTO} which needs to be updated from the {@link LeaderboardDTO} just received
*/
private void updateRaceColumnDTOsToRaceColumns(LeaderboardDTO leaderboard) {
for (RaceColumnDTO newRace : leaderboard.getRaceList()) {
RaceColumn<?> raceColumn = getRaceColumnByRaceColumnName(newRace.name);
if (raceColumn != null) {
raceColumn.setRace(newRace);
}
}
}
/**
* Due to a course change, a race may change its number of legs. All expanded race columns that show leg columns and
* whose leg count changed need to be collapsed before the leaderboard is replaced, and expanded afterwards again.
* Race columns whose toggling is {@link ExpandableSortableColumn#isTogglingInProcess() currently in progress} are
@@ -1755,7 +1927,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
}
/**
* If header information doesn't match the race column's actual state (tracked races attached meaning expanable;
* If header information doesn't match the race column's actual state (tracked races attached meaning expandable;
* medal race), the column is removed and inserted again
*
* @param raceColumn
@@ -2076,7 +2248,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
: preSelectedRace.getRaceName(),
/* don't change nameOfRaceColumnToShow */null,
/* set nameOfRaceToShow if race was pre-selected */preSelectedRace == null ? null : preSelectedRace
.getRaceName(), getRaceColumnSelection()));
.getRaceName(), getRaceColumnSelection(), showOverallLeaderboardsOnSamePage));
}
currentlyHandlingPlayStateChange = false;
oldPlayMode = playMode;
@@ -2129,7 +2301,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
Collections.unmodifiableList(selectedLegDetails), Collections.unmodifiableList(selectedRaceDetails),
Collections.unmodifiableList(selectedOverallDetailColumns), /* All races to select */ leaderboard.getRaceList(),
raceColumnSelection.getSelectedRaceColumnsOrderedAsInLeaderboard(leaderboard), raceColumnSelection, autoExpandPreSelectedRace,
timer.getRefreshInterval(), timer.getLivePlayDelayInMillis(), stringMessages);
showOverallLeaderboardsOnSamePage, timer.getRefreshInterval(), timer.getLivePlayDelayInMillis(), stringMessages);
}
@Override
@@ -30,6 +30,7 @@ public class LeaderboardSettings {
private final Long delayBetweenAutoAdvancesInMilliseconds;
private final Long delayInMilliseconds;
private final boolean updateUponPlayStateChange;
private final boolean showOverallLeaderboardsOnSamePage;
/**
* There are two ways to select race columns.
@@ -52,7 +53,8 @@ public class LeaderboardSettings {
List<DetailType> raceDetailsToShow, List<DetailType> overallDetailsToShow,
List<String> namesOfRaceColumnsToShow, List<String> namesOfRacesToShow, Integer numberOfLastRacesToShow,
boolean autoExpandPreSelectedRace, Long delayBetweenAutoAdvancesInMilliseconds, Long delayInMilliseconds,
String nameOfRaceToSort, boolean sortAscending, boolean updateUponPlayStateChange, RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy) {
String nameOfRaceToSort, boolean sortAscending, boolean updateUponPlayStateChange,
RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy, boolean showOverallLeaderboardsOnSamePage) {
if (namesOfRacesToShow != null && namesOfRaceColumnsToShow != null) {
throw new IllegalArgumentException("You can identify races either only by their race or by their column names, not both");
}
@@ -70,6 +72,7 @@ public class LeaderboardSettings {
this.nameOfRaceToSort = nameOfRaceToSort;
this.sortAscending = sortAscending;
this.updateUponPlayStateChange = updateUponPlayStateChange;
this.showOverallLeaderboardsOnSamePage = showOverallLeaderboardsOnSamePage;
}
public List<DetailType> getManeuverDetailsToShow() {
@@ -151,4 +154,7 @@ public class LeaderboardSettings {
return activeRaceColumnSelectionStrategy;
}
public boolean isShowOverallLeaderboardsOnSamePage() {
return showOverallLeaderboardsOnSamePage;
}
}
@@ -46,6 +46,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
private LongBox refreshIntervalInSecondsBox;
private LongBox delayInSecondsBox;
private final boolean autoExpandPreSelectedRace;
private final boolean showOverallLeaderboardOnSamePage;
private final long delayBetweenAutoAdvancesInMilliseconds;
private final long delayInMilliseconds;
private final Integer numberOfLastRacesToShow;
@@ -53,11 +54,14 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
private RadioButton explicitRaceColumnSelectionRadioBtn;
private RadioButton lastNRacesColumnSelectionRadioBtn;
private IntegerBox numberOfLastRacesToShowBox;
private CheckBox showOverallLeaderboardOnSamePageCheckbox;
public LeaderboardSettingsDialogComponent(List<DetailType> maneuverDetailSelection,
List<DetailType> legDetailSelection, List<DetailType> raceDetailSelection, List<DetailType> overallDetailSelection,
List<RaceColumnDTO> raceAllRaceColumns, Iterable<RaceColumnDTO> raceColumnSelection, RaceColumnSelection raceColumnSelectionStrategy,
boolean autoExpandPreSelectedRace, long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds, StringMessages stringMessages) {
List<DetailType> legDetailSelection, List<DetailType> raceDetailSelection,
List<DetailType> overallDetailSelection, List<RaceColumnDTO> raceAllRaceColumns,
Iterable<RaceColumnDTO> raceColumnSelection, RaceColumnSelection raceColumnSelectionStrategy,
boolean autoExpandPreSelectedRace, boolean showOverallLeaderboardOnSamePage,
long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds, StringMessages stringMessages) {
this.raceAllRaceColumns = raceAllRaceColumns;
this.numberOfLastRacesToShow = raceColumnSelectionStrategy.getNumberOfLastRaceColumnsToShow();
this.activeRaceColumnSelectionStrategy = raceColumnSelectionStrategy.getType();
@@ -75,12 +79,14 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
this.autoExpandPreSelectedRace = autoExpandPreSelectedRace;
this.delayBetweenAutoAdvancesInMilliseconds = delayBetweenAutoAdvancesInMilliseconds;
this.delayInMilliseconds = delayInMilliseconds;
this.showOverallLeaderboardOnSamePage = showOverallLeaderboardOnSamePage;
}
@Override
public Widget getAdditionalWidget(DataEntryDialog<?> dialog) {
FlowPanel dialogPanel = new FlowPanel();
dialogPanel.add(createSelectedRacesPanel(dialog));
dialogPanel.add(createOverallLeaderboardSelectionPanel(dialog));
dialogPanel.add(createOverallDetailPanel(dialog));
dialogPanel.add(createRaceDetailPanel(dialog));
dialogPanel.add(createLegDetailsPanel(dialog));
@@ -274,6 +280,19 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
return selectedRacesPanel;
}
private FlowPanel createOverallLeaderboardSelectionPanel(DataEntryDialog<?> dialog) {
FlowPanel overallLeaderboardSelectionPanel = new FlowPanel();
overallLeaderboardSelectionPanel.addStyleName("SettingsDialogComponent");
overallLeaderboardSelectionPanel.add(dialog.createHeadline(stringMessages.overallLeaderboardSelection(), true));
FlowPanel overallLeaderboardSelectionContent = new FlowPanel();
overallLeaderboardSelectionContent.addStyleName("dialogInnerContent");
overallLeaderboardSelectionPanel.add(overallLeaderboardSelectionContent);
showOverallLeaderboardOnSamePageCheckbox = dialog.createCheckbox(stringMessages.showOverallLeaderboardOnSamePage());
showOverallLeaderboardOnSamePageCheckbox.setValue(showOverallLeaderboardOnSamePage);
overallLeaderboardSelectionContent.add(showOverallLeaderboardOnSamePageCheckbox);
return overallLeaderboardSelectionPanel;
}
@Override
public LeaderboardSettings getResult() {
List<DetailType> maneuverDetailsToShow = new ArrayList<DetailType>();
@@ -318,7 +337,8 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
lastNRacesToShowValue,
autoExpandPreSelectedRace, 1000l * (delayBetweenAutoAdvancesValue == null ? 0l : delayBetweenAutoAdvancesValue.longValue()),
1000 * (delayInSecondsValue == null ? 0 : delayInSecondsValue.longValue()),
null, true, /* updateUponPlayStateChange */ true, activeRaceColumnSelectionStrategy);
null, true, /* updateUponPlayStateChange */ true, activeRaceColumnSelectionStrategy,
showOverallLeaderboardOnSamePageCheckbox.getValue());
}
@Override
@@ -39,7 +39,7 @@ public class LeaderboardSettingsFactory {
* this argument
*/
public LeaderboardSettings createNewSettingsForPlayMode(PlayModes playMode, String nameOfRaceToSort, String nameOfRaceColumnToShow,
String nameOfRaceToShow, RaceColumnSelection raceColumnSelection) {
String nameOfRaceToShow, RaceColumnSelection raceColumnSelection, boolean showOverallLeaderboardsOnSamePage) {
if (nameOfRaceColumnToShow != null && nameOfRaceToShow != null) {
throw new IllegalArgumentException("Can identify only one race to show, either by race name or by its column name, but not both");
}
@@ -70,11 +70,11 @@ public class LeaderboardSettingsFactory {
/* set autoExpandPreSelectedRace to true if we look at a single race */ nameOfRaceColumnToShow != null || nameOfRaceToShow != null,
/* refresh interval */ null, /* delay to live */ null,
/* name of race to sort */ nameOfRaceToSort, /* ascending */ true, /* updateUponPlayStateChange */ true,
raceColumnSelection.getType());
raceColumnSelection.getType(), showOverallLeaderboardsOnSamePage);
break;
case Replay:
settings = createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, nameOfRaceToSort, /* autoExpandFirstRace */
nameOfRaceColumnToShow != null);
nameOfRaceColumnToShow != null, showOverallLeaderboardsOnSamePage);
break;
}
return settings;
@@ -89,28 +89,33 @@ public class LeaderboardSettingsFactory {
* @param namesOfRacesToShow
* alternatively, races to show can also be specified by their race names; if not <code>null</code>,
* <code>namesOfRaceColumnsToShow</code> must be <code>null</code>
* @param showOverallLeaderboardsOnSamePage
* tells whether a meta leaderboard should be shown beneath the actual leaderboard on the same page, if
* such a meta leaderboard exists; if multiple such meta leaderboards exist, they are all shown
*/
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
List<String> namesOfRacesToShow, String nameOfRaceToSort, boolean autoExpandPreSelectedRace) {
List<String> namesOfRacesToShow, String nameOfRaceToSort, boolean autoExpandPreSelectedRace, boolean showOverallLeaderboardsOnSamePage) {
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, /* leave overallDetailsToShow unchanged */ null,
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null);
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null, showOverallLeaderboardsOnSamePage);
}
/**
* Like {@link #createNewDefaultSettings(List, List, String, boolean)}, only that an additional refresh interval for auto-refresh
* Like {@link #createNewDefaultSettings(List, List, String, boolean, boolean)}, only that an additional refresh interval for auto-refresh
* may be specified; if <code>null</code>, no auto-refresh shall be performed
*/
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis) {
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, overallDetailsToShow, nameOfRaceToSort, autoExpandPreSelectedRace, refreshIntervalMillis,
/* numberOfLastRacesToShow */ null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT);
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, boolean showOverallLeaderboardsOnSamePage) {
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, overallDetailsToShow,
nameOfRaceToSort, autoExpandPreSelectedRace, refreshIntervalMillis,
/* numberOfLastRacesToShow */null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT,
showOverallLeaderboardsOnSamePage);
}
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, Integer numberOfLastRacesToShow,
RaceColumnSelectionStrategies raceColumnSelectionStrategy) {
RaceColumnSelectionStrategies raceColumnSelectionStrategy, boolean showOverallLeaderboardsOnSamePage) {
if (namesOfRaceColumnsToShow != null && namesOfRacesToShow != null) {
throw new IllegalArgumentException("Can specify race columns either by column or by race name, not both");
}
@@ -129,6 +134,6 @@ public class LeaderboardSettingsFactory {
namesOfRacesToShow, numberOfLastRacesToShow,
autoExpandPreSelectedRace, refreshIntervalMillis, /* delay to live */ null,
/* sort by column */ nameOfRaceToSort, /* ascending */ true, /* updateUponPlayStateChange */ true,
raceColumnSelectionStrategy);
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
}
}
@@ -91,14 +91,14 @@ public class LeaderboardUrlConfigurationDialog extends SettingsDialog<Leaderboar
}
LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
namesOfRaceColumnsToShow, /* namesOfRacesToShow */null, /* nameOfRaceToSort */null, /* autoExpandPreSelectedRace */
false);
false, /* showMetaLeaderboardsOnSamePage */ false);
List<DetailType> overallDetailsToShow = Collections.emptyList();
leaderboardSettingsDialogComponent = new LeaderboardSettingsDialogComponent(settings.getManeuverDetailsToShow(),
settings.getLegDetailsToShow(), settings.getRaceDetailsToShow(), overallDetailsToShow, raceList,
/* select all races by default */ raceList, new ExplicitRaceColumnSelection(),
/* autoExpandPreSelectedRace */ false,
/* delayBetweenAutoAdvancesInMilliseconds */ 3000l,
/* delayInMilliseconds */ 3000l, stringMessages);
/* showOverallLeaderboardOnSamePage */ false,
/* delayBetweenAutoAdvancesInMilliseconds */ 3000l, /* delayInMilliseconds */ 3000l, stringMessages);
}
private void updateURL(LeaderboardUrlSettings settings, String leaderboardName) {
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.InvertibleComparator;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.SortingOrder;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.LegDetailColumn.LegDetailField;
@@ -94,6 +95,20 @@ public class LegColumn extends ExpandableSortableColumn<String> {
}
}
private class GapChangeSinceLegStartInSeconds extends AbstractLegDetailField<Double> {
@Override
protected Double getFromNonNullEntry(LegEntryDTO entry) {
return entry.gapChangeSinceLegStartInSeconds;
}
}
private class SideToWhichMarkAtLegStartWasRounded extends AbstractLegDetailField<Tack> {
@Override
protected Tack getFromNonNullEntry(LegEntryDTO entry) {
return entry.sideToWhichMarkAtLegStartWasRounded;
}
}
private class VelocityMadeGoodInKnots extends AbstractLegDetailField<Double> {
@Override
protected Double getFromNonNullEntry(LegEntryDTO entry) {
@@ -212,7 +227,8 @@ public class LegColumn extends ExpandableSortableColumn<String> {
public static DetailType[] getAvailableLegDetailColumnTypes() {
return new DetailType[] { DetailType.AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, DetailType.DISTANCE_TRAVELED,
DetailType.GAP_TO_LEADER_IN_SECONDS, DetailType.CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
DetailType.GAP_TO_LEADER_IN_SECONDS, DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS,
DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED, DetailType.CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
DetailType.WINDWARD_DISTANCE_TO_GO_IN_METERS, DetailType.NUMBER_OF_MANEUVERS,
DetailType.ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS, DetailType.VELOCITY_MADE_GOOD_IN_KNOTS,
DetailType.TIME_TRAVELED, DetailType.AVERAGE_CROSS_TRACK_ERROR_IN_METERS, DetailType.RANK_GAIN };
@@ -247,6 +263,14 @@ public class LegColumn extends ExpandableSortableColumn<String> {
new GapToLeaderInSeconds(), DetailType.GAP_TO_LEADER_IN_SECONDS.getPrecision(),
DetailType.GAP_TO_LEADER_IN_SECONDS.getDefaultSortingOrder(),
detailHeaderStyle, detailColumnStyle));
result.put(DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS,
new FormattedDoubleLegDetailColumn(stringMessages.gapChangeSinceLegStartInSeconds(), "["+stringMessages.gapToLeaderInSecondsUnit()+"]",
new GapChangeSinceLegStartInSeconds(), DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS.getPrecision(),
DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS.getDefaultSortingOrder(),
detailHeaderStyle, detailColumnStyle));
result.put(DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED,
new SideToWhichMarkAtLegStartWasRoundedColumn(stringMessages.sideToWhichMarkAtLegStartWasRounded(),
new SideToWhichMarkAtLegStartWasRounded(), detailHeaderStyle, detailColumnStyle));
result.put(DetailType.VELOCITY_MADE_GOOD_IN_KNOTS,
new FormattedDoubleLegDetailColumn(stringMessages.velocityMadeGoodInKnots(),
"["+stringMessages.velocityMadeGoodInKnotsUnit()+"]", new VelocityMadeGoodInKnots(),
@@ -0,0 +1,18 @@
package com.sap.sailing.gwt.ui.leaderboard;
import com.google.gwt.cell.client.TextCell;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
public class SideToWhichMarkAtLegStartWasRoundedColumn extends LegDetailColumn<Tack, String> {
public SideToWhichMarkAtLegStartWasRoundedColumn(String title, LegDetailField<Tack> field, String headerStyle, String columnStyle) {
super(title, null, field, new TextCell(), DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED.getDefaultSortingOrder(),
headerStyle, columnStyle);
}
@Override
public String getValue(LeaderboardRowDTO row) {
return getField().get(row) == null ? null : getField().get(row).name();
}
}
@@ -461,9 +461,10 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
String leaderboardName, String leaderboardGroupName, final ErrorReporter errorReporter,
final StringMessages stringMessages, UserAgentDetails userAgent) {
super(sailingService, asyncActionsExecutor, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
/* racesToShow */ null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */false),
/* racesToShow */ null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false,
/* showMetaLeaderboardsOnSamePage */ false),
new CompetitorSelectionModel(/* hasMultiSelection */true),
leaderboardName, leaderboardGroupName, errorReporter, stringMessages, userAgent, /* showRaceDetails */ true);
leaderboardName, errorReporter, stringMessages, userAgent, /* showRaceDetails */ true);
suppressedCompetitorsShown = new ListDataProvider<CompetitorDTO>(new ArrayList<CompetitorDTO>());
suppressedCompetitorsTable = createSuppressedCompetitorsTable();
ImageResource importIcon = resources.importIcon();
@@ -19,7 +19,7 @@ public class LeaderboardEditPage extends AbstractEntryPoint {
public void onSuccess(List<String> leaderboardNames) {
String leaderboardName = Window.Location.getParameter("name");
if (leaderboardNames.contains(leaderboardName)) {
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages.editScores(), stringMessages);
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages.editScores(), stringMessages, LeaderboardEditPage.this);
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
EditableLeaderboardPanel leaderboardPanel = new EditableLeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardName, null,
LeaderboardEditPage.this, stringMessages, userAgent);
@@ -23,16 +23,12 @@ public class GlobalNavigationPanel extends FlowPanel {
public GlobalNavigationPanel(StringMessages stringMessages, boolean showHomeNavigation, String leaderboardName, String leaderboardGroupName) {
super();
setStyleName("globalNavigation");
debugParam = Window.Location.getParameter("gwt.codesvr");
String spectatorViewLink = "/gwt/Spectator.html";
String leaderboardViewLink = "/gwt/Leaderboard.html";
String homeLink = "/";
if(showHomeNavigation) {
if (showHomeNavigation) {
if (leaderboardGroupName != null && !leaderboardGroupName.isEmpty()) {
String leaderBoardGroupLink = spectatorViewLink + "?showRaceDetails=true&leaderboardGroupName=" + leaderboardGroupName;
addNavigationLink(leaderboardGroupName, leaderBoardGroupLink, "leaderBoardGroup", "Go to the Event overview.");
@@ -36,6 +36,8 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
private String leaderboardGroupName;
private RaceBoardViewModes viewMode;
private GlobalNavigationPanel globalNavigationPanel;
@Override
public void onModuleLoad() {
super.onModuleLoad();
@@ -172,12 +174,22 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
}
private FlowPanel createLogoAndTitlePanel(RaceBoardPanel raceBoardPanel) {
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(regattaName, selectedRace.name, stringMessages);
globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, leaderboardName, leaderboardGroupName);
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(regattaName, selectedRace.name, stringMessages, this) {
@Override
public void onResize() {
super.onResize();
if (isSmallWidth()) {
remove(globalNavigationPanel);
} else {
add(globalNavigationPanel);
}
}
};
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, leaderboardName, leaderboardGroupName);
logoAndTitlePanel.add(globalNavigationPanel);
if (!isSmallWidth()) {
logoAndTitlePanel.add(globalNavigationPanel);
}
return logoAndTitlePanel;
}
@@ -144,6 +144,7 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
private void createOneScreenView(String leaderboardName, String leaderboardGroupName, FlowPanel mainPanel) {
// create the default leaderboard and select the right race
leaderboardPanel = createLeaderboardPanel(leaderboardName, leaderboardGroupName);
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
RaceMap raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelectionModel, stringMessages);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceMap);
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
@@ -186,10 +187,11 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
.createNewSettingsForPlayMode(timer.getPlayMode(),
/* nameOfRaceToSort */ selectedRaceIdentifier.getRaceName(),
/* nameOfRaceColumnToShow */ null, /* nameOfRaceToShow */ selectedRaceIdentifier.getRaceName(),
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier));
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier),
/* showOverallLeaderboardsOnSamePage */ false);
return new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderBoardSettings, selectedRaceIdentifier,
competitorSelectionModel, timer, leaderboardName, leaderboardGroupName, errorReporter, stringMessages,
userAgent, /* showRaceDetails */ true, raceTimesInfoProvider, /* autoExpandLastRaceColumn */ false);
competitorSelectionModel, timer, leaderboardName, errorReporter, stringMessages, userAgent,
/* showRaceDetails */ true, raceTimesInfoProvider, /* autoExpandLastRaceColumn */ false);
}
private <SettingsType> void addComponentToNavigationMenu(final ComponentViewer componentViewer,
@@ -36,7 +36,6 @@ import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.Course;
@@ -44,6 +43,7 @@ import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Gate;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.RaceDefinition;
@@ -108,6 +108,7 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RacesHandle;
@@ -128,9 +129,9 @@ import com.sap.sailing.geocoding.ReverseGeocoder;
import com.sap.sailing.gwt.ui.client.SailingService;
import com.sap.sailing.gwt.ui.shared.BoatClassDTO;
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CourseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
@@ -144,15 +145,16 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
import com.sap.sailing.gwt.ui.shared.LegEntryDTO;
import com.sap.sailing.gwt.ui.shared.LegInfoDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.MarkpassingManeuverDTO;
import com.sap.sailing.gwt.ui.shared.PlacemarkDTO;
import com.sap.sailing.gwt.ui.shared.PlacemarkOrderDTO;
import com.sap.sailing.gwt.ui.shared.PositionDTO;
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseMarksDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnInSeriesDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseMarksDTO;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
import com.sap.sailing.gwt.ui.shared.RaceEventDTO;
import com.sap.sailing.gwt.ui.shared.RaceEventLogDTO;
@@ -832,6 +834,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
result.finished = trackedLeg.hasFinishedLeg(timePoint);
result.gapToLeaderInSeconds = trackedLeg.getGapToLeaderInSeconds(timePoint,
legRanksCache.get(trackedLeg.getLeg()).entrySet().iterator().next().getKey());
if (result.gapToLeaderInSeconds != null) {
// FIXME problem: asking just after the beginning of the leg yields very different values from asking for the end of the previous leg.
// This is because for the previous leg it's decided based on the mark passings; for the next (current) leg it's decided based on
// windward distance and VMG
Double gapAtEndOfPreviousLegInSeconds = getGapAtEndOfPreviousLegInSeconds(trackedLeg);
if (gapAtEndOfPreviousLegInSeconds != null) {
result.gapChangeSinceLegStartInSeconds = result.gapToLeaderInSeconds - gapAtEndOfPreviousLegInSeconds;
}
}
LinkedHashMap<Competitor, Integer> legRanks = legRanksCache.get(trackedLeg.getLeg());
if (legRanks != null) {
result.rank = legRanks.get(trackedLeg.getCompetitor());
@@ -849,34 +860,58 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
Distance windwardDistanceToGo = trackedLeg.getWindwardDistanceToGo(timePoint);
result.windwardDistanceToGoInMeters = windwardDistanceToGo == null ? null : windwardDistanceToGo
.getMeters();
List<Maneuver> maneuvers = trackedLeg.getManeuvers(timePoint, waitForLatestAnalyses);
if (maneuvers != null) {
result.numberOfManeuvers = new HashMap<ManeuverType, Integer>();
result.numberOfManeuvers.put(ManeuverType.TACK, 0);
result.numberOfManeuvers.put(ManeuverType.JIBE, 0);
result.numberOfManeuvers.put(ManeuverType.PENALTY_CIRCLE, 0);
Map<ManeuverType, Double> totalManeuverLossInMeters = new HashMap<ManeuverType, Double>();
totalManeuverLossInMeters.put(ManeuverType.TACK, 0.0);
totalManeuverLossInMeters.put(ManeuverType.JIBE, 0.0);
totalManeuverLossInMeters.put(ManeuverType.PENALTY_CIRCLE, 0.0);
for (Maneuver maneuver : maneuvers) {
switch (maneuver.getType()) {
case TACK:
case JIBE:
case PENALTY_CIRCLE:
if (maneuver.getManeuverLoss() != null) {
result.numberOfManeuvers.put(maneuver.getType(), result.numberOfManeuvers.get(maneuver.getType())+1);
totalManeuverLossInMeters.put(maneuver.getType(),
totalManeuverLossInMeters.get(maneuver.getType()) + maneuver.getManeuverLoss().getMeters());
final TimePoint startOfRace = trackedLeg.getTrackedLeg().getTrackedRace().getStartOfRace();
if (startOfRace != null && trackedLeg.hasStartedLeg(timePoint)) {
// not using trackedLeg.getManeuvers(...) because it may not catch the mark passing maneuver starting this leg
// because that may have been detected as slightly before the mark passing time, hence associated with the previous leg
List<Maneuver> maneuvers = trackedLeg.getTrackedLeg().getTrackedRace()
.getManeuvers(trackedLeg.getCompetitor(), startOfRace, timePoint, waitForLatestAnalyses);
if (maneuvers != null) {
result.numberOfManeuvers = new HashMap<ManeuverType, Integer>();
result.numberOfManeuvers.put(ManeuverType.TACK, 0);
result.numberOfManeuvers.put(ManeuverType.JIBE, 0);
result.numberOfManeuvers.put(ManeuverType.PENALTY_CIRCLE, 0);
Map<ManeuverType, Double> totalManeuverLossInMeters = new HashMap<ManeuverType, Double>();
totalManeuverLossInMeters.put(ManeuverType.TACK, 0.0);
totalManeuverLossInMeters.put(ManeuverType.JIBE, 0.0);
totalManeuverLossInMeters.put(ManeuverType.PENALTY_CIRCLE, 0.0);
TimePoint startOfLeg = trackedLeg.getStartTime();
for (Maneuver maneuver : maneuvers) {
// don't count maneuvers that were in previous legs
switch (maneuver.getType()) {
case TACK:
case JIBE:
case PENALTY_CIRCLE:
if (!maneuver.getTimePoint().before(startOfLeg) && (!trackedLeg.hasFinishedLeg(timePoint) ||
maneuver.getTimePoint().before(trackedLeg.getFinishTime()))) {
if (maneuver.getManeuverLoss() != null) {
result.numberOfManeuvers.put(maneuver.getType(),
result.numberOfManeuvers.get(maneuver.getType()) + 1);
totalManeuverLossInMeters.put(maneuver.getType(),
totalManeuverLossInMeters.get(maneuver.getType())
+ maneuver.getManeuverLoss().getMeters());
}
}
break;
case MARK_PASSING:
// analyze all mark passings, not only those after this leg's start, to catch the mark passing
// maneuver starting this leg, even if its time point is slightly before the mark passing starting this leg
MarkPassingManeuver mpm = (MarkPassingManeuver) maneuver;
if (mpm.getWaypointPassed() == trackedLeg.getLeg().getFrom()) {
result.sideToWhichMarkAtLegStartWasRounded = mpm.getSide();
}
break;
}
break;
}
}
result.averageManeuverLossInMeters = new HashMap<ManeuverType, Double>();
for (ManeuverType maneuverType : new ManeuverType[] { ManeuverType.TACK, ManeuverType.JIBE, ManeuverType.PENALTY_CIRCLE }) {
if (result.numberOfManeuvers.get(maneuverType) != 0) {
result.averageManeuverLossInMeters.put(maneuverType,
totalManeuverLossInMeters.get(maneuverType)/result.numberOfManeuvers.get(maneuverType));
result.averageManeuverLossInMeters = new HashMap<ManeuverType, Double>();
for (ManeuverType maneuverType : new ManeuverType[] { ManeuverType.TACK, ManeuverType.JIBE,
ManeuverType.PENALTY_CIRCLE }) {
if (result.numberOfManeuvers.get(maneuverType) != 0) {
result.averageManeuverLossInMeters.put(
maneuverType,
totalManeuverLossInMeters.get(maneuverType)
/ result.numberOfManeuvers.get(maneuverType));
}
}
}
}
@@ -884,6 +919,40 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return result;
}
private Double getGapAtEndOfPreviousLegInSeconds(TrackedLegOfCompetitor trackedLeg) throws NoWindException {
final Double result;
final Course course = trackedLeg.getTrackedLeg().getTrackedRace().getRace().getCourse();
course.lockForRead();
try {
int indexOfStartWaypoint = course.getIndexOfWaypoint(trackedLeg.getLeg().getFrom());
if (indexOfStartWaypoint == 0) {
// trackedLeg was the first leg; gap is determined by gap of start line passing time points
Iterable<MarkPassing> markPassingsForLegStart = trackedLeg.getTrackedLeg().getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getFrom());
final Iterator<MarkPassing> markPassingsIter = markPassingsForLegStart.iterator();
if (markPassingsIter.hasNext()) {
TimePoint firstStart = markPassingsIter.next().getTimePoint();
final MarkPassing markPassingForFrom = trackedLeg.getTrackedLeg().getTrackedRace().
getMarkPassing(trackedLeg.getCompetitor(), trackedLeg.getLeg().getFrom());
if (markPassingForFrom != null) {
TimePoint competitorStart = markPassingForFrom.getTimePoint();
result = (double) (competitorStart.asMillis() - firstStart.asMillis()) / 1000.;
} else {
result = null;
}
} else {
result = null;
}
} else {
TrackedLeg previousTrackedLeg = trackedLeg.getTrackedLeg().getTrackedRace().getTrackedLeg(course.getLegs().get(indexOfStartWaypoint-1));
TrackedLegOfCompetitor previousTrackedLegOfCompetitor = previousTrackedLeg.getTrackedLeg(trackedLeg.getCompetitor());
result = previousTrackedLegOfCompetitor.getGapToLeaderInSeconds(previousTrackedLegOfCompetitor.getFinishTime());
}
return result;
} finally {
course.unlockAfterRead();
}
}
@Override
public List<RegattaDTO> getRegattas() throws IllegalArgumentException {
List<RegattaDTO> result = new ArrayList<RegattaDTO>();
@@ -2163,7 +2232,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public Void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity, String comment) {
return getService().apply(
new UpdateLeaderboardScoreCorrectionMetadata(leaderboardName, new MillisecondsTimePoint(timePointOfLastCorrectionValidity), comment));
new UpdateLeaderboardScoreCorrectionMetadata(leaderboardName,
timePointOfLastCorrectionValidity == null ? null : new MillisecondsTimePoint(timePointOfLastCorrectionValidity),
comment));
}
@Override
@@ -2460,12 +2531,23 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private List<ManeuverDTO> createManeuverDTOsForCompetitor(List<Maneuver> maneuvers, TrackedRace trackedRace, Competitor competitor) {
List<ManeuverDTO> result = new ArrayList<ManeuverDTO>();
for (Maneuver maneuver : maneuvers) {
ManeuverDTO maneuverDTO = new ManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
final ManeuverDTO maneuverDTO;
if (maneuver.getType() == ManeuverType.MARK_PASSING) {
maneuverDTO = new MarkpassingManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
new PositionDTO(maneuver.getPosition().getLatDeg(), maneuver.getPosition().getLngDeg()),
maneuver.getTimePoint().asDate(),
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingBefore()),
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingAfter()),
maneuver.getDirectionChangeInDegrees(), maneuver.getManeuverLoss()==null?null:maneuver.getManeuverLoss().getMeters());
maneuver.getDirectionChangeInDegrees(), maneuver.getManeuverLoss()==null?null:maneuver.getManeuverLoss().getMeters(),
((MarkPassingManeuver) maneuver).getSide());
} else {
maneuverDTO = new ManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
new PositionDTO(maneuver.getPosition().getLatDeg(), maneuver.getPosition().getLngDeg()),
maneuver.getTimePoint().asDate(),
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingBefore()),
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingAfter()),
maneuver.getDirectionChangeInDegrees(), maneuver.getManeuverLoss()==null?null:maneuver.getManeuverLoss().getMeters());
}
result.add(maneuverDTO);
}
return result;
@@ -2839,6 +2921,24 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
List<CompetitorDTO> competitorList = getCompetitorDTOList(preResult.get(raceColumn));
result.add(new Pair<String, List<CompetitorDTO>>(raceColumn.getName(), competitorList));
}
return result;
}
@Override
public List<String> getOverallLeaderboardNamesContaining(String leaderboardName) {
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
if (leaderboard == null) {
throw new IllegalArgumentException("Couldn't find leaderboard named "+leaderboardName);
}
List<String> result = new ArrayList<String>();
for (Map.Entry<String, Leaderboard> leaderboardEntry : getService().getLeaderboards().entrySet()) {
if (leaderboardEntry.getValue() instanceof MetaLeaderboard) {
MetaLeaderboard metaLeaderboard = (MetaLeaderboard) leaderboardEntry.getValue();
if (Util.contains(metaLeaderboard.getLeaderboards(), leaderboard)) {
result.add(leaderboardEntry.getKey());
}
}
}
return result;
}
}
@@ -11,6 +11,7 @@ import java.util.Set;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.impl.Util.Pair;
public abstract class AbstractLeaderboardDTO implements IsSerializable {
public String name;
@@ -261,7 +262,7 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
/**
* @return <code>true</code> if the leaderboard contains a race which is live
*/
public boolean containsLiveRace() {
public boolean hasLiveRace() {
for (RaceColumnDTO race : getRaceList()) {
for (FleetDTO fleet : race.getFleets()) {
if (race.isLive(fleet)) {
@@ -272,6 +273,18 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
return false;
}
public List<Pair<RaceColumnDTO, FleetDTO>> getLiveRaces() {
List<Pair<RaceColumnDTO, FleetDTO>> result = new ArrayList<Pair<RaceColumnDTO, FleetDTO>>();
for (RaceColumnDTO race : getRaceList()) {
for (FleetDTO fleet : race.getFleets()) {
if (race.isLive(fleet)) {
result.add(new Pair<RaceColumnDTO, FleetDTO>(race, fleet));
}
}
}
return result;
}
@Override
public int hashCode() {
final int prime = 31;
@@ -130,9 +130,9 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
/**
* @return <code>true</code> if the group contains a race which is live.
*/
public boolean containsLiveRace() {
public boolean hasLiveRace() {
for (StrippedLeaderboardDTO leaderboard : leaderboards) {
if (leaderboard.containsLiveRace()) {
if (leaderboard.hasLiveRace()) {
return true;
}
}
@@ -4,6 +4,7 @@ import java.util.Map;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Tack;
/**
* Holds data about one competitor's performance in one leg of one race represented in the
@@ -18,6 +19,8 @@ public class LegEntryDTO implements IsSerializable {
public Double averageSpeedOverGroundInKnots;
public int rank;
public Double gapToLeaderInSeconds;
public Double gapChangeSinceLegStartInSeconds;
public Tack sideToWhichMarkAtLegStartWasRounded;
public Double estimatedTimeToNextWaypointInSeconds;
/**
@@ -41,11 +44,13 @@ public class LegEntryDTO implements IsSerializable {
public Map<ManeuverType, Integer> numberOfManeuvers;
public Map<ManeuverType, Double> averageManeuverLossInMeters;
public Double averageCrossTrackErrorInMeters;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((averageCrossTrackErrorInMeters == null) ? 0 : averageCrossTrackErrorInMeters.hashCode());
result = prime * result + ((averageManeuverLossInMeters == null) ? 0 : averageManeuverLossInMeters.hashCode());
result = prime * result
+ ((averageSpeedOverGroundInKnots == null) ? 0 : averageSpeedOverGroundInKnots.hashCode());
result = prime * result
@@ -55,11 +60,14 @@ public class LegEntryDTO implements IsSerializable {
* result
+ ((estimatedTimeToNextWaypointInSeconds == null) ? 0 : estimatedTimeToNextWaypointInSeconds.hashCode());
result = prime * result + (finished ? 1231 : 1237);
result = prime * result + ((gapChangeSinceLegStartInSeconds == null) ? 0 : gapChangeSinceLegStartInSeconds.hashCode());
result = prime * result + ((gapToLeaderInSeconds == null) ? 0 : gapToLeaderInSeconds.hashCode());
result = prime * result + ((numberOfManeuvers == null) ? 0 : numberOfManeuvers.hashCode());
result = prime * result + rank;
result = prime * result
+ ((sideToWhichMarkAtLegStartWasRounded == null) ? 0 : sideToWhichMarkAtLegStartWasRounded.hashCode());
result = prime * result + (started ? 1231 : 1237);
result = prime * result + (int) (timeInMilliseconds ^ (timeInMilliseconds >>> 32));
result = prime * result + ((timeInMilliseconds == null) ? 0 : timeInMilliseconds.hashCode());
result = prime * result + ((velocityMadeGoodInKnots == null) ? 0 : velocityMadeGoodInKnots.hashCode());
result = prime * result
+ ((windwardDistanceToGoInMeters == null) ? 0 : windwardDistanceToGoInMeters.hashCode());
@@ -74,6 +82,16 @@ public class LegEntryDTO implements IsSerializable {
if (getClass() != obj.getClass())
return false;
LegEntryDTO other = (LegEntryDTO) obj;
if (averageCrossTrackErrorInMeters == null) {
if (other.averageCrossTrackErrorInMeters != null)
return false;
} else if (!averageCrossTrackErrorInMeters.equals(other.averageCrossTrackErrorInMeters))
return false;
if (averageManeuverLossInMeters == null) {
if (other.averageManeuverLossInMeters != null)
return false;
} else if (!averageManeuverLossInMeters.equals(other.averageManeuverLossInMeters))
return false;
if (averageSpeedOverGroundInKnots == null) {
if (other.averageSpeedOverGroundInKnots != null)
return false;
@@ -96,6 +114,11 @@ public class LegEntryDTO implements IsSerializable {
return false;
if (finished != other.finished)
return false;
if (gapChangeSinceLegStartInSeconds == null) {
if (other.gapChangeSinceLegStartInSeconds != null)
return false;
} else if (!gapChangeSinceLegStartInSeconds.equals(other.gapChangeSinceLegStartInSeconds))
return false;
if (gapToLeaderInSeconds == null) {
if (other.gapToLeaderInSeconds != null)
return false;
@@ -108,9 +131,14 @@ public class LegEntryDTO implements IsSerializable {
return false;
if (rank != other.rank)
return false;
if (sideToWhichMarkAtLegStartWasRounded != other.sideToWhichMarkAtLegStartWasRounded)
return false;
if (started != other.started)
return false;
if (timeInMilliseconds != other.timeInMilliseconds)
if (timeInMilliseconds == null) {
if (other.timeInMilliseconds != null)
return false;
} else if (!timeInMilliseconds.equals(other.timeInMilliseconds))
return false;
if (velocityMadeGoodInKnots == null) {
if (other.velocityMadeGoodInKnots != null)
@@ -124,6 +152,4 @@ public class LegEntryDTO implements IsSerializable {
return false;
return true;
}
}
@@ -2,9 +2,13 @@ package com.sap.sailing.gwt.ui.shared;
import java.util.Date;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class ManeuverDTO implements IsSerializable {
public ManeuverType type;
@@ -37,5 +41,23 @@ public class ManeuverDTO implements IsSerializable {
this.directionChangeInDegrees = directionChangeInDegrees;
this.maneuverLossInMeters = maneuverLossInMeters;
}
public String toString(StringMessages stringMessages) {
SpeedWithBearingDTO before = this.speedWithBearingBefore;
SpeedWithBearingDTO after = this.speedWithBearingAfter;
String timeAndManeuver = DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(this.timepoint)
+ ": " + this.type.name();
String directionChange = stringMessages.directionChange() + ": "
+ ((int) this.directionChangeInDegrees) + " "+stringMessages.degreesShort()+" ("
+ ((int) before.bearingInDegrees) + " deg -> " + ((int) after.bearingInDegrees) + " "+stringMessages.degreesShort()+")";
String speedChange = stringMessages.speedChange() + ": "
+ NumberFormat.getDecimalFormat().format(after.speedInKnots - before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" ("
+ NumberFormat.getDecimalFormat().format(before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" -> "
+ NumberFormat.getDecimalFormat().format(after.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+")";
String maneuverLoss = this.maneuverLossInMeters == null ? "" : ("; "+stringMessages.maneuverLoss()+": "+
NumberFormat.getDecimalFormat().format(this.maneuverLossInMeters)+"m");
String maneuverTitle = timeAndManeuver + "; " + directionChange + "; " + speedChange + maneuverLoss;
return maneuverTitle;
}
}
@@ -0,0 +1,29 @@
package com.sap.sailing.gwt.ui.shared;
import java.util.Date;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class MarkpassingManeuverDTO extends ManeuverDTO {
public Tack side;
MarkpassingManeuverDTO() {}
public MarkpassingManeuverDTO(ManeuverType type, Tack newTack, PositionDTO position, Date timepoint,
SpeedWithBearingDTO speedWithBearingBefore, SpeedWithBearingDTO speedWithBearingAfter,
double directionChangeInDegrees, Double maneuverLossInMeters, Tack side) {
super(type, newTack, position, timepoint, speedWithBearingBefore, speedWithBearingAfter, directionChangeInDegrees,
maneuverLossInMeters);
this.side = side;
}
@Override
public String toString(StringMessages stringMessages) {
StringBuilder result = new StringBuilder(super.toString(stringMessages));
result.append(", ");
result.append(stringMessages.passedTo(side.name()));
return result.toString();
}
}
@@ -143,14 +143,13 @@ public class RaceColumnDTO extends NamedDTO implements IsSerializable {
}
/**
* @param fleet TODO
* @return <code>true</code> if the startOfTracking is after the current date and there's no end of the race
*/
public boolean isLive(FleetDTO fleet) {
boolean result = false;
final long now = System.currentTimeMillis();
if (getWhenLastTrackedRaceWasLive(fleet) != null
&& getWhenLastTrackedRaceWasLive(fleet).getTime() > now - IS_LIVE_GRACE_PERIOD_IN_MILLIS) {
Date whenLastTrackedRaceWasLive = getWhenLastTrackedRaceWasLive(fleet);
if (whenLastTrackedRaceWasLive != null && whenLastTrackedRaceWasLive.getTime() > now - IS_LIVE_GRACE_PERIOD_IN_MILLIS) {
result = true;
}
return result;
@@ -96,13 +96,10 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
dataSeriesByCompetitor = new HashMap<CompetitorDTO, Series>();
markPassingSeriesByCompetitor = new HashMap<CompetitorDTO, Series>();
setSize("100%", "100%");
noCompetitorsSelectedLabel = new Label(stringMessages.selectAtLeastOneCompetitor() + ".");
noCompetitorsSelectedLabel.setStyleName("abstractChartPanel-importantMessageOfChart");
chart = createChart();
createChart();
setSelectedDetailType(detailType);
competitorSelectionProvider.addCompetitorSelectionChangeListener(this);
raceSelectionProvider.addRaceSelectionChangeListener(this);
}
@@ -124,7 +121,7 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
.setMarginRight(65)
.setBorderColor(new Color("#CACACA"))
.setBackgroundColor(new Color("#EBEBEB"))
.setBorderWidth(1)
.setBorderWidth(0)
.setBorderRadius(0)
.setPlotBorderWidth(0)
.setCredits(new Credits().setEnabled(false))
@@ -316,18 +313,7 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
newRaceDataPoints = new Point[currentPointIndex];
System.arraycopy(raceDataPointsToAdd, 0, newRaceDataPoints, 0, currentPointIndex);
}
if(timeRangeWithZoomProvider.isZoomed()) {
Pair<Date, Date> timeZoom = timeRangeWithZoomProvider.getTimeZoom();
resetMinMaxAndExtremesInterval(false);
competitorDataSeries.setPoints(newRaceDataPoints, false);
changeMinMaxAndExtremesInterval(timeZoom.getA(), timeZoom.getB(), false);
} else {
competitorDataSeries.setPoints(newRaceDataPoints, false);
}
setSeriesPoints(competitorDataSeries, newRaceDataPoints);
// Adding the series if chart doesn't contain it
if (!chartSeries.contains(competitorDataSeries)) {
chart.addSeries(competitorDataSeries);
@@ -335,14 +321,12 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
}
}
}
if(timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > chartData.getRequestedFromTime().getTime()) {
if (timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > chartData.getRequestedFromTime().getTime()) {
timeOfEarliestRequestInMillis = chartData.getRequestedFromTime().getTime();
}
if(timeOfLatestRequestInMillis == null || timeOfLatestRequestInMillis < chartData.getRequestedToTime().getTime()) {
if (timeOfLatestRequestInMillis == null || timeOfLatestRequestInMillis < chartData.getRequestedToTime().getTime()) {
timeOfLatestRequestInMillis = chartData.getRequestedToTime().getTime();
}
chart.redraw();
}
@@ -482,6 +466,10 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
return this.selectedDetailType;
}
private boolean hasReversedYAxis(DetailType detailType) {
return detailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER || detailType == DetailType.GAP_TO_LEADER_IN_SECONDS;
}
/**
* Updates the {@link #selectedDetailType} field, clears the chart for the new <code>selectedDetailType</code> and
* clears the {@link #chartData}.<br />
@@ -492,12 +480,20 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
protected boolean setSelectedDetailType(DetailType newSelectedDetailType) {
boolean hasDetailTypeChanged = newSelectedDetailType != this.selectedDetailType;
if (hasDetailTypeChanged) {
boolean oldReversedYAxis = hasReversedYAxis(this.selectedDetailType);
this.selectedDetailType = newSelectedDetailType;
clearChart();
chart.setTitle(new ChartTitle().setText(DetailTypeFormatter.format(selectedDetailType, stringMessages)),
null);
// TODO There is a bug in the highcharts library which prevents to change the reverse property of the YAxis
// Because we need this functionality we need to recreate the chart each time the YAxis changes
if (oldReversedYAxis != hasReversedYAxis(selectedDetailType)) {
chart = createChart();
if (isZoomed) {
Pair<Date, Date> zoomRange = timeRangeWithZoomProvider.getTimeZoom();
onTimeZoomChanged(zoomRange.getA(), zoomRange.getB());
} else {
resetMinMaxAndExtremesInterval(/* redraw */ true);
}
}
chart.setTitle(new ChartTitle().setText(DetailTypeFormatter.format(selectedDetailType, stringMessages)), null);
final String unit = getDetailTypeUnit();
if (!compactChart) {
chart.getYAxis().setAxisTitleText(
@@ -505,11 +501,8 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
} else {
chart.getYAxis().setAxisTitleText("[" + unit + "]");
}
chart.getYAxis()
.setReversed(
(selectedDetailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER || selectedDetailType == DetailType.GAP_TO_LEADER_IN_SECONDS) ? true
: false);
chart.getYAxis().setReversed(
selectedDetailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER || selectedDetailType == DetailType.GAP_TO_LEADER_IN_SECONDS);
final NumberFormat numberFormat = DetailTypeFormatter.getNumberFormat(selectedDetailType);
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
@Override
@@ -3,6 +3,8 @@ package com.sap.sailing.gwt.ui.shared.charts;
import java.util.Date;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.Point;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.XAxis;
import org.moxieapps.gwt.highcharts.client.events.ChartClickEvent;
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
@@ -10,6 +12,7 @@ import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
@@ -108,7 +111,6 @@ public abstract class RaceChart extends SimplePanel implements RaceSelectionChan
protected void changeMinMaxAndExtremesInterval(Date minTimepoint, Date maxTimepoint, boolean redraw) {
XAxis xAxis = chart.getXAxis();
if (minTimepoint != null) {
xAxis.setMin(minTimepoint.getTime());
}
@@ -120,26 +122,41 @@ public abstract class RaceChart extends SimplePanel implements RaceSelectionChan
long tickInterval = (maxTimepoint.getTime() - minTimepoint.getTime()) / TICKCOUNT;
xAxis.setTickInterval(tickInterval);
}
if(redraw) {
if (redraw) {
chart.redraw();
}
}
protected void setSeriesPoints(Series series, Point[] points) {
if (timeRangeWithZoomProvider.isZoomed()) {
Pair<Date, Date> timeZoom = timeRangeWithZoomProvider.getTimeZoom();
resetMinMaxAndExtremesInterval(/* redraw */ false);
series.setPoints(points, false);
changeMinMaxAndExtremesInterval(timeZoom.getA(), timeZoom.getB(), /* redraw */ false);
} else {
series.setPoints(points, false);
}
}
protected void resetMinMaxAndExtremesInterval(boolean redraw) {
changeMinMaxAndExtremesInterval(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), redraw);
}
@Override
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
changeMinMaxAndExtremesInterval(zoomStartTimepoint, zoomEndTimepoint, true);
// Probably there is a function for this in a newer version of highcharts: http://jsfiddle.net/mqz3N/1071/
// chart.showResetZoom();
}
@Override
public void onTimeRangeChanged(Date fromTime, Date toTime) {
if(isZoomed && timer.getPlayMode() != PlayModes.Live)
changeMinMaxAndExtremesInterval(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), true);
if (!(isZoomed && timer.getPlayMode() == PlayModes.Live)) {
resetMinMaxAndExtremesInterval(/* redraw */ true);
}
}
@Override
public void onTimeZoomReset() {
resetMinMaxAndExtremesInterval(true);
}
@@ -104,7 +104,7 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
.setWidth100()
.setHeight100()
.setBorderColor(new Color("#CACACA"))
.setBorderWidth(1)
.setBorderWidth(0)
.setBorderRadius(0)
.setBackgroundColor(new Color("#EBEBEB"))
.setPlotBorderWidth(0)
@@ -381,11 +381,10 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
newDirectionPoints = directionPoints;
newSpeedPoints = speedPoints;
}
directionSeries.setPoints(newDirectionPoints);
setSeriesPoints(directionSeries, newDirectionPoints);
windSourceDirectionPoints.put(windSource, newDirectionPoints);
if (windSource.getType().useSpeed()) {
speedSeries.setPoints(newSpeedPoints);
setSeriesPoints(speedSeries, newSpeedPoints);
windSourceSpeedPoints.put(windSource, newSpeedPoints);
}
}
@@ -11,6 +11,8 @@ public interface IsEmbeddableComponent {
Widget getToolbarWidget();
Widget getLegendWidget();
BusyIndicator getBusyIndicator();
boolean isEmbedded();
@@ -17,8 +17,6 @@ import java.util.Set;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.maps.client.InfoWindowContent;
import com.google.gwt.maps.client.MapWidget;
@@ -75,17 +73,16 @@ import com.sap.sailing.gwt.ui.client.TimeListener;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayStates;
import com.sap.sailing.gwt.ui.client.WindSourceTypeFormatter;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.CourseDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LegInfoDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.PositionDTO;
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO;
@@ -1286,21 +1283,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
LatLng latLng = LatLng.newInstance(maneuver.position.latDeg, maneuver.position.lngDeg);
MarkerOptions options = MarkerOptions.newInstance();
//TODO Introduce user role dependent view (Spectator, Admin)
SpeedWithBearingDTO before = maneuver.speedWithBearingBefore;
SpeedWithBearingDTO after = maneuver.speedWithBearingAfter;
String timeAndManeuver = DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.timepoint)
+ ": " + maneuver.type.name();
String directionChange = stringMessages.directionChange() + ": "
+ ((int) maneuver.directionChangeInDegrees) + " "+stringMessages.degreesShort()+" ("
+ ((int) before.bearingInDegrees) + " deg -> " + ((int) after.bearingInDegrees) + " "+stringMessages.degreesShort()+")";
String speedChange = stringMessages.speedChange() + ": "
+ NumberFormat.getDecimalFormat().format(after.speedInKnots - before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" ("
+ NumberFormat.getDecimalFormat().format(before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" -> "
+ NumberFormat.getDecimalFormat().format(after.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+")";
String maneuverLoss = maneuver.maneuverLossInMeters == null ? "" : ("; "+stringMessages.maneuverLoss()+": "+
NumberFormat.getDecimalFormat().format(maneuver.maneuverLossInMeters)+"m");
options.setTitle(timeAndManeuver + "; " + directionChange + "; " + speedChange + maneuverLoss);
options.setTitle(maneuver.toString(stringMessages));
options.setIcon(raceMapImageManager.maneuverIconsForTypeAndTargetTack
.get(new Util.Pair<ManeuverType, Tack>(maneuver.type, maneuver.newTack)));
Marker marker = new Marker(latLng, options);
@@ -611,7 +611,7 @@ public class EventOverviewPanel extends FormPanel {
result = textContainsStringsToCheck(event.name, name.split("\\s"));
}
if (result && onlyLiveCheckBox.getValue()) {
result = event.leaderboardGroup.containsLiveRace();
result = event.leaderboardGroup.hasLiveRace();
}else if (result) {
Date startDate = event.leaderboardGroup.getGroupStartDate();
if (startDate != null) {
@@ -611,7 +611,7 @@ public class LeaderboardGroupOverviewPanel extends FormPanel {
result = textContainsStringsToCheck(forGroup.name, name.split("\\s"));
}
if (result && onlyLiveCheckBox.getValue()) {
result = forGroup.containsLiveRace();
result = forGroup.hasLiveRace();
}else if (result) {
Date startDate = forGroup.getGroupStartDate();
if (startDate != null) {
@@ -176,7 +176,7 @@ public class LeaderboardGroupPanel extends FormPanel implements HasWelcomeWidget
mainPanel.add(leaderboardGroupDescriptionLabel);
DockPanel labelAndLegendPanel = new DockPanel();
labelAndLegendPanel.setWidth("100%");
labelAndLegendPanel.setWidth("90%");
labelAndLegendPanel.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);
Label leaderboardsTableLabel = new Label(stringMessages.leaderboards());
leaderboardsTableLabel.setStyleName(STYLE_NAME_PREFIX + "LeaderboardsTableLabel");
@@ -236,7 +236,7 @@ public class LeaderboardGroupPanel extends FormPanel implements HasWelcomeWidget
LeaderboardGroupFullTableResources tableResources = GWT.create(LeaderboardGroupFullTableResources.class);
CellTable<StrippedLeaderboardDTO> leaderboardsTable = new CellTable<StrippedLeaderboardDTO>(10000, tableResources);
leaderboardsTable.setWidth("100%");
leaderboardsTable.setWidth("90%");
leaderboardsTable.setSelectionModel(new NoSelectionModel<StrippedLeaderboardDTO>());
leaderboardsTable.addColumn(leaderboardNameColumn, stringMessages.regatta());
leaderboardsTable.addColumn(overviewColumn, "");
@@ -56,10 +56,8 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements RegattaRe
&& Window.Location.getParameter("embedded").equalsIgnoreCase("true");
if (!embedded) {
String title = groupName != null ? groupName : stringMessages.overview();
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(title, stringMessages);
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(title, stringMessages, this);
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, null, null);
logoAndTitlePanel.add(globalNavigationPanel);
@@ -58,7 +58,7 @@ public class TVEntryPoint extends AbstractEntryPoint {
if(leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
leaderboardDisplayName = leaderboardName;
}
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages);
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages, this);
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
mainPanel.addNorth(logoAndTitlePanel, 68);
}
@@ -72,7 +72,8 @@ public class TVViewController implements RaceTimesInfoProviderListener {
showWindChart = false;
leaderboard = null;
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false);
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false,
/* showMetaLeaderboardsOnSamePage */ false);
timer = new Timer(PlayModes.Live, /* delayBetweenAutoAdvancesInMilliseconds */1000l);
timer.setLivePlayDelayInMillis(delayToLiveInMillis);
@@ -85,8 +86,8 @@ public class TVViewController implements RaceTimesInfoProviderListener {
private LeaderboardPanel createLeaderboardPanel(String leaderboardName, boolean showRaceDetails) {
CompetitorSelectionModel selectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true);
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardSettings,
/* preSelectedRace */null, selectionModel, timer, leaderboardName, null, errorReporter, stringMessages,
userAgent, showRaceDetails, /* raceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false) {
/* preSelectedRace */null, selectionModel, timer, leaderboardName, errorReporter, stringMessages, userAgent,
showRaceDetails, /* raceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false) {
@Override
protected void setLeaderboard(LeaderboardDTO leaderboard) {
super.setLeaderboard(leaderboard);
@@ -94,6 +95,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
updateRaceTimesInfoProvider();
}
};
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
return leaderboardPanel;
}
@@ -135,30 +137,15 @@ public class TVViewController implements RaceTimesInfoProviderListener {
}
private void showLeaderboard() {
if(activeTvView != TVViews.Leaderboard) {
if (activeTvView != TVViews.Leaderboard) {
clearContentPanels();
LeaderboardPanel leaderboardPanel = createLeaderboardPanel(leaderboardName, showRaceDetails);
ScrollPanel leaderboardContentPanel = new ScrollPanel();
leaderboardContentPanel.add(leaderboardPanel);
// if(leaderboard != null) {
// //Resetting the settings of the leaderboard panel to prevent, that some race columns get lost
// List<String> namesOfRaceColumnsToShow = new ArrayList<String>();
// for (RaceColumnDTO race : leaderboard.getRaceList()) {
// namesOfRaceColumnsToShow.add(race.getRaceColumnName());
// }
// LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
// namesOfRaceColumnsToShow, null, null, false);
// leaderboardPanel.updateSettings(settings);
// }
dockPanel.add(leaderboardContentPanel);
if(logoAndTitlePanel != null) {
logoAndTitlePanel.setSubTitle(leaderboardName);
}
currentLiveRace = null;
activeTvView = TVViews.Leaderboard;
}
@@ -61,12 +61,12 @@
</servlet-mapping>
<servlet>
<servlet-name>RaceRankingAsRssFeedServlet</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.impl.RaceRankingAsRssFeedServlet</servlet-class>
<servlet-name>LiveRaceRankingAsRssFeedServlet</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.impl.LiveRaceRankingAsRssFeedServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RaceRankingAsRssFeedServlet</servlet-name>
<url-pattern>/rss/raceranking</url-pattern>
<servlet-name>LiveRaceRankingAsRssFeedServlet</servlet-name>
<url-pattern>/rss/liveRaceRanking</url-pattern>
</servlet-mapping>
<servlet id="jsp">
@@ -26,64 +26,88 @@ import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.SyndFeedOutput;
public class RaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
public class LiveRaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
private static final long serialVersionUID = -508373964426868319L;
private static final String PARAM_NAME_LEADERBOARDNAME = "leaderboardName";
private static final String PARAM_NAME_RACENAME = "raceName";
private static final String PARAM_MAX_COMPETITORS = "max";
private String charEncoding = "UTF-8";
private int maxCompetitorsToShow = 1000;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// to allow access to the json document directly from a client side javascript
// to allow access to the rss feed directly from a client side javascript
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setContentType("application/rss+xml");
resp.setCharacterEncoding(charEncoding);
TimePoint timePoint = MillisecondsTimePoint.now();
String leaderboardName = req.getParameter(PARAM_NAME_LEADERBOARDNAME);
String raceName = req.getParameter(PARAM_NAME_RACENAME);
String maxCompetitorsParam = req.getParameter(PARAM_MAX_COMPETITORS);
if(maxCompetitorsParam != null) {
try {
maxCompetitorsToShow = Integer.parseInt(maxCompetitorsParam);
} catch (NumberFormatException e) {
}
}
if (leaderboardName == null) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Need to specify a leaderboard name using the "+
PARAM_NAME_LEADERBOARDNAME+" parameter");
} else if (raceName == null) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Need to specify a race column name using the "+
PARAM_NAME_RACENAME+" parameter");
} else {
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
if (leaderboard == null) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Leaderboard "+leaderboardName+" not found");
} else {
try {
RaceColumn raceColumnToShow = null;
RaceColumn raceColumnToShow = null;
TrackedRace liveTrackedRace = null;
// find a live race in the leaderboard
Iterable<RaceColumn> races = leaderboard.getRaceColumns();
for (RaceColumn raceInLeaderboard : races) {
for (Fleet fleet : raceInLeaderboard.getFleets()) {
TrackedRace trackedRace = raceInLeaderboard.getTrackedRace(fleet);
if (trackedRace != null && raceInLeaderboard.getName().equals(raceName)) {
raceColumnToShow = raceInLeaderboard;
break;
TrackedRace trackedRaceOfFleet = raceInLeaderboard.getTrackedRace(fleet);
if(trackedRaceOfFleet != null && isLive(trackedRaceOfFleet)) {
raceColumnToShow = raceInLeaderboard;
liveTrackedRace = trackedRaceOfFleet;
break;
}
}
}
if(raceColumnToShow != null) {
List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(raceColumnToShow, MillisecondsTimePoint.now());
String eventName = "STG meets 'Berlin Match Race'";
String title = eventName + ": " + leaderboardName;
SyndFeed feed = new SyndFeedImpl();
String title = leaderboard.getName();
feed.setTitle(title);
feed.setFeedType("rss_2.0");
feed.setLink("http://www.sapsaling.com");
feed.setDescription("SAP Sailing Analytics");
feed.setPublishedDate(new Date());
feed.setEncoding(charEncoding);
List<SyndEntry> entries = new ArrayList<SyndEntry>();
SyndContent description = new SyndContentImpl();
SyndEntry entry = new SyndEntryImpl();
entry.setLink("http://www.sapsailing.com");
description.setType("text/plain");
if(raceColumnToShow != null && liveTrackedRace != null) {
String entryTitle = "Live ranking of race '" + raceColumnToShow.getName() + "'";
entry.setTitle(entryTitle);
SyndFeed feed = new SyndFeedImpl();
feed.setTitle(title);
feed.setFeedType("rss_2.0");
feed.setLink("http://www.sapsaling.com");
feed.setDescription("SAP Sailing Analytics");
feed.setPublishedDate(new Date());
feed.setEncoding(charEncoding);
List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(raceColumnToShow, timePoint);
int currentCompetitorCounter = 0;
String feedText = "";
for (Competitor competitor : competitorsFromBestToWorst) {
if(currentCompetitorCounter == maxCompetitorsToShow) {
break;
}
currentCompetitorCounter++;
String sailID = competitor.getBoat().getSailID();
feedText += currentCompetitorCounter + ". " + sailID + "\n";
// String competitorName = competitor.getName();
// final String displayName = leaderboard.getDisplayName(competitor);
// String id = competitor.getId().toString();
@@ -92,7 +116,6 @@ public class RaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
// String countryCode = nationality != null ? nationality.getCountryCode().getTwoLetterISOCode(): null;
// String totalRank = "" +competitorsFromBestToWorst.indexOf(competitor) + 1;
// String totalPoints = "" + leaderboard.getTotalPoints(competitor, timePoint);
//
// String raceColumnName = raceColumnToShow.getName();
// final Fleet fleetOfCompetitor = raceColumnToShow.getFleetOfCompetitor(competitor);
// String fleet = fleetOfCompetitor==null?"":fleetOfCompetitor.getName();
@@ -103,37 +126,33 @@ public class RaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
// String isDiscarded = String.valueOf(leaderboard.isDiscarded(competitor, raceColumnToShow, timePoint));
// String isCorrected = String.valueOf(leaderboard.getScoreCorrection().isScoreCorrected(competitor, raceColumnToShow));
final TrackedRace trackedRace = raceColumnToShow.getTrackedRace(competitor);
boolean isLive = trackedRace != null && trackedRace.getEndOfRace() == null && (trackedRace.getStartOfTracking() != null ? new Date().after(trackedRace.getStartOfTracking().asDate()) : false);
if(isLive) {
String raceRank = "" + trackedRace.getRank(competitor, timePoint);
feedText += raceRank + ". " + sailID + "\n";
}
}
List<SyndEntry> entries = new ArrayList<SyndEntry>();
SyndContent description = new SyndContentImpl();
SyndEntry entry = new SyndEntryImpl();
entry.setTitle("Live Race ranking of race " + raceName);
entry.setLink("http://obmr2012.sapsailing.com");
description.setType("text/plain");
description.setValue(feedText);
entry.setDescription(description);
entries.add(entry);
feed.setEntries(entries);
PrintWriter writer = resp.getWriter();
SyndFeedOutput feedOutput = new SyndFeedOutput();
feedOutput.output(feed, writer);
} else {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "No live race at the moment");
String entryTitle = "No live race right now";
entry.setTitle(entryTitle);
description.setValue("");
}
entry.setDescription(description);
entries.add(entry);
feed.setEntries(entries);
PrintWriter writer = resp.getWriter();
SyndFeedOutput feedOutput = new SyndFeedOutput();
feedOutput.output(feed, writer);
} catch (Exception e) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
}
}
}
}
/**
* @return <code>true</code> if the startOfTracking is after the current date and there's no end of the race
*/
private boolean isLive(TrackedRace trackedRace) {
return trackedRace.getEndOfRace() == null && (trackedRace.getStartOfTracking() != null ? new Date().after(trackedRace.getStartOfTracking().asDate()) : false);
}
}
@@ -4,8 +4,6 @@ bin.includes = META-INF/,\
WEB-INF/classes/,\
WEB-INF/web.xml,\
images/,\
winnercircle2012/,\
main.css,\
ess40/,\
fontface.css,\
sap.ico,\
@@ -26,7 +26,7 @@
</p>
</div>
<div id='navigation'>
<a class='navButton event' href='http://ess40-2012.sapsailing.com/gwt/Leaderboard.html?name=Extreme%2040%20Rio&amp;refreshIntervalMillis=10000&amp;lastN=3&amp;raceDetail=DISPLAY_LEGS'>
<a class='navButton event' href='http://ess40-2012.sapsailing.com/gwt/Leaderboard.html?name=Extreme%2040%20Rio&amp;refreshIntervalMillis=10000&amp;lastN=3'>
Live Rio Act Leaderboard
</a>
<a class='navButton overall' href='http://ess40-2012.sapsailing.com/gwt/Leaderboard.html?name=Extreme%20Sailing%20Series%202012%20Overall&amp;showRaceDetails=false&amp;refreshIntervalMillis=10000&amp;displayName=Overall%20Standings&amp;leaderboardGroupName=Extreme%20Sailing%20Series%202012&amp;root=leaderboardGroupPanel'>