@runnerpro/backend 1.33.0 → 1.33.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"garmin.d.ts","sourceRoot":"","sources":["../../../../src/workout/garmin.ts"],"names":[],"mappings":"AAoBA,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED,UAAU,aAAa;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AA4FD,QAAA,MAAM,sBAAsB,iCAyD3B,CAAC;AA+EF,QAAA,MAAM,mBAAmB;;;EAWxB,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,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,GAC5B,CAAC"}
1
+ {"version":3,"file":"garmin.d.ts","sourceRoot":"","sources":["../../../../src/workout/garmin.ts"],"names":[],"mappings":"AAoBA,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED,UAAU,aAAa;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAgHD,QAAA,MAAM,sBAAsB,iCA4D3B,CAAC;AAmFF,QAAA,MAAM,mBAAmB;;;EAcxB,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,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,GAC5B,CAAC"}
@@ -1 +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"}
1
+ {"version":3,"file":"sendToWatch.d.ts","sourceRoot":"","sources":["../../../../src/workout/sendToWatch.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,kBAAkB;;;;;;EAuBvB,CAAC;AAEF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -65,22 +65,40 @@ const garminOauthActivity = ({ url, httpMethod, userid, params = {} }) => __awai
65
65
  const secondsPerKmToMetersPerSecond = (secondsPerKm) => {
66
66
  return Math.round((1000 / secondsPerKm) * 100) / 100;
67
67
  };
68
+ /**
69
+ * Devuelve la metrica planificada como numero, o null si no la hay.
70
+ *
71
+ * OJO: Postgres devuelve las columnas `numeric` como TEXTO, asi que una distancia
72
+ * de 0 llega como la cadena "0", que en JS es truthy. Los entrenos de bici y
73
+ * natacion se planifican por tiempo y guardan `DISTANCE PLANNED` = 0: comparar
74
+ * en crudo hacia colar entrenos de 0 metros a Garmin.
75
+ */
76
+ const metricaPlanificada = (valor) => {
77
+ const n = Number(valor);
78
+ return Number.isFinite(n) && n > 0 ? n : null;
79
+ };
68
80
  const getWorkoutGarminDuration = (workout) => {
81
+ const distancia = metricaPlanificada(workout.distancePlanned);
82
+ const duracion = metricaPlanificada(workout.durationPlanned);
69
83
  const rangePlanning = (0, common_1.getWorkoutRangePlanning)({
70
84
  workoutType: workout.type,
71
85
  distancePlanned: workout.distancePlanned,
72
86
  durationPlanned: workout.durationPlanned,
73
87
  zonePlanned: workout.zonePlanned,
74
88
  });
75
- if (rangePlanning.fixedMetric === common_1.WORKOUT_FIXED_METRIC.TIME) {
89
+ // `fixedMetric` solo se calcula cuando hay rango de ritmo, y eso solo pasa en
90
+ // CORRER/COMPETICION. Para bici y natacion siempre es null, asi que sin este
91
+ // fallback caian SIEMPRE por distancia (y se enviaba un paso de 0 metros).
92
+ const prefiereTiempo = rangePlanning.fixedMetric === common_1.WORKOUT_FIXED_METRIC.TIME;
93
+ if (duracion && (prefiereTiempo || !distancia)) {
76
94
  return {
77
95
  type: 'TIME',
78
- value: Number(workout.durationPlanned),
96
+ value: duracion,
79
97
  };
80
98
  }
81
99
  return {
82
100
  type: 'DISTANCE',
83
- value: Number(workout.distancePlanned),
101
+ value: distancia,
84
102
  valueType: 'METER',
85
103
  };
86
104
  };
@@ -126,7 +144,10 @@ const translateWorkoutGarmin = (workout) => {
126
144
  });
127
145
  }
128
146
  else if (!workout.estructura) {
129
- if (!workout.distancePlanned || !workout.durationPlanned)
147
+ // Basta con una de las dos: bici/natacion se planifican por tiempo y correr
148
+ // por distancia. Exigir las dos (comparando ademas en crudo) era lo que
149
+ // dejaba fuera a los entrenos por tiempo.
150
+ if (!metricaPlanificada(workout.distancePlanned) && !metricaPlanificada(workout.durationPlanned))
130
151
  return null;
131
152
  const targetType = getWorkoutGarminTargetType(workout, null);
132
153
  const targetValue = getWorkoutGarminTargetValue(workout, null);
@@ -203,7 +224,11 @@ const getWorkoutGarminTargetValue = (workout, row) => {
203
224
  // } else {
204
225
  // El resto calcular el ritmo
205
226
  // No se puede añadir potencia ya que el valor que hay que dar es una zona, no absoluto
206
- value = Number(workout.distancePlanned) / Number(workout.durationPlanned);
227
+ const distancia = metricaPlanificada(workout.distancePlanned);
228
+ const duracion = metricaPlanificada(workout.durationPlanned);
229
+ // Sin las dos metricas no hay ritmo objetivo: el paso va sin target (que es
230
+ // lo que ya se hace hoy en natacion y Garmin acepta).
231
+ value = distancia && duracion ? distancia / duracion : null;
207
232
  paceRange = (0, common_1.getWorkoutRangePlanning)({
208
233
  workoutType: workout.type,
209
234
  distancePlanned: workout.distancePlanned,
@@ -247,6 +272,10 @@ const getWorkoutGarminTargetValue = (workout, row) => {
247
272
  };
248
273
  const createWorkoutGarmin = (userid, workout) => __awaiter(void 0, void 0, void 0, function* () {
249
274
  const workoutGarmin = translateWorkoutGarmin(workout);
275
+ // Sin payload no se manda nada: antes se hacia el POST con body null y Garmin
276
+ // respondia un error opaco.
277
+ if (!workoutGarmin)
278
+ throw new Error(`Workout ${workout.id} no es traducible a Garmin (sin estructura y sin distancia ni duracion)`);
250
279
  const data = yield garminOauthActivity({
251
280
  url: 'https://apis.garmin.com/training-api/workout',
252
281
  userid,
@@ -28,6 +28,10 @@ const sendWorkoutToWatch = (clientId, workoutId) => __awaiter(void 0, void 0, vo
28
28
  const estructura = yield (0, db_1.query)('SELECT * FROM [WORKOUT STRUCTURE] WHERE [ID WORKOUT] = ? ORDER BY [INDEX]', [workoutId]);
29
29
  if (estructura.length > 0)
30
30
  workout.estructura = estructura;
31
+ // Si el entreno no se puede traducir, se avisa con un 400 legible en vez de
32
+ // dejar que el POST a Garmin reviente con un 500 opaco.
33
+ if (!(0, garmin_1.translateWorkoutGarmin)(workout))
34
+ return { error: 'Este entrenamiento no se puede enviar al reloj' };
31
35
  // Llamar a la función que envía el workout al Garmin
32
36
  const { workoutGarminId } = yield (0, garmin_1.createWorkoutGarmin)(clientId, workout);
33
37
  yield (0, garmin_1.scheduleWorkoutGarmin)(clientId, { date: (0, moment_1.default)(workout.date).format('YYYY-MM-DD'), workoutGarminId });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runnerpro/backend",
3
- "version": "1.33.0",
3
+ "version": "1.33.1",
4
4
  "description": "A collection of common backend functions",
5
5
  "exports": {
6
6
  ".": "./lib/cjs/index.js"