@dgpholdings/greatoak-shared 1.2.20 → 1.2.22

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.
@@ -119,6 +119,8 @@ export type TExercise = {
119
119
  thumbnailBlurHash?: string;
120
120
  instructionsHtml: string;
121
121
  importantTipsHtml: string;
122
+ popularityIndex: number;
123
+ isFavorite?: boolean;
122
124
  };
123
125
  export type TBodyPartExercises = Record<TBodyPart, TExercise[]>;
124
126
  export type TApiCreateOrUpdateExerciseReq = {
@@ -1,7 +1,6 @@
1
1
  export { toNumber } from "./number.util";
2
2
  export { mmssToSecs, isUserAllowedToUpdate, getDaysAndHoursDifference, } from "./time.util";
3
3
  export { countryToCurrencyCode } from "./billing.utils";
4
- export { calculateExerciseScore } from "./scoring.utils";
5
4
  export { isDefined, isDefinedNumber } from "./isDefined.utils";
6
5
  export { slugifyText } from "./slugify.util";
7
6
  export { toError } from "./toError.util";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateTotalVolume = exports.calculateExerciseScoreV2 = exports.NOOP = exports.isEmail = exports.isAnonymousEmail = exports.maskEmail = exports.generatePlanCode = exports.toError = exports.slugifyText = exports.isDefinedNumber = exports.isDefined = exports.calculateExerciseScore = exports.countryToCurrencyCode = exports.getDaysAndHoursDifference = exports.isUserAllowedToUpdate = exports.mmssToSecs = exports.toNumber = void 0;
3
+ exports.calculateTotalVolume = exports.calculateExerciseScoreV2 = exports.NOOP = exports.isEmail = exports.isAnonymousEmail = exports.maskEmail = exports.generatePlanCode = exports.toError = exports.slugifyText = exports.isDefinedNumber = exports.isDefined = exports.countryToCurrencyCode = exports.getDaysAndHoursDifference = exports.isUserAllowedToUpdate = exports.mmssToSecs = exports.toNumber = void 0;
4
4
  var number_util_1 = require("./number.util");
5
5
  Object.defineProperty(exports, "toNumber", { enumerable: true, get: function () { return number_util_1.toNumber; } });
6
6
  var time_util_1 = require("./time.util");
@@ -9,8 +9,6 @@ Object.defineProperty(exports, "isUserAllowedToUpdate", { enumerable: true, get:
9
9
  Object.defineProperty(exports, "getDaysAndHoursDifference", { enumerable: true, get: function () { return time_util_1.getDaysAndHoursDifference; } });
10
10
  var billing_utils_1 = require("./billing.utils");
11
11
  Object.defineProperty(exports, "countryToCurrencyCode", { enumerable: true, get: function () { return billing_utils_1.countryToCurrencyCode; } });
12
- var scoring_utils_1 = require("./scoring.utils");
13
- Object.defineProperty(exports, "calculateExerciseScore", { enumerable: true, get: function () { return scoring_utils_1.calculateExerciseScore; } });
14
12
  var isDefined_utils_1 = require("./isDefined.utils");
15
13
  Object.defineProperty(exports, "isDefined", { enumerable: true, get: function () { return isDefined_utils_1.isDefined; } });
16
14
  Object.defineProperty(exports, "isDefinedNumber", { enumerable: true, get: function () { return isDefined_utils_1.isDefinedNumber; } });
@@ -55,14 +55,15 @@ interface IRawRecord {
55
55
  * @param parsedSets Cleaned sets (from parseRecords) — only completed sets
56
56
  * @param rawRecords Original TRecord[] — needed for completion count (includes skipped)
57
57
  * @param timingGuardrails Exercise's guardrails — for rest period validation
58
+ * @param isStrictTimingModeScoring If false, ignores the rest discipline penalty.
58
59
  * @returns { score: 0–100, breakdown: { completion, consistency, effortAdequacy, restDiscipline } }
59
60
  *
60
61
  * @example
61
- * const { score, breakdown } = calculateQualityScore(parsed, raw, guardrails);
62
+ * const { score, breakdown } = calculateQualityScore(parsed, raw, guardrails, false);
62
63
  * // score: 81
63
- * // breakdown: { completion: 100, consistency: 75, effortAdequacy: 80, restDiscipline: 73 }
64
+ * // breakdown: { completion: 100, consistency: 75, effortAdequacy: 80, restDiscipline: 65 }
64
65
  */
65
- export declare function calculateQualityScore(parsedSets: IParsedSet[], rawRecords: IRawRecord[], timingGuardrails?: ITimingGuardrails): {
66
+ export declare function calculateQualityScore(parsedSets: IParsedSet[], rawRecords: IRawRecord[], timingGuardrails?: ITimingGuardrails, isStrictTimingModeScoring?: boolean): {
66
67
  score: number;
67
68
  breakdown: IQualityBreakdown;
68
69
  };
@@ -13,14 +13,15 @@ const helpers_1 = require("./helpers");
13
13
  * @param parsedSets Cleaned sets (from parseRecords) — only completed sets
14
14
  * @param rawRecords Original TRecord[] — needed for completion count (includes skipped)
15
15
  * @param timingGuardrails Exercise's guardrails — for rest period validation
16
+ * @param isStrictTimingModeScoring If false, ignores the rest discipline penalty.
16
17
  * @returns { score: 0–100, breakdown: { completion, consistency, effortAdequacy, restDiscipline } }
17
18
  *
18
19
  * @example
19
- * const { score, breakdown } = calculateQualityScore(parsed, raw, guardrails);
20
+ * const { score, breakdown } = calculateQualityScore(parsed, raw, guardrails, false);
20
21
  * // score: 81
21
- * // breakdown: { completion: 100, consistency: 75, effortAdequacy: 80, restDiscipline: 73 }
22
+ * // breakdown: { completion: 100, consistency: 75, effortAdequacy: 80, restDiscipline: 65 }
22
23
  */
23
- function calculateQualityScore(parsedSets, rawRecords, timingGuardrails) {
24
+ function calculateQualityScore(parsedSets, rawRecords, timingGuardrails, isStrictTimingModeScoring = false) {
24
25
  // Edge case: no records at all
25
26
  if (rawRecords.length === 0) {
26
27
  return {
@@ -50,7 +51,7 @@ function calculateQualityScore(parsedSets, rawRecords, timingGuardrails) {
50
51
  const completion = calculateCompletion(parsedSets, rawRecords);
51
52
  const consistency = calculateConsistency(parsedSets);
52
53
  const effortAdequacy = calculateEffortAdequacy(rawRecords);
53
- const restDiscipline = calculateRestDiscipline(parsedSets, timingGuardrails);
54
+ const restDiscipline = isStrictTimingModeScoring ? calculateRestDiscipline(parsedSets, timingGuardrails) : constants_1.REST_NO_DATA_SCORE; // 65 by default if not strict
54
55
  const score = Math.round(completion * constants_1.QUALITY_WEIGHTS.completion +
55
56
  consistency * constants_1.QUALITY_WEIGHTS.consistency +
56
57
  effortAdequacy * constants_1.QUALITY_WEIGHTS.effortAdequacy +
@@ -267,11 +268,11 @@ function scoreAgainstRange(value, range) {
267
268
  *
268
269
  * Uses the exercise's timingGuardrails.restPeriods:
269
270
  * optimalRange [min, max] → 100 points
270
- * within [minimum, maximum] → 60–100 (proportional to how close to optimal)
271
- * outside [minimum, maximum] → 30 points
271
+ * within [minimum, maximum] → 70–100 (proportional to how close to optimal)
272
+ * outside [minimum, maximum] → 50 points (Reduced penalty so it doesn't tank the score too much)
272
273
  *
273
274
  * Only scores sets that HAVE rest data (last set often doesn't).
274
- * If no valid rest data exists at all → neutral 65.
275
+ * If no valid rest data exists at all → neutral 70.
275
276
  *
276
277
  * The stressRestBonus from timingGuardrails is applied as a final multiplier:
277
278
  * exercises where proper rest is especially important (heavy compounds)
@@ -282,7 +283,7 @@ function calculateRestDiscipline(parsedSets, timingGuardrails) {
282
283
  // Gather sets that have rest data (non-null)
283
284
  const setsWithRest = parsedSets.filter((s) => s.restDurationSecs !== null);
284
285
  if (setsWithRest.length === 0)
285
- return constants_1.REST_NO_DATA_SCORE;
286
+ return 70; // Increased from REST_NO_DATA_SCORE (which was likely lower)
286
287
  // Extract rest period bounds from guardrails
287
288
  const restConfig = timingGuardrails === null || timingGuardrails === void 0 ? void 0 : timingGuardrails.restPeriods;
288
289
  const optimalMin = (_b = (_a = restConfig === null || restConfig === void 0 ? void 0 : restConfig.optimalRange) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ((_c = restConfig === null || restConfig === void 0 ? void 0 : restConfig.typical) !== null && _c !== void 0 ? _c : constants_1.FALLBACK_REST_SECS) * 0.75;
@@ -294,11 +295,12 @@ function calculateRestDiscipline(parsedSets, timingGuardrails) {
294
295
  const rest = set.restDurationSecs; // Guaranteed non-null by filter
295
296
  if (rest >= optimalMin && rest <= optimalMax) {
296
297
  // Within optimal range → perfect score
297
- setScores.push(constants_1.REST_OPTIMAL_SCORE);
298
+ setScores.push(100); // REST_OPTIMAL_SCORE
298
299
  }
299
300
  else if (rest >= acceptableMin && rest <= acceptableMax) {
300
301
  // Within acceptable range but not optimal
301
- // Score proportionally based on how close to optimal
302
+ // Score proportionally based on how close to optimal, base is 70 now
303
+ const baseAcceptableScore = 70;
302
304
  let ratio;
303
305
  if (rest < optimalMin) {
304
306
  // Between acceptable min and optimal min
@@ -308,12 +310,12 @@ function calculateRestDiscipline(parsedSets, timingGuardrails) {
308
310
  // Between optimal max and acceptable max
309
311
  ratio = (acceptableMax - rest) / (acceptableMax - optimalMax);
310
312
  }
311
- setScores.push(constants_1.REST_ACCEPTABLE_BASE +
312
- ratio * (constants_1.REST_OPTIMAL_SCORE - constants_1.REST_ACCEPTABLE_BASE));
313
+ setScores.push(baseAcceptableScore +
314
+ ratio * (100 - baseAcceptableScore));
313
315
  }
314
316
  else {
315
- // Outside even the acceptable range
316
- setScores.push(constants_1.REST_OUTSIDE_SCORE);
317
+ // Outside even the acceptable range, reduced penalty to 50
318
+ setScores.push(50);
317
319
  }
318
320
  }
319
321
  // Average rest scores
@@ -184,8 +184,8 @@ export declare const EFFORT_NO_DATA_SCORE = 70;
184
184
  /** Score when rest is within the optimal range */
185
185
  export declare const REST_OPTIMAL_SCORE = 100;
186
186
  /** Score when rest is within acceptable (min–max) but not optimal */
187
- export declare const REST_ACCEPTABLE_BASE = 60;
187
+ export declare const REST_ACCEPTABLE_BASE = 70;
188
188
  /** Score when rest is completely outside the acceptable range */
189
- export declare const REST_OUTSIDE_SCORE = 30;
189
+ export declare const REST_OUTSIDE_SCORE = 50;
190
190
  /** Default rest discipline score when no valid rest data exists */
191
- export declare const REST_NO_DATA_SCORE = 65;
191
+ export declare const REST_NO_DATA_SCORE = 75;
@@ -220,8 +220,8 @@ exports.EFFORT_NO_DATA_SCORE = 70;
220
220
  /** Score when rest is within the optimal range */
221
221
  exports.REST_OPTIMAL_SCORE = 100;
222
222
  /** Score when rest is within acceptable (min–max) but not optimal */
223
- exports.REST_ACCEPTABLE_BASE = 60;
223
+ exports.REST_ACCEPTABLE_BASE = 70;
224
224
  /** Score when rest is completely outside the acceptable range */
225
- exports.REST_OUTSIDE_SCORE = 30;
225
+ exports.REST_OUTSIDE_SCORE = 50;
226
226
  /** Default rest discipline score when no valid rest data exists */
227
- exports.REST_NO_DATA_SCORE = 65;
227
+ exports.REST_NO_DATA_SCORE = 75;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dgpholdings/greatoak-shared",
3
- "version": "1.2.20",
3
+ "version": "1.2.22",
4
4
  "description": "Shared TypeScript types and utilities for @dgpholdings projects",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",