garminclimb 1.0.0

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.
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # Garmin Climb Charts 🧗‍♂️⌚️📈
2
+
3
+ Garmin connect doesn't have the charts I want so I decided to make my own CLI to display the charts I want to see for rock climbing.
4
+
5
+ # Usage
6
+
7
+ ```shell
8
+ npm i -g garminclimb
9
+ garminclimb login -u username -p password
10
+ garminclimb download # download your data
11
+ garminclimb charts
12
+ ```
13
+
14
+ You can see your raw data at `~/.gc_data`
15
+
16
+ ```shell
17
+ cd ~/.gc_data
18
+ ls
19
+ ```
20
+
21
+ # Develop locally or make your own charts
22
+
23
+ ```shell
24
+ npm i
25
+ npm link
26
+ npm run watch
27
+ # change parse.ts in /src to edit charts
28
+ # then all commaands will be locally linked to your code
29
+ garminclimb login -u username -p password
30
+ garminclimb download # download your data
31
+ garminclimb charts
32
+ ```
33
+
34
+
35
+ # Example Charts
36
+
37
+
38
+ ```
39
+ Max Grade Per Session Indoor Climbing
40
+
41
+
42
+ 512A ┤ ┏━━━━━━━━━━┓ ┏━━━━━━━━━━┓
43
+ 511D ┤ ┏━━━━━━━━━━┓ ┃ ┃ ┃ ┃
44
+ │ ┃ ┃ ┃ ┃ ┃ ┃
45
+ 511B ┤━━━━━━━━━━┓ ┃ ┃ ┃ ┃ ┃ ┗━
46
+ 511A ┤ ┗━━━━━━━━━━┓ ┃ ┃ ┃ ┃ ┃
47
+ 510D ┤ ┗━━━━━━━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━┓ ┃ ┗━━━━━━━━━━┛
48
+ │ ┃ ┃
49
+ 510B ┤ ┗━━━━━━━━━━┛
50
+ └┬──────────┬──────────┬──────────┬──────────┬──────────┬─────────┬──────────┬──────────┬──────────┬──────────┬▶
51
+ 2024-12-12 2024-12-18 2025-01-01 2025-01-09 2025-01-31 2025-02-062025-02-09 2025-02-12 2025-02-16 2025-02-25 2025-02-27
52
+
53
+ Number of Sends per session
54
+
55
+
56
+ 22┤ ┏━━━━━━━━━━┓
57
+ │ ┃ ┃
58
+ │ ┃ ┃
59
+ │ ┃ ┃
60
+ │ ┃ ┃
61
+ 17┤ ┃ ┃ ┏━━━━━━━━━━┓
62
+ │ ┃ ┃ ┃ ┃
63
+ │ ┃ ┃ ┃ ┃
64
+ │ ┃ ┃ ┃ ┃
65
+ │ ┃ ┃ ┃ ┃
66
+ 11┤ ┃ ┃ ┃ ┗━
67
+ │ ┃ ┃ ┃
68
+ 9┤━━━━━━━━━━┓ ┏━━━━━━━━━┓ ┃ ┃ ┃
69
+ │ ┃ ┃ ┃ ┃ ┃ ┃
70
+ │ ┃ ┃ ┃ ┃ ┃ ┃
71
+ 6┤ ┗━━━━━━━━━━┓ ┃ ┃ ┃ ┗━━━━━━━━━━┛
72
+ │ ┃ ┃ ┃ ┃
73
+ 4┤ ┃ ┏━━━━━━━━━━┛ ┃ ┃
74
+ 3┤ ┗━━━━━━━━━━┓ ┃ ┃ ┃
75
+ 2┤ ┗━━━━━━━━━━┛ ┗━━━━━━━━━━┛
76
+ └┬──────────┬──────────┬──────────┬──────────┬──────────┬─────────┬──────────┬──────────┬──────────┬──────────┬▶
77
+ 2024-12-12 2024-12-18 2025-01-01 2025-01-09 2025-01-31 2025-02-062025-02-09 2025-02-12 2025-02-16 2025-02-25 2025-02-27
78
+
79
+ Total Feet Per Session
80
+
81
+ ▲ █
82
+ 604┤ █
83
+ │ █ █
84
+ 509┤ █ █ █
85
+ 436┤█ █ █ █
86
+ 348┤█ █ █ █ █ █
87
+ 312┤█ █ █ █ █ █ █
88
+ 272┤█ █ █ █ █ █ █ █ █ █
89
+ 208┤█ █ █ █ █ █ █ █ █ █
90
+ │█ █ █ █ █ █ █ █ █ █ █
91
+ 70┤█ █ █ █ █ █ █ █ █ █ █
92
+ └┬──────────┬──────────┬──────────┬──────────┬──────────┬─────────┬──────────┬──────────┬──────────┬──────────┬▶
93
+ 2024-12-12 2024-12-18 2025-01-01 2025-01-09 2025-01-31 2025-02-062025-02-09 2025-02-12 2025-02-16 2025-02-25 2025-02-27
94
+
95
+ Total Route Feet By Month
96
+
97
+ ▲ █
98
+ 2341┤ █
99
+ │ █
100
+ │ █
101
+ │ █
102
+ │ █
103
+ │ █
104
+ │ █
105
+ │ █
106
+ │ █
107
+ │ █
108
+ │ █
109
+ │ █
110
+ │ █
111
+ │ █
112
+ │ █
113
+ │ █
114
+ │ █
115
+ │█ █
116
+ 620┤█ █ █
117
+ 480┤█ █ █
118
+ └┬─────────┬────────┬▶
119
+ 2024-12 2025-01 2025-02
120
+
121
+ Active Minutes Per Session
122
+
123
+ ▲ █
124
+ 28┤ █ █
125
+ 27┤ █ █
126
+ │ █ █ █
127
+ 25┤ █ █ █
128
+ │ █ █ █ █ █
129
+ 23┤█ █ █ █ █ █
130
+ 22┤█ █ █ █ █ █ █
131
+ 21┤█ █ █ █ █ █ █
132
+ │█ █ █ █ █ █ █ █ █
133
+ 19┤█ █ █ █ █ █ █ █ █ █
134
+ 18┤█ █ █ █ █ █ █ █ █ █
135
+ │█ █ █ █ █ █ █ █ █ █
136
+ │█ █ █ █ █ █ █ █ █ █
137
+ │█ █ █ █ █ █ █ █ █ █
138
+ │█ █ █ █ █ █ █ █ █ █
139
+ │█ █ █ █ █ █ █ █ █ █
140
+ │█ █ █ █ █ █ █ █ █ █
141
+ │█ █ █ █ █ █ █ █ █ █
142
+ │█ █ █ █ █ █ █ █ █ █ █
143
+ 9┤█ █ █ █ █ █ █ █ █ █ █
144
+ └┬──────────┬──────────┬──────────┬──────────┬──────────┬─────────┬──────────┬──────────┬──────────┬──────────┬▶
145
+ 2024-12-12 2024-12-18 2025-01-01 2025-01-09 2025-01-31 2025-02-062025-02-09 2025-02-12 2025-02-16 2025-02-25 2025-02-27
146
+
147
+
148
+
149
+ Bouldering===============================
150
+ Max Grade Per Session Bouldering
151
+
152
+
153
+ V7┤ ┏━━━━━━━━━━┓
154
+ │ ┃ ┃
155
+ │ ┃ ┃
156
+ V6┤ ┏━━━━━━━━━┓ ┏━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━┛ ┃
157
+ │ ┃ ┃ ┃ ┃ ┃ ┃
158
+ │ ┃ ┃ ┃ ┃ ┃ ┃
159
+ V5┤ ┃ ┗━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┗━
160
+ │ ┃
161
+ │ ┃
162
+ V4┤━━━━━━━━━━┛
163
+ └┬──────────┬─────────┬──────────┬──────────┬─────────┬──────────┬──────────┬──────────┬─────────┬──────────┬──────────┬─────────┬──────────┬▶
164
+ 2024-12-20 2024-12-282025-01-04 2025-01-08 2025-01-122025-01-19 2025-01-22 2025-01-29 2025-02-022025-02-14 2025-02-18 2025-02-232025-03-02 2025-03-05
165
+
166
+ Number of Climbs per session
167
+
168
+ ▲ █
169
+ 48┤ █
170
+ │ █
171
+ │ █ █
172
+ 35┤ █ █ █ █
173
+ 30┤ █ █ █ █ █ █ █ █
174
+ 25┤ █ █ █ █ █ █ █ █ █ █
175
+ 22┤ █ █ █ █ █ █ █ █ █ █ █ █ █
176
+ 18┤ █ █ █ █ █ █ █ █ █ █ █ █ █
177
+ │█ █ █ █ █ █ █ █ █ █ █ █ █ █
178
+ 7┤█ █ █ █ █ █ █ █ █ █ █ █ █ █
179
+ └┬──────────┬─────────┬──────────┬──────────┬─────────┬──────────┬──────────┬──────────┬─────────┬──────────┬──────────┬─────────┬──────────┬▶
180
+ 2024-12-20 2024-12-282025-01-04 2025-01-08 2025-01-122025-01-19 2025-01-22 2025-01-29 2025-02-022025-02-14 2025-02-18 2025-02-232025-03-02 2025-03-05
181
+
182
+ Active Minutes Bouldering Per Session
183
+
184
+ ▲ █
185
+ 30┤ █
186
+ │ █
187
+ │ █ █ █
188
+ 22┤ █ █ █ █ █
189
+ 18┤ █ █ █ █ █ █
190
+ 17┤ █ █ █ █ █ █ █ █ █ █
191
+ 13┤ █ █ █ █ █ █ █ █ █ █ █ █ █
192
+ 10┤ █ █ █ █ █ █ █ █ █ █ █ █ █
193
+ │█ █ █ █ █ █ █ █ █ █ █ █ █ █
194
+ 6┤█ █ █ █ █ █ █ █ █ █ █ █ █ █
195
+ └┬──────────┬─────────┬──────────┬──────────┬─────────┬──────────┬──────────┬──────────┬─────────┬──────────┬──────────┬─────────┬──────────┬▶
196
+ 2024-12-20 2024-12-282025-01-04 2025-01-08 2025-01-122025-01-19 2025-01-22 2025-01-29 2025-02-022025-02-14 2025-02-18 2025-02-232025-03-02 2025-03-05
197
+
198
+ Approx Boulder Feet By Month
199
+
200
+ ▲ █
201
+ 976┤ █ █
202
+ 944┤ █ █
203
+ │ █ █
204
+ │ █ █
205
+ │ █ █
206
+ │ █ █
207
+ │ █ █
208
+ │ █ █
209
+ │ █ █
210
+ │ █ █ █
211
+ 592┤ █ █ █
212
+ │ █ █ █
213
+ │ █ █ █
214
+ │ █ █ █
215
+ │ █ █ █
216
+ │ █ █ █
217
+ │ █ █ █
218
+ │ █ █ █
219
+ │█ █ █ █
220
+ 256┤█ █ █ █
221
+ └┬─────┬──────┬─────┬▶
222
+ 2024-12 2025-02
223
+ 2025-01 2025-03
224
+
225
+ Total Feet By Month Combined
226
+
227
+ ▲ █
228
+ 3285┤ █
229
+ │ █
230
+ │ █
231
+ │ █
232
+ │ █
233
+ │ █
234
+ │ █
235
+ │ █
236
+ │ █
237
+ │ █
238
+ │ █
239
+ │ █
240
+ │ █
241
+ │ █ █
242
+ 1456┤ █ █
243
+ │ █ █
244
+ │ █ █
245
+ │ █ █
246
+ │█ █ █
247
+ 876┤█ █ █
248
+ └┬─────────┬────────┬▶
249
+ 2024-12 2025-01 2025-02
250
+ ```
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.download = exports.credentialsPath = void 0;
46
+ const dotenv = __importStar(require("dotenv"));
47
+ const path = require("path");
48
+ const homeDir = require("os").homedir();
49
+ exports.credentialsPath = path.join(homeDir, ".gc_credentials");
50
+ dotenv.config({ path: exports.credentialsPath });
51
+ const fs = __importStar(require("fs"));
52
+ // @ts-ignore
53
+ const garmin_connect_1 = require("garmin-connect");
54
+ const _1 = require(".");
55
+ const download = () => __awaiter(void 0, void 0, void 0, function* () {
56
+ const GCClient = new garmin_connect_1.GarminConnect({
57
+ username: process.env.GARMIN_USERNAME || "",
58
+ password: process.env.GARMIN_PASSWORD || "",
59
+ });
60
+ yield GCClient.login(process.env.GARMIN_USERNAME || "", process.env.GARMIN_PASSWORD || "");
61
+ const activities = yield GCClient.getActivities();
62
+ const indoorClimbingActivities = activities === null || activities === void 0 ? void 0 : activities.filter((activity) => { var _a; return ((_a = activity === null || activity === void 0 ? void 0 : activity.activityType) === null || _a === void 0 ? void 0 : _a.typeKey) === "indoor_climbing"; });
63
+ if (!fs.existsSync(_1.garminDataFolder)) {
64
+ fs.mkdirSync(_1.garminDataFolder);
65
+ }
66
+ fs.writeFileSync(`${_1.garminDataFolder}/indoorClimbingActivities.json`, JSON.stringify(indoorClimbingActivities, null, 2));
67
+ const boulderingActivities = activities === null || activities === void 0 ? void 0 : activities.filter((activity) => { var _a; return ((_a = activity === null || activity === void 0 ? void 0 : activity.activityType) === null || _a === void 0 ? void 0 : _a.typeKey) === "bouldering"; });
68
+ fs.writeFileSync(`${_1.garminDataFolder}/boulderingActivities.json`, JSON.stringify(boulderingActivities, null, 2));
69
+ console.log("Downloaded climbing activities from Garmin Connect");
70
+ });
71
+ exports.download = download;
72
+ //# sourceMappingURL=download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAEjC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;AAC3B,QAAA,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;AACrE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,uBAAe,EAAE,CAAC,CAAC;AAEzC,uCAAyB;AACzB,aAAa;AACb,mDAA+C;AAC/C,wBAAqC;AAC9B,MAAM,QAAQ,GAAG,GAAwB,EAAE;IAChD,MAAM,QAAQ,GAAG,IAAI,8BAAa,CAAC;QACjC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE;QAC3C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE;KAC5C,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,KAAK,CAClB,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,EACjC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAClC,CAAC;IAEF,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,CAAC;IAElD,MAAM,wBAAwB,GAAG,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,MAAM,CACjD,CAAC,QAAQ,EAAE,EAAE,WAAC,OAAA,CAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,YAAY,0CAAE,OAAO,MAAK,iBAAiB,CAAA,EAAA,CACpE,CAAC;IAEF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,mBAAgB,CAAC,EAAE,CAAC;QACrC,EAAE,CAAC,SAAS,CAAC,mBAAgB,CAAC,CAAC;IACjC,CAAC;IACD,EAAE,CAAC,aAAa,CACd,GAAG,mBAAgB,gCAAgC,EACnD,IAAI,CAAC,SAAS,CAAC,wBAAwB,EAAE,IAAI,EAAE,CAAC,CAAC,CAClD,CAAC;IAEF,MAAM,oBAAoB,GAAG,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,MAAM,CAC7C,CAAC,QAAQ,EAAE,EAAE,WAAC,OAAA,CAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,YAAY,0CAAE,OAAO,MAAK,YAAY,CAAA,EAAA,CAC/D,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,GAAG,mBAAgB,4BAA4B,EAC/C,IAAI,CAAC,SAAS,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAC,CAC9C,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;AACpE,CAAC,CAAA,CAAC;AAlCW,QAAA,QAAQ,YAkCnB"}
package/bin/index.js ADDED
@@ -0,0 +1,71 @@
1
+ #! /usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.garminDataFolder = exports.credentialsPath = void 0;
38
+ const fs = __importStar(require("fs"));
39
+ const commander_1 = require("commander");
40
+ const os = require("os");
41
+ const path = require("path");
42
+ const homeDir = os.homedir();
43
+ exports.credentialsPath = path.join(homeDir, ".gc_credentials");
44
+ exports.garminDataFolder = path.join(homeDir, ".gc_data");
45
+ const download_1 = require("./download");
46
+ const parse_1 = require("./parse");
47
+ commander_1.program
48
+ .command("login")
49
+ .option("-u, --username <username>", "Garmin Connect username")
50
+ .option("-p, --password <password>", "Garmin Connect password")
51
+ .description("Set GARMIN_USERNAME and GARMIN_PASSWORD")
52
+ .action((options) => {
53
+ const GARMIN_USERNAME = options.username;
54
+ const GARMIN_PASSWORD = options.password;
55
+ const newEnv = `GARMIN_USERNAME=${GARMIN_USERNAME}\nGARMIN_PASSWORD=${GARMIN_PASSWORD}`;
56
+ console.log("Setting credentials...");
57
+ fs.writeFileSync(exports.credentialsPath, newEnv);
58
+ console.log("Credentials set!");
59
+ });
60
+ commander_1.program
61
+ .command("download")
62
+ .description("Download activities from Garmin Connect")
63
+ .action(() => {
64
+ (0, download_1.download)();
65
+ });
66
+ commander_1.program.command("charts").description("Parse activities from Garmin Connect").action(() => {
67
+ console.log("Parsing activities...");
68
+ (0, parse_1.parse)();
69
+ });
70
+ commander_1.program.parse();
71
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,uCAAyB;AAEzB,yCAAoC;AACpC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AACzB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;AAChB,QAAA,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;AACxD,QAAA,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC/D,yCAAsC;AACtC,mCAAgC;AAEhC,mBAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,2BAA2B,EAAE,yBAAyB,CAAC;KAC9D,MAAM,CAAC,2BAA2B,EAAE,yBAAyB,CAAC;KAC9D,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;IAClB,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IACzC,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IACzC,MAAM,MAAM,GAAG,mBAAmB,eAAe,qBAAqB,eAAe,EAAE,CAAC;IACxF,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,EAAE,CAAC,aAAa,CAAC,uBAAe,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEL,mBAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,GAAG,EAAE;IACX,IAAA,mBAAQ,GAAE,CAAC;AACb,CAAC,CAAC,CAAC;AAEL,mBAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,sCAAsC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE;IACxF,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,IAAA,aAAK,GAAE,CAAC;AACV,CAAC,CAAC,CAAC;AAGH,mBAAO,CAAC,KAAK,EAAE,CAAC"}
package/bin/parse.js ADDED
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.parse = void 0;
49
+ const fs = __importStar(require("fs"));
50
+ const simple_ascii_chart_1 = __importDefault(require("simple-ascii-chart"));
51
+ const _1 = require(".");
52
+ const YDS_GRADE_MAP = {
53
+ _5_6: 0,
54
+ _5_7: 1,
55
+ _5_8: 2,
56
+ _5_9: 3,
57
+ _5_10A: 4,
58
+ _5_10_MINUS: 4,
59
+ _5_10B: 5,
60
+ _5_10: 5,
61
+ _5_10C: 6,
62
+ _5_10_PLUS: 7,
63
+ _5_10D: 7,
64
+ _5_11A: 8,
65
+ _5_11_MINUS: 8,
66
+ _5_11B: 9,
67
+ _5_11: 9,
68
+ _5_11C: 10,
69
+ _5_11_PLUS: 11,
70
+ _5_11D: 11,
71
+ _5_12A: 12,
72
+ _5_12_MINUS: 12,
73
+ _5_12B: 13,
74
+ _5_12: 13,
75
+ _5_12C: 14,
76
+ _5_12_PLUS: 15,
77
+ _5_12D: 15,
78
+ _5_13A: 16,
79
+ _5_13_MINUS: 16,
80
+ _5_13B: 17,
81
+ _5_13: 17,
82
+ _5_13C: 18,
83
+ _5_13_PLUS: 19,
84
+ _5_13D: 19,
85
+ _5_14A: 20,
86
+ _5_14_MINUS: 20,
87
+ _5_14B: 21,
88
+ _5_14: 21,
89
+ _5_14C: 22,
90
+ _5_14_PLUS: 23,
91
+ _5_14D: 23,
92
+ _5_15A: 24,
93
+ _5_15_MINUS: 24,
94
+ _5_15B: 25,
95
+ _5_15: 25,
96
+ _5_15C: 26,
97
+ _5_15_PLUS: 27,
98
+ _5_15D: 27,
99
+ _5_16A: 28,
100
+ _5_16_MINUS: 28,
101
+ _5_16B: 29,
102
+ _5_16: 29,
103
+ _5_16C: 30,
104
+ _5_16_PLUS: 31,
105
+ _5_16D: 31,
106
+ };
107
+ const numberToGrade = (number) => {
108
+ var _a, _b, _c;
109
+ const grade = ((_c = (_b = (_a = Object.keys(YDS_GRADE_MAP)
110
+ .find((key) => YDS_GRADE_MAP[key] === number)) === null || _a === void 0 ? void 0 : _a.replace(/_/g, "")) === null || _b === void 0 ? void 0 : _b.replace("PLUS", "D")) === null || _c === void 0 ? void 0 : _c.replace("MINUS", "A")) || "";
111
+ return grade.padEnd(8, " ");
112
+ };
113
+ const parse = () => __awaiter(void 0, void 0, void 0, function* () {
114
+ const indoorClimbingActivities = JSON.parse(fs.readFileSync(`${_1.garminDataFolder}/indoorClimbingActivities.json`, "utf8"));
115
+ const activeIndoorSplits = indoorClimbingActivities.map((activity) => {
116
+ var _a, _b, _c, _d;
117
+ return ((_b = (_a = activity === null || activity === void 0 ? void 0 : activity.splitSummaries) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.splitType) === "CLIMB_ACTIVE"
118
+ ? Object.assign(Object.assign({}, (_c = activity === null || activity === void 0 ? void 0 : activity.splitSummaries) === null || _c === void 0 ? void 0 : _c[0]), { start: activity === null || activity === void 0 ? void 0 : activity.startTimeLocal }) : Object.assign(Object.assign({}, (_d = activity === null || activity === void 0 ? void 0 : activity.splitSummaries) === null || _d === void 0 ? void 0 : _d[1]), { start: activity === null || activity === void 0 ? void 0 : activity.startTimeLocal });
119
+ });
120
+ const boulderingActivities = JSON.parse(fs.readFileSync(`${_1.garminDataFolder}/boulderingActivities.json`, "utf8"));
121
+ const activeBoulderingSplits = boulderingActivities === null || boulderingActivities === void 0 ? void 0 : boulderingActivities.reverse().map((activity) => {
122
+ var _a, _b, _c, _d;
123
+ return ((_b = (_a = activity === null || activity === void 0 ? void 0 : activity.splitSummaries) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.splitType) === "CLIMB_ACTIVE"
124
+ ? Object.assign(Object.assign({}, (_c = activity === null || activity === void 0 ? void 0 : activity.splitSummaries) === null || _c === void 0 ? void 0 : _c[0]), { start: activity === null || activity === void 0 ? void 0 : activity.startTimeLocal }) : Object.assign(Object.assign({}, (_d = activity === null || activity === void 0 ? void 0 : activity.splitSummaries) === null || _d === void 0 ? void 0 : _d[1]), { start: activity === null || activity === void 0 ? void 0 : activity.startTimeLocal });
125
+ }).filter((split) => (split === null || split === void 0 ? void 0 : split.noOfSplits) !== 1);
126
+ const metersToFeet = (meters) => Math.round(meters * 3.28084);
127
+ const generateChart = (data, title, options) => {
128
+ console.log(title);
129
+ console.log((0, simple_ascii_chart_1.default)(data, options));
130
+ };
131
+ generateChart(activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.reverse().map((split, index) => {
132
+ var _a;
133
+ return [
134
+ index,
135
+ YDS_GRADE_MAP === null || YDS_GRADE_MAP === void 0 ? void 0 : YDS_GRADE_MAP[(_a = split === null || split === void 0 ? void 0 : split.maxGradeValue) === null || _a === void 0 ? void 0 : _a.valueKey],
136
+ ];
137
+ }), "Max Grade Per Session Indoor Climbing", {
138
+ width: (activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.length) * 10,
139
+ formatter: (value, { axis }) => {
140
+ var _a, _b;
141
+ return axis === "y"
142
+ ? `${numberToGrade(value)}`
143
+ : (_b = (_a = activeIndoorSplits[value]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0];
144
+ },
145
+ });
146
+ generateChart(activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.map((split, index) => [
147
+ index,
148
+ split === null || split === void 0 ? void 0 : split.numClimbSends,
149
+ ]), "Number of Sends per session", {
150
+ height: 20,
151
+ width: (activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.length) * 10,
152
+ formatter: (value, { axis }) => { var _a, _b; return axis === "y" ? value : (_b = (_a = activeIndoorSplits[value]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0]; },
153
+ });
154
+ generateChart(activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.map((split, index) => [
155
+ index,
156
+ metersToFeet(split === null || split === void 0 ? void 0 : split.totalAscent),
157
+ ]), "Total Feet Per Session", {
158
+ height: 10,
159
+ width: (activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.length) * 10,
160
+ barChart: true,
161
+ formatter: (value, { axis }) => { var _a, _b; return axis === "y" ? value : (_b = (_a = activeIndoorSplits[value]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0]; },
162
+ });
163
+ const totalFeetByMonthYear = activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.reduce((acc, split) => {
164
+ var _a, _b;
165
+ const month = (_a = split === null || split === void 0 ? void 0 : split.start) === null || _a === void 0 ? void 0 : _a.split(" ")[0].split("-")[1];
166
+ const year = (_b = split === null || split === void 0 ? void 0 : split.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0].split("-")[0];
167
+ acc[`${year}-${month}`] =
168
+ (acc[`${year}-${month}`] || 0) + metersToFeet(split === null || split === void 0 ? void 0 : split.totalAscent);
169
+ return acc;
170
+ }, {});
171
+ generateChart(Object.keys(totalFeetByMonthYear).map((month, index) => [
172
+ index,
173
+ totalFeetByMonthYear[month],
174
+ ]), "Total Route Feet By Month", {
175
+ height: 20,
176
+ width: 20,
177
+ barChart: true,
178
+ formatter: (value, { axis }) => axis === "y" ? value : Object.keys(totalFeetByMonthYear)[value],
179
+ });
180
+ generateChart(activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.map((split, index) => [
181
+ index,
182
+ Math.round((split === null || split === void 0 ? void 0 : split.duration) / 60),
183
+ ]), "Active Minutes Per Session", {
184
+ height: 20,
185
+ barChart: true,
186
+ width: (activeIndoorSplits === null || activeIndoorSplits === void 0 ? void 0 : activeIndoorSplits.length) * 10,
187
+ formatter: (value, { axis }) => { var _a, _b; return axis === "y" ? value : (_b = (_a = activeIndoorSplits[value]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0]; },
188
+ });
189
+ console.log("\n\nBouldering===============================");
190
+ generateChart(activeBoulderingSplits === null || activeBoulderingSplits === void 0 ? void 0 : activeBoulderingSplits.map((split, index) => {
191
+ var _a, _b;
192
+ return [
193
+ index,
194
+ parseInt((_b = (_a = split === null || split === void 0 ? void 0 : split.maxGradeValue) === null || _a === void 0 ? void 0 : _a.valueKey) === null || _b === void 0 ? void 0 : _b.replace(/\D/g, "")),
195
+ ];
196
+ }), "Max Grade Per Session Bouldering", {
197
+ height: 10,
198
+ width: (activeBoulderingSplits === null || activeBoulderingSplits === void 0 ? void 0 : activeBoulderingSplits.length) * 10,
199
+ formatter: (value, { axis }) => {
200
+ var _a, _b;
201
+ return axis === "y"
202
+ ? `V${value}`
203
+ : (_b = (_a = activeBoulderingSplits[value]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0];
204
+ },
205
+ });
206
+ generateChart(activeBoulderingSplits === null || activeBoulderingSplits === void 0 ? void 0 : activeBoulderingSplits.map((split, index) => [
207
+ index,
208
+ split === null || split === void 0 ? void 0 : split.noOfSplits,
209
+ ]), "Number of Climbs per session", {
210
+ height: 10,
211
+ barChart: true,
212
+ width: (activeBoulderingSplits === null || activeBoulderingSplits === void 0 ? void 0 : activeBoulderingSplits.length) * 10,
213
+ formatter: (value, { axis }) => {
214
+ var _a, _b;
215
+ return axis === "y"
216
+ ? value
217
+ : (_b = (_a = activeBoulderingSplits[value]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0];
218
+ },
219
+ });
220
+ generateChart(activeBoulderingSplits === null || activeBoulderingSplits === void 0 ? void 0 : activeBoulderingSplits.map((split, index) => [
221
+ index,
222
+ Math.round((split === null || split === void 0 ? void 0 : split.duration) / 60),
223
+ ]), "Active Minutes Bouldering Per Session", {
224
+ height: 10,
225
+ barChart: true,
226
+ width: (activeBoulderingSplits === null || activeBoulderingSplits === void 0 ? void 0 : activeBoulderingSplits.length) * 10,
227
+ formatter: (value, { axis }) => {
228
+ var _a, _b;
229
+ return axis === "y"
230
+ ? value
231
+ : (_b = (_a = activeBoulderingSplits[value]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0];
232
+ },
233
+ });
234
+ // each boulder is ~8 feet
235
+ const totalFeetByMonthYearBouldering = activeBoulderingSplits === null || activeBoulderingSplits === void 0 ? void 0 : activeBoulderingSplits.reduce((acc, split) => {
236
+ var _a, _b;
237
+ const month = (_a = split === null || split === void 0 ? void 0 : split.start) === null || _a === void 0 ? void 0 : _a.split(" ")[0].split("-")[1];
238
+ const year = (_b = split === null || split === void 0 ? void 0 : split.start) === null || _b === void 0 ? void 0 : _b.split(" ")[0].split("-")[0];
239
+ acc[`${year}-${month}`] =
240
+ (acc[`${year}-${month}`] || 0) + (split === null || split === void 0 ? void 0 : split.noOfSplits) * 8;
241
+ return acc;
242
+ }, {});
243
+ generateChart(Object.keys(totalFeetByMonthYearBouldering).map((month, index) => [
244
+ index,
245
+ totalFeetByMonthYearBouldering[month],
246
+ ]), "Approx Boulder Feet By Month", {
247
+ height: 20,
248
+ width: 20,
249
+ barChart: true,
250
+ formatter: (value, { axis }) => axis === "y"
251
+ ? value
252
+ : Object.keys(totalFeetByMonthYearBouldering)[value],
253
+ });
254
+ const totalFeetByMonthYearCombined = Object.keys(totalFeetByMonthYear).reduce((acc, key) => {
255
+ acc[key] =
256
+ totalFeetByMonthYear[key] + (totalFeetByMonthYearBouldering[key] || 0);
257
+ return acc;
258
+ }, {});
259
+ generateChart(Object.keys(totalFeetByMonthYearCombined).map((month, index) => [
260
+ index,
261
+ totalFeetByMonthYearCombined[month],
262
+ ]), "Total Feet By Month Combined", {
263
+ height: 20,
264
+ width: 20,
265
+ barChart: true,
266
+ formatter: (value, { axis }) => axis === "y" ? value : Object.keys(totalFeetByMonthYearCombined)[value],
267
+ });
268
+ });
269
+ exports.parse = parse;
270
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "garminclimb",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "build": "npx tsc",
9
+ "watch": "npx tsc -w"
10
+ },
11
+ "author": "John Franke",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/jottenlips/garminclimb.git"
15
+ },
16
+ "license": "MIT",
17
+ "devDependencies": {
18
+ "@types/node": "^22.13.5",
19
+ "typescript": "^5.8.2"
20
+ },
21
+ "peerDependencies": {
22
+ "typescript": "^5.8.2"
23
+ },
24
+ "dependencies": {
25
+ "@types/node": "^22.13.5",
26
+ "asciichart": "^1.5.25",
27
+ "commander": "^13.1.0",
28
+ "dotenv": "^16.4.7",
29
+ "garmin-connect": "^1.6.2",
30
+ "simple-ascii-chart": "^4.2.0",
31
+ "yargs": "^17.7.2"
32
+ },
33
+ "bin": {
34
+ "garminclimb": "./bin/index.js"
35
+ }
36
+ }
@@ -0,0 +1,46 @@
1
+ import * as dotenv from "dotenv";
2
+
3
+ const path = require("path");
4
+ const homeDir = require("os").homedir();
5
+ export const credentialsPath = path.join(homeDir, ".gc_credentials");
6
+ dotenv.config({ path: credentialsPath });
7
+
8
+ import * as fs from "fs";
9
+ // @ts-ignore
10
+ import { GarminConnect } from "garmin-connect";
11
+ import { garminDataFolder } from ".";
12
+ export const download = async (): Promise<void> => {
13
+ const GCClient = new GarminConnect({
14
+ username: process.env.GARMIN_USERNAME || "",
15
+ password: process.env.GARMIN_PASSWORD || "",
16
+ });
17
+
18
+ await GCClient.login(
19
+ process.env.GARMIN_USERNAME || "",
20
+ process.env.GARMIN_PASSWORD || ""
21
+ );
22
+
23
+ const activities = await GCClient.getActivities();
24
+
25
+ const indoorClimbingActivities = activities?.filter(
26
+ (activity) => activity?.activityType?.typeKey === "indoor_climbing"
27
+ );
28
+
29
+ if (!fs.existsSync(garminDataFolder)) {
30
+ fs.mkdirSync(garminDataFolder);
31
+ }
32
+ fs.writeFileSync(
33
+ `${garminDataFolder}/indoorClimbingActivities.json`,
34
+ JSON.stringify(indoorClimbingActivities, null, 2)
35
+ );
36
+
37
+ const boulderingActivities = activities?.filter(
38
+ (activity) => activity?.activityType?.typeKey === "bouldering"
39
+ );
40
+
41
+ fs.writeFileSync(
42
+ `${garminDataFolder}/boulderingActivities.json`,
43
+ JSON.stringify(boulderingActivities, null, 2)
44
+ );
45
+ console.log("Downloaded climbing activities from Garmin Connect");
46
+ };
package/src/index.ts ADDED
@@ -0,0 +1,41 @@
1
+ #! /usr/bin/env node
2
+
3
+ import * as fs from "fs";
4
+
5
+ import { program } from "commander";
6
+ const os = require("os");
7
+ const path = require("path");
8
+ const homeDir = os.homedir();
9
+ export const credentialsPath = path.join(homeDir, ".gc_credentials");
10
+ export const garminDataFolder = path.join(homeDir, ".gc_data");
11
+ import { download } from "./download";
12
+ import { parse } from "./parse";
13
+
14
+ program
15
+ .command("login")
16
+ .option("-u, --username <username>", "Garmin Connect username")
17
+ .option("-p, --password <password>", "Garmin Connect password")
18
+ .description("Set GARMIN_USERNAME and GARMIN_PASSWORD")
19
+ .action((options) => {
20
+ const GARMIN_USERNAME = options.username;
21
+ const GARMIN_PASSWORD = options.password;
22
+ const newEnv = `GARMIN_USERNAME=${GARMIN_USERNAME}\nGARMIN_PASSWORD=${GARMIN_PASSWORD}`;
23
+ console.log("Setting credentials...");
24
+ fs.writeFileSync(credentialsPath, newEnv);
25
+ console.log("Credentials set!");
26
+ });
27
+
28
+ program
29
+ .command("download")
30
+ .description("Download activities from Garmin Connect")
31
+ .action(() => {
32
+ download();
33
+ });
34
+
35
+ program.command("charts").description("Parse activities from Garmin Connect").action(() => {
36
+ console.log("Parsing activities...");
37
+ parse();
38
+ });
39
+
40
+
41
+ program.parse();
package/src/parse.ts ADDED
@@ -0,0 +1,293 @@
1
+ import * as fs from "fs";
2
+ import plot from "simple-ascii-chart";
3
+
4
+ type YDSGradeMap = { [key: string]: number };
5
+ import { garminDataFolder } from ".";
6
+
7
+ const YDS_GRADE_MAP: YDSGradeMap = {
8
+ _5_6: 0,
9
+ _5_7: 1,
10
+ _5_8: 2,
11
+ _5_9: 3,
12
+ _5_10A: 4,
13
+ _5_10_MINUS: 4,
14
+ _5_10B: 5,
15
+ _5_10: 5,
16
+ _5_10C: 6,
17
+ _5_10_PLUS: 7,
18
+ _5_10D: 7,
19
+ _5_11A: 8,
20
+ _5_11_MINUS: 8,
21
+ _5_11B: 9,
22
+ _5_11: 9,
23
+ _5_11C: 10,
24
+ _5_11_PLUS: 11,
25
+ _5_11D: 11,
26
+ _5_12A: 12,
27
+ _5_12_MINUS: 12,
28
+ _5_12B: 13,
29
+ _5_12: 13,
30
+ _5_12C: 14,
31
+ _5_12_PLUS: 15,
32
+ _5_12D: 15,
33
+ _5_13A: 16,
34
+ _5_13_MINUS: 16,
35
+ _5_13B: 17,
36
+ _5_13: 17,
37
+ _5_13C: 18,
38
+ _5_13_PLUS: 19,
39
+ _5_13D: 19,
40
+ _5_14A: 20,
41
+ _5_14_MINUS: 20,
42
+ _5_14B: 21,
43
+ _5_14: 21,
44
+ _5_14C: 22,
45
+ _5_14_PLUS: 23,
46
+ _5_14D: 23,
47
+ _5_15A: 24,
48
+ _5_15_MINUS: 24,
49
+ _5_15B: 25,
50
+ _5_15: 25,
51
+ _5_15C: 26,
52
+ _5_15_PLUS: 27,
53
+ _5_15D: 27,
54
+ _5_16A: 28,
55
+ _5_16_MINUS: 28,
56
+ _5_16B: 29,
57
+ _5_16: 29,
58
+ _5_16C: 30,
59
+ _5_16_PLUS: 31,
60
+ _5_16D: 31,
61
+ };
62
+
63
+ const numberToGrade = (number: number): string => {
64
+ const grade =
65
+ Object.keys(YDS_GRADE_MAP)
66
+ .find((key) => YDS_GRADE_MAP[key] === number)
67
+ ?.replace(/_/g, "")
68
+ ?.replace("PLUS", "D")
69
+ ?.replace("MINUS", "A") || "";
70
+ return grade.padEnd(8, " ");
71
+ };
72
+
73
+ export const parse = async () => {
74
+ const indoorClimbingActivities = JSON.parse(
75
+ fs.readFileSync(`${garminDataFolder}/indoorClimbingActivities.json`, "utf8")
76
+ );
77
+
78
+ const activeIndoorSplits = indoorClimbingActivities.map((activity: any) =>
79
+ activity?.splitSummaries?.[0]?.splitType === "CLIMB_ACTIVE"
80
+ ? { ...activity?.splitSummaries?.[0], start: activity?.startTimeLocal }
81
+ : { ...activity?.splitSummaries?.[1], start: activity?.startTimeLocal }
82
+ );
83
+
84
+ const boulderingActivities = JSON.parse(
85
+ fs.readFileSync(`${garminDataFolder}/boulderingActivities.json`, "utf8")
86
+ );
87
+
88
+ const activeBoulderingSplits = boulderingActivities
89
+ ?.reverse()
90
+ .map((activity: any) =>
91
+ activity?.splitSummaries?.[0]?.splitType === "CLIMB_ACTIVE"
92
+ ? { ...activity?.splitSummaries?.[0], start: activity?.startTimeLocal }
93
+ : { ...activity?.splitSummaries?.[1], start: activity?.startTimeLocal }
94
+ )
95
+ .filter((split: any) => split?.noOfSplits !== 1);
96
+
97
+ const metersToFeet = (meters: number): number => Math.round(meters * 3.28084);
98
+
99
+ const generateChart = (data: any[], title: string, options: any) => {
100
+ console.log(title);
101
+ console.log(plot(data, options));
102
+ };
103
+
104
+ generateChart(
105
+ activeIndoorSplits
106
+ ?.reverse()
107
+ .map((split: any, index: number) => [
108
+ index,
109
+ YDS_GRADE_MAP?.[split?.maxGradeValue?.valueKey],
110
+ ]),
111
+ "Max Grade Per Session Indoor Climbing",
112
+ {
113
+ width: activeIndoorSplits?.length * 10,
114
+ formatter: (value: number, { axis }: any) =>
115
+ axis === "y"
116
+ ? `${numberToGrade(value)}`
117
+ : activeIndoorSplits[value]?.start?.split(" ")[0],
118
+ }
119
+ );
120
+
121
+ generateChart(
122
+ activeIndoorSplits?.map((split: any, index: number) => [
123
+ index,
124
+ split?.numClimbSends,
125
+ ]),
126
+ "Number of Sends per session",
127
+ {
128
+ height: 20,
129
+ width: activeIndoorSplits?.length * 10,
130
+ formatter: (value: number, { axis }: any) =>
131
+ axis === "y" ? value : activeIndoorSplits[value]?.start?.split(" ")[0],
132
+ }
133
+ );
134
+
135
+ generateChart(
136
+ activeIndoorSplits?.map((split: any, index: number) => [
137
+ index,
138
+ metersToFeet(split?.totalAscent),
139
+ ]),
140
+ "Total Feet Per Session",
141
+ {
142
+ height: 10,
143
+ width: activeIndoorSplits?.length * 10,
144
+ barChart: true,
145
+ formatter: (value: number, { axis }: any) =>
146
+ axis === "y" ? value : activeIndoorSplits[value]?.start?.split(" ")[0],
147
+ }
148
+ );
149
+
150
+ const totalFeetByMonthYear = activeIndoorSplits?.reduce(
151
+ (acc: any, split: any) => {
152
+ const month = split?.start?.split(" ")[0].split("-")[1];
153
+ const year = split?.start?.split(" ")[0].split("-")[0];
154
+ acc[`${year}-${month}`] =
155
+ (acc[`${year}-${month}`] || 0) + metersToFeet(split?.totalAscent);
156
+ return acc;
157
+ },
158
+ {}
159
+ );
160
+
161
+ generateChart(
162
+ Object.keys(totalFeetByMonthYear).map((month: string, index) => [
163
+ index,
164
+ totalFeetByMonthYear[month],
165
+ ]),
166
+ "Total Route Feet By Month",
167
+ {
168
+ height: 20,
169
+ width: 20,
170
+ barChart: true,
171
+ formatter: (value: number, { axis }: any) =>
172
+ axis === "y" ? value : Object.keys(totalFeetByMonthYear)[value],
173
+ }
174
+ );
175
+
176
+ generateChart(
177
+ activeIndoorSplits?.map((split: any, index: number) => [
178
+ index,
179
+ Math.round(split?.duration / 60),
180
+ ]),
181
+ "Active Minutes Per Session",
182
+ {
183
+ height: 20,
184
+ barChart: true,
185
+ width: activeIndoorSplits?.length * 10,
186
+ formatter: (value: number, { axis }: any) =>
187
+ axis === "y" ? value : activeIndoorSplits[value]?.start?.split(" ")[0],
188
+ }
189
+ );
190
+
191
+ console.log("\n\nBouldering===============================");
192
+ generateChart(
193
+ activeBoulderingSplits?.map((split: any, index: number) => [
194
+ index,
195
+ parseInt(split?.maxGradeValue?.valueKey?.replace(/\D/g, "")),
196
+ ]),
197
+ "Max Grade Per Session Bouldering",
198
+ {
199
+ height: 10,
200
+ width: activeBoulderingSplits?.length * 10,
201
+ formatter: (value: number, { axis }: any) =>
202
+ axis === "y"
203
+ ? `V${value}`
204
+ : activeBoulderingSplits[value]?.start?.split(" ")[0],
205
+ }
206
+ );
207
+
208
+ generateChart(
209
+ activeBoulderingSplits?.map((split: any, index: number) => [
210
+ index,
211
+ split?.noOfSplits,
212
+ ]),
213
+ "Number of Climbs per session",
214
+ {
215
+ height: 10,
216
+ barChart: true,
217
+ width: activeBoulderingSplits?.length * 10,
218
+ formatter: (value: number, { axis }: any) =>
219
+ axis === "y"
220
+ ? value
221
+ : activeBoulderingSplits[value]?.start?.split(" ")[0],
222
+ }
223
+ );
224
+
225
+ generateChart(
226
+ activeBoulderingSplits?.map((split: any, index: number) => [
227
+ index,
228
+ Math.round(split?.duration / 60),
229
+ ]),
230
+ "Active Minutes Bouldering Per Session",
231
+ {
232
+ height: 10,
233
+ barChart: true,
234
+ width: activeBoulderingSplits?.length * 10,
235
+ formatter: (value: number, { axis }: any) =>
236
+ axis === "y"
237
+ ? value
238
+ : activeBoulderingSplits[value]?.start?.split(" ")[0],
239
+ }
240
+ );
241
+
242
+ // each boulder is ~8 feet
243
+ const totalFeetByMonthYearBouldering = activeBoulderingSplits?.reduce(
244
+ (acc: any, split: any) => {
245
+ const month = split?.start?.split(" ")[0].split("-")[1];
246
+ const year = split?.start?.split(" ")[0].split("-")[0];
247
+ acc[`${year}-${month}`] =
248
+ (acc[`${year}-${month}`] || 0) + split?.noOfSplits * 8;
249
+ return acc;
250
+ },
251
+ {}
252
+ );
253
+
254
+ generateChart(
255
+ Object.keys(totalFeetByMonthYearBouldering).map((month: string, index) => [
256
+ index,
257
+ totalFeetByMonthYearBouldering[month],
258
+ ]),
259
+ "Approx Boulder Feet By Month",
260
+ {
261
+ height: 20,
262
+ width: 20,
263
+ barChart: true,
264
+ formatter: (value: number, { axis }: any) =>
265
+ axis === "y"
266
+ ? value
267
+ : Object.keys(totalFeetByMonthYearBouldering)[value],
268
+ }
269
+ );
270
+
271
+ const totalFeetByMonthYearCombined = Object.keys(totalFeetByMonthYear).reduce(
272
+ (acc: any, key: string) => {
273
+ acc[key] =
274
+ totalFeetByMonthYear[key] + (totalFeetByMonthYearBouldering[key] || 0);
275
+ return acc;
276
+ },
277
+ {}
278
+ );
279
+ generateChart(
280
+ Object.keys(totalFeetByMonthYearCombined).map((month: string, index) => [
281
+ index,
282
+ totalFeetByMonthYearCombined[month],
283
+ ]),
284
+ "Total Feet By Month Combined",
285
+ {
286
+ height: 20,
287
+ width: 20,
288
+ barChart: true,
289
+ formatter: (value: number, { axis }: any) =>
290
+ axis === "y" ? value : Object.keys(totalFeetByMonthYearCombined)[value],
291
+ }
292
+ );
293
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "src",
4
+ "outDir": "bin",
5
+ "strict": true,
6
+ "target": "es6",
7
+ "module": "commonjs",
8
+ "sourceMap": true,
9
+ "esModuleInterop": true,
10
+ "moduleResolution": "node",
11
+
12
+ },
13
+ "include": [
14
+ "src/**/*"
15
+ ],
16
+ "exclude": [
17
+ "node_modules/",
18
+ "**/*.spec.ts",
19
+ "**/*.js",
20
+ ]
21
+ }