garminclimb 1.0.5 → 1.1.6
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/.claude/settings.local.json +26 -0
- package/README.md +42 -0
- package/bin/download.js +42 -0
- package/bin/download.js.map +1 -1
- package/bin/index.js +104 -0
- package/bin/index.js.map +1 -1
- package/bin/mtnproject.js +64 -0
- package/bin/mtnproject.js.map +1 -0
- package/bin/parse.js +370 -26
- package/bin/parse.js.map +1 -1
- package/bouldering.json +10207 -0
- package/charts-embed.html +475 -0
- package/indoor_climbing.json +5918 -0
- package/package.json +1 -1
- package/src/download.ts +78 -0
- package/src/index.ts +68 -0
- package/src/mtnproject.ts +66 -0
- package/src/parse.ts +617 -61
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(npm install:*)",
|
|
5
|
+
"Bash(find /Users/johnottenlips/garminclimb/node_modules/garmin-connect/dist -name \"activity*\" -o -name \"types*\" 2>/dev/null | head -20)",
|
|
6
|
+
"Bash(npx tsc:*)",
|
|
7
|
+
"Bash(node dist/index.js charts 2>&1 | tail -80)",
|
|
8
|
+
"Bash(node bin/index.js charts 2>&1 | grep -A 2 \"Strength Training\")",
|
|
9
|
+
"Bash(node bin/index.js charts 2>&1 | tail -120)",
|
|
10
|
+
"Bash(node bin/index.js chartjs)",
|
|
11
|
+
"Read(//tmp/**)",
|
|
12
|
+
"Bash(open /tmp/strength_test.html)",
|
|
13
|
+
"Bash(cat ~/.gc_data/indoorClimbingActivities.json | python3 -c \"import json,sys; data=json.load\\(sys.stdin\\); print\\(f'Activities: {len\\(data\\)}'\\); [print\\(f' {a[\\\\\"startTimeLocal\\\\\"]} - {a[\\\\\"activityName\\\\\"]}'\\) for a in data[:5]]\" 2>&1)",
|
|
14
|
+
"Bash(cat ~/.gc_data/strengthTrainingActivities.json | python3 -c \"\nimport json,sys\ndata=json.load\\(sys.stdin\\)\nprint\\(f'Activities: {len\\(data\\)}'\\)\nfor a in data[:3]:\n print\\(f' {a.get\\(\\\\\"startTimeLocal\\\\\"\\)} - {a.get\\(\\\\\"activityName\\\\\"\\)}'\\)\n sets = a.get\\('exerciseSets'\\) or a.get\\('summarizedExerciseSets'\\) or []\n print\\(f' exerciseSets: {len\\(sets\\)} sets'\\)\n for s in sets[:3]:\n print\\(f' {s}'\\)\n\" 2>&1)",
|
|
15
|
+
"Bash(chmod +x /Users/johnottenlips/garminclimb/bin/index.js && ls -la /Users/johnottenlips/garminclimb/bin/index.js)",
|
|
16
|
+
"Bash(cat ~/.gc_data/strengthTrainingActivities.json | python3 -c \"\nimport json,sys\ndata=json.load\\(sys.stdin\\)\nprint\\(f'Total activities: {len\\(data\\)}'\\)\nfor a in data[:5]:\n print\\(f' {a.get\\(\\\\\"startTimeLocal\\\\\"\\)} - {a.get\\(\\\\\"activityName\\\\\"\\)}'\\)\n sets = a.get\\('exerciseSets'\\) or a.get\\('fullSummarizedExerciseSets'\\) or a.get\\('summarizedExerciseSets'\\) or []\n print\\(f' sets: {len\\(sets\\)}'\\)\n for s in sets[:4]:\n print\\(f' name={s.get\\(\\\\\"exerciseName\\\\\",\\\\\"?\\\\\"\\)} cat={s.get\\(\\\\\"exerciseCategory\\\\\",\\\\\"?\\\\\"\\)} type={s.get\\(\\\\\"setType\\\\\",\\\\\"?\\\\\"\\)} w={s.get\\(\\\\\"weight\\\\\",\\\\\"?\\\\\"\\)} r={s.get\\(\\\\\"repetitionCount\\\\\",\\\\\"?\\\\\"\\)}'\\)\nprint\\('...'\\)\nfor a in data[-3:]:\n print\\(f' {a.get\\(\\\\\"startTimeLocal\\\\\"\\)} - {a.get\\(\\\\\"activityName\\\\\"\\)}'\\)\n sets = a.get\\('exerciseSets'\\) or a.get\\('fullSummarizedExerciseSets'\\) or a.get\\('summarizedExerciseSets'\\) or []\n print\\(f' sets: {len\\(sets\\)}'\\)\n for s in sets[:4]:\n print\\(f' name={s.get\\(\\\\\"exerciseName\\\\\",\\\\\"?\\\\\"\\)} cat={s.get\\(\\\\\"exerciseCategory\\\\\",\\\\\"?\\\\\"\\)} type={s.get\\(\\\\\"setType\\\\\",\\\\\"?\\\\\"\\)} w={s.get\\(\\\\\"weight\\\\\",\\\\\"?\\\\\"\\)} r={s.get\\(\\\\\"repetitionCount\\\\\",\\\\\"?\\\\\"\\)}'\\)\n\" 2>&1)",
|
|
17
|
+
"Bash(cat ~/.gc_data/strengthTrainingActivities.json | python3 -c \"\nimport json,sys\ndata=json.load\\(sys.stdin\\)\na = data[0]\nsets = a.get\\('exerciseSets'\\) or a.get\\('fullSummarizedExerciseSets'\\) or a.get\\('summarizedExerciseSets'\\) or []\nprint\\('First activity keys:', list\\(a.keys\\(\\)\\)[:20]\\)\nprint\\(\\)\nfor i, s in enumerate\\(sets[:2]\\):\n print\\(f'Set {i} keys: {list\\(s.keys\\(\\)\\)}'\\)\n print\\(json.dumps\\(s, indent=2\\)[:500]\\)\n print\\(\\)\n\" 2>&1)",
|
|
18
|
+
"Bash(node bin/index.js charts 2>&1 | sed -n '/Strength Training/,$p')",
|
|
19
|
+
"Bash(node bin/index.js charts 2>&1 | sed -n '/Weighted Pull-Up/,/```/p' | head -30)",
|
|
20
|
+
"Bash(node bin/index.js charts 2>&1 | grep -A 5 \"Romanian Deadlift\")",
|
|
21
|
+
"Bash(chmod +x /Users/johnottenlips/garminclimb/bin/index.js)",
|
|
22
|
+
"Bash(grep -r \"Cycling\" /Users/johnottenlips/garminclimb/node_modules/garmin-connect/dist/garmin/types/activity* 2>/dev/null | head -20)",
|
|
23
|
+
"Bash(node -e \"const { ActivityType } = require\\('garmin-connect/dist/garmin/types/activity'\\); console.log\\('ActivityType.Cycling =', ActivityType.Cycling\\); console.log\\('All types:', JSON.stringify\\(ActivityType, null, 2\\)\\);\")"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
package/README.md
CHANGED
|
@@ -17,6 +17,48 @@ cd ~/.gc_data
|
|
|
17
17
|
ls
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
# Interactive Chart.js Dashboard
|
|
21
|
+
|
|
22
|
+
Generate a self-contained HTML file with interactive Chart.js graphs. The HTML includes your climbing data inline so it can be opened in any browser without a server.
|
|
23
|
+
|
|
24
|
+
```shell
|
|
25
|
+
garminclimb chartjs > climbing_dashboard.html
|
|
26
|
+
open climbing_dashboard.html
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This outputs a single HTML file with all your data embedded. Charts include:
|
|
30
|
+
|
|
31
|
+
- Yearly and monthly totals for feet climbed (rope, boulder, combined)
|
|
32
|
+
- Yearly and monthly max grades (rope and boulder)
|
|
33
|
+
- Yearly and monthly session counts
|
|
34
|
+
- Per-session stats: max grade, sends, feet, active minutes
|
|
35
|
+
- Per-session bouldering: max grade, splits, active minutes
|
|
36
|
+
|
|
37
|
+
## Embed in a Markdown File
|
|
38
|
+
|
|
39
|
+
Output an HTML snippet you can paste directly into any markdown file that supports inline HTML (GitHub Pages, Jekyll, static site generators, etc.):
|
|
40
|
+
|
|
41
|
+
```shell
|
|
42
|
+
garminclimb chartjs-embed > climbing-charts.html
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then include it in your markdown:
|
|
46
|
+
|
|
47
|
+
```markdown
|
|
48
|
+
# My Climbing Stats
|
|
49
|
+
|
|
50
|
+
Here are my climbing charts from Garmin:
|
|
51
|
+
|
|
52
|
+
{% include climbing-charts.html %}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or paste the output directly into your `.md` file — the snippet includes scoped CSS (`.gc-dashboard` prefix) so it won't conflict with your site's styles.
|
|
56
|
+
|
|
57
|
+
```shell
|
|
58
|
+
# Append charts to an existing markdown file
|
|
59
|
+
garminclimb chartjs-embed >> climbing.md
|
|
60
|
+
```
|
|
61
|
+
|
|
20
62
|
# Develop locally or make your own charts
|
|
21
63
|
|
|
22
64
|
```shell
|
package/bin/download.js
CHANGED
|
@@ -65,6 +65,48 @@ const download = (username, password) => __awaiter(void 0, void 0, void 0, funct
|
|
|
65
65
|
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"; });
|
|
66
66
|
fs.writeFileSync(`${_1.garminDataFolder}/boulderingActivities.json`, JSON.stringify(boulderingActivities, null, 2));
|
|
67
67
|
console.log("Downloaded climbing activities from Garmin Connect");
|
|
68
|
+
// Download strength training activities
|
|
69
|
+
const strengthActivities = yield GCClient.getActivities(0, 6000, activity_1.ActivityType.FitnessEquipment,
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
"strength_training");
|
|
72
|
+
const strengthTrainingActivities = strengthActivities === null || strengthActivities === void 0 ? void 0 : strengthActivities.filter((activity) => { var _a; return ((_a = activity === null || activity === void 0 ? void 0 : activity.activityType) === null || _a === void 0 ? void 0 : _a.typeKey) === "strength_training"; });
|
|
73
|
+
// Fetch detailed data for each strength activity to get exercise sets
|
|
74
|
+
console.log(`Found ${(strengthTrainingActivities === null || strengthTrainingActivities === void 0 ? void 0 : strengthTrainingActivities.length) || 0} strength training activities, fetching details...`);
|
|
75
|
+
const detailedActivities = [];
|
|
76
|
+
for (const activity of strengthTrainingActivities || []) {
|
|
77
|
+
try {
|
|
78
|
+
const detail = yield GCClient.getActivity({ activityId: activity.activityId });
|
|
79
|
+
detailedActivities.push(Object.assign(Object.assign({}, activity), { exerciseSets: (detail === null || detail === void 0 ? void 0 : detail.exerciseSets) || (detail === null || detail === void 0 ? void 0 : detail.summarizedExerciseSets) || (activity === null || activity === void 0 ? void 0 : activity.summarizedExerciseSets) || [], fullSummarizedExerciseSets: (detail === null || detail === void 0 ? void 0 : detail.summarizedExerciseSets) || (activity === null || activity === void 0 ? void 0 : activity.summarizedExerciseSets) || [] }));
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
// fallback to list data if detail fetch fails
|
|
83
|
+
detailedActivities.push(activity);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
fs.writeFileSync(`${_1.garminDataFolder}/strengthTrainingActivities.json`, JSON.stringify(detailedActivities, null, 2));
|
|
87
|
+
console.log("Downloaded strength training activities from Garmin Connect");
|
|
88
|
+
// Download running activities (street_running gets all running subtypes)
|
|
89
|
+
const runningActivities = yield GCClient.getActivities(0, 6000,
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
"running");
|
|
92
|
+
const filteredRunning = (runningActivities || []).filter((a) => {
|
|
93
|
+
var _a;
|
|
94
|
+
const key = ((_a = a === null || a === void 0 ? void 0 : a.activityType) === null || _a === void 0 ? void 0 : _a.typeKey) || "";
|
|
95
|
+
return key.includes("running");
|
|
96
|
+
});
|
|
97
|
+
fs.writeFileSync(`${_1.garminDataFolder}/runningActivities.json`, JSON.stringify(filteredRunning, null, 2));
|
|
98
|
+
console.log(`Downloaded ${filteredRunning.length} running activities from Garmin Connect`);
|
|
99
|
+
// Download cycling activities
|
|
100
|
+
const cyclingActivities = yield GCClient.getActivities(0, 6000,
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
"cycling");
|
|
103
|
+
const filteredCycling = (cyclingActivities || []).filter((a) => {
|
|
104
|
+
var _a;
|
|
105
|
+
const key = ((_a = a === null || a === void 0 ? void 0 : a.activityType) === null || _a === void 0 ? void 0 : _a.typeKey) || "";
|
|
106
|
+
return key.includes("cycling") || key.includes("biking") || key === "virtual_ride";
|
|
107
|
+
});
|
|
108
|
+
fs.writeFileSync(`${_1.garminDataFolder}/cyclingActivities.json`, JSON.stringify(filteredCycling, null, 2));
|
|
109
|
+
console.log(`Downloaded ${filteredCycling.length} cycling activities from Garmin Connect`);
|
|
68
110
|
});
|
|
69
111
|
exports.download = download;
|
|
70
112
|
//# sourceMappingURL=download.js.map
|
package/bin/download.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,aAAa;AACb,mDAA+C;AAC/C,wBAAqC;AACrC,wEAGmD;AAC5C,MAAM,QAAQ,GAAG,CACtB,QAAgB,EAChB,QAAgB,EACD,EAAE;IACjB,MAAM,QAAQ,GAAG,IAAI,8BAAa,CAAC;QACjC,QAAQ;QACR,QAAQ;KACT,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEzC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,aAAa,CAC7C,CAAC,EACD,IAAI,EACJ,uBAAY,CAAC,gBAAgB;IAC7B,aAAa;IACb,iBAAiB,CAClB,CAAC;IAEF,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;
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,aAAa;AACb,mDAA+C;AAC/C,wBAAqC;AACrC,wEAGmD;AAC5C,MAAM,QAAQ,GAAG,CACtB,QAAgB,EAChB,QAAgB,EACD,EAAE;IACjB,MAAM,QAAQ,GAAG,IAAI,8BAAa,CAAC;QACjC,QAAQ;QACR,QAAQ;KACT,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEzC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,aAAa,CAC7C,CAAC,EACD,IAAI,EACJ,uBAAY,CAAC,gBAAgB;IAC7B,aAAa;IACb,iBAAiB,CAClB,CAAC;IAEF,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;IAElE,wCAAwC;IACxC,MAAM,kBAAkB,GAAG,MAAM,QAAQ,CAAC,aAAa,CACrD,CAAC,EACD,IAAI,EACJ,uBAAY,CAAC,gBAAgB;IAC7B,aAAa;IACb,mBAAmB,CACpB,CAAC;IAEF,MAAM,0BAA0B,GAAG,kBAAkB,aAAlB,kBAAkB,uBAAlB,kBAAkB,CAAE,MAAM,CAC3D,CAAC,QAAa,EAAE,EAAE,WAAC,OAAA,CAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,YAAY,0CAAE,OAAO,MAAK,mBAAmB,CAAA,EAAA,CAC3E,CAAC;IAEF,sEAAsE;IACtE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAA,0BAA0B,aAA1B,0BAA0B,uBAA1B,0BAA0B,CAAE,MAAM,KAAI,CAAC,oDAAoD,CAAC,CAAC;IAClH,MAAM,kBAAkB,GAAU,EAAE,CAAC;IACrC,KAAK,MAAM,QAAQ,IAAI,0BAA0B,IAAI,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,MAAM,MAAM,GAAQ,MAAM,QAAQ,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACpF,kBAAkB,CAAC,IAAI,iCAClB,QAAQ,KACX,YAAY,EAAE,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,YAAY,MAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,sBAAsB,CAAA,KAAK,QAAgB,aAAhB,QAAQ,uBAAR,QAAQ,CAAU,sBAAsB,CAAA,IAAI,EAAE,EACvH,0BAA0B,EAAE,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,sBAAsB,MAAK,QAAgB,aAAhB,QAAQ,uBAAR,QAAQ,CAAU,sBAAsB,CAAA,IAAI,EAAE,IAC7G,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,8CAA8C;YAC9C,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,EAAE,CAAC,aAAa,CACd,GAAG,mBAAgB,kCAAkC,EACrD,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC,CAC5C,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;IAE3E,yEAAyE;IACzE,MAAM,iBAAiB,GAAG,MAAM,QAAQ,CAAC,aAAa,CACpD,CAAC,EACD,IAAI;IACJ,aAAa;IACb,SAAS,CACV,CAAC;IAEF,MAAM,eAAe,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,MAAM,CACtD,CAAC,CAAM,EAAE,EAAE;;QACT,MAAM,GAAG,GAAG,CAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,YAAY,0CAAE,OAAO,KAAI,EAAE,CAAC;QAC3C,OAAO,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC,CACF,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,GAAG,mBAAgB,yBAAyB,EAC5C,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CACzC,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,cAAc,eAAe,CAAC,MAAM,yCAAyC,CAAC,CAAC;IAE3F,8BAA8B;IAC9B,MAAM,iBAAiB,GAAG,MAAM,QAAQ,CAAC,aAAa,CACpD,CAAC,EACD,IAAI;IACJ,aAAa;IACb,SAAS,CACV,CAAC;IAEF,MAAM,eAAe,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,MAAM,CACtD,CAAC,CAAM,EAAE,EAAE;;QACT,MAAM,GAAG,GAAG,CAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,YAAY,0CAAE,OAAO,KAAI,EAAE,CAAC;QAC3C,OAAO,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,cAAc,CAAC;IACrF,CAAC,CACF,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,GAAG,mBAAgB,yBAAyB,EAC5C,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CACzC,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,cAAc,eAAe,CAAC,MAAM,yCAAyC,CAAC,CAAC;AAC7F,CAAC,CAAA,CAAC;AAtHW,QAAA,QAAQ,YAsHnB"}
|
package/bin/index.js
CHANGED
|
@@ -1,8 +1,42 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
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
|
+
})();
|
|
3
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
37
|
exports.garminDataFolder = void 0;
|
|
5
38
|
const commander_1 = require("commander");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
6
40
|
const os = require("os");
|
|
7
41
|
const path = require("path");
|
|
8
42
|
const homeDir = os.homedir();
|
|
@@ -25,5 +59,75 @@ commander_1.program
|
|
|
25
59
|
.action(() => {
|
|
26
60
|
(0, parse_1.parse)();
|
|
27
61
|
});
|
|
62
|
+
function loadData() {
|
|
63
|
+
const indoorData = fs.readFileSync(path.join(exports.garminDataFolder, "indoorClimbingActivities.json"), "utf8");
|
|
64
|
+
const boulderData = fs.readFileSync(path.join(exports.garminDataFolder, "boulderingActivities.json"), "utf8");
|
|
65
|
+
let strengthData = "[]";
|
|
66
|
+
try {
|
|
67
|
+
strengthData = fs.readFileSync(path.join(exports.garminDataFolder, "strengthTrainingActivities.json"), "utf8");
|
|
68
|
+
}
|
|
69
|
+
catch (e) { }
|
|
70
|
+
let runningData = "[]";
|
|
71
|
+
try {
|
|
72
|
+
runningData = fs.readFileSync(path.join(exports.garminDataFolder, "runningActivities.json"), "utf8");
|
|
73
|
+
}
|
|
74
|
+
catch (e) { }
|
|
75
|
+
let cyclingData = "[]";
|
|
76
|
+
try {
|
|
77
|
+
cyclingData = fs.readFileSync(path.join(exports.garminDataFolder, "cyclingActivities.json"), "utf8");
|
|
78
|
+
}
|
|
79
|
+
catch (e) { }
|
|
80
|
+
return `var INDOOR_DATA = ${indoorData};\nvar BOULDER_DATA = ${boulderData};\nvar STRENGTH_DATA = ${strengthData};\nvar CYCLING_DATA = ${cyclingData};\nvar RUNNING_DATA = ${runningData};\n`;
|
|
81
|
+
}
|
|
82
|
+
commander_1.program
|
|
83
|
+
.command("chartjs")
|
|
84
|
+
.description("Output a standalone Chart.js HTML dashboard")
|
|
85
|
+
.action(() => {
|
|
86
|
+
const embedHtml = fs.readFileSync(path.join(__dirname, "..", "charts-embed.html"), "utf8");
|
|
87
|
+
const dataScript = loadData();
|
|
88
|
+
const output = `<!DOCTYPE html>
|
|
89
|
+
<html lang="en">
|
|
90
|
+
<head>
|
|
91
|
+
<meta charset="UTF-8">
|
|
92
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
93
|
+
<title>Garmin Stats</title>
|
|
94
|
+
<style>
|
|
95
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
96
|
+
body {
|
|
97
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
98
|
+
line-height: 1.6;
|
|
99
|
+
color: #333;
|
|
100
|
+
max-width: 720px;
|
|
101
|
+
margin: 0 auto;
|
|
102
|
+
padding: 20px;
|
|
103
|
+
background: #fafaf8;
|
|
104
|
+
}
|
|
105
|
+
h1 { color: #2d5016; margin-bottom: 16px; }
|
|
106
|
+
h2 { color: #3a6b1e; margin: 24px 0 12px; }
|
|
107
|
+
a { color: #2d5016; }
|
|
108
|
+
hr { border: none; border-top: 2px solid #2d5016; margin: 24px 0; }
|
|
109
|
+
@media (max-width: 600px) {
|
|
110
|
+
body { padding: 12px; }
|
|
111
|
+
h1 { font-size: 1.4em; }
|
|
112
|
+
h2 { font-size: 1.15em; }
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
115
|
+
</head>
|
|
116
|
+
<body>
|
|
117
|
+
<script>${dataScript}</script>
|
|
118
|
+
${embedHtml}
|
|
119
|
+
</body>
|
|
120
|
+
</html>`;
|
|
121
|
+
console.log(output);
|
|
122
|
+
});
|
|
123
|
+
commander_1.program
|
|
124
|
+
.command("chartjs-embed")
|
|
125
|
+
.description("Output an embeddable Chart.js HTML snippet for markdown files")
|
|
126
|
+
.action(() => {
|
|
127
|
+
const embedHtml = fs.readFileSync(path.join(__dirname, "..", "charts-embed.html"), "utf8");
|
|
128
|
+
const dataScript = loadData();
|
|
129
|
+
const output = embedHtml.replace("<script>\n(function() {", `<script>\n${dataScript}(function() {`);
|
|
130
|
+
console.log(output);
|
|
131
|
+
});
|
|
28
132
|
commander_1.program.parse();
|
|
29
133
|
//# sourceMappingURL=index.js.map
|
package/bin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// // https://www.mountainproject.com/rss/user-ticks/201166134
|
|
3
|
+
// // <rss xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
|
|
4
|
+
// // <channel>
|
|
5
|
+
// // <title>Ticks for John Ottenlips Franke on Mountain Project</title>
|
|
6
|
+
// // <description>The Definitive Climbing Resource</description>
|
|
7
|
+
// // <link>https://www.mountainproject.com</link>
|
|
8
|
+
// // <language>en-us</language>
|
|
9
|
+
// // <lastBuildDate>Sat, 24 Jan 2026 19:09:10 +0000</lastBuildDate>
|
|
10
|
+
// // <item>
|
|
11
|
+
// // <title>Tick: Grape Vine (5.7)</title>
|
|
12
|
+
// // <link>https://www.mountainproject.com/route/121871647/grape-vine</link>
|
|
13
|
+
// // <guid isPermaLink="false">MPObject_202273988</guid>
|
|
14
|
+
// // <pubDate>Sat, 27 Dec 2025 21:06:07 +0000</pubDate>
|
|
15
|
+
// // <description><div class="fr-view"><p>Work through a low crux through a series of small ledges. Finish in a small dihedral just before the anchor. Watch out for lots of loose rock.</p></div><br /><img class="" src='https://mountainproject.com/assets/photos/climb/122083536_smallMed_1647451882_topo.jpg?cache=1714597316' alt="Really fun route!"><br><a href="https://www.mountainproject.com/area/105899020/missouri">Missouri</a> > <a href="https://www.mountainproject.com/area/116617972/em-rockwoods-reservation">EM: Rockwoods R…</a> > <a href="https://www.mountainproject.com/area/121388630/colony-wall">Colony Wall</a> </description>
|
|
16
|
+
// // </item>
|
|
17
|
+
// // <item>
|
|
18
|
+
// // <title>Tick: Xylem (5.10a)</title>
|
|
19
|
+
// // <link>https://www.mountainproject.com/route/121676838/xylem</link>
|
|
20
|
+
// // <guid isPermaLink="false">MPObject_202273688</guid>
|
|
21
|
+
// // <pubDate>Sat, 27 Dec 2025 19:56:10 +0000</pubDate>
|
|
22
|
+
// // <description><div class="fr-view"><p>Begin with a gentle slab start to a comfortable ledge rest, followed by more vertical climbing as the dihedral crack thins into the crux. Careful footwork and body position will help you gain the second ledge to clip the anchors. The dihedral provides some shade, which may be helpful in warmer weather.</p></div><br /><img class="" src='https://mountainproject.com/assets/photos/climb/126305529_smallMed_1717616410.jpg?cache=1725482195' alt="From Above"><br><a href="https://www.mountainproject.com/area/105899020/missouri">Missouri</a> > <a href="https://www.mountainproject.com/area/116617972/em-rockwoods-reservation">EM: Rockwoods R…</a> > <a href="https://www.mountainproject.com/area/121388630/colony-wall">Colony Wall</a> </description>
|
|
23
|
+
// // </item>
|
|
24
|
+
// // <item>
|
|
25
|
+
// // craswl mtn project urls for route stats
|
|
26
|
+
// import fetch from "node-fetch";
|
|
27
|
+
// import * as xml2js from "xml2js";
|
|
28
|
+
// export const fetchMountainProjectTicks = async (
|
|
29
|
+
// userId: string
|
|
30
|
+
// ): Promise<any[]> => {
|
|
31
|
+
// const url = `https://www.mountainproject.com/rss/user-ticks/${userId}`;
|
|
32
|
+
// const response = await fetch(url);
|
|
33
|
+
// const xmlData = await response.text();
|
|
34
|
+
// const parser = new xml2js.Parser();
|
|
35
|
+
// const result = await parser.parseStringPromise(xmlData);
|
|
36
|
+
// const items = result.rss.channel[0].item;
|
|
37
|
+
// return items.map((item: any) => ({
|
|
38
|
+
// title: item.title[0],
|
|
39
|
+
// link: item.link[0],
|
|
40
|
+
// pubDate: item.pubDate[0],
|
|
41
|
+
// description: item.description[0],
|
|
42
|
+
// }));
|
|
43
|
+
// };
|
|
44
|
+
// // get route height
|
|
45
|
+
// export const fetchRouteHeight = async (routeUrl: string): Promise<string> => {
|
|
46
|
+
// const response = await fetch(routeUrl);
|
|
47
|
+
// const htmlData = await response.text();
|
|
48
|
+
// const heightMatch = htmlData.match(
|
|
49
|
+
// /<li><strong>Height:<\/strong>\s*([\d\sftm]+)/
|
|
50
|
+
// );
|
|
51
|
+
// if (heightMatch && heightMatch[1]) {
|
|
52
|
+
// return heightMatch[1].trim();
|
|
53
|
+
// }
|
|
54
|
+
// return "Unknown";
|
|
55
|
+
// };
|
|
56
|
+
// // Example usage:
|
|
57
|
+
// // (async () => {
|
|
58
|
+
// // const ticks = await fetchMountainProjectTicks("201166134");
|
|
59
|
+
// // for (const tick of ticks) {
|
|
60
|
+
// // const height = await fetchRouteHeight(tick.link);
|
|
61
|
+
// // console.log(`${tick.title} - Height: ${height}`);
|
|
62
|
+
// // }
|
|
63
|
+
// // })();
|
|
64
|
+
//# sourceMappingURL=mtnproject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mtnproject.js","sourceRoot":"","sources":["../src/mtnproject.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mLAAmL;AACnL,eAAe;AACf,wEAAwE;AACxE,iEAAiE;AACjE,kDAAkD;AAClD,gCAAgC;AAChC,oEAAoE;AACpE,YAAY;AACZ,2CAA2C;AAC3C,6EAA6E;AAC7E,yDAAyD;AACzD,wDAAwD;AACxD,2oBAA2oB;AAC3oB,aAAa;AACb,YAAY;AACZ,wCAAwC;AACxC,wEAAwE;AACxE,yDAAyD;AACzD,wDAAwD;AACxD,4yBAA4yB;AAC5yB,aAAa;AACb,YAAY;AAEZ,6CAA6C;AAC7C,kCAAkC;AAClC,oCAAoC;AAEpC,mDAAmD;AACnD,mBAAmB;AACnB,yBAAyB;AACzB,4EAA4E;AAC5E,uCAAuC;AACvC,2CAA2C;AAE3C,wCAAwC;AACxC,6DAA6D;AAE7D,8CAA8C;AAC9C,uCAAuC;AACvC,4BAA4B;AAC5B,0BAA0B;AAC1B,gCAAgC;AAChC,wCAAwC;AACxC,SAAS;AACT,KAAK;AACL,sBAAsB;AACtB,iFAAiF;AACjF,4CAA4C;AAC5C,4CAA4C;AAC5C,wCAAwC;AACxC,qDAAqD;AACrD,OAAO;AACP,yCAAyC;AACzC,oCAAoC;AACpC,MAAM;AACN,sBAAsB;AACtB,KAAK;AACL,oBAAoB;AACpB,oBAAoB;AACpB,mEAAmE;AACnE,mCAAmC;AACnC,2DAA2D;AAC3D,2DAA2D;AAC3D,SAAS;AACT,WAAW"}
|