Skip to content

Commit e6699ef

Browse files
committed
Progress on individual race support.
1 parent e2241d4 commit e6699ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+11220
-38
lines changed

src/main/java/common/Category.java

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package common;
22

3+
import java.util.List;
4+
35
public interface Category {
46

57
boolean includes(final Category other_category);

src/main/java/common/Race.java

+21
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,27 @@
33
import java.io.FileInputStream;
44
import java.io.IOException;
55
import java.nio.file.Path;
6+
import java.nio.file.Paths;
67
import java.time.Duration;
78
import java.util.Properties;
89

910
public abstract class Race {
1011

12+
13+
protected static final String DUMMY_DURATION_STRING = "23:59:59";
14+
public static final Duration DUMMY_DURATION = parseTime(DUMMY_DURATION_STRING);
15+
protected static final String ZERO_TIME_STRING = "0:0:0";
16+
public static final Duration ZERO_TIME = parseTime(ZERO_TIME_STRING);
17+
1118
protected Properties properties;
1219
protected Path working_directory_path;
1320

21+
// String read from configuration file specifying all the runners who did have a finish
22+
// time recorded but were declared DNF.
23+
protected String dnf_string;
24+
25+
protected RawResult[] raw_results;
26+
1427
public Race(final String config_file_path) throws IOException {
1528

1629
this(readProperties(config_file_path));
@@ -40,6 +53,11 @@ protected static Properties readProperties(final String config_file_path) throws
4053
}
4154
}
4255

56+
protected void readProperties() {
57+
working_directory_path = Paths.get(properties.getProperty("WORKING_DIRECTORY"));
58+
dnf_string = properties.getProperty("DNF_LEGS");
59+
}
60+
4361
public Path getWorkingDirectoryPath() {
4462
return working_directory_path;
4563
}
@@ -50,6 +68,9 @@ protected String getPropertyWithDefault(final String property_key, final String
5068
return value == null || value.isBlank() ? default_value : value;
5169
}
5270

71+
72+
73+
5374
protected static Duration parseTime(String element) {
5475

5576
element = element.strip();

src/main/java/individual_race/IndividualRace.java

+149-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
package individual_race;
22

3+
import common.Category;
34
import common.Race;
5+
import common.RawResult;
46

57
import java.io.IOException;
6-
import java.util.Properties;
8+
import java.util.*;
79

810
public class IndividualRace extends Race {
911

10-
// TODO variable number of prizes per category; open prizes in addition to gender categories; non-binary category; optional team prizes
12+
// TODO variable number of prizes per category; open prizes in addition to gender categories; non-binary category; optional team prizes, by cumulative positions and times
13+
14+
IndividualRaceInput input;
15+
IndividualRaceOutput output_CSV, output_HTML, output_text, output_PDF;
16+
IndividualRacePrizes prizes;
17+
18+
Runner[] entries;
19+
Result[] overall_results;
20+
Map<Category, List<Runner>> prize_winners = new HashMap<>();
1121

1222
public IndividualRace(final String config_file_path) throws IOException {
1323
super(config_file_path);
@@ -19,9 +29,146 @@ public IndividualRace(final Properties properties) throws IOException {
1929

2030
@Override
2131
protected void configure() throws IOException {
32+
33+
readProperties();
34+
35+
configureHelpers();
36+
configureInputData();
2237
}
2338

2439
@Override
2540
public void processResults() throws IOException {
41+
42+
initialiseResults();
43+
44+
fillFinishTimes();
45+
fillDNFs();
46+
calculateResults();
47+
allocatePrizes();
48+
49+
printOverallResults();
50+
printPrizes();
51+
printCombined();
52+
}
53+
54+
private void configureHelpers() {
55+
56+
input = new IndividualRaceInput(this);
57+
58+
output_CSV = new IndividualRaceOutputCSV(this);
59+
output_HTML = new IndividualRaceOutputHTML(this);
60+
output_text = new IndividualRaceOutputText(this);
61+
output_PDF = new IndividualRaceOutputPDF(this);
62+
63+
prizes = new IndividualRacePrizes(this);
64+
}
65+
66+
private void configureInputData() throws IOException {
67+
68+
raw_results = input.loadRawResults();
69+
entries = input.loadEntries();
70+
}
71+
72+
private void initialiseResults() {
73+
74+
overall_results = new Result[entries.length];
75+
76+
for (int i = 0; i < overall_results.length; i++)
77+
overall_results[i] = new Result(entries[i], this);
78+
}
79+
80+
private void fillFinishTimes() {
81+
82+
for (final RawResult raw_result : raw_results) {
83+
84+
final int runner_index = findIndexOfRunnerWithBibNumber(raw_result.getBibNumber());
85+
final Result result = overall_results[runner_index];
86+
87+
result.finish_time = raw_result.getRecordedFinishTime();
88+
89+
// Provisionally this leg is not DNF since a finish time was recorded.
90+
// However, it might still be set to DNF in fillDNFs() if the runner didn't complete the course.
91+
result.DNF = false;
92+
}
93+
}
94+
95+
private void fillDNFs() {
96+
97+
// This fills in the DNF results that were specified explicitly in the config
98+
// file, corresponding to cases where the runner reported not completing the course.
99+
100+
// DNF cases where there is no recorded leg result are captured by the
101+
// default value of Result.DNF being true.
102+
103+
if (dnf_string != null && !dnf_string.isBlank()) {
104+
105+
for (final String dnf_string : dnf_string.split(",")) {
106+
107+
try {
108+
final Result result = getResultWithIndex(dnf_string);
109+
result.DNF = true;
110+
}
111+
catch (Exception e) {
112+
throw new RuntimeException("illegal DNF time");
113+
}
114+
}
115+
}
116+
}
117+
118+
private Result getResultWithIndex(final String bib) {
119+
120+
final int bib_number = Integer.parseInt(bib);
121+
122+
return overall_results[findIndexOfRunnerWithBibNumber(bib_number)];
123+
}
124+
125+
int findIndexOfRunnerWithBibNumber(final int bib_number) {
126+
127+
for (int i = 0; i < overall_results.length; i++)
128+
if (overall_results[i].runner.bib_number == bib_number) return i;
129+
130+
throw new RuntimeException("unregistered team: " + bib_number);
131+
}
132+
133+
private void calculateResults() {
134+
135+
// Sort in order of recorded time.
136+
// DNF results are sorted in increasing order of bib number.
137+
// Where two runners have the same recorded time, the order in which they were recorded is preserved.
138+
Arrays.sort(overall_results);
139+
}
140+
141+
int getRecordedPosition(final int bib_number) {
142+
143+
for (int i = 0; i < raw_results.length; i++) {
144+
if (raw_results[i].getBibNumber() == bib_number) {
145+
return i + 1;
146+
}
147+
}
148+
149+
return Integer.MAX_VALUE;
150+
}
151+
152+
private void allocatePrizes() {
153+
154+
prizes.allocatePrizes();
155+
}
156+
157+
private void printOverallResults() throws IOException {
158+
159+
output_CSV.printOverallResults();
160+
output_HTML.printOverallResults();
161+
}
162+
163+
private void printPrizes() throws IOException {
164+
165+
output_text.printPrizes();
166+
output_PDF.printPrizes();
167+
output_HTML.printPrizes();
168+
}
169+
170+
private void printCombined() throws IOException {
171+
172+
((IndividualRaceOutputHTML)output_HTML).printCombined();
26173
}
27174
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package individual_race;
2+
3+
import common.Category;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
8+
public enum IndividualRaceCategory implements Category {
9+
10+
// Declared in order of decreasing generality.
11+
// The order is significant in that it defines the order of iteration when allocating prizes.
12+
13+
OPEN_SENIOR("Open Senior", "OS", "Open", 15, 3),
14+
MEN_SENIOR("Men Senior", "MS", "Men", 20, 3),
15+
WOMEN_SENIOR("Women Senior", "FS", "Women", 20, 3),
16+
MEN_UNDER_20("Men Junior", "MU20", "Men", 15, 1),
17+
WOMEN_UNDER_20("Women Junior", "FU20", "Women", 15, 1),
18+
MEN_40("Men 40-49", "M40","Men", 40, 1),
19+
WOMEN_40("Women 40-49", "F40","Women", 40, 1),
20+
MEN_50("Men 50-59", "M50","Men", 50, 1),
21+
WOMEN_50("Women 50-59", "F50","Women", 50, 1),
22+
MEN_60("Men 60-69", "M60","Men", 60, 1),
23+
WOMEN_60("Women 60-69", "F60","Women", 60, 1),
24+
MEN_70("Men 70+", "M70","Men", 70, 1),
25+
WOMEN_70("Women 70+", "F70","Women", 70, 1);
26+
27+
private final String category_long_name;
28+
private final String category_short_name;
29+
private final String gender;
30+
private final int minimum_age;
31+
public final int number_of_prizes;
32+
33+
IndividualRaceCategory(final String category_long_name, final String category_short_name, final String gender, final int minimum_age, final int number_of_prizes) {
34+
35+
this.category_long_name = category_long_name;
36+
this.category_short_name = category_short_name;
37+
this.gender = gender;
38+
this.minimum_age = minimum_age;
39+
this.number_of_prizes = number_of_prizes;
40+
}
41+
42+
@Override
43+
public boolean includes(final Category other_category) {
44+
45+
// Only the open category includes any other categories.
46+
return this == other_category || this == OPEN_SENIOR;
47+
}
48+
49+
public static final List<Category> getCategoriesInReportOrder() {
50+
return Arrays.asList(
51+
OPEN_SENIOR,
52+
WOMEN_SENIOR,
53+
MEN_SENIOR,
54+
WOMEN_UNDER_20,
55+
MEN_UNDER_20,
56+
WOMEN_40,
57+
MEN_40,
58+
WOMEN_50,
59+
MEN_50,
60+
WOMEN_60,
61+
MEN_60,
62+
WOMEN_70,
63+
MEN_70);
64+
}
65+
66+
public static Category parse(final String s) {
67+
68+
for (Category category : values())
69+
if (category.shortName().equals(s)) return category;
70+
71+
throw new RuntimeException("undefined category: " + s);
72+
}
73+
74+
@Override
75+
public String shortName() {
76+
return category_short_name;
77+
}
78+
79+
@Override
80+
public String longName() {
81+
return category_long_name;
82+
}
83+
84+
@Override
85+
public int numberOfPrizes() {
86+
return number_of_prizes;
87+
}
88+
}

0 commit comments

Comments
 (0)