@rasifix/orienteering-utils 2.0.62 → 2.0.64

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.
@@ -39,8 +39,12 @@ var PicoeventsFormat = /** @class */ (function () {
39
39
  var townIdx = header.indexOf("[TOWN]");
40
40
  var clubIdx = header.indexOf("[CLUB]");
41
41
  var runtimeNetIdx = header.indexOf("[RUNTIMENET]");
42
+ var startNumIdx = header.indexOf("[STARTNUM]");
43
+ var runOrLegIdx = header.indexOf("[RUNORLEG]");
44
+ var baseClassIdx = header.indexOf("[BASECLASS]");
45
+ var teamIdx = header.indexOf("[GROUPNAME]");
42
46
  lines.forEach(function (line, idx) {
43
- var tokens = line.split(",");
47
+ var tokens = parseCSVLine(line);
44
48
  if (tokens.length < 50) {
45
49
  return;
46
50
  }
@@ -61,7 +65,6 @@ var PicoeventsFormat = /** @class */ (function () {
61
65
  result.categories.push(category);
62
66
  }
63
67
  var status = tokens[statusIdx];
64
- console.log("Parsing runner ".concat(idx + 1, ": ").concat(tokens[firstNameIdx], " ").concat(tokens[familyNameIdx], " with status ").concat(status, " in category ").concat(name));
65
68
  if (status !== "5" && status !== "2") {
66
69
  return;
67
70
  }
@@ -75,6 +78,10 @@ var PicoeventsFormat = /** @class */ (function () {
75
78
  club: clean(tokens[clubIdx]),
76
79
  time: (0, time_1.formatTime)(parseInt(tokens[runtimeNetIdx])),
77
80
  startTime: (0, time_1.formatTime)(startTime),
81
+ startNumber: clean(tokens[startNumIdx]),
82
+ runOrLeg: tokens[runOrLegIdx] ? parseInt(tokens[runOrLegIdx]) : undefined,
83
+ baseCategory: clean(tokens[baseClassIdx]),
84
+ team: clean(tokens[teamIdx]),
78
85
  splits: [],
79
86
  };
80
87
  for (var i = termIdx + 3; i < tokens.length - 2; i += 2) {
@@ -89,10 +96,7 @@ var PicoeventsFormat = /** @class */ (function () {
89
96
  return result;
90
97
  };
91
98
  PicoeventsFormat.prototype.serialize = function (event) {
92
- var result = "BasicData,".concat(event.id, ",\"").concat(event.name, "\",").concat(event.date, ",").concat(event.startTime, ",,,\"").concat(event.map, "\",\n");
93
- result += "EXTCLASS=[DATATYPE],[SORTKEY],[ACTITEM],[NOFITEMS],[POINTER],[POSSPLITS],[RUNORLEG],[CLASSSORT],[BASECLASS],[FULLCLASS],[SUBSTCLASS],[COURSE],[MULTIHEATNUM],[REGTIME],[ISCLIQUE],[FAMILYNAME],[FIRSTNAME],[YOB],[SEX],[SEXLOC],[ZIP],[TOWN],[REGION],[COUNTRY],[FEDNR],[CLUB],[CLUBID],[NATION],[NATIONCODE],[IOFID],[RANKING],[GROUPNAME],[GROUPCLUB],[FOREIGNKEY],[REFPERS],[REFHEAT],[REFGRP],[REFEXT],[CARDHASDATA],[CARDNUM],[CARDNUMORIG],[RFID],[STARTNUM],[CLASSSTA],[COMBINATION],[DATEH0],[TIMEPREC],[STARTTIMELIST],[STARTTIMEGATE],[STARTTIMELATE],[STARTFULLPREC],[FINISHFULLPREC],[STARTPRECADJ],[FINISHPRECADJ],[RUNTIMEEFF],[RUNTIMENET],[RANKNET],[BEHINDNET],[PENALTY],[CREDIT],[NEUTRAL],[POINTS],[TIMEUSERMOD],[CARDUSERMOD],[RESPERSIDX],[RESCARDIDX],[IOFRESSTATTEXT],[INFOALL],[INFOMAND],[NOTCLASSTEXT],[RANKTEXT],[RESULTTEXT],[BEHINDTEXT],[PENCRENEUTTEXT],[SCHEDULED],[STARTED],[FINISHED],[SLIADDTEXT],[RESADDTEXT],[RENMERGINFO],[LIVEOFFSET],[LIVEINVALID1],[LIVEINVALID2],[LIVEINVALID3],[LEGMASSSTART],[LEGMAXTIMELIMIT],[LEGMAXTIMENCLA],[RELAYSTARTTIME],[RELCUMRUNTIMEEFF],[RELCUMRUNTIMENET],[RELCUMRANKNET],[RELCUMBEHINDNET],[RELCUMRANKTEXT],[RELCUMRESULTTEXT],[RELCUMBEHINDTEXT],[RELCUMPERSRESIDX],[RELCUMIOFSTATTEXT],[RELCUMINFOALL],[RELCUMINFOMAND],[RELCUMNOTCLATEXT],[RELCUMMASTAFLAG],[RELCUMSTARTED],[RELCUMFINISHED],[RELCUMORDERRES],[RELCUMSPARE2],[RELCUMSPARE3],[EXCLUDED],[NEGRUNTIME],[CLASSOKNOTREADY],[RESULTINVALID],[DOPSTATOK],[SLIORDER],[SORTORDERRES],[SUBSECRUNTIMENET],[STARTTIMEEXT],[FINISHTIMEEXT],[RUNTIMENETFULLPREC],[IMPORTGROUPID],[IMPORTUSER],[HIDETIME],[PAID],[RESERVE8],[RESERVE7],[RESERVE6],[RESERVE5],[RESERVE4],[LASTUPDATE],[MISSLISTCODE],[EXTRALISTCODE],[EXTRALISTTIME],[RADIOLISTCODE],[NOFSPLITS],[NOFSPLITPARAMS],[SPLITTYPE],[SPLITSTATUS],[TERM]\n";
94
- // FIXME: implement serialization of runners
95
- return result;
99
+ throw new Error("format does not implement serialization");
96
100
  };
97
101
  PicoeventsFormat.prototype.check = function (text) {
98
102
  return text.startsWith("BasicData,");
@@ -100,3 +104,35 @@ var PicoeventsFormat = /** @class */ (function () {
100
104
  return PicoeventsFormat;
101
105
  }());
102
106
  exports.PicoeventsFormat = PicoeventsFormat;
107
+ function parseCSVLine(line) {
108
+ var fields = [];
109
+ var current = '';
110
+ var inQuotes = false;
111
+ var i = 0;
112
+ while (i < line.length) {
113
+ var char = line[i];
114
+ if (char === '"') {
115
+ if (inQuotes && i + 1 < line.length && line[i + 1] === '"') {
116
+ // Escaped quote
117
+ current += '"';
118
+ i++; // Skip next quote
119
+ }
120
+ else {
121
+ // Toggle quote state
122
+ inQuotes = !inQuotes;
123
+ }
124
+ }
125
+ else if (char === ',' && !inQuotes) {
126
+ // Field separator
127
+ fields.push(current);
128
+ current = '';
129
+ }
130
+ else {
131
+ current += char;
132
+ }
133
+ i++;
134
+ }
135
+ // Add the last field
136
+ fields.push(current);
137
+ return fields;
138
+ }
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as formats from './formats';
2
2
  import * as ranking from './utils/ranking';
3
+ import * as relay from './utils/relay';
3
4
  import * as model from './model';
4
5
  import { Format } from './format';
5
6
  import { formatTime, parseTime } from './time';
6
- export { formats, ranking, model, Format, formatTime, parseTime };
7
+ export { formats, ranking, relay, model, Format, formatTime, parseTime };
package/lib/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseTime = exports.formatTime = exports.model = exports.ranking = exports.formats = void 0;
3
+ exports.parseTime = exports.formatTime = exports.model = exports.relay = exports.ranking = exports.formats = void 0;
4
4
  var formats = require("./formats");
5
5
  exports.formats = formats;
6
6
  var ranking = require("./utils/ranking");
7
7
  exports.ranking = ranking;
8
+ var relay = require("./utils/relay");
9
+ exports.relay = relay;
8
10
  var model = require("./model");
9
11
  exports.model = model;
10
12
  var time_1 = require("./time");
@@ -11,6 +11,10 @@ export interface Runner {
11
11
  club?: string;
12
12
  time?: string;
13
13
  startTime: string;
14
+ startNumber?: string;
15
+ runOrLeg?: number;
16
+ baseCategory?: string;
17
+ team?: string;
14
18
  splits: Split[];
15
19
  }
16
20
  export declare enum Sex {
@@ -94,7 +94,7 @@ function parseRanking(runners) {
94
94
  leg.weight = leg.idealSplit / idealTime;
95
95
  }
96
96
  if (!leg.weight || isNaN(leg.weight)) {
97
- console.log("invalid weight for leg ", code, leg.idealSplit, idealTime);
97
+ console.warn("invalid weight for leg ", code, leg.idealSplit, idealTime);
98
98
  }
99
99
  });
100
100
  // now assing the leg information (such as idealTime, weight, ...) to the individual splits of the runners
@@ -174,7 +174,7 @@ function parseRanking(runners) {
174
174
  return;
175
175
  }
176
176
  if (!times || times.length === 0) {
177
- console.log("no times at position ", split.position, split.weight, " for runner ", runner.fullName, runner.time, times.length);
177
+ console.warn("no times at position ", split.position, split.weight, " for runner ", runner.fullName, runner.time, times.length);
178
178
  return;
179
179
  }
180
180
  var rank = 1;
@@ -273,7 +273,6 @@ function defineCourses(runners) {
273
273
  courses[course].runners.push(runner.id);
274
274
  }
275
275
  });
276
- console.log("defined courses: ", Object.keys(courses));
277
276
  return Object.keys(courses).map(function (key) { return courses[key]; });
278
277
  }
279
278
  function defineRunners(runners) {
@@ -412,12 +411,12 @@ function defineLegProperties(legs) {
412
411
  if (selected.length > 0) {
413
412
  leg.idealSplit = Math.round(selected.reduce(sum, 0) / selected.length);
414
413
  if (leg.idealSplit < 0 || isNaN(leg.idealSplit)) {
415
- console.log("invalid ideal split calculated for leg " + code, leg.idealSplit, selected);
414
+ console.warn("invalid ideal split calculated for leg " + code, leg.idealSplit, selected);
416
415
  leg.idealSplit = 30;
417
416
  }
418
417
  }
419
418
  else {
420
- console.log("no valid splits for leg " + code + " found, setting ideal split to 30s");
419
+ console.warn("no valid splits for leg " + code + " found, setting ideal split to 30s");
421
420
  leg.idealSplit = 30;
422
421
  }
423
422
  // only if there are valid splits for this leg
@@ -442,7 +441,7 @@ function defineLegProperties(legs) {
442
441
  runner.performanceIndex = Math.round(((1.0 * leg.idealSplit) / runner.split) * 100);
443
442
  }
444
443
  else {
445
- console.log("cannot calculate performance index for runner ", runner.fullName, " on leg ", code);
444
+ console.warn("cannot calculate performance index for runner ", runner.fullName, " on leg ", code);
446
445
  }
447
446
  });
448
447
  }
@@ -0,0 +1,29 @@
1
+ import { Runner } from "../model";
2
+ import { Ranking } from "./ranking";
3
+ export interface Team {
4
+ name: string;
5
+ startNumber: number;
6
+ }
7
+ /**
8
+ * Select the runners having the same base category.
9
+ *
10
+ * @param runners all the runners to filter
11
+ * @param category the base category to filter by
12
+ * @returns a filtered list of runners
13
+ */
14
+ export declare function selectRunnersOfCategory(runners: Runner[], category: string): Runner[];
15
+ /**
16
+ * Extract all unique teams from the given runners.
17
+ *
18
+ * @param runners the runners to extract teams from
19
+ * @returns a list of unique teams with their name and start number
20
+ */
21
+ export declare function getTeams(runners: Runner[]): Team[];
22
+ /**
23
+ * Create a ranking for a relay event from the given runners. The runners must
24
+ * belong to the same base category.
25
+ *
26
+ * @param runners the runners to create a ranking from
27
+ * @returns a ranking for the relay event
28
+ */
29
+ export declare function parseRelayRanking(runners: Runner[]): Ranking;
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ // I need functions to process relay data
3
+ // in case of relay the event returns Runners with startNumber, runOrLeg, baseCategory, team
4
+ // runners of the same team have the same baseCategory and same startNumber
5
+ // runOrLeg indicates which leg of the relay the runner is running
6
+ // e.g. for a 3-leg relay, there will be runners with runOrLeg 1, 2 and 3
7
+ // all runners with runOrLeg 1 belong to the first leg of their team, etc.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.selectRunnersOfCategory = selectRunnersOfCategory;
10
+ exports.getTeams = getTeams;
11
+ exports.parseRelayRanking = parseRelayRanking;
12
+ var ranking_1 = require("./ranking");
13
+ var time_1 = require("../time");
14
+ /**
15
+ * Select the runners having the same base category.
16
+ *
17
+ * @param runners all the runners to filter
18
+ * @param category the base category to filter by
19
+ * @returns a filtered list of runners
20
+ */
21
+ function selectRunnersOfCategory(runners, category) {
22
+ return runners.filter(function (runner) { return runner.baseCategory === category; });
23
+ }
24
+ /**
25
+ * Extract all unique teams from the given runners.
26
+ *
27
+ * @param runners the runners to extract teams from
28
+ * @returns a list of unique teams with their name and start number
29
+ */
30
+ function getTeams(runners) {
31
+ var teams = new Set();
32
+ runners.forEach(function (runner) {
33
+ if (runner.team && runner.team.trim().length > 0) {
34
+ teams.add({
35
+ name: runner.team,
36
+ startNumber: runner.startNumber ? parseInt(runner.startNumber) : 0
37
+ });
38
+ }
39
+ });
40
+ return Array.from(teams);
41
+ }
42
+ /**
43
+ * Create a ranking for a relay event from the given runners. The runners must
44
+ * belong to the same base category.
45
+ *
46
+ * @param runners the runners to create a ranking from
47
+ * @returns a ranking for the relay event
48
+ */
49
+ function parseRelayRanking(runners) {
50
+ // Group runners by team (startNumber)
51
+ var teamMap = new Map();
52
+ runners.forEach(function (runner) {
53
+ var team = runner.startNumber || '';
54
+ if (!teamMap.has(team)) {
55
+ teamMap.set(team, []);
56
+ }
57
+ teamMap.get(team).push(runner);
58
+ });
59
+ // Create combined runners for each team
60
+ var combinedRunners = [];
61
+ teamMap.forEach(function (teamRunners, startNumber) {
62
+ // Sort runners by runOrLeg to ensure correct order
63
+ teamRunners.sort(function (a, b) { return (a.runOrLeg || 0) - (b.runOrLeg || 0); });
64
+ // Start with the first leg runner as the base
65
+ var firstRunner = teamRunners[0];
66
+ var combinedRunner = {
67
+ id: firstRunner.id,
68
+ category: firstRunner.category,
69
+ fullName: teamRunners.map(function (r) { return r.fullName; }).join(' / '),
70
+ startTime: firstRunner.startTime,
71
+ startNumber: startNumber,
72
+ baseCategory: firstRunner.baseCategory,
73
+ team: firstRunner.team,
74
+ splits: []
75
+ };
76
+ // Combine splits from all legs
77
+ var accumulatedTime = 0;
78
+ teamRunners.forEach(function (runner) {
79
+ runner.splits.forEach(function (split) {
80
+ var splitTime = (0, time_1.parseTime)(split.time);
81
+ if (splitTime !== undefined && splitTime > 0) {
82
+ // Accumulate time from previous legs
83
+ var adjustedTime = splitTime + accumulatedTime;
84
+ // Add the adjusted split
85
+ combinedRunner.splits.push({
86
+ code: split.code,
87
+ time: (0, time_1.formatTime)(adjustedTime)
88
+ });
89
+ }
90
+ });
91
+ // Accumulate the total time of this leg for the next leg
92
+ if (runner.time) {
93
+ var runnerTotalTime = (0, time_1.parseTime)(runner.time);
94
+ if (runnerTotalTime !== undefined && runnerTotalTime > 0) {
95
+ accumulatedTime += runnerTotalTime;
96
+ }
97
+ }
98
+ });
99
+ // Set the final combined time
100
+ combinedRunner.time = (0, time_1.formatTime)(accumulatedTime);
101
+ combinedRunners.push(combinedRunner);
102
+ });
103
+ // Parse the ranking as usual
104
+ return (0, ranking_1.parseRanking)(combinedRunners);
105
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rasifix/orienteering-utils",
3
- "version": "2.0.62",
3
+ "version": "2.0.64",
4
4
  "description": "utility functions for orienteering result analyzis",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",