@runnerpro/backend 1.6.8 → 1.6.10
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/lib/cjs/chat/api/conversation.js +12 -12
- package/lib/cjs/chat/api/listConversations.js +12 -12
- package/lib/cjs/chat/utils/getCountNotificaciones.js +12 -12
- package/lib/cjs/db/index.js +2 -1
- package/lib/cjs/index.js +4 -1
- package/lib/cjs/sendMail/index.js +65 -65
- package/lib/cjs/types/db/index.d.ts +3 -1
- package/lib/cjs/types/db/index.d.ts.map +1 -1
- package/lib/cjs/types/image/generateShareMap.d.ts +2 -1
- package/lib/cjs/types/image/generateShareMap.d.ts.map +1 -1
- package/lib/cjs/types/index.d.ts +3 -2
- package/lib/cjs/types/index.d.ts.map +1 -1
- package/lib/cjs/types/workout/estructuraWorkout.d.ts +5 -0
- package/lib/cjs/types/workout/estructuraWorkout.d.ts.map +1 -0
- package/lib/cjs/types/workout/garmin.d.ts +10 -0
- package/lib/cjs/types/workout/garmin.d.ts.map +1 -0
- package/lib/cjs/types/workout/sendToWatch.d.ts +9 -0
- package/lib/cjs/types/workout/sendToWatch.d.ts.map +1 -0
- package/lib/cjs/workout/estructuraWorkout.js +151 -0
- package/lib/cjs/workout/garmin.js +267 -0
- package/lib/cjs/workout/sendToWatch.js +36 -0
- package/package.json +73 -72
|
@@ -55,13 +55,13 @@ exports.conversationRoute = conversationRoute;
|
|
|
55
55
|
const getConversation = (req, res, { query, isClient }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
56
56
|
const idCliente = isClient ? req.session.userid : req.query.id;
|
|
57
57
|
const [header] = yield query('SELECT [NAME], [PREFERRED LANGUAGE] FROM [CLIENTE] WHERE [ID] = ?', [idCliente]);
|
|
58
|
-
let messages = yield query(`SELECT [CHAT MESSAGE].*,
|
|
59
|
-
[WORKOUT].[DATE] AS [WORKOUT DATE], [WORKOUT].[TYPE] AS [WORKOUT TYPE], [WORKOUT].[TITLE] AS [WORKOUT TITLE],
|
|
60
|
-
[WORKOUT].[PHOTO URL SHARE] AS [WORKOUT PHOTO URL SHARE], [FEELINGS], [FEELINGS DESCRIPTION]
|
|
61
|
-
FROM [CHAT MESSAGE]
|
|
62
|
-
LEFT JOIN [WORKOUT] ON [WORKOUT].[ID] = [CHAT MESSAGE].[ID WORKOUT]
|
|
63
|
-
WHERE [CHAT MESSAGE].[ID CLIENTE] = ? AND (${isClient} = FALSE OR [CHAT MESSAGE].[SHOW CLIENT] = TRUE)
|
|
64
|
-
ORDER BY [CHAT MESSAGE].[DATE] DESC
|
|
58
|
+
let messages = yield query(`SELECT [CHAT MESSAGE].*,
|
|
59
|
+
[WORKOUT].[DATE] AS [WORKOUT DATE], [WORKOUT].[TYPE] AS [WORKOUT TYPE], [WORKOUT].[TITLE] AS [WORKOUT TITLE],
|
|
60
|
+
[WORKOUT].[PHOTO URL SHARE] AS [WORKOUT PHOTO URL SHARE], [FEELINGS], [FEELINGS DESCRIPTION]
|
|
61
|
+
FROM [CHAT MESSAGE]
|
|
62
|
+
LEFT JOIN [WORKOUT] ON [WORKOUT].[ID] = [CHAT MESSAGE].[ID WORKOUT]
|
|
63
|
+
WHERE [CHAT MESSAGE].[ID CLIENTE] = ? AND (${isClient} = FALSE OR [CHAT MESSAGE].[SHOW CLIENT] = TRUE)
|
|
64
|
+
ORDER BY [CHAT MESSAGE].[DATE] DESC
|
|
65
65
|
LIMIT 100`, [idCliente]);
|
|
66
66
|
messages = messages.reverse();
|
|
67
67
|
// Si el cliente no habla español, se muestran los mensajes en el idioma del cliente. El entrenador siempre ve los mensajes en español que están en la columna [TEXT]
|
|
@@ -300,11 +300,11 @@ const saveResponseTime = ({ query, isClient, idCliente }) => __awaiter(void 0, v
|
|
|
300
300
|
let firstNotReadMessage;
|
|
301
301
|
// eslint-disable-next-line no-constant-condition
|
|
302
302
|
while (true) {
|
|
303
|
-
const [lastMessage] = yield query(`
|
|
304
|
-
SELECT * FROM [CHAT MESSAGE]
|
|
305
|
-
WHERE [ID CLIENTE] = ? AND [ID] < ?
|
|
306
|
-
ORDER BY [DATE] DESC
|
|
307
|
-
LIMIT 1
|
|
303
|
+
const [lastMessage] = yield query(`
|
|
304
|
+
SELECT * FROM [CHAT MESSAGE]
|
|
305
|
+
WHERE [ID CLIENTE] = ? AND [ID] < ?
|
|
306
|
+
ORDER BY [DATE] DESC
|
|
307
|
+
LIMIT 1
|
|
308
308
|
`, [idCliente, (lastNotReadMessage === null || lastNotReadMessage === void 0 ? void 0 : lastNotReadMessage.id) || 2147483646]);
|
|
309
309
|
// Si no hay más mensajes || el mensaje no es del cliente || ya tiene tiempo de respuesta => salimos del bucle
|
|
310
310
|
if (!lastMessage || (lastMessage.idSender && lastMessage.idCliente !== lastMessage.idSender) || lastMessage.tiempoRespuesta)
|
|
@@ -32,18 +32,18 @@ const getList = (req, res, { query, isClient }) => __awaiter(void 0, void 0, voi
|
|
|
32
32
|
else {
|
|
33
33
|
const { page } = req.query;
|
|
34
34
|
const limit = 20;
|
|
35
|
-
const list = yield query(`
|
|
36
|
-
SELECT [C].[ID], [C].[NAME],
|
|
37
|
-
[CM].[DATE], [CM].[TEXT],
|
|
38
|
-
CASE WHEN [CM].[READ] = FALSE AND [CM].[ID SENDER] = [CM].[ID CLIENTE] THEN 0
|
|
39
|
-
ELSE 1 END AS [READ]
|
|
40
|
-
FROM [CLIENTE] AS [C]
|
|
41
|
-
LEFT JOIN (
|
|
42
|
-
SELECT *, ROW_NUMBER() OVER (PARTITION BY [ID CLIENTE] ORDER BY [DATE] DESC) AS [ROW]
|
|
43
|
-
FROM [CHAT MESSAGE]
|
|
44
|
-
) AS [CM] ON [CM].[ID CLIENTE] = [C].[ID] AND [CM].[ROW] = 1
|
|
45
|
-
ORDER BY CASE WHEN [CM].[DATE] IS NULL THEN 1 ELSE 0 END, [CM].[DATE] DESC
|
|
46
|
-
LIMIT ? OFFSET ?
|
|
35
|
+
const list = yield query(`
|
|
36
|
+
SELECT [C].[ID], [C].[NAME],
|
|
37
|
+
[CM].[DATE], [CM].[TEXT],
|
|
38
|
+
CASE WHEN [CM].[READ] = FALSE AND [CM].[ID SENDER] = [CM].[ID CLIENTE] THEN 0
|
|
39
|
+
ELSE 1 END AS [READ]
|
|
40
|
+
FROM [CLIENTE] AS [C]
|
|
41
|
+
LEFT JOIN (
|
|
42
|
+
SELECT *, ROW_NUMBER() OVER (PARTITION BY [ID CLIENTE] ORDER BY [DATE] DESC) AS [ROW]
|
|
43
|
+
FROM [CHAT MESSAGE]
|
|
44
|
+
) AS [CM] ON [CM].[ID CLIENTE] = [C].[ID] AND [CM].[ROW] = 1
|
|
45
|
+
ORDER BY CASE WHEN [CM].[DATE] IS NULL THEN 1 ELSE 0 END, [CM].[DATE] DESC
|
|
46
|
+
LIMIT ? OFFSET ?
|
|
47
47
|
`, [limit, limit * page]);
|
|
48
48
|
res.send(list);
|
|
49
49
|
}
|
|
@@ -12,22 +12,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.getCountNotificaciones = void 0;
|
|
13
13
|
const getCountNotificaciones = ({ query, isClient, idClient }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
14
|
if (isClient) {
|
|
15
|
-
const [{ chat }] = yield query(`
|
|
16
|
-
SELECT COUNT([ID]) AS [CHAT]
|
|
17
|
-
FROM [CHAT MESSAGE]
|
|
18
|
-
WHERE [ID CLIENTE] = ? AND [READ] = FALSE AND [ID SENDER] != [ID CLIENTE]
|
|
15
|
+
const [{ chat }] = yield query(`
|
|
16
|
+
SELECT COUNT([ID]) AS [CHAT]
|
|
17
|
+
FROM [CHAT MESSAGE]
|
|
18
|
+
WHERE [ID CLIENTE] = ? AND [READ] = FALSE AND [ID SENDER] != [ID CLIENTE]
|
|
19
19
|
`, [idClient]);
|
|
20
20
|
return chat;
|
|
21
21
|
}
|
|
22
22
|
else {
|
|
23
|
-
const [{ chat }] = yield query(`
|
|
24
|
-
SELECT COUNT([C].[ID]) AS [CHAT]
|
|
25
|
-
FROM [CLIENTE] AS [C]
|
|
26
|
-
LEFT JOIN (
|
|
27
|
-
SELECT *, ROW_NUMBER() OVER (PARTITION BY [ID CLIENTE] ORDER BY [DATE] DESC) AS [ROW]
|
|
28
|
-
FROM [CHAT MESSAGE]
|
|
29
|
-
) AS [CM] ON [CM].[ID CLIENTE] = [C].[ID] AND [CM].[ROW] = 1
|
|
30
|
-
WHERE [CM].[READ] = FALSE AND [CM].[ID SENDER] = [CM].[ID CLIENTE]
|
|
23
|
+
const [{ chat }] = yield query(`
|
|
24
|
+
SELECT COUNT([C].[ID]) AS [CHAT]
|
|
25
|
+
FROM [CLIENTE] AS [C]
|
|
26
|
+
LEFT JOIN (
|
|
27
|
+
SELECT *, ROW_NUMBER() OVER (PARTITION BY [ID CLIENTE] ORDER BY [DATE] DESC) AS [ROW]
|
|
28
|
+
FROM [CHAT MESSAGE]
|
|
29
|
+
) AS [CM] ON [CM].[ID CLIENTE] = [C].[ID] AND [CM].[ROW] = 1
|
|
30
|
+
WHERE [CM].[READ] = FALSE AND [CM].[ID SENDER] = [CM].[ID CLIENTE]
|
|
31
31
|
`);
|
|
32
32
|
return chat;
|
|
33
33
|
}
|
package/lib/cjs/db/index.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.toPgArray = exports.batchQuery = exports.query = void 0;
|
|
15
|
+
exports.pool = exports.toPgArray = exports.batchQuery = exports.query = void 0;
|
|
16
16
|
const pg_1 = __importDefault(require("pg"));
|
|
17
17
|
const { Pool } = pg_1.default;
|
|
18
18
|
const pool = new Pool({
|
|
@@ -22,6 +22,7 @@ const pool = new Pool({
|
|
|
22
22
|
host: process.env.DB_HOST,
|
|
23
23
|
port: 5432,
|
|
24
24
|
});
|
|
25
|
+
exports.pool = pool;
|
|
25
26
|
const query = (queryText, values = []) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
27
|
const client = yield pool.connect();
|
|
27
28
|
const text = getParseQuery(queryText, values);
|
package/lib/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDefaultWorkoutImage = exports.generateShareMap = exports.reduceSizeImage = exports.getLetter = exports.getNumberByLetter = exports.appendSheet = exports.writeSheet = exports.findCellByValue = exports.readSheet = exports.NOTION_DATABASES_ID = exports.notionEditPage = exports.notionAddPage = exports.notionGetDatabase = exports.notionGetUsers = exports.getCountNotificaciones = exports.chatExposed = exports.chatApi = exports.chat = exports.getExerciseTranslatedDescription = exports.useTranslation = exports.LANGUAGES = exports.translate = exports.CHANNEL_SLACK = exports.notifySlack = exports.fetchIA = exports.err = exports.sendMail = exports.toPgArray = exports.batchQuery = exports.query = exports.sleep = exports.sendNotification = void 0;
|
|
3
|
+
exports.sendWorkoutToWatch = exports.getDefaultWorkoutImage = exports.generateShareMap = exports.reduceSizeImage = exports.getLetter = exports.getNumberByLetter = exports.appendSheet = exports.writeSheet = exports.findCellByValue = exports.readSheet = exports.NOTION_DATABASES_ID = exports.notionEditPage = exports.notionAddPage = exports.notionGetDatabase = exports.notionGetUsers = exports.getCountNotificaciones = exports.chatExposed = exports.chatApi = exports.chat = exports.getExerciseTranslatedDescription = exports.useTranslation = exports.LANGUAGES = exports.translate = exports.CHANNEL_SLACK = exports.notifySlack = exports.fetchIA = exports.err = exports.sendMail = exports.pool = exports.toPgArray = exports.batchQuery = exports.query = exports.sleep = exports.sendNotification = void 0;
|
|
4
4
|
const sendNotification_1 = require("./sendNotification");
|
|
5
5
|
Object.defineProperty(exports, "sendNotification", { enumerable: true, get: function () { return sendNotification_1.sendNotification; } });
|
|
6
6
|
const sleep_1 = require("./sleep");
|
|
@@ -9,6 +9,7 @@ const db_1 = require("./db");
|
|
|
9
9
|
Object.defineProperty(exports, "query", { enumerable: true, get: function () { return db_1.query; } });
|
|
10
10
|
Object.defineProperty(exports, "batchQuery", { enumerable: true, get: function () { return db_1.batchQuery; } });
|
|
11
11
|
Object.defineProperty(exports, "toPgArray", { enumerable: true, get: function () { return db_1.toPgArray; } });
|
|
12
|
+
Object.defineProperty(exports, "pool", { enumerable: true, get: function () { return db_1.pool; } });
|
|
12
13
|
const sendMail_1 = require("./sendMail");
|
|
13
14
|
Object.defineProperty(exports, "sendMail", { enumerable: true, get: function () { return sendMail_1.sendMail; } });
|
|
14
15
|
const err_1 = require("./err");
|
|
@@ -47,3 +48,5 @@ const generateShareMap_1 = require("./image/generateShareMap");
|
|
|
47
48
|
Object.defineProperty(exports, "generateShareMap", { enumerable: true, get: function () { return generateShareMap_1.generateShareMap; } });
|
|
48
49
|
const getDefaultWorkoutImage_1 = require("./image/getDefaultWorkoutImage");
|
|
49
50
|
Object.defineProperty(exports, "getDefaultWorkoutImage", { enumerable: true, get: function () { return getDefaultWorkoutImage_1.getDefaultWorkoutImage; } });
|
|
51
|
+
const sendToWatch_1 = require("./workout/sendToWatch");
|
|
52
|
+
Object.defineProperty(exports, "sendWorkoutToWatch", { enumerable: true, get: function () { return sendToWatch_1.sendWorkoutToWatch; } });
|
|
@@ -45,72 +45,72 @@ const sendMail = ({ subject, title, body, to, link, attachments, bcc }) => __awa
|
|
|
45
45
|
});
|
|
46
46
|
exports.sendMail = sendMail;
|
|
47
47
|
function getBodyHTML(title, body, link) {
|
|
48
|
-
return `
|
|
49
|
-
<html lang="es">
|
|
50
|
-
<head>
|
|
51
|
-
<meta charset="UTF-8">
|
|
52
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
53
|
-
<title>Correo con estilo</title>
|
|
54
|
-
<style>
|
|
55
|
-
/* Estilos generales */
|
|
56
|
-
body {
|
|
57
|
-
margin: 0;
|
|
58
|
-
padding: 0;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/* Estilos específicos del botón */
|
|
62
|
-
.button {
|
|
63
|
-
display: inline-block;
|
|
64
|
-
padding: 12px 26px;
|
|
65
|
-
font-size: 17px;
|
|
66
|
-
text-align: center;
|
|
67
|
-
text-decoration: none;
|
|
68
|
-
background-color: #ea5b1b;
|
|
69
|
-
color: #ffffff !important;
|
|
70
|
-
margin: 40px 0 20px 0;
|
|
71
|
-
font-family: 'Sofia Sans', 'Roboto', sans-serif;
|
|
72
|
-
font-weight: bold;
|
|
73
|
-
border-radius: 4px;
|
|
74
|
-
border: 2px solid #ea5b1b;
|
|
75
|
-
}
|
|
76
|
-
</style>
|
|
77
|
-
</head>
|
|
78
|
-
<body style="background: #f0f0f0; padding: 0 0 40px 0">
|
|
79
|
-
<table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#f0f0f0">
|
|
80
|
-
<tr>
|
|
81
|
-
<td align="center" style="padding:0;">
|
|
82
|
-
<table role="presentation" style="width:100%;border-collapse:collapse;border-spacing:0;text-align:left;">
|
|
83
|
-
<tr>
|
|
84
|
-
<td align="center" style="padding:5px 0 0 0;background:#ea5b1b;">
|
|
85
|
-
<img src="cid:logo" alt="" width="220" style="height:auto;display:block;">
|
|
86
|
-
</td>
|
|
87
|
-
</tr>
|
|
88
|
-
<tr>
|
|
89
|
-
<td style="padding:20px 24px 0px 24px; max-width: 600px" align="center">
|
|
90
|
-
<h1 style="font-size:22px;text-align:center;margin:16px 0 6px 0;font-family:'Sofia Sans', 'Roboto', sans-serif;font-style: italic">
|
|
91
|
-
${title || ''}
|
|
92
|
-
</h1>
|
|
93
|
-
<p style="text-align:left;margin:0 0 12px 0;font-size:15px;line-height:24px;font-family:'Sofia Sans', 'Roboto', sans-serif;">
|
|
94
|
-
${body.split('\n').join('<br>')}
|
|
95
|
-
</p>
|
|
48
|
+
return `
|
|
49
|
+
<html lang="es">
|
|
50
|
+
<head>
|
|
51
|
+
<meta charset="UTF-8">
|
|
52
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
53
|
+
<title>Correo con estilo</title>
|
|
54
|
+
<style>
|
|
55
|
+
/* Estilos generales */
|
|
56
|
+
body {
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Estilos específicos del botón */
|
|
62
|
+
.button {
|
|
63
|
+
display: inline-block;
|
|
64
|
+
padding: 12px 26px;
|
|
65
|
+
font-size: 17px;
|
|
66
|
+
text-align: center;
|
|
67
|
+
text-decoration: none;
|
|
68
|
+
background-color: #ea5b1b;
|
|
69
|
+
color: #ffffff !important;
|
|
70
|
+
margin: 40px 0 20px 0;
|
|
71
|
+
font-family: 'Sofia Sans', 'Roboto', sans-serif;
|
|
72
|
+
font-weight: bold;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
border: 2px solid #ea5b1b;
|
|
75
|
+
}
|
|
76
|
+
</style>
|
|
77
|
+
</head>
|
|
78
|
+
<body style="background: #f0f0f0; padding: 0 0 40px 0">
|
|
79
|
+
<table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#f0f0f0">
|
|
80
|
+
<tr>
|
|
81
|
+
<td align="center" style="padding:0;">
|
|
82
|
+
<table role="presentation" style="width:100%;border-collapse:collapse;border-spacing:0;text-align:left;">
|
|
83
|
+
<tr>
|
|
84
|
+
<td align="center" style="padding:5px 0 0 0;background:#ea5b1b;">
|
|
85
|
+
<img src="cid:logo" alt="" width="220" style="height:auto;display:block;">
|
|
86
|
+
</td>
|
|
87
|
+
</tr>
|
|
88
|
+
<tr>
|
|
89
|
+
<td style="padding:20px 24px 0px 24px; max-width: 600px" align="center">
|
|
90
|
+
<h1 style="font-size:22px;text-align:center;margin:16px 0 6px 0;font-family:'Sofia Sans', 'Roboto', sans-serif;font-style: italic">
|
|
91
|
+
${title || ''}
|
|
92
|
+
</h1>
|
|
93
|
+
<p style="text-align:left;margin:0 0 12px 0;font-size:15px;line-height:24px;font-family:'Sofia Sans', 'Roboto', sans-serif;">
|
|
94
|
+
${body.split('\n').join('<br>')}
|
|
95
|
+
</p>
|
|
96
96
|
${link
|
|
97
|
-
? `
|
|
98
|
-
<table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#f0f0f0;">
|
|
99
|
-
<tr>
|
|
100
|
-
<td align="center" style="padding:0;">
|
|
101
|
-
<a href="${link.external ? '' : process.env.FRONTEND_URL}${link.url}" class="button" style="color:white">${link.text}</a>
|
|
102
|
-
</td>
|
|
103
|
-
</tr>
|
|
104
|
-
</table>
|
|
97
|
+
? `
|
|
98
|
+
<table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#f0f0f0;">
|
|
99
|
+
<tr>
|
|
100
|
+
<td align="center" style="padding:0;">
|
|
101
|
+
<a href="${link.external ? '' : process.env.FRONTEND_URL}${link.url}" class="button" style="color:white">${link.text}</a>
|
|
102
|
+
</td>
|
|
103
|
+
</tr>
|
|
104
|
+
</table>
|
|
105
105
|
`
|
|
106
|
-
: ''}
|
|
107
|
-
</td>
|
|
108
|
-
</tr>
|
|
109
|
-
</table>
|
|
110
|
-
</td>
|
|
111
|
-
</tr>
|
|
112
|
-
</table>
|
|
113
|
-
</body>
|
|
114
|
-
</html>
|
|
106
|
+
: ''}
|
|
107
|
+
</td>
|
|
108
|
+
</tr>
|
|
109
|
+
</table>
|
|
110
|
+
</td>
|
|
111
|
+
</tr>
|
|
112
|
+
</table>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
115
|
`;
|
|
116
116
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import postgresql from 'pg';
|
|
2
|
+
declare const pool: postgresql.Pool;
|
|
1
3
|
declare const query: (queryText: string, values?: (string | number | boolean)[]) => Promise<any>;
|
|
2
4
|
declare const toPgArray: (arr: string[]) => string;
|
|
3
5
|
declare const batchQuery: (queries: any, length?: number) => Promise<void>;
|
|
4
|
-
export { query, batchQuery, toPgArray };
|
|
6
|
+
export { query, batchQuery, toPgArray, pool };
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/db/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,IAAI,CAAC;AAI5B,QAAA,MAAM,IAAI,iBAMR,CAAC;AAEH,QAAA,MAAM,KAAK,cAAqB,MAAM,WAAU,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,iBAsB5E,CAAC;AAEF,QAAA,MAAM,SAAS,QAAS,MAAM,EAAE,WAM/B,CAAC;AA+BF,QAAA,MAAM,UAAU,YAAmB,GAAG,mCAUrC,CAAC;AAEF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
declare const generateShareMap: (image: any, idWorkout: any, options: any) => Promise<Buffer>;
|
|
2
3
|
export { generateShareMap, };
|
|
3
4
|
//# sourceMappingURL=generateShareMap.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateShareMap.d.ts","sourceRoot":"","sources":["../../../../src/image/generateShareMap.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"generateShareMap.d.ts","sourceRoot":"","sources":["../../../../src/image/generateShareMap.ts"],"names":[],"mappings":";AAcA,QAAA,MAAM,gBAAgB,+DAkBrB,CAAC;AA0HF,OAAO,EACL,gBAAgB,GACjB,CAAC"}
|
package/lib/cjs/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sendNotification } from './sendNotification';
|
|
2
2
|
import { sleep } from './sleep';
|
|
3
|
-
import { query, batchQuery, toPgArray } from './db';
|
|
3
|
+
import { query, batchQuery, toPgArray, pool } from './db';
|
|
4
4
|
import { sendMail } from './sendMail';
|
|
5
5
|
import { err } from './err';
|
|
6
6
|
import { fetchIA } from './fetch/fetchIA';
|
|
@@ -12,5 +12,6 @@ import { readSheet, findCellByValue, writeSheet, appendSheet, getNumberByLetter,
|
|
|
12
12
|
import { reduceSizeImage } from './image/reduceSizeImage';
|
|
13
13
|
import { generateShareMap } from './image/generateShareMap';
|
|
14
14
|
import { getDefaultWorkoutImage } from './image/getDefaultWorkoutImage';
|
|
15
|
-
|
|
15
|
+
import { sendWorkoutToWatch } from './workout/sendToWatch';
|
|
16
|
+
export { sendNotification, sleep, query, batchQuery, toPgArray, pool, sendMail, err, fetchIA, notifySlack, CHANNEL_SLACK, translate, LANGUAGES, useTranslation, getExerciseTranslatedDescription, chat, chatApi, chatExposed, getCountNotificaciones, notionGetUsers, notionGetDatabase, notionAddPage, notionEditPage, NOTION_DATABASES_ID, readSheet, findCellByValue, writeSheet, appendSheet, getNumberByLetter, getLetter, reduceSizeImage, generateShareMap, getDefaultWorkoutImage, sendWorkoutToWatch, };
|
|
16
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,gCAAgC,EAAE,MAAM,eAAe,CAAC;AACvG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACjH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAClH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EACL,gBAAgB,EAChB,KAAK,EACL,KAAK,EACL,UAAU,EACV,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,OAAO,EACP,WAAW,EACX,aAAa,EACb,SAAS,EACT,SAAS,EACT,cAAc,EACd,gCAAgC,EAChC,IAAI,EACJ,OAAO,EACP,WAAW,EACX,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,GACnB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare const getStructuraWorkout: (workout: any, estructura: any) => any;
|
|
2
|
+
declare const saveWorkoutEstructuraNotDone: (idWorkout: any, data: any) => Promise<void>;
|
|
3
|
+
declare const savePlantillaEstructuraNotDone: (idPlantilla: any, data: any) => Promise<void>;
|
|
4
|
+
export { getStructuraWorkout, saveWorkoutEstructuraNotDone, savePlantillaEstructuraNotDone };
|
|
5
|
+
//# sourceMappingURL=estructuraWorkout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estructuraWorkout.d.ts","sourceRoot":"","sources":["../../../../src/workout/estructuraWorkout.ts"],"names":[],"mappings":"AA2DA,QAAA,MAAM,mBAAmB,wCAiCxB,CAAC;AAmCF,QAAA,MAAM,4BAA4B,8CAkBjC,CAAC;AAEF,QAAA,MAAM,8BAA8B,gDAkBnC,CAAC;AAEF,OAAO,EAAE,mBAAmB,EAAE,4BAA4B,EAAE,8BAA8B,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const createWorkoutGarmin: (userid: any, workout: any) => Promise<{
|
|
2
|
+
workoutGarminId: any;
|
|
3
|
+
workoutGarminOwnerId: any;
|
|
4
|
+
}>;
|
|
5
|
+
declare const updateWorkoutGarmin: (userid: any, workout: any) => Promise<any>;
|
|
6
|
+
declare const deleteWorkoutGarmin: (userid: any, workout: any) => Promise<any>;
|
|
7
|
+
declare const scheduleWorkoutGarmin: (userid: any, workout: any) => Promise<any>;
|
|
8
|
+
declare const updateScheduleWorkoutGarmin: (userid: any, schedule: any, workout: any) => Promise<any>;
|
|
9
|
+
export { createWorkoutGarmin, updateWorkoutGarmin, deleteWorkoutGarmin, scheduleWorkoutGarmin, updateScheduleWorkoutGarmin };
|
|
10
|
+
//# sourceMappingURL=garmin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"garmin.d.ts","sourceRoot":"","sources":["../../../../src/workout/garmin.ts"],"names":[],"mappings":"AAkOA,QAAA,MAAM,mBAAmB;;;EAYxB,CAAC;AAEF,QAAA,MAAM,mBAAmB,6CASxB,CAAC;AAEF,QAAA,MAAM,mBAAmB,6CAMxB,CAAC;AAEF,QAAA,MAAM,qBAAqB,6CAU1B,CAAC;AAEF,QAAA,MAAM,2BAA2B,4DAWhC,CAAC;AAIF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sendToWatch.d.ts","sourceRoot":"","sources":["../../../../src/workout/sendToWatch.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,kBAAkB;;;;;;EAmBvB,CAAC;AAEF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.savePlantillaEstructuraNotDone = exports.saveWorkoutEstructuraNotDone = exports.getStructuraWorkout = void 0;
|
|
13
|
+
const db_1 = require("../db");
|
|
14
|
+
const calculateZone = (values) => {
|
|
15
|
+
const percentage = Number(values.pacePercentage);
|
|
16
|
+
if (percentage <= 70)
|
|
17
|
+
values.zone = 1;
|
|
18
|
+
else if (percentage <= 77.5)
|
|
19
|
+
values.zone = 2;
|
|
20
|
+
else if (percentage <= 87.5)
|
|
21
|
+
values.zone = 3;
|
|
22
|
+
else if (percentage <= 95)
|
|
23
|
+
values.zone = 4;
|
|
24
|
+
else if (percentage <= 100)
|
|
25
|
+
values.zone = 5;
|
|
26
|
+
};
|
|
27
|
+
const parseWorkoutEstructuraNotDone = ({ estructura }) => {
|
|
28
|
+
let idSerie = 0;
|
|
29
|
+
const rows = [];
|
|
30
|
+
estructura.forEach((row) => {
|
|
31
|
+
if (row.number) {
|
|
32
|
+
for (let iNumber = 0; iNumber < Number(row.number); iNumber++) {
|
|
33
|
+
row.steps.forEach((step) => {
|
|
34
|
+
rows.push(Object.assign(Object.assign({}, step), { idSerie, indexSerie: iNumber }));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
idSerie++;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
rows.push(row);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return rows;
|
|
44
|
+
};
|
|
45
|
+
const getStructuraWorkout = (workout, estructura) => {
|
|
46
|
+
if (workout.done)
|
|
47
|
+
return estructura;
|
|
48
|
+
else {
|
|
49
|
+
const rows = [];
|
|
50
|
+
for (const s of estructura) {
|
|
51
|
+
if (s.pacePercentage)
|
|
52
|
+
calculateZone(s);
|
|
53
|
+
if (!s.idSerie) {
|
|
54
|
+
rows.push(s);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const index = rows.findIndex((r) => r.idSerie && Number(r.idSerie) === Number(s.idSerie));
|
|
58
|
+
if (index !== -1) {
|
|
59
|
+
if (!rows[index].steps)
|
|
60
|
+
rows[index].steps = [];
|
|
61
|
+
rows[index].steps.push(s);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
rows.push({
|
|
65
|
+
idSerie: s.idSerie,
|
|
66
|
+
steps: [s],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return rows.map((r) => {
|
|
72
|
+
if (!r.idSerie)
|
|
73
|
+
return r;
|
|
74
|
+
else {
|
|
75
|
+
if (!r.steps)
|
|
76
|
+
return r;
|
|
77
|
+
const filteredSteps = r.steps.filter((s) => Number(s.indexSerie) === 0);
|
|
78
|
+
return {
|
|
79
|
+
steps: filteredSteps,
|
|
80
|
+
number: r.steps.length / filteredSteps.length,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
exports.getStructuraWorkout = getStructuraWorkout;
|
|
87
|
+
const insertWorkoutEstructura = (idWorkout, row, index) => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
yield (0, db_1.query)(`
|
|
89
|
+
INSERT INTO [WORKOUT STRUCTURE] ([ID WORKOUT], [INDEX], [ID SERIE], [INDEX SERIE], [TYPE], [TYPE METRIC 1],
|
|
90
|
+
[VALUE METRIC 1], [TYPE METRIC 2], [VALUE METRIC 2], [DESNIVEL PLANNED])
|
|
91
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
92
|
+
`, [
|
|
93
|
+
idWorkout,
|
|
94
|
+
index,
|
|
95
|
+
row.idSerie,
|
|
96
|
+
row.indexSerie,
|
|
97
|
+
row.type,
|
|
98
|
+
row.typeMetric1,
|
|
99
|
+
row.valueMetric1,
|
|
100
|
+
row.typeMetric2,
|
|
101
|
+
row.valueMetric2,
|
|
102
|
+
row.desnivelPlanned || null,
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
const insertPlantillaEstructura = (idPlantilla, row, index) => __awaiter(void 0, void 0, void 0, function* () {
|
|
106
|
+
yield (0, db_1.query)(`
|
|
107
|
+
INSERT INTO [PLANTILLAS WORKOUT STRUCTURE] ([ID WORKOUT], [INDEX], [ID SERIE], [INDEX SERIE], [TYPE], [TYPE METRIC 1],
|
|
108
|
+
[VALUE METRIC 1], [PACE PERCENTAGE], [DESNIVEL PLANNED])
|
|
109
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
110
|
+
`, [idPlantilla, index, row.idSerie, row.indexSerie, row.type, row.typeMetric1, row.valueMetric1, row.pacePercentage, row.desnivelPlanned || null]);
|
|
111
|
+
});
|
|
112
|
+
const saveWorkoutEstructuraNotDone = (idWorkout, data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
113
|
+
const rows = parseWorkoutEstructuraNotDone(data);
|
|
114
|
+
yield (0, db_1.query)('DELETE FROM [WORKOUT STRUCTURE] WHERE [ID WORKOUT] = ?;', [idWorkout]);
|
|
115
|
+
let index = 0;
|
|
116
|
+
for (const row of rows) {
|
|
117
|
+
if (row.number && row.steps) {
|
|
118
|
+
for (let iNumber = 0; iNumber < row.number; iNumber++) {
|
|
119
|
+
for (const step of row.steps) {
|
|
120
|
+
yield insertWorkoutEstructura(idWorkout, step, index);
|
|
121
|
+
index++;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
yield insertWorkoutEstructura(idWorkout, row, index);
|
|
127
|
+
index++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
exports.saveWorkoutEstructuraNotDone = saveWorkoutEstructuraNotDone;
|
|
132
|
+
const savePlantillaEstructuraNotDone = (idPlantilla, data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
+
const rows = parseWorkoutEstructuraNotDone(data);
|
|
134
|
+
yield (0, db_1.query)('DELETE FROM [PLANTILLAS WORKOUT STRUCTURE] WHERE [ID WORKOUT] = ?;', [idPlantilla]);
|
|
135
|
+
let index = 0;
|
|
136
|
+
for (const row of rows) {
|
|
137
|
+
if (row.number && row.steps) {
|
|
138
|
+
for (let iNumber = 0; iNumber < row.number; iNumber++) {
|
|
139
|
+
for (const step of row.steps) {
|
|
140
|
+
yield insertPlantillaEstructura(idPlantilla, step, index);
|
|
141
|
+
index++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
yield insertPlantillaEstructura(idPlantilla, row, index);
|
|
147
|
+
index++;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
exports.savePlantillaEstructuraNotDone = savePlantillaEstructuraNotDone;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.updateScheduleWorkoutGarmin = exports.scheduleWorkoutGarmin = exports.deleteWorkoutGarmin = exports.updateWorkoutGarmin = exports.createWorkoutGarmin = void 0;
|
|
16
|
+
const db_1 = require("../db");
|
|
17
|
+
const common_1 = require("@runnerpro/common");
|
|
18
|
+
const estructuraWorkout_1 = require("./estructuraWorkout");
|
|
19
|
+
const axios_1 = __importDefault(require("axios"));
|
|
20
|
+
const oauth_signature_1 = __importDefault(require("oauth-signature"));
|
|
21
|
+
const garminOauthActivity = ({ url, httpMethod, userid, params = {} }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
const [tokens] = yield (0, db_1.query)(`SELECT * FROM [CLIENTE APLICACIONES GARMIN] WHERE [ID] =
|
|
23
|
+
(SELECT [ID APLICATION] FROM [CLIENTE APLICACIONES] WHERE [ID CLIENTE] = ? AND [TIPO APLICATION] = ?)`, [userid, common_1.TIPO_APLICACION.GARMIN]);
|
|
24
|
+
const nonce = 15456;
|
|
25
|
+
const timestamp = Date.now();
|
|
26
|
+
const method = 'HMAC-SHA1';
|
|
27
|
+
const version = '1.0';
|
|
28
|
+
const parameters = {
|
|
29
|
+
oauth_consumer_key: process.env.GARMIN_CONSUMER_KEY,
|
|
30
|
+
oauth_nonce: nonce,
|
|
31
|
+
oauth_signature_method: method,
|
|
32
|
+
oauth_timestamp: timestamp,
|
|
33
|
+
oauth_token: tokens.accessToken,
|
|
34
|
+
oauth_version: version,
|
|
35
|
+
};
|
|
36
|
+
const encodedSignature = oauth_signature_1.default.generate(httpMethod, url, parameters, process.env.GARMIN_CONSUMER_SECRET, tokens.accessTokenSecret);
|
|
37
|
+
const oauth = [['oauth_signature', encodedSignature], ...Object.entries(parameters)].map(([k, v]) => `${k}="${v}"`).join(', ');
|
|
38
|
+
let data;
|
|
39
|
+
if (httpMethod === 'POST') {
|
|
40
|
+
data = yield axios_1.default.post(url, params, {
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `OAuth ${oauth}`,
|
|
43
|
+
Accept: '*/*',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else if (httpMethod === 'PUT') {
|
|
48
|
+
data = yield axios_1.default.put(url, params, {
|
|
49
|
+
headers: {
|
|
50
|
+
Authorization: `OAuth ${oauth}`,
|
|
51
|
+
Accept: '*/*',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else if (httpMethod === 'DELETE') {
|
|
56
|
+
data = yield axios_1.default.delete(url, {
|
|
57
|
+
headers: {
|
|
58
|
+
Authorization: `OAuth ${oauth}`,
|
|
59
|
+
Accept: '*/*',
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return data.data;
|
|
64
|
+
});
|
|
65
|
+
const secondsPerKmToMetersPerSecond = (secondsPerKm) => {
|
|
66
|
+
return Math.round((1000 / secondsPerKm) * 100) / 100;
|
|
67
|
+
};
|
|
68
|
+
const typeTranslateWorkoutGarmin = (type) => {
|
|
69
|
+
if (type === common_1.WORKOUT_TYPE.FUERZA)
|
|
70
|
+
return 'STRENGTH_TRAINING';
|
|
71
|
+
if (type === common_1.WORKOUT_TYPE.SWIM)
|
|
72
|
+
return 'LAP_SWIMMING';
|
|
73
|
+
if (type === common_1.WORKOUT_TYPE.BIKE)
|
|
74
|
+
return 'CYCLING';
|
|
75
|
+
if (type === common_1.WORKOUT_TYPE.WALK)
|
|
76
|
+
return 'WALKING';
|
|
77
|
+
else
|
|
78
|
+
return 'RUNNING';
|
|
79
|
+
};
|
|
80
|
+
const intensityTranslateWorkoutGarmin = (intensity) => {
|
|
81
|
+
if (Number(intensity) === Number(common_1.STEP_TYPE.CALENTAMIENTO))
|
|
82
|
+
return 'WARMUP';
|
|
83
|
+
if ([Number(common_1.STEP_TYPE.CORRER), Number(common_1.STEP_TYPE.BICICLETA), Number(common_1.STEP_TYPE.NADAR)].includes(Number(intensity)))
|
|
84
|
+
return 'INTERVAL';
|
|
85
|
+
if (Number(intensity) === Number(common_1.STEP_TYPE.DESCANSO))
|
|
86
|
+
return 'REST';
|
|
87
|
+
if (Number(intensity) === Number(common_1.STEP_TYPE.ENFRIAMIENTO))
|
|
88
|
+
return 'COOLDOWN';
|
|
89
|
+
if (Number(intensity) === Number(common_1.STEP_TYPE.CAMINAR))
|
|
90
|
+
return 'RECOVERY';
|
|
91
|
+
};
|
|
92
|
+
const translateWorkoutGarmin = (workout) => {
|
|
93
|
+
const workoutGarmin = {
|
|
94
|
+
workoutName: workout.titlePreferredLanguage || workout.title,
|
|
95
|
+
description: workout.descriptionPreferredLanguage || workout.description,
|
|
96
|
+
sport: typeTranslateWorkoutGarmin(workout.type),
|
|
97
|
+
workoutProvider: 'RUNNERPRO',
|
|
98
|
+
workoutSourceId: 'RUNNERPRO',
|
|
99
|
+
steps: [],
|
|
100
|
+
};
|
|
101
|
+
if (workout.type === common_1.WORKOUT_TYPE.FUERZA) {
|
|
102
|
+
workoutGarmin.steps.push({
|
|
103
|
+
type: 'WorkoutStep',
|
|
104
|
+
stepOrder: 1,
|
|
105
|
+
intensity: 'ACTIVE',
|
|
106
|
+
durationType: 'OPEN',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else if (!workout.estructura) {
|
|
110
|
+
if (!workout.distancePlanned || !workout.durationPlanned)
|
|
111
|
+
return null;
|
|
112
|
+
const targetType = getWorkoutGarminTargetType(workout, null);
|
|
113
|
+
const targetValue = getWorkoutGarminTargetValue(workout, null);
|
|
114
|
+
workoutGarmin.steps.push({
|
|
115
|
+
type: 'WorkoutStep',
|
|
116
|
+
stepOrder: 1,
|
|
117
|
+
intensity: 'ACTIVE',
|
|
118
|
+
durationType: 'DISTANCE',
|
|
119
|
+
durationValue: workout.distancePlanned,
|
|
120
|
+
durationValueType: 'METER',
|
|
121
|
+
targetType,
|
|
122
|
+
targetValue: targetValue.value,
|
|
123
|
+
targetValueLow: targetValue.low,
|
|
124
|
+
targetValueHigh: targetValue.high,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const estructura = (0, estructuraWorkout_1.getStructuraWorkout)(Object.assign(Object.assign({}, workout), { done: false }), workout.estructura);
|
|
129
|
+
let stepOrder = 0;
|
|
130
|
+
estructura.forEach((row) => {
|
|
131
|
+
stepOrder++;
|
|
132
|
+
if (row.number) {
|
|
133
|
+
workoutGarmin.steps.push({
|
|
134
|
+
type: 'WorkoutRepeatStep',
|
|
135
|
+
stepOrder,
|
|
136
|
+
repeatType: 'REPEAT_UNTIL_STEPS_CMPLT',
|
|
137
|
+
repeatValue: row.number,
|
|
138
|
+
steps: row.steps.map((s) => {
|
|
139
|
+
stepOrder++;
|
|
140
|
+
return getWorkoutGarminStep(workout, s, stepOrder);
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
workoutGarmin.steps.push(getWorkoutGarminStep(workout, row, stepOrder));
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return workoutGarmin;
|
|
150
|
+
};
|
|
151
|
+
const getWorkoutGarminStep = (workout, row, stepOrder) => {
|
|
152
|
+
const targetType = getWorkoutGarminTargetType(workout, row);
|
|
153
|
+
const targetValue = getWorkoutGarminTargetValue(workout, row);
|
|
154
|
+
return {
|
|
155
|
+
type: 'WorkoutStep',
|
|
156
|
+
stepOrder,
|
|
157
|
+
intensity: intensityTranslateWorkoutGarmin(row.type),
|
|
158
|
+
durationType: Number(row.typeMetric1) === 1 ? 'DISTANCE' : 'TIME',
|
|
159
|
+
durationValue: row.valueMetric1,
|
|
160
|
+
targetType,
|
|
161
|
+
targetValue: targetValue.value,
|
|
162
|
+
targetValueLow: targetValue.low,
|
|
163
|
+
targetValueHigh: targetValue.high,
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
const getWorkoutGarminTargetType = (workout, row) => {
|
|
167
|
+
// Si es natación o descanso no tiene tipo
|
|
168
|
+
if (workout.type === common_1.WORKOUT_TYPE.SWIM || (row && Number(row.type) === 3))
|
|
169
|
+
return null;
|
|
170
|
+
// Si es entrenamiento de bicicleta y no tiene estructura o si tiene estructura y el tipo es potencia
|
|
171
|
+
// No se puede añadir potencia ya que el valor que hay que dar es una zona, no absoluto
|
|
172
|
+
// if ((workout.type === WORKOUT_TYPE.BIKE && !row) || (row && Number(row.typeMetric2) === 3)) return 'POWER';
|
|
173
|
+
return 'PACE';
|
|
174
|
+
};
|
|
175
|
+
const getWorkoutGarminTargetValue = (workout, row) => {
|
|
176
|
+
let value;
|
|
177
|
+
if (!row) {
|
|
178
|
+
// Si es entrenamiento de bicicleta y tiene potencia -> value = potencia
|
|
179
|
+
// if (workout.type === WORKOUT_TYPE.BIKE && workout.powerPlanned) {
|
|
180
|
+
// value = Number(workout.powerPlanned);
|
|
181
|
+
// } else {
|
|
182
|
+
// El resto calcular el ritmo
|
|
183
|
+
// No se puede añadir potencia ya que el valor que hay que dar es una zona, no absoluto
|
|
184
|
+
value = Number(workout.distancePlanned) / Number(workout.durationPlanned);
|
|
185
|
+
// }
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// Si es descanso -> value = null
|
|
189
|
+
if (Number(row.type) === 3)
|
|
190
|
+
value = null;
|
|
191
|
+
// Si es de tipo potencia -> value = valor potencia
|
|
192
|
+
else if (Number(row.typeMetric2) === 3)
|
|
193
|
+
value = Number(row.valueMetric2);
|
|
194
|
+
// El resto de casos se calcula el ritmo
|
|
195
|
+
else
|
|
196
|
+
value = secondsPerKmToMetersPerSecond(row.valueMetric2);
|
|
197
|
+
}
|
|
198
|
+
if (!value) {
|
|
199
|
+
return {
|
|
200
|
+
value: null,
|
|
201
|
+
low: null,
|
|
202
|
+
high: null,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
return {
|
|
207
|
+
value,
|
|
208
|
+
low: value - value * 0.02,
|
|
209
|
+
high: value + value * 0.02,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
const createWorkoutGarmin = (userid, workout) => __awaiter(void 0, void 0, void 0, function* () {
|
|
214
|
+
const workoutGarmin = translateWorkoutGarmin(workout);
|
|
215
|
+
const data = yield garminOauthActivity({
|
|
216
|
+
url: 'https://apis.garmin.com/training-api/workout',
|
|
217
|
+
userid,
|
|
218
|
+
params: workoutGarmin,
|
|
219
|
+
httpMethod: 'POST',
|
|
220
|
+
});
|
|
221
|
+
yield (0, db_1.query)('UPDATE [WORKOUT] SET [WORKOUT ID GARMIN] = ?, [WORKOUT OWNER GARMIN] = ? WHERE [ID] = ?', [data.workoutId, data.ownerId, workout.id]);
|
|
222
|
+
return { workoutGarminId: data.workoutId, workoutGarminOwnerId: data.ownerId };
|
|
223
|
+
});
|
|
224
|
+
exports.createWorkoutGarmin = createWorkoutGarmin;
|
|
225
|
+
const updateWorkoutGarmin = (userid, workout) => {
|
|
226
|
+
const workoutGarmin = translateWorkoutGarmin(workout);
|
|
227
|
+
return garminOauthActivity({
|
|
228
|
+
url: 'https://apis.garmin.com/training-api/workout/' + workout.workoutIdGarmin,
|
|
229
|
+
userid,
|
|
230
|
+
params: workoutGarmin,
|
|
231
|
+
httpMethod: 'PUT',
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
exports.updateWorkoutGarmin = updateWorkoutGarmin;
|
|
235
|
+
const deleteWorkoutGarmin = (userid, workout) => {
|
|
236
|
+
return garminOauthActivity({
|
|
237
|
+
url: 'https://apis.garmin.com/training-api/workout/' + workout.workoutIdGarmin,
|
|
238
|
+
userid,
|
|
239
|
+
httpMethod: 'DELETE',
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
exports.deleteWorkoutGarmin = deleteWorkoutGarmin;
|
|
243
|
+
const scheduleWorkoutGarmin = (userid, workout) => {
|
|
244
|
+
return garminOauthActivity({
|
|
245
|
+
url: 'https://apis.garmin.com/training-api/schedule',
|
|
246
|
+
userid,
|
|
247
|
+
httpMethod: 'POST',
|
|
248
|
+
params: {
|
|
249
|
+
workoutId: workout.workoutIdGarmin,
|
|
250
|
+
date: workout.date,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
exports.scheduleWorkoutGarmin = scheduleWorkoutGarmin;
|
|
255
|
+
const updateScheduleWorkoutGarmin = (userid, schedule, workout) => {
|
|
256
|
+
return garminOauthActivity({
|
|
257
|
+
url: 'https://apis.garmin.com/training-api/schedule/' + schedule.scheduleId,
|
|
258
|
+
userid,
|
|
259
|
+
httpMethod: 'PUT',
|
|
260
|
+
params: {
|
|
261
|
+
scheduleId: schedule.scheduleId,
|
|
262
|
+
workoutId: schedule.workoutId,
|
|
263
|
+
date: workout.date,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
exports.updateScheduleWorkoutGarmin = updateScheduleWorkoutGarmin;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.sendWorkoutToWatch = void 0;
|
|
16
|
+
const db_1 = require("../db");
|
|
17
|
+
const garmin_1 = require("./garmin");
|
|
18
|
+
const common_1 = require("@runnerpro/common");
|
|
19
|
+
const moment_1 = __importDefault(require("moment"));
|
|
20
|
+
const sendWorkoutToWatch = (clientId, workoutId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
+
const [aplicacion] = yield (0, db_1.query)('SELECT [TIPO APLICATION] FROM [CLIENTE APLICACIONES] WHERE [ID CLIENTE] = ? AND [TIPO APLICATION] = ?', [clientId, common_1.TIPO_APLICACION.GARMIN]);
|
|
22
|
+
if (!aplicacion)
|
|
23
|
+
return { error: 'No tiene Garmin vinculado' };
|
|
24
|
+
// Obtener el workout
|
|
25
|
+
const [workout] = yield (0, db_1.query)('SELECT * FROM [WORKOUT] WHERE [ID] = ? AND [ID CLIENTE] = ?', [workoutId, clientId]);
|
|
26
|
+
if (!workout)
|
|
27
|
+
throw new Error('Workout no encontrado');
|
|
28
|
+
const estructura = yield (0, db_1.query)('SELECT * FROM [WORKOUT STRUCTURE] WHERE [ID WORKOUT] = ? ORDER BY [INDEX]', [workoutId]);
|
|
29
|
+
if (estructura.length > 0)
|
|
30
|
+
workout.estructura = estructura;
|
|
31
|
+
// Llamar a la función que envía el workout al Garmin
|
|
32
|
+
const { workoutGarminId } = yield (0, garmin_1.createWorkoutGarmin)(clientId, workout);
|
|
33
|
+
yield (0, garmin_1.scheduleWorkoutGarmin)(clientId, { date: (0, moment_1.default)(workout.date).format('YYYY-MM-DD'), workoutId: workoutGarminId });
|
|
34
|
+
return { success: true };
|
|
35
|
+
});
|
|
36
|
+
exports.sendWorkoutToWatch = sendWorkoutToWatch;
|
package/package.json
CHANGED
|
@@ -1,72 +1,73 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@runnerpro/backend",
|
|
3
|
-
"version": "1.6.
|
|
4
|
-
"description": "A collection of common backend functions",
|
|
5
|
-
"exports": {
|
|
6
|
-
".": "./lib/cjs/index.js"
|
|
7
|
-
},
|
|
8
|
-
"types": "./lib/cjs/types/index.d.ts",
|
|
9
|
-
"main": "./lib/cjs/index.js",
|
|
10
|
-
"files": [
|
|
11
|
-
"lib/**/*"
|
|
12
|
-
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"clean": "del-cli ./lib",
|
|
15
|
-
"build": "npm run clean && tsc -p ./configs/tsconfig.cjs.json",
|
|
16
|
-
"deploy": "npm run build && npm publish",
|
|
17
|
-
"semantic-release": "semantic-release",
|
|
18
|
-
"lint": "eslint --ext .ts --ignore-path .gitignore .",
|
|
19
|
-
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
|
|
20
|
-
"prepare": "husky",
|
|
21
|
-
"test": "jest"
|
|
22
|
-
},
|
|
23
|
-
"release": {
|
|
24
|
-
"branches": [
|
|
25
|
-
"main"
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
"publishConfig": {
|
|
29
|
-
"access": "public"
|
|
30
|
-
},
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "https://gitlab.com/runner-pro/runnerpro-backend.git"
|
|
34
|
-
},
|
|
35
|
-
"author": "Runner Pro",
|
|
36
|
-
"license": "MIT",
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"@eslint/js": "^9.6.0",
|
|
39
|
-
"@types/eslint__js": "^8.42.3",
|
|
40
|
-
"@types/nodemailer": "^6.4.15",
|
|
41
|
-
"@types/pg": "^8.11.3",
|
|
42
|
-
"del-cli": "5.1.0",
|
|
43
|
-
"eslint": "^8.57.0",
|
|
44
|
-
"eslint-config-prettier": "^9.1.0",
|
|
45
|
-
"eslint-plugin-exception-handling": "^1.0.2",
|
|
46
|
-
"eslint-plugin-sonarjs": "^1.0.3",
|
|
47
|
-
"husky": "^9.0.11",
|
|
48
|
-
"jest": "^29.7.0",
|
|
49
|
-
"semantic-release": "23.0.2",
|
|
50
|
-
"ts-node": "10.9.2",
|
|
51
|
-
"typescript": "5.3.3",
|
|
52
|
-
"typescript-eslint": "^7.15.0"
|
|
53
|
-
},
|
|
54
|
-
"peerDependencies": {
|
|
55
|
-
"@napi-rs/canvas": "^0.1.53",
|
|
56
|
-
"@runnerpro/common": "^1.5.1",
|
|
57
|
-
"axios": "^1.6.7",
|
|
58
|
-
"image-size": "^1.0.2",
|
|
59
|
-
"jimp": "^0.22.10",
|
|
60
|
-
"nodemailer": "6.9.9",
|
|
61
|
-
"pg": "8.11.3",
|
|
62
|
-
"uuidv4": "^6.2.13"
|
|
63
|
-
},
|
|
64
|
-
"dependencies": {
|
|
65
|
-
"@google-cloud/translate": "^8.3.0",
|
|
66
|
-
"@notionhq/client": "^2.2.15",
|
|
67
|
-
"exifr": "^7.1.3",
|
|
68
|
-
"googleapis": "^144.0.0",
|
|
69
|
-
"multer": "^1.4.5-lts.1",
|
|
70
|
-
"socket.io": "^4.7.2"
|
|
71
|
-
|
|
72
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@runnerpro/backend",
|
|
3
|
+
"version": "1.6.10",
|
|
4
|
+
"description": "A collection of common backend functions",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./lib/cjs/index.js"
|
|
7
|
+
},
|
|
8
|
+
"types": "./lib/cjs/types/index.d.ts",
|
|
9
|
+
"main": "./lib/cjs/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"lib/**/*"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"clean": "del-cli ./lib",
|
|
15
|
+
"build": "npm run clean && tsc -p ./configs/tsconfig.cjs.json",
|
|
16
|
+
"deploy": "npm run build && npm publish",
|
|
17
|
+
"semantic-release": "semantic-release",
|
|
18
|
+
"lint": "eslint --ext .ts --ignore-path .gitignore .",
|
|
19
|
+
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
|
|
20
|
+
"prepare": "husky",
|
|
21
|
+
"test": "jest"
|
|
22
|
+
},
|
|
23
|
+
"release": {
|
|
24
|
+
"branches": [
|
|
25
|
+
"main"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://gitlab.com/runner-pro/runnerpro-backend.git"
|
|
34
|
+
},
|
|
35
|
+
"author": "Runner Pro",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@eslint/js": "^9.6.0",
|
|
39
|
+
"@types/eslint__js": "^8.42.3",
|
|
40
|
+
"@types/nodemailer": "^6.4.15",
|
|
41
|
+
"@types/pg": "^8.11.3",
|
|
42
|
+
"del-cli": "5.1.0",
|
|
43
|
+
"eslint": "^8.57.0",
|
|
44
|
+
"eslint-config-prettier": "^9.1.0",
|
|
45
|
+
"eslint-plugin-exception-handling": "^1.0.2",
|
|
46
|
+
"eslint-plugin-sonarjs": "^1.0.3",
|
|
47
|
+
"husky": "^9.0.11",
|
|
48
|
+
"jest": "^29.7.0",
|
|
49
|
+
"semantic-release": "23.0.2",
|
|
50
|
+
"ts-node": "10.9.2",
|
|
51
|
+
"typescript": "5.3.3",
|
|
52
|
+
"typescript-eslint": "^7.15.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@napi-rs/canvas": "^0.1.53",
|
|
56
|
+
"@runnerpro/common": "^1.5.1",
|
|
57
|
+
"axios": "^1.6.7",
|
|
58
|
+
"image-size": "^1.0.2",
|
|
59
|
+
"jimp": "^0.22.10",
|
|
60
|
+
"nodemailer": "6.9.9",
|
|
61
|
+
"pg": "8.11.3",
|
|
62
|
+
"uuidv4": "^6.2.13"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@google-cloud/translate": "^8.3.0",
|
|
66
|
+
"@notionhq/client": "^2.2.15",
|
|
67
|
+
"exifr": "^7.1.3",
|
|
68
|
+
"googleapis": "^144.0.0",
|
|
69
|
+
"multer": "^1.4.5-lts.1",
|
|
70
|
+
"socket.io": "^4.7.2",
|
|
71
|
+
"oauth-signature": "1.5.0"
|
|
72
|
+
}
|
|
73
|
+
}
|