@vue-skuilder/common 0.1.25 → 0.1.27
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/dist/course-data.d.ts +24 -7
- package/dist/course-data.d.ts.map +1 -1
- package/dist/course-data.js.map +1 -1
- package/dist/elo.d.ts +17 -4
- package/dist/elo.d.ts.map +1 -1
- package/dist/elo.js +28 -4
- package/dist/elo.js.map +1 -1
- package/dist/elo.mjs +28 -4
- package/package.json +2 -2
- package/src/course-data.ts +24 -7
- package/src/elo.ts +29 -4
package/dist/course-data.d.ts
CHANGED
|
@@ -21,13 +21,26 @@ export type Performance = number | TaggedPerformance;
|
|
|
21
21
|
* Questions that exercise multiple skills (e.g., spelling with multiple GPCs)
|
|
22
22
|
* can provide individual scores per tag for granular ELO updates.
|
|
23
23
|
*
|
|
24
|
+
* Tags can have scores (for exercise tags) or `null` (for count-only exposure tags).
|
|
25
|
+
* Count-only tags increment their count but maintain a sentinel score of -1,
|
|
26
|
+
* making them easily identifiable and preventing them from polluting real ELO data.
|
|
27
|
+
*
|
|
24
28
|
* @example
|
|
25
29
|
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
26
30
|
* {
|
|
27
|
-
* _global: 0.67,
|
|
28
|
-
* '
|
|
29
|
-
* '
|
|
30
|
-
* '
|
|
31
|
+
* _global: 0.67, // 2/3 correct, used for SRS and global ELO
|
|
32
|
+
* 'gpc:exercise:c-K': 0, // incorrect
|
|
33
|
+
* 'gpc:exercise:a-AE': 1, // correct
|
|
34
|
+
* 'gpc:exercise:t-T': 1, // correct
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // WhoSaidThat card exercising 'sh' while exposing distractors
|
|
39
|
+
* {
|
|
40
|
+
* _global: 1.0,
|
|
41
|
+
* 'gpc:exercise:sh-SH': 1.0, // exercised and correct
|
|
42
|
+
* 'gpc:expose:s-S': null, // count-only exposure (no score)
|
|
43
|
+
* 'gpc:expose:ch-CH': null, // count-only exposure (no score)
|
|
31
44
|
* }
|
|
32
45
|
*/
|
|
33
46
|
export interface TaggedPerformance {
|
|
@@ -38,11 +51,15 @@ export interface TaggedPerformance {
|
|
|
38
51
|
*/
|
|
39
52
|
_global: number;
|
|
40
53
|
/**
|
|
41
|
-
* Per-tag scores
|
|
54
|
+
* Per-tag scores or count-only markers.
|
|
55
|
+
*
|
|
56
|
+
* - **Number (0-1)**: Tag is exercised; score updates via ELO formula
|
|
57
|
+
* - **null**: Count-only tag (e.g., exposure); increments count, score stays -1 (sentinel)
|
|
58
|
+
*
|
|
42
59
|
* Tags not present on the card will be created dynamically.
|
|
43
|
-
*
|
|
60
|
+
* Count-only tags (null) do not update card ELO.
|
|
44
61
|
*/
|
|
45
|
-
[tag: string]: number;
|
|
62
|
+
[tag: string]: number | null;
|
|
46
63
|
}
|
|
47
64
|
/**
|
|
48
65
|
* Type guard to check if performance is structured (TaggedPerformance).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"course-data.d.ts","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAW,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAKtD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,EAGhB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAA;CAAE,GACrD,eAAe,CAsFjB;AAED;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAErD
|
|
1
|
+
{"version":3,"file":"course-data.d.ts","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAW,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAKtD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,EAGhB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAA;CAAE,GACrD,eAAe,CAsFjB;AAED;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;;;OAQG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,IAAI,iBAAiB,CAE1E"}
|
package/dist/course-data.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"course-data.js","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"course-data.js","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,OAAO,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,UAAkB,EAClB,KAAgB;AAChB,iBAAiB;AACjB,8DAA8D;AAC9D,IAAS,EACT,MAAc,EACd,KAAe,EACf,OAAsD;IAEtD,MAAM,WAAW,GAAG,UAAU,CAAC,kBAAkB,CAAC;QAChD,MAAM,EAAE,UAAU;QAClB,SAAS,EAAE,KAAK,CAAC,IAAI;KACtB,CAAC,CAAC;IAEH,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM;SAClC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,uDAAuD;QACvD,MAAM,IAAI,GAAoB;YAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAChB,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,CAAC;IAC1E,CAAC,CAAC;SACD,MAAM,CAAC;QACN;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,SAAS,CAAC,KAAK;SACtB;KACF,CAAC,CAAC;IAEL,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACvB,gBAAgB,CAAC,IAAI,CAAC;gBACpB,IAAI,EAAE,SAAS,CAAC,EAAE;gBAClB,IAAI,EAAE,SAAS,CAAC,KAAK;aACtB,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACvB,gBAAgB,CAAC,IAAI,CAAC;gBACpB,IAAI,EAAE,SAAS,CAAC,EAAE;gBAClB,IAAI,EAAE,SAAS,CAAC,KAAK;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,WAAW,GAAqD,EAAE,CAAC;IACzE,MAAM,OAAO,GAAoB;QAC/B,MAAM,EAAE,QAAQ;QAChB,IAAI,EAAE,EAAE;QACR,OAAO,EAAE,OAAO,CAAC,gBAAgB;QACjC,YAAY,EAAE,WAAW;KAC1B,CAAC;IAEF,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAC1B,CAAC;IAED,gBAAgB,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QACpC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE;IACF,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACjC,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9E,OAAO,CAAC,YAAY,GAAG,WAAW,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,MAAM;SACT,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAChB,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,CAAC;IAC1E,CAAC,CAAC;SACD,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,OAAO,OAAO,CAAC;AACjB,CAAC;AAiED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAc;IAChD,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,SAAS,IAAI,CAAC,CAAC;AAC/D,CAAC"}
|
package/dist/elo.d.ts
CHANGED
|
@@ -47,18 +47,31 @@ export declare function adjustCourseScores(aElo: Eloish, bElo: Eloish, userScore
|
|
|
47
47
|
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
48
48
|
* this function allows different scores per tag for granular skill tracking.
|
|
49
49
|
*
|
|
50
|
+
* Tags can be scored (number 0-1) or count-only (null). Count-only tags are
|
|
51
|
+
* useful for exposure tracking (e.g., gpc:expose:*) where we only care about
|
|
52
|
+
* "how many times has the user seen this?" without measuring performance.
|
|
53
|
+
*
|
|
50
54
|
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
51
55
|
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
52
|
-
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
56
|
+
* @param taggedPerformance - Object with _global score and per-tag scores/null
|
|
53
57
|
* @returns Updated user and card ELOs
|
|
54
58
|
*
|
|
55
59
|
* @example
|
|
56
60
|
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
57
61
|
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
58
62
|
* _global: 0.67,
|
|
59
|
-
* '
|
|
60
|
-
* '
|
|
61
|
-
* '
|
|
63
|
+
* 'gpc:exercise:c-K': 0,
|
|
64
|
+
* 'gpc:exercise:a-AE': 1,
|
|
65
|
+
* 'gpc:exercise:t-T': 1,
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // WhoSaidThat - exercise target, expose distractors (count-only)
|
|
70
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
71
|
+
* _global: 1.0,
|
|
72
|
+
* 'gpc:exercise:sh-SH': 1.0,
|
|
73
|
+
* 'gpc:expose:s-S': null, // count-only
|
|
74
|
+
* 'gpc:expose:ch-CH': null, // count-only
|
|
62
75
|
* });
|
|
63
76
|
*/
|
|
64
77
|
export declare function adjustCourseScoresPerTag(aElo: Eloish, bElo: Eloish, taggedPerformance: TaggedPerformance): {
|
package/dist/elo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elo.d.ts","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,qBAAa,SAAS;IACD,CAAC,EAAE,MAAM;gBAAT,CAAC,GAAE,MAAW;IAEjC,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAG3B,UAAU,IAAI,MAAM;IAIpB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAGzC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAGxE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;IACF,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,wBAAgB,cAAc,IAAI,SAAS,CAS1C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAS/C;AACD,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CASpD;AACD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CA+B9D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,SAAS,CAMtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IACR,UAAU,EAAE,OAAO,CAAC;CACrB,GACA;IACD,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CA+BA;AAqCD
|
|
1
|
+
{"version":3,"file":"elo.d.ts","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,qBAAa,SAAS;IACD,CAAC,EAAE,MAAM;gBAAT,CAAC,GAAE,MAAW;IAEjC,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAG3B,UAAU,IAAI,MAAM;IAIpB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAGzC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAGxE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;IACF,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,wBAAgB,cAAc,IAAI,SAAS,CAS1C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAS/C;AACD,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CASpD;AACD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CA+B9D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,SAAS,CAMtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IACR,UAAU,EAAE,OAAO,CAAC;CACrB,GACA;IACD,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CA+BA;AAqCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,iBAAiB,GACnC;IACD,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CAyDA"}
|
package/dist/elo.js
CHANGED
|
@@ -166,18 +166,31 @@ function adjustScores(userElo, cardElo, userScore) {
|
|
|
166
166
|
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
167
167
|
* this function allows different scores per tag for granular skill tracking.
|
|
168
168
|
*
|
|
169
|
+
* Tags can be scored (number 0-1) or count-only (null). Count-only tags are
|
|
170
|
+
* useful for exposure tracking (e.g., gpc:expose:*) where we only care about
|
|
171
|
+
* "how many times has the user seen this?" without measuring performance.
|
|
172
|
+
*
|
|
169
173
|
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
170
174
|
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
171
|
-
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
175
|
+
* @param taggedPerformance - Object with _global score and per-tag scores/null
|
|
172
176
|
* @returns Updated user and card ELOs
|
|
173
177
|
*
|
|
174
178
|
* @example
|
|
175
179
|
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
176
180
|
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
177
181
|
* _global: 0.67,
|
|
178
|
-
* '
|
|
179
|
-
* '
|
|
180
|
-
* '
|
|
182
|
+
* 'gpc:exercise:c-K': 0,
|
|
183
|
+
* 'gpc:exercise:a-AE': 1,
|
|
184
|
+
* 'gpc:exercise:t-T': 1,
|
|
185
|
+
* });
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* // WhoSaidThat - exercise target, expose distractors (count-only)
|
|
189
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
190
|
+
* _global: 1.0,
|
|
191
|
+
* 'gpc:exercise:sh-SH': 1.0,
|
|
192
|
+
* 'gpc:expose:s-S': null, // count-only
|
|
193
|
+
* 'gpc:expose:ch-CH': null, // count-only
|
|
181
194
|
* });
|
|
182
195
|
*/
|
|
183
196
|
function adjustCourseScoresPerTag(aElo, bElo, taggedPerformance) {
|
|
@@ -191,6 +204,17 @@ function adjustCourseScoresPerTag(aElo, bElo, taggedPerformance) {
|
|
|
191
204
|
for (const [key, tagScore] of Object.entries(taggedPerformance)) {
|
|
192
205
|
if (key === '_global')
|
|
193
206
|
continue;
|
|
207
|
+
// Count-only tag (exposure tracking): increment count, use -1 sentinel score
|
|
208
|
+
if (tagScore === null) {
|
|
209
|
+
userElo.tags[key] = userElo.tags[key] ?? { count: 0, score: -1 };
|
|
210
|
+
userElo.tags[key] = {
|
|
211
|
+
...userElo.tags[key],
|
|
212
|
+
count: userElo.tags[key].count + 1,
|
|
213
|
+
score: -1, // Sentinel: clearly not a real ELO score
|
|
214
|
+
};
|
|
215
|
+
// Skip card ELO update for count-only tags
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
194
218
|
if (typeof tagScore !== 'number' || tagScore < 0 || tagScore > 1) {
|
|
195
219
|
throw new Error(`ELO tag score for '${key}' must be between 0 and 1 - received ${tagScore}`);
|
|
196
220
|
}
|
package/dist/elo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elo.js","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"elo.js","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"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"}
|
package/dist/elo.mjs
CHANGED
|
@@ -155,18 +155,31 @@ function adjustScores(userElo, cardElo, userScore) {
|
|
|
155
155
|
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
156
156
|
* this function allows different scores per tag for granular skill tracking.
|
|
157
157
|
*
|
|
158
|
+
* Tags can be scored (number 0-1) or count-only (null). Count-only tags are
|
|
159
|
+
* useful for exposure tracking (e.g., gpc:expose:*) where we only care about
|
|
160
|
+
* "how many times has the user seen this?" without measuring performance.
|
|
161
|
+
*
|
|
158
162
|
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
159
163
|
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
160
|
-
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
164
|
+
* @param taggedPerformance - Object with _global score and per-tag scores/null
|
|
161
165
|
* @returns Updated user and card ELOs
|
|
162
166
|
*
|
|
163
167
|
* @example
|
|
164
168
|
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
165
169
|
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
166
170
|
* _global: 0.67,
|
|
167
|
-
* '
|
|
168
|
-
* '
|
|
169
|
-
* '
|
|
171
|
+
* 'gpc:exercise:c-K': 0,
|
|
172
|
+
* 'gpc:exercise:a-AE': 1,
|
|
173
|
+
* 'gpc:exercise:t-T': 1,
|
|
174
|
+
* });
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* // WhoSaidThat - exercise target, expose distractors (count-only)
|
|
178
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
179
|
+
* _global: 1.0,
|
|
180
|
+
* 'gpc:exercise:sh-SH': 1.0,
|
|
181
|
+
* 'gpc:expose:s-S': null, // count-only
|
|
182
|
+
* 'gpc:expose:ch-CH': null, // count-only
|
|
170
183
|
* });
|
|
171
184
|
*/
|
|
172
185
|
export function adjustCourseScoresPerTag(aElo, bElo, taggedPerformance) {
|
|
@@ -180,6 +193,17 @@ export function adjustCourseScoresPerTag(aElo, bElo, taggedPerformance) {
|
|
|
180
193
|
for (const [key, tagScore] of Object.entries(taggedPerformance)) {
|
|
181
194
|
if (key === '_global')
|
|
182
195
|
continue;
|
|
196
|
+
// Count-only tag (exposure tracking): increment count, use -1 sentinel score
|
|
197
|
+
if (tagScore === null) {
|
|
198
|
+
userElo.tags[key] = userElo.tags[key] ?? { count: 0, score: -1 };
|
|
199
|
+
userElo.tags[key] = {
|
|
200
|
+
...userElo.tags[key],
|
|
201
|
+
count: userElo.tags[key].count + 1,
|
|
202
|
+
score: -1, // Sentinel: clearly not a real ELO score
|
|
203
|
+
};
|
|
204
|
+
// Skip card ELO update for count-only tags
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
183
207
|
if (typeof tagScore !== 'number' || tagScore < 0 || tagScore > 1) {
|
|
184
208
|
throw new Error(`ELO tag score for '${key}' must be between 0 and 1 - received ${tagScore}`);
|
|
185
209
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.27",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"module": "dist/index.mjs",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"zod": "^3.23.8",
|
|
37
37
|
"zod-to-json-schema": "^3.23.5"
|
|
38
38
|
},
|
|
39
|
-
"stableVersion": "0.1.
|
|
39
|
+
"stableVersion": "0.1.27"
|
|
40
40
|
}
|
package/src/course-data.ts
CHANGED
|
@@ -124,13 +124,26 @@ export type Performance = number | TaggedPerformance;
|
|
|
124
124
|
* Questions that exercise multiple skills (e.g., spelling with multiple GPCs)
|
|
125
125
|
* can provide individual scores per tag for granular ELO updates.
|
|
126
126
|
*
|
|
127
|
+
* Tags can have scores (for exercise tags) or `null` (for count-only exposure tags).
|
|
128
|
+
* Count-only tags increment their count but maintain a sentinel score of -1,
|
|
129
|
+
* making them easily identifiable and preventing them from polluting real ELO data.
|
|
130
|
+
*
|
|
127
131
|
* @example
|
|
128
132
|
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
129
133
|
* {
|
|
130
|
-
* _global: 0.67,
|
|
131
|
-
* '
|
|
132
|
-
* '
|
|
133
|
-
* '
|
|
134
|
+
* _global: 0.67, // 2/3 correct, used for SRS and global ELO
|
|
135
|
+
* 'gpc:exercise:c-K': 0, // incorrect
|
|
136
|
+
* 'gpc:exercise:a-AE': 1, // correct
|
|
137
|
+
* 'gpc:exercise:t-T': 1, // correct
|
|
138
|
+
* }
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* // WhoSaidThat card exercising 'sh' while exposing distractors
|
|
142
|
+
* {
|
|
143
|
+
* _global: 1.0,
|
|
144
|
+
* 'gpc:exercise:sh-SH': 1.0, // exercised and correct
|
|
145
|
+
* 'gpc:expose:s-S': null, // count-only exposure (no score)
|
|
146
|
+
* 'gpc:expose:ch-CH': null, // count-only exposure (no score)
|
|
134
147
|
* }
|
|
135
148
|
*/
|
|
136
149
|
export interface TaggedPerformance {
|
|
@@ -142,11 +155,15 @@ export interface TaggedPerformance {
|
|
|
142
155
|
_global: number;
|
|
143
156
|
|
|
144
157
|
/**
|
|
145
|
-
* Per-tag scores
|
|
158
|
+
* Per-tag scores or count-only markers.
|
|
159
|
+
*
|
|
160
|
+
* - **Number (0-1)**: Tag is exercised; score updates via ELO formula
|
|
161
|
+
* - **null**: Count-only tag (e.g., exposure); increments count, score stays -1 (sentinel)
|
|
162
|
+
*
|
|
146
163
|
* Tags not present on the card will be created dynamically.
|
|
147
|
-
*
|
|
164
|
+
* Count-only tags (null) do not update card ELO.
|
|
148
165
|
*/
|
|
149
|
-
[tag: string]: number;
|
|
166
|
+
[tag: string]: number | null;
|
|
150
167
|
}
|
|
151
168
|
|
|
152
169
|
/**
|
package/src/elo.ts
CHANGED
|
@@ -200,18 +200,31 @@ function adjustScores(
|
|
|
200
200
|
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
201
201
|
* this function allows different scores per tag for granular skill tracking.
|
|
202
202
|
*
|
|
203
|
+
* Tags can be scored (number 0-1) or count-only (null). Count-only tags are
|
|
204
|
+
* useful for exposure tracking (e.g., gpc:expose:*) where we only care about
|
|
205
|
+
* "how many times has the user seen this?" without measuring performance.
|
|
206
|
+
*
|
|
203
207
|
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
204
208
|
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
205
|
-
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
209
|
+
* @param taggedPerformance - Object with _global score and per-tag scores/null
|
|
206
210
|
* @returns Updated user and card ELOs
|
|
207
211
|
*
|
|
208
212
|
* @example
|
|
209
213
|
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
210
214
|
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
211
215
|
* _global: 0.67,
|
|
212
|
-
* '
|
|
213
|
-
* '
|
|
214
|
-
* '
|
|
216
|
+
* 'gpc:exercise:c-K': 0,
|
|
217
|
+
* 'gpc:exercise:a-AE': 1,
|
|
218
|
+
* 'gpc:exercise:t-T': 1,
|
|
219
|
+
* });
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* // WhoSaidThat - exercise target, expose distractors (count-only)
|
|
223
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
224
|
+
* _global: 1.0,
|
|
225
|
+
* 'gpc:exercise:sh-SH': 1.0,
|
|
226
|
+
* 'gpc:expose:s-S': null, // count-only
|
|
227
|
+
* 'gpc:expose:ch-CH': null, // count-only
|
|
215
228
|
* });
|
|
216
229
|
*/
|
|
217
230
|
export function adjustCourseScoresPerTag(
|
|
@@ -235,6 +248,18 @@ export function adjustCourseScoresPerTag(
|
|
|
235
248
|
for (const [key, tagScore] of Object.entries(taggedPerformance)) {
|
|
236
249
|
if (key === '_global') continue;
|
|
237
250
|
|
|
251
|
+
// Count-only tag (exposure tracking): increment count, use -1 sentinel score
|
|
252
|
+
if (tagScore === null) {
|
|
253
|
+
userElo.tags[key] = userElo.tags[key] ?? { count: 0, score: -1 };
|
|
254
|
+
userElo.tags[key] = {
|
|
255
|
+
...userElo.tags[key],
|
|
256
|
+
count: userElo.tags[key].count + 1,
|
|
257
|
+
score: -1, // Sentinel: clearly not a real ELO score
|
|
258
|
+
};
|
|
259
|
+
// Skip card ELO update for count-only tags
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
238
263
|
if (typeof tagScore !== 'number' || tagScore < 0 || tagScore > 1) {
|
|
239
264
|
throw new Error(`ELO tag score for '${key}' must be between 0 and 1 - received ${tagScore}`);
|
|
240
265
|
}
|