@rasifix/orienteering-utils 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/lib/format.d.ts +6 -0
  2. package/lib/format.js +2 -0
  3. package/{src/formats/index.ts → lib/formats/index.d.ts} +1 -6
  4. package/lib/formats/index.js +9 -0
  5. package/lib/formats/kraemer.d.ts +14 -0
  6. package/lib/formats/kraemer.js +131 -0
  7. package/lib/formats/oware.d.ts +7 -0
  8. package/lib/formats/oware.js +129 -0
  9. package/lib/formats/solv.d.ts +14 -0
  10. package/lib/formats/solv.js +151 -0
  11. package/lib/index.d.ts +2 -0
  12. package/lib/index.js +5 -0
  13. package/{src/model/category.ts → lib/model/category.d.ts} +1 -2
  14. package/lib/model/category.js +2 -0
  15. package/{src/model/competition.ts → lib/model/competition.d.ts} +1 -2
  16. package/lib/model/competition.js +2 -0
  17. package/lib/model/ranking.d.ts +27 -0
  18. package/{src/model/ranking.ts → lib/model/ranking.js} +6 -37
  19. package/lib/model/runner.d.ts +21 -0
  20. package/lib/model/runner.js +8 -0
  21. package/{src/model/split.ts → lib/model/split.d.ts} +2 -2
  22. package/lib/model/split.js +2 -0
  23. package/lib/time.d.ts +10 -0
  24. package/{src/time.ts → lib/time.js} +22 -18
  25. package/lib/utils/anonymizer.d.ts +2 -0
  26. package/{src/utils/anonymizer.ts → lib/utils/anonymizer.js} +22 -28
  27. package/lib/utils/ranking.d.ts +71 -0
  28. package/lib/utils/ranking.js +383 -0
  29. package/package.json +9 -5
  30. package/lib/analyzis.js +0 -25
  31. package/src/format.ts +0 -11
  32. package/src/formats/kraemer.ts +0 -145
  33. package/src/formats/oware.ts +0 -143
  34. package/src/formats/solv.ts +0 -169
  35. package/src/index.ts +0 -5
  36. package/src/model/runner.ts +0 -23
  37. package/src/utils/ranking.ts +0 -535
  38. package/src/utils/reorganize.ts +0 -212
  39. package/test/butterfly-oware.csv +0 -120
  40. package/test/kraemer-test.ts +0 -78
  41. package/test/kraemer.csv +0 -130
  42. package/test/oware-test.ts +0 -79
  43. package/test/oware.csv +0 -101
  44. package/test/ranking-test.ts +0 -55
  45. package/test/solv-test.ts +0 -65
  46. package/test/solv.csv +0 -1833
  47. package/test/test.js +0 -14
  48. package/tsconfig.json +0 -11
package/src/format.ts DELETED
@@ -1,11 +0,0 @@
1
- import { Competition } from "./model/competition";
2
-
3
- export interface Format {
4
-
5
- check(content:string):boolean;
6
-
7
- parse(content:string, options:any):Competition;
8
-
9
- serialize(competition:Competition):string;
10
-
11
- }
@@ -1,145 +0,0 @@
1
- import { Format } from "../format";
2
- import { Category } from "../model/category";
3
- import { Competition } from "../model/competition";
4
- import { Runner } from "../model/runner";
5
- import { parseTime, formatTime } from "../time";
6
-
7
- function strip(str: string | undefined) {
8
- if (!str) {
9
- return "";
10
- } else if (str.length > 0 && str[0] === '"') {
11
- return str.substring(1, str.length - 1);
12
- } else {
13
- return str;
14
- }
15
- }
16
-
17
- export class KraemerFormater implements Format {
18
- // OE0014;Stnr;XStnr;Chipnr;Datenbank Id;Nachname;Vorname;Jg;G;Block; AK; Start;Ziel; Zeit; Wertung;Gutschrift -;Zuschlag +;Kommentar;Club-Nr.;Abk; Ort; Nat; Sitz;Region;Katnr;Kurz; Lang; MeldeKat. Nr;MeldeKat. (kurz);MeldeKat. (lang);Rang;Ranglistenpunkte;Num1;Num2; Num3; Text1; Text2; Text3; Adr. Nachname;Adr. Vorname;Straße;Zeile2;PLZ; Adr. Ort;Tel; Mobil; Fax; EMail; Gemietet;Startgeld;Bezahlt;Mannschaft;Bahnnummer;Bahn; km; Hm; Bahn Posten;Platz; Startstempel;Zielstempel;Posten1;Stempel1;Posten2;Stempel2;Posten3; Stempel3;Posten4;Stempel4;Posten5;Stempel5;Posten6;Stempel6;Posten7;Stempel7;Posten8;Stempel8;Posten9;Stempel9;Posten10;Stempel10;Posten11;Stempel11;Posten12;Stempel12;Posten13;Stempel13;Posten14;Stempel14;Posten15;Stempel15;Posten16;Stempel16;Posten17;Stempel17;Posten18;Stempel18;Posten19;Stempel19;Posten20;Stempel20;Posten21;Stempel21;Posten22;Stempel22;Posten23;Stempel23;Posten24;Stempel24;Posten25;Stempel25;Posten26;Stempel26;Posten27;Stempel27;Posten28;Stempel28;Posten29;Stempel29;Posten30;Stempel30;Posten31;Stempel31;Posten32;Stempel32;Posten33;Stempel33;Posten34;Stempel34;Posten35;Stempel35;Posten36;Stempel36;Posten37;Stempel37;Posten38;Stempel38;Posten39;Stempel39;Posten40;Stempel40;Posten41;Stempel41;Posten42;Stempel42;Posten43;Stempel43;Posten44;Stempel44;Posten45;Stempel45;Posten46;Stempel46;Posten47;Stempel47;Posten48;Stempel48;Posten49;Stempel49;Posten50;Stempel50;Posten51;Stempel51;Posten52;Stempel52;Posten53;Stempel53;Posten54;Stempel54;Posten55;Stempel55;Posten56;Stempel56;Posten57;Stempel57;Posten58;Stempel58;Posten59;Stempel59;Posten60;Stempel60;Posten61;Stempel61;Posten62;Stempel62;Posten63;Stempel63;Posten64;Stempel64;
19
- // Stnr ;Chip;Datenbank Id;Nachname;Vorname;Jg;G;Block;AK;Start;Ziel;Zeit; Wertung;Club-Nr.;Abk; Ort; Nat; Katnr; Kurz; Lang;Num1;Num2;Num3;Text1; Text2;Text3;Adr. Name;Straße; Zeile2; PLZ; Ort; Tel; Fax; EMail;Id/Verein;Gemietet;Startgeld;Bezahlt;Bahnnummer; Bahn; km; Hm; Bahn Posten;Pl; Startstempel;Zielstempel;Posten1;Stempel1;Posten2; Stempel2; Posten3;Stempel3; Posten4; Stempel4;Posten5;Stempel5;Posten6; Stempel6;Posten7; Stempel7; Posten8;Stempel8;Posten9;Stempel9;Posten10;Stempel10;(und weitere)...
20
- parse(text: string, options: any = {}) {
21
- // split text into lines
22
- var lines = text.split(/\r?\n|\r|\n/g);
23
-
24
- // extract header
25
- var header = lines[0].split(";");
26
- var firstTimeIdx: number;
27
-
28
- // trying to get hold of correct column indices
29
- var indices: { [key: string]: number } = {};
30
- indices["Melde Id"] = header.indexOf("Melde Id");
31
- indices["Nachname"] = header.indexOf("Nachname");
32
- indices["Vorname"] = header.indexOf("Vorname");
33
- indices["Jg"] = header.indexOf("Jg");
34
- indices["G"] = header.indexOf("G");
35
- indices["Datenbank Id"] = header.indexOf("Datenbank Id");
36
- indices["Abk"] = header.indexOf("Abk");
37
- indices["Club"] = header.indexOf("Ort");
38
- indices["Ort"] = header.indexOf("Adr. Ort");
39
- indices["Nat"] = header.indexOf("Nat");
40
- indices["Start"] = header.indexOf("Start");
41
- indices["Ziel"] = header.indexOf("Ziel");
42
- indices["Zeit"] = header.indexOf("Zeit");
43
- indices["Katnr"] = header.indexOf("Kurz");
44
- indices["Rang"] = header.indexOf("Rang");
45
- indices["Wertung"] = header.indexOf("Wertung");
46
- indices["Posten"] = header.indexOf("Bahn Posten");
47
- indices["km"] = header.indexOf("km");
48
- indices["hm"] = header.indexOf("Hm");
49
- firstTimeIdx = header.indexOf("Posten1");
50
-
51
- if (header[0] === "OE0014") {
52
- indices["Chip"] = header.indexOf("Chipnr");
53
- } else {
54
- indices["Chip"] = header.indexOf("Chip");
55
- }
56
-
57
- lines = lines.slice(1);
58
-
59
- // the result object
60
- var categories: { [key: string]: Category } = {};
61
-
62
- function objectify(cols: string[]): any {
63
- var result: any = {};
64
- Object.keys(indices).forEach(function (key) {
65
- result[key] = cols[indices[key]];
66
- });
67
- return result;
68
- }
69
-
70
- lines.forEach(function (line) {
71
- var cols = line.split(";");
72
- var lineObj = objectify(cols);
73
-
74
- if (lineObj["Wertung"] !== "0") {
75
- return;
76
- }
77
-
78
- let categoryName = strip(lineObj["Katnr"]);
79
- let runnerName = strip(lineObj["Nachname"]);
80
- let runnerFirstname = strip(lineObj["Vorname"]);
81
-
82
- let runTime = parseTime(lineObj["Zeit"]);
83
- let startTime = parseTime(lineObj["Start"]);
84
-
85
- var runner: Runner = {
86
- id: lineObj["Melde Id"],
87
- category: categoryName,
88
- firstName: runnerFirstname,
89
- name: runnerName,
90
- fullName: [runnerFirstname, runnerName].join(" "),
91
- yearOfBirth: lineObj["Jg"],
92
- club: (strip(lineObj["Abk"]) + " " + strip(lineObj["Club"])).trim(),
93
- city: strip(lineObj["Ort"]),
94
- nation: strip(lineObj["Nat"]),
95
- time: formatTime(runTime) || "",
96
- startTime: formatTime(startTime) || "",
97
- splits: [],
98
- };
99
-
100
- var category = categories[categoryName];
101
- if (typeof category === "undefined") {
102
- category = {
103
- name: strip(lineObj["Katnr"]),
104
- distance: parseInt(strip(lineObj["km"]), 10) * 1000,
105
- ascent: parseInt(strip(lineObj["hm"])),
106
- controls: parseInt(strip(lineObj["Posten"])),
107
- runners: [],
108
- };
109
- categories[category.name] = category;
110
- }
111
-
112
- var times = cols.slice(firstTimeIdx);
113
- for (var idx = 0; idx < parseInt(lineObj["Posten"], 10) * 2; idx += 2) {
114
- if (idx === times.length - 1) {
115
- continue;
116
- }
117
- const parsedTime = parseTime(times[idx + 1]);
118
- runner.splits.push({ code: times[idx], time: formatTime(parsedTime) });
119
- }
120
-
121
- category.runners.push(runner);
122
- });
123
-
124
- return {
125
- name: options.event || "Anonymous Event",
126
- map: options.map || "Unknown Map",
127
- date: options.date || "",
128
- startTime: options.startTime || "",
129
- categories: Object.keys(categories).map(function (category) {
130
- return categories[category];
131
- }),
132
- };
133
- }
134
-
135
- check(text: string) {
136
- return (
137
- text.substring(0, 6) === "OE0014" ||
138
- text.substring(0, 23) === "Stnr;Chip;Datenbank Id;"
139
- );
140
- }
141
-
142
- serialize(competition: Competition): string {
143
- throw new Error("format does not implement serialization");
144
- }
145
- }
@@ -1,143 +0,0 @@
1
- import { Format } from "../format";
2
- import { Category } from "../model/category";
3
- import { Competition } from "../model/competition";
4
- import { Runner } from "../model/runner";
5
-
6
- function parseCategory(row: string[]) {
7
- return {
8
- name: row[0],
9
- distance: parseInt(row[1], 10),
10
- ascent: parseInt(row[2], 10),
11
- controls: parseInt(row[3], 10),
12
- runners: [],
13
- };
14
- }
15
-
16
- function parseRunner(row: string[], category: string, id: number):Runner {
17
- var headerLength = 15;
18
- var i;
19
-
20
- var splits = [];
21
- for (i = headerLength; i < row.length; i += 2) {
22
- splits.push({ code: row[i], time: row[i + 1] });
23
- }
24
-
25
- // split cleanup - detect two following splits with identical time
26
- // --> control not working properly; set 's' as split time (substitute)
27
- // going from back to front to catch several not working controls
28
- for (i = splits.length - 1; i > 0; i--) {
29
- if (splits[i].time === splits[i - 1].time && splits[i].time !== "-") {
30
- splits[i].time = "s";
31
- }
32
- }
33
-
34
- return {
35
- id: id,
36
- category: category,
37
- rank: row[0] ? parseInt(row[0]) : undefined,
38
- firstName: row[2],
39
- name: row[1],
40
- fullName: [row[2], row[1]].join(" "),
41
- yearOfBirth: row[3],
42
- club: row[8],
43
- city: row[7],
44
- nation: row[9],
45
- time: row[12],
46
- startTime: row[13],
47
- splits: splits,
48
- };
49
- }
50
-
51
- export class OwareFormat implements Format {
52
- parse(text: string):Competition {
53
- // split text into lines
54
- var lines = text.trim().split(/[\r\n]+/);
55
-
56
- // throw away first row containing headers
57
- lines = lines.splice(1);
58
-
59
- // second row contains information about the event
60
- var header = lines[0].split(";");
61
-
62
- var competition: Competition = {
63
- // row starts with a double slash
64
- name: header[0].substring(2, header[0].length),
65
- map: header[1],
66
- date: header[2],
67
- startTime: header[3],
68
- categories: [],
69
- };
70
-
71
- // throw a way the now parsed header
72
- lines = lines.splice(1);
73
-
74
- var category: Category;
75
-
76
- let idx = 0;
77
- lines
78
- .filter((line) => line.trim().length > 0)
79
- .forEach(function (line) {
80
- var cols = line.split(";");
81
- if (cols.length === 4) {
82
- category = parseCategory(cols);
83
- competition.categories.push(category);
84
- } else {
85
- category.runners.push(parseRunner(cols, category.name, ++idx));
86
- }
87
- });
88
-
89
- return competition;
90
- }
91
-
92
- serialize(event: Competition) {
93
- var result =
94
- "//Format: Rank;Name;Firstname;YearOfBirth;SexMF;FedNr;Zip;Town;Club;NationIOF;StartNr;eCardNr;RunTime;StartTime;FinishTime;CtrlCode;SplitTime; ...\n";
95
- result +=
96
- "//" +
97
- [event.name, event.map, event.date, event.startTime, ""].join(";") +
98
- "\n";
99
-
100
- event.categories.forEach(function (category) {
101
- result +=
102
- [
103
- category.name,
104
- category.distance,
105
- category.ascent,
106
- category.controls,
107
- ].join(";") + "\n";
108
- category.runners.forEach(function (runner) {
109
- result += [
110
- runner.rank,
111
- runner.fullName,
112
- "",
113
- runner.yearOfBirth,
114
- "",
115
- "",
116
- "",
117
- runner.city,
118
- runner.club,
119
- runner.nation,
120
- "",
121
- "", // ecard
122
- runner.time,
123
- runner.startTime,
124
- "",
125
- ].join(";");
126
- result +=
127
- ";" +
128
- runner.splits
129
- .map(function (split) {
130
- return split.code + ";" + split.time;
131
- })
132
- .join(";") +
133
- "\n";
134
- });
135
- });
136
-
137
- return result;
138
- }
139
-
140
- check(text: string) {
141
- return text.substring(0, 8) === "//Format";
142
- }
143
- }
@@ -1,169 +0,0 @@
1
- import { Format } from "../format";
2
- import { Category } from "../model/category";
3
- import { Competition } from "../model/competition";
4
- import { Runner } from "../model/runner";
5
- import { formatTime, parseTime } from "../time";
6
-
7
- function reformatTime(str: string): string | undefined {
8
- // "special" total times (like wrong or missing control)
9
- if (str.indexOf(":") === -1) {
10
- return str;
11
- }
12
-
13
- const parsed = parseTime(str);
14
- return parsed ? formatTime(parsed) : undefined;
15
- }
16
-
17
- function reformatSplitTime(str: string): string | undefined {
18
- // normalize missing punch time
19
- if (str === "-" || str === "-----") {
20
- return "-";
21
- }
22
-
23
- // normalize not working control
24
- if (str === "0.00") {
25
- return "s";
26
- }
27
-
28
- const parsed = parseTime(str);
29
- return parsed ? formatTime(parsed) : undefined;
30
- }
31
-
32
- // flat csv file format - every row contains full info including category
33
- // Kategorie;Laenge;Steigung;PoAnz;Rang;Name;Jahrgang;Ort;Club;Zeit;Startzeit;Zielzeit;Zwischenzeiten
34
- export class SolvFormat implements Format {
35
- parse(text: string, options: any = {}) {
36
- const categories: { [key: string]: Category } = {};
37
-
38
- const lines = text.split("\n");
39
-
40
- // drop header column
41
- lines.splice(0, 1)[0].split(";");
42
-
43
- lines.forEach(function (line, idx) {
44
- const tokens = line.split(";");
45
- if (tokens.length < 11) {
46
- // invalid input? not enough data for runner
47
- return;
48
- }
49
-
50
- const categoryName = tokens[0];
51
- let category = categories[categoryName];
52
- if (!category) {
53
- category = {
54
- name: categoryName,
55
- distance: Math.round(parseFloat(tokens[1]) * 1000),
56
- ascent: parseInt(tokens[2]),
57
- controls: parseInt(tokens[3]),
58
- runners: [],
59
- };
60
- categories[categoryName] = category;
61
- }
62
-
63
- const name = tokens[5].split(" ");
64
- const runner:Runner = {
65
- id: idx,
66
- category: categoryName,
67
- rank: tokens[4] ? parseInt(tokens[4]) : undefined,
68
- firstName: name[0],
69
- name: name.slice(1).join(" "),
70
- fullName: tokens[5],
71
- yearOfBirth: tokens[6],
72
- city: tokens[7],
73
- club: tokens[8],
74
- time: reformatTime(tokens[9]),
75
- startTime: tokens[10],
76
- splits: [],
77
- };
78
-
79
- if (tokens.length - 12 < category.controls * 2) {
80
- // some crappy SOLV data...
81
- console.log(
82
- "fix crappy data from SOLV - not enough tokens on line for runner " +
83
- runner.fullName
84
- );
85
- for (var i = tokens.length; i < category.controls * 2 + 12; i++) {
86
- if (i % 2 === 0) {
87
- tokens[i] =
88
- category.runners.length === 0
89
- ? "???"
90
- : category.runners[0].splits[(i - 12) / 2].code;
91
- } else {
92
- tokens[i] = "-";
93
- }
94
- }
95
- }
96
-
97
- for (let i = 12; i < tokens.length - 1; i += 2) {
98
- let time = reformatSplitTime(tokens[i + 1]);
99
- if (runner.splits.length > 0 && time) {
100
- let prev = runner.splits[runner.splits.length - 1].time;
101
- let parsedTime = parseTime(tokens[i + 1]);
102
- if (
103
- time === prev ||
104
- tokens[i + 1] === "0.00" || (parsedTime && parsedTime > 180 * 60)) {
105
- // normalize valid manual punches
106
- time = "s";
107
- }
108
- }
109
- runner.splits.push({ code: tokens[i], time: time });
110
- }
111
-
112
- category.runners.push(runner);
113
- });
114
-
115
- return {
116
- name: options.event || "Anonymous Event",
117
- map: options.map || "Unknown Map",
118
- date: options.date || "",
119
- startTime: options.startTime || "",
120
- categories: Object.keys(categories).map(function (category) {
121
- return categories[category];
122
- }),
123
- };
124
- }
125
-
126
- serialize(competition: Competition) {
127
- var result =
128
- "Kategorie;Laenge;Steigung;PoAnz;Rang;Name;Jahrgang;Ort;Club;Zeit;Startzeit;Zielzeit;Zwischenzeiten\n";
129
-
130
- competition.categories.forEach(function (category) {
131
- category.runners.forEach(function (runner) {
132
- const distance = category.distance
133
- ? category.distance
134
- : null;
135
- const runTime = parseTime(runner.time);
136
- const startTime = parseTime(runner.startTime);
137
- const finishTime = runTime && startTime ? formatTime(runTime + startTime) : '';
138
- result += [
139
- category.name,
140
- distance,
141
- category.ascent,
142
- category.controls,
143
- runner.rank,
144
- runner.fullName,
145
- runner.yearOfBirth,
146
- runner.city,
147
- runner.club,
148
- runner.time,
149
- runner.startTime,
150
- finishTime,
151
- ].join(";");
152
- result +=
153
- ";" +
154
- runner.splits
155
- .map(function (split) {
156
- return split.code + ";" + split.time;
157
- })
158
- .join(";") +
159
- "\n";
160
- });
161
- });
162
-
163
- return result;
164
- }
165
-
166
- check(text: string) {
167
- return text.indexOf("Kategorie;Laenge;Steigung;PoAnz;Rang;") === 0;
168
- }
169
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- import * as formats from './formats';
2
-
3
- export {
4
- formats
5
- }
@@ -1,23 +0,0 @@
1
- import { Split } from "./split";
2
-
3
- export interface Runner {
4
- id: number;
5
- category: string;
6
- rank?: number;
7
- name: string;
8
- firstName: string;
9
- fullName: string;
10
- yearOfBirth?: string;
11
- sex?: Sex;
12
- city?: string;
13
- nation?: string;
14
- club?: string;
15
- time?: string;
16
- startTime: string;
17
- splits: Split[];
18
- }
19
-
20
- export enum Sex {
21
- male = 'm',
22
- female = 'f'
23
- }