@shaxpir/duiduidui-models 1.9.31 → 1.10.0
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/models/Condition.d.ts +44 -1
- package/dist/models/Condition.js +122 -0
- package/dist/models/Phrase.d.ts +4 -0
- package/dist/models/RecordType.d.ts +50 -0
- package/dist/models/RecordType.js +79 -0
- package/dist/models/Term.d.ts +6 -2
- package/dist/models/Term.js +20 -3
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/util/ConditionMatcher.d.ts +3 -0
- package/dist/util/ConditionMatcher.js +21 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CompactDateTime } from "@shaxpir/shaxpir-common";
|
|
2
|
+
import { RecordType } from "./RecordType";
|
|
2
3
|
export interface BaseCondition {
|
|
3
4
|
type: string;
|
|
4
5
|
}
|
|
@@ -46,7 +47,15 @@ export interface ComponentCondition extends BaseCondition {
|
|
|
46
47
|
components: string[];
|
|
47
48
|
mode: 'any' | 'all';
|
|
48
49
|
}
|
|
49
|
-
export
|
|
50
|
+
export interface RecordTypeCondition extends BaseCondition {
|
|
51
|
+
type: 'record_type';
|
|
52
|
+
recordType: RecordType;
|
|
53
|
+
}
|
|
54
|
+
export interface ExcludeRecordTypeCondition extends BaseCondition {
|
|
55
|
+
type: 'exclude_record_type';
|
|
56
|
+
recordType: RecordType;
|
|
57
|
+
}
|
|
58
|
+
export type AnyCondition = TagCondition | GradeCondition | ThetaCondition | TemporalCondition | CountCondition | StarredCondition | DifficultyCondition | HasTermCondition | ComponentCondition | RecordTypeCondition | ExcludeRecordTypeCondition;
|
|
50
59
|
export interface Conditions {
|
|
51
60
|
all?: AnyCondition[];
|
|
52
61
|
any?: AnyCondition[];
|
|
@@ -87,6 +96,15 @@ export declare const Condition: {
|
|
|
87
96
|
hasComponent: (component: string) => ComponentCondition;
|
|
88
97
|
hasAnyComponent: (components: string[]) => ComponentCondition;
|
|
89
98
|
hasAllComponents: (components: string[]) => ComponentCondition;
|
|
99
|
+
recordType: (recordType: RecordType) => RecordTypeCondition;
|
|
100
|
+
excludeRecordType: (recordType: RecordType) => ExcludeRecordTypeCondition;
|
|
101
|
+
strokes: () => RecordTypeCondition;
|
|
102
|
+
radicals: () => RecordTypeCondition;
|
|
103
|
+
characters: () => RecordTypeCondition;
|
|
104
|
+
words: () => RecordTypeCondition;
|
|
105
|
+
phrases: () => RecordTypeCondition;
|
|
106
|
+
patterns: () => RecordTypeCondition;
|
|
107
|
+
sentences: () => RecordTypeCondition;
|
|
90
108
|
/**
|
|
91
109
|
* Check if starred condition is required (in 'all' section)
|
|
92
110
|
*/
|
|
@@ -128,6 +146,31 @@ export declare const Condition: {
|
|
|
128
146
|
* Returns an array of error messages, empty if valid
|
|
129
147
|
*/
|
|
130
148
|
validate: (conditions?: Conditions) => string[];
|
|
149
|
+
/**
|
|
150
|
+
* Extract included record types from conditions.
|
|
151
|
+
* Returns null if no record type conditions are specified (meaning all types are included).
|
|
152
|
+
* Returns an array of included types if record_type conditions are in 'all' or 'any'.
|
|
153
|
+
*/
|
|
154
|
+
getIncludedRecordTypes: (conditions?: Conditions) => RecordType[] | null;
|
|
155
|
+
/**
|
|
156
|
+
* Extract excluded record types from conditions.
|
|
157
|
+
* Returns an array of types to exclude based on exclude_record_type conditions in 'all'
|
|
158
|
+
* or record_type conditions in 'none'.
|
|
159
|
+
*/
|
|
160
|
+
getExcludedRecordTypes: (conditions?: Conditions) => RecordType[];
|
|
161
|
+
/**
|
|
162
|
+
* Check if a specific record type is required (in 'all' section)
|
|
163
|
+
*/
|
|
164
|
+
requiresRecordType: (conditions: Conditions | undefined, recordType: RecordType) => boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Check if a specific record type is excluded
|
|
167
|
+
*/
|
|
168
|
+
excludesRecordType: (conditions: Conditions | undefined, recordType: RecordType) => boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Get the effective record types to query based on conditions.
|
|
171
|
+
* Returns the set of types that should be included in a query.
|
|
172
|
+
*/
|
|
173
|
+
getEffectiveRecordTypes: (conditions?: Conditions) => RecordType[];
|
|
131
174
|
hasStarredInAll: (filters?: ConditionFilters) => boolean;
|
|
132
175
|
hasStarredInAny: (filters?: ConditionFilters) => boolean;
|
|
133
176
|
hasStarredInNone: (filters?: ConditionFilters) => boolean;
|
package/dist/models/Condition.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Condition = void 0;
|
|
4
|
+
const RecordType_1 = require("./RecordType");
|
|
4
5
|
// Factory functions for creating conditions
|
|
5
6
|
exports.Condition = {
|
|
6
7
|
// Tag conditions
|
|
@@ -45,6 +46,17 @@ exports.Condition = {
|
|
|
45
46
|
hasComponent: (component) => ({ type: 'component', components: [component], mode: 'any' }),
|
|
46
47
|
hasAnyComponent: (components) => ({ type: 'component', components, mode: 'any' }),
|
|
47
48
|
hasAllComponents: (components) => ({ type: 'component', components, mode: 'all' }),
|
|
49
|
+
// Record type conditions
|
|
50
|
+
recordType: (recordType) => ({ type: 'record_type', recordType }),
|
|
51
|
+
excludeRecordType: (recordType) => ({ type: 'exclude_record_type', recordType }),
|
|
52
|
+
// Convenience methods for common record types
|
|
53
|
+
strokes: () => ({ type: 'record_type', recordType: 'stroke' }),
|
|
54
|
+
radicals: () => ({ type: 'record_type', recordType: 'radical' }),
|
|
55
|
+
characters: () => ({ type: 'record_type', recordType: 'character' }),
|
|
56
|
+
words: () => ({ type: 'record_type', recordType: 'word' }),
|
|
57
|
+
phrases: () => ({ type: 'record_type', recordType: 'phrase' }),
|
|
58
|
+
patterns: () => ({ type: 'record_type', recordType: 'pattern' }),
|
|
59
|
+
sentences: () => ({ type: 'record_type', recordType: 'sentence' }),
|
|
48
60
|
// Helper methods to check if conditions are present in filters
|
|
49
61
|
/**
|
|
50
62
|
* Check if starred condition is required (in 'all' section)
|
|
@@ -168,8 +180,118 @@ exports.Condition = {
|
|
|
168
180
|
errors.push(`Min difficulty (${min}) must be less than max difficulty (${max})`);
|
|
169
181
|
}
|
|
170
182
|
}
|
|
183
|
+
// Check for contradictory record type conditions
|
|
184
|
+
const allRecordTypes = conditions.all
|
|
185
|
+
?.filter(c => c.type === 'record_type')
|
|
186
|
+
.map(c => c.recordType) || [];
|
|
187
|
+
const noneRecordTypes = conditions.none
|
|
188
|
+
?.filter(c => c.type === 'record_type')
|
|
189
|
+
.map(c => c.recordType) || [];
|
|
190
|
+
const excludedRecordTypes = conditions.all
|
|
191
|
+
?.filter(c => c.type === 'exclude_record_type')
|
|
192
|
+
.map(c => c.recordType) || [];
|
|
193
|
+
// Check for multiple required record types (an item can only have one record type)
|
|
194
|
+
if (allRecordTypes.length > 1) {
|
|
195
|
+
errors.push(`Cannot require multiple record types: ${allRecordTypes.join(', ')} - an item can only have one record type`);
|
|
196
|
+
}
|
|
197
|
+
// Check for record_type in 'all' conflicting with same type in 'none'
|
|
198
|
+
allRecordTypes.forEach(rt => {
|
|
199
|
+
if (noneRecordTypes.includes(rt)) {
|
|
200
|
+
errors.push(`Record type "${rt}" cannot be both required and excluded`);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
// Check for record_type in 'all' conflicting with exclude_record_type for same type
|
|
204
|
+
allRecordTypes.forEach(rt => {
|
|
205
|
+
if (excludedRecordTypes.includes(rt)) {
|
|
206
|
+
errors.push(`Record type "${rt}" cannot be both required (record_type) and excluded (exclude_record_type)`);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
// Check if all record types would be excluded (empty result set)
|
|
210
|
+
const effectiveTypes = exports.Condition.getEffectiveRecordTypes(conditions);
|
|
211
|
+
if (effectiveTypes.length === 0) {
|
|
212
|
+
errors.push('All record types are excluded - query would return no results');
|
|
213
|
+
}
|
|
171
214
|
return errors;
|
|
172
215
|
},
|
|
216
|
+
/**
|
|
217
|
+
* Extract included record types from conditions.
|
|
218
|
+
* Returns null if no record type conditions are specified (meaning all types are included).
|
|
219
|
+
* Returns an array of included types if record_type conditions are in 'all' or 'any'.
|
|
220
|
+
*/
|
|
221
|
+
getIncludedRecordTypes: (conditions) => {
|
|
222
|
+
if (!conditions)
|
|
223
|
+
return null;
|
|
224
|
+
// Check 'all' section for record_type conditions
|
|
225
|
+
const allRecordTypes = conditions.all
|
|
226
|
+
?.filter(c => c.type === 'record_type')
|
|
227
|
+
.map(c => c.recordType) || [];
|
|
228
|
+
// Check 'any' section for record_type conditions
|
|
229
|
+
const anyRecordTypes = conditions.any
|
|
230
|
+
?.filter(c => c.type === 'record_type')
|
|
231
|
+
.map(c => c.recordType) || [];
|
|
232
|
+
// If there are record_type conditions in 'all', those are required
|
|
233
|
+
if (allRecordTypes.length > 0) {
|
|
234
|
+
return allRecordTypes;
|
|
235
|
+
}
|
|
236
|
+
// If there are record_type conditions in 'any', those are the allowed types
|
|
237
|
+
if (anyRecordTypes.length > 0) {
|
|
238
|
+
return anyRecordTypes;
|
|
239
|
+
}
|
|
240
|
+
// No record type constraints
|
|
241
|
+
return null;
|
|
242
|
+
},
|
|
243
|
+
/**
|
|
244
|
+
* Extract excluded record types from conditions.
|
|
245
|
+
* Returns an array of types to exclude based on exclude_record_type conditions in 'all'
|
|
246
|
+
* or record_type conditions in 'none'.
|
|
247
|
+
*/
|
|
248
|
+
getExcludedRecordTypes: (conditions) => {
|
|
249
|
+
if (!conditions)
|
|
250
|
+
return [];
|
|
251
|
+
const excluded = [];
|
|
252
|
+
// Check 'all' section for exclude_record_type conditions
|
|
253
|
+
const allExclusions = conditions.all
|
|
254
|
+
?.filter(c => c.type === 'exclude_record_type')
|
|
255
|
+
.map(c => c.recordType) || [];
|
|
256
|
+
excluded.push(...allExclusions);
|
|
257
|
+
// Check 'none' section for record_type conditions (these types should be excluded)
|
|
258
|
+
const noneRecordTypes = conditions.none
|
|
259
|
+
?.filter(c => c.type === 'record_type')
|
|
260
|
+
.map(c => c.recordType) || [];
|
|
261
|
+
excluded.push(...noneRecordTypes);
|
|
262
|
+
return [...new Set(excluded)]; // Deduplicate
|
|
263
|
+
},
|
|
264
|
+
/**
|
|
265
|
+
* Check if a specific record type is required (in 'all' section)
|
|
266
|
+
*/
|
|
267
|
+
requiresRecordType: (conditions, recordType) => {
|
|
268
|
+
return !!(conditions?.all?.some(c => c.type === 'record_type' && c.recordType === recordType));
|
|
269
|
+
},
|
|
270
|
+
/**
|
|
271
|
+
* Check if a specific record type is excluded
|
|
272
|
+
*/
|
|
273
|
+
excludesRecordType: (conditions, recordType) => {
|
|
274
|
+
// Check exclude_record_type in 'all'
|
|
275
|
+
const hasExcludeCondition = conditions?.all?.some(c => c.type === 'exclude_record_type' && c.recordType === recordType);
|
|
276
|
+
// Check record_type in 'none'
|
|
277
|
+
const hasNoneCondition = conditions?.none?.some(c => c.type === 'record_type' && c.recordType === recordType);
|
|
278
|
+
return !!(hasExcludeCondition || hasNoneCondition);
|
|
279
|
+
},
|
|
280
|
+
/**
|
|
281
|
+
* Get the effective record types to query based on conditions.
|
|
282
|
+
* Returns the set of types that should be included in a query.
|
|
283
|
+
*/
|
|
284
|
+
getEffectiveRecordTypes: (conditions) => {
|
|
285
|
+
const included = exports.Condition.getIncludedRecordTypes(conditions);
|
|
286
|
+
const excluded = exports.Condition.getExcludedRecordTypes(conditions);
|
|
287
|
+
// Start with included types, or all types if none specified
|
|
288
|
+
let types = included ?? [...RecordType_1.RECORD_TYPES];
|
|
289
|
+
// Remove excluded types
|
|
290
|
+
if (excluded.length > 0) {
|
|
291
|
+
types = types.filter(t => !excluded.includes(t));
|
|
292
|
+
}
|
|
293
|
+
return types;
|
|
294
|
+
},
|
|
173
295
|
// Backward compatibility aliases (deprecated - use requiresStarred/allowsStarred/excludesStarred instead)
|
|
174
296
|
hasStarredInAll: (filters) => exports.Condition.requiresStarred(filters),
|
|
175
297
|
hasStarredInAny: (filters) => exports.Condition.allowsStarred(filters),
|
package/dist/models/Phrase.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CompactDateTime } from "@shaxpir/shaxpir-common";
|
|
2
2
|
import { ContentId } from "./Content";
|
|
3
|
+
import { RecordType } from "./RecordType";
|
|
3
4
|
import { ReviewLike } from "./Review";
|
|
4
5
|
export interface PhraseExample {
|
|
5
6
|
id: number;
|
|
@@ -8,6 +9,7 @@ export interface PhraseExample {
|
|
|
8
9
|
translation: string;
|
|
9
10
|
difficulty: number;
|
|
10
11
|
sense_rank: number;
|
|
12
|
+
type?: RecordType;
|
|
11
13
|
}
|
|
12
14
|
export interface Phrase {
|
|
13
15
|
text: string;
|
|
@@ -26,10 +28,12 @@ export interface Phrase {
|
|
|
26
28
|
}
|
|
27
29
|
export interface BuiltInPhrase extends Phrase {
|
|
28
30
|
id: number;
|
|
31
|
+
type: RecordType;
|
|
29
32
|
}
|
|
30
33
|
export interface AnnotatedPhrase extends Phrase {
|
|
31
34
|
id: string;
|
|
32
35
|
phrase_id?: number;
|
|
36
|
+
type?: RecordType | null;
|
|
33
37
|
content_id?: ContentId;
|
|
34
38
|
starred_at: CompactDateTime | null;
|
|
35
39
|
alpha: number | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record types in the dictionary database.
|
|
3
|
+
*
|
|
4
|
+
* Each record type has its own table and a well-known ID range.
|
|
5
|
+
* This allows determining record type from ID alone.
|
|
6
|
+
*/
|
|
7
|
+
export type RecordType = 'stroke' | 'radical' | 'character' | 'word' | 'phrase' | 'pattern' | 'sentence';
|
|
8
|
+
/**
|
|
9
|
+
* Ordered array of all record types.
|
|
10
|
+
* Order reflects linguistic hierarchy from smallest to largest units.
|
|
11
|
+
*/
|
|
12
|
+
export declare const RECORD_TYPES: RecordType[];
|
|
13
|
+
/**
|
|
14
|
+
* ID ranges for each record type.
|
|
15
|
+
* Records are assigned IDs in ascending difficulty order within their type's range.
|
|
16
|
+
*/
|
|
17
|
+
export declare const ID_RANGES: Record<RecordType, {
|
|
18
|
+
min: number;
|
|
19
|
+
max: number;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Determine the record type from an ID based on ID ranges.
|
|
23
|
+
*
|
|
24
|
+
* @param id - The record ID
|
|
25
|
+
* @returns The record type, or null if ID is outside all known ranges
|
|
26
|
+
*/
|
|
27
|
+
export declare function getRecordTypeFromId(id: number): RecordType | null;
|
|
28
|
+
/**
|
|
29
|
+
* Check if an ID belongs to a specific record type.
|
|
30
|
+
*
|
|
31
|
+
* @param id - The record ID
|
|
32
|
+
* @param type - The record type to check
|
|
33
|
+
* @returns true if the ID is within the range for the given type
|
|
34
|
+
*/
|
|
35
|
+
export declare function isRecordType(id: number, type: RecordType): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Get the table name for a record type.
|
|
38
|
+
* Table names match record type names exactly.
|
|
39
|
+
*
|
|
40
|
+
* @param type - The record type
|
|
41
|
+
* @returns The table name (same as type)
|
|
42
|
+
*/
|
|
43
|
+
export declare function getTableForRecordType(type: RecordType): string;
|
|
44
|
+
/**
|
|
45
|
+
* Get the table name for a record ID.
|
|
46
|
+
*
|
|
47
|
+
* @param id - The record ID
|
|
48
|
+
* @returns The table name, or null if ID is outside all known ranges
|
|
49
|
+
*/
|
|
50
|
+
export declare function getTableForId(id: number): string | null;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ID_RANGES = exports.RECORD_TYPES = void 0;
|
|
4
|
+
exports.getRecordTypeFromId = getRecordTypeFromId;
|
|
5
|
+
exports.isRecordType = isRecordType;
|
|
6
|
+
exports.getTableForRecordType = getTableForRecordType;
|
|
7
|
+
exports.getTableForId = getTableForId;
|
|
8
|
+
/**
|
|
9
|
+
* Ordered array of all record types.
|
|
10
|
+
* Order reflects linguistic hierarchy from smallest to largest units.
|
|
11
|
+
*/
|
|
12
|
+
exports.RECORD_TYPES = [
|
|
13
|
+
'stroke',
|
|
14
|
+
'radical',
|
|
15
|
+
'character',
|
|
16
|
+
'word',
|
|
17
|
+
'phrase',
|
|
18
|
+
'pattern',
|
|
19
|
+
'sentence'
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* ID ranges for each record type.
|
|
23
|
+
* Records are assigned IDs in ascending difficulty order within their type's range.
|
|
24
|
+
*/
|
|
25
|
+
exports.ID_RANGES = {
|
|
26
|
+
stroke: { min: 0, max: 999 },
|
|
27
|
+
radical: { min: 1000, max: 9999 },
|
|
28
|
+
character: { min: 10000, max: 49999 },
|
|
29
|
+
word: { min: 50000, max: 149999 },
|
|
30
|
+
phrase: { min: 150000, max: 239999 },
|
|
31
|
+
pattern: { min: 240000, max: 249999 },
|
|
32
|
+
sentence: { min: 250000, max: 999999 }
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Determine the record type from an ID based on ID ranges.
|
|
36
|
+
*
|
|
37
|
+
* @param id - The record ID
|
|
38
|
+
* @returns The record type, or null if ID is outside all known ranges
|
|
39
|
+
*/
|
|
40
|
+
function getRecordTypeFromId(id) {
|
|
41
|
+
for (const type of exports.RECORD_TYPES) {
|
|
42
|
+
const range = exports.ID_RANGES[type];
|
|
43
|
+
if (id >= range.min && id <= range.max) {
|
|
44
|
+
return type;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if an ID belongs to a specific record type.
|
|
51
|
+
*
|
|
52
|
+
* @param id - The record ID
|
|
53
|
+
* @param type - The record type to check
|
|
54
|
+
* @returns true if the ID is within the range for the given type
|
|
55
|
+
*/
|
|
56
|
+
function isRecordType(id, type) {
|
|
57
|
+
const range = exports.ID_RANGES[type];
|
|
58
|
+
return id >= range.min && id <= range.max;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the table name for a record type.
|
|
62
|
+
* Table names match record type names exactly.
|
|
63
|
+
*
|
|
64
|
+
* @param type - The record type
|
|
65
|
+
* @returns The table name (same as type)
|
|
66
|
+
*/
|
|
67
|
+
function getTableForRecordType(type) {
|
|
68
|
+
return type;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the table name for a record ID.
|
|
72
|
+
*
|
|
73
|
+
* @param id - The record ID
|
|
74
|
+
* @returns The table name, or null if ID is outside all known ranges
|
|
75
|
+
*/
|
|
76
|
+
function getTableForId(id) {
|
|
77
|
+
const type = getRecordTypeFromId(id);
|
|
78
|
+
return type;
|
|
79
|
+
}
|
package/dist/models/Term.d.ts
CHANGED
|
@@ -4,12 +4,14 @@ import { ShareSync } from '../repo';
|
|
|
4
4
|
import { BayesianScore } from './BayesianScore';
|
|
5
5
|
import { Content, ContentBody, ContentId, ContentMeta } from "./Content";
|
|
6
6
|
import { BuiltInPhrase, Phrase, PhraseExample } from './Phrase';
|
|
7
|
+
import { RecordType } from './RecordType';
|
|
7
8
|
import { ImpliedReview, ReviewLike } from "./Review";
|
|
8
9
|
export interface TermSummary {
|
|
9
10
|
text: string;
|
|
10
11
|
sense_rank: number;
|
|
11
12
|
difficulty: number;
|
|
12
13
|
phrase_id: number | null;
|
|
14
|
+
type: RecordType;
|
|
13
15
|
starred_at: CompactDateTime | null;
|
|
14
16
|
alpha: number;
|
|
15
17
|
beta: number;
|
|
@@ -25,6 +27,7 @@ export interface TermPayload extends BayesianScore {
|
|
|
25
27
|
sense_rank: number;
|
|
26
28
|
difficulty: number;
|
|
27
29
|
phrase_id: number | null;
|
|
30
|
+
type: RecordType;
|
|
28
31
|
starred_at: CompactDateTime | null;
|
|
29
32
|
alpha: number;
|
|
30
33
|
beta: number;
|
|
@@ -57,13 +60,14 @@ export declare class Term extends Content {
|
|
|
57
60
|
private _impliedReviewsView;
|
|
58
61
|
private _userTagsView;
|
|
59
62
|
constructor(doc: Doc, shouldAcquire: boolean, shareSync: ShareSync);
|
|
60
|
-
static forUserPhrase(userId: ContentId, text: string, senseRank: number, difficulty: number, phraseId?: number): Term;
|
|
61
|
-
static forUserPhrase(userId: ContentId, phrase: Phrase): Term;
|
|
63
|
+
static forUserPhrase(userId: ContentId, text: string, senseRank: number, difficulty: number, type: RecordType, phraseId?: number): Term;
|
|
64
|
+
static forUserPhrase(userId: ContentId, phrase: Phrase, type: RecordType): Term;
|
|
62
65
|
static forBuiltinPhrase(userId: ContentId, phrase: BuiltInPhrase): Term;
|
|
63
66
|
get payload(): TermPayload;
|
|
64
67
|
get text(): string;
|
|
65
68
|
get senseRank(): number;
|
|
66
69
|
get difficulty(): number;
|
|
70
|
+
get type(): RecordType;
|
|
67
71
|
get alpha(): number;
|
|
68
72
|
get beta(): number;
|
|
69
73
|
get theta(): number;
|
package/dist/models/Term.js
CHANGED
|
@@ -8,6 +8,7 @@ const BayesianScore_1 = require("./BayesianScore");
|
|
|
8
8
|
const Content_1 = require("./Content");
|
|
9
9
|
const ContentKind_1 = require("./ContentKind");
|
|
10
10
|
const Operation_1 = require("./Operation");
|
|
11
|
+
const RecordType_1 = require("./RecordType");
|
|
11
12
|
class Term extends Content_1.Content {
|
|
12
13
|
static makeTermId(userId, text, senseRank) {
|
|
13
14
|
return shaxpir_common_1.CachingHasher.makeMd5Base62Hash(`${ContentKind_1.ContentKind.TERM}-${userId}-${text}-${senseRank}`);
|
|
@@ -18,11 +19,18 @@ class Term extends Content_1.Content {
|
|
|
18
19
|
this._impliedReviewsView = new ArrayView_1.ArrayView(this, ['payload', 'implied_reviews']);
|
|
19
20
|
this._userTagsView = new ArrayView_1.ArrayView(this, ['payload', 'user_tags']);
|
|
20
21
|
}
|
|
21
|
-
static forUserPhrase(userId, textOrPhrase,
|
|
22
|
+
static forUserPhrase(userId, textOrPhrase, senseRankOrType, difficultyOrNothing, typeOrPhraseId, phraseId) {
|
|
22
23
|
const now = shaxpir_common_1.ClockService.getClock().now();
|
|
23
24
|
// Handle overloaded parameters
|
|
24
25
|
let phrase;
|
|
26
|
+
let recordType;
|
|
27
|
+
let resolvedPhraseId;
|
|
25
28
|
if (typeof textOrPhrase === 'string') {
|
|
29
|
+
// First overload: (userId, text, senseRank, difficulty, type, phraseId?)
|
|
30
|
+
const senseRank = senseRankOrType;
|
|
31
|
+
const difficulty = difficultyOrNothing;
|
|
32
|
+
recordType = typeOrPhraseId;
|
|
33
|
+
resolvedPhraseId = phraseId;
|
|
26
34
|
// Create a minimal Phrase object from individual parameters
|
|
27
35
|
phrase = {
|
|
28
36
|
text: textOrPhrase,
|
|
@@ -40,9 +48,12 @@ class Term extends Content_1.Content {
|
|
|
40
48
|
};
|
|
41
49
|
}
|
|
42
50
|
else {
|
|
51
|
+
// Second overload: (userId, phrase, type)
|
|
43
52
|
phrase = textOrPhrase;
|
|
53
|
+
recordType = senseRankOrType;
|
|
54
|
+
resolvedPhraseId = undefined;
|
|
44
55
|
}
|
|
45
|
-
const termId = Term.makeTermId(userId, phrase.text,
|
|
56
|
+
const termId = Term.makeTermId(userId, phrase.text, phrase.sense_rank);
|
|
46
57
|
return repo_1.ShareSyncFactory.get().createContent({
|
|
47
58
|
meta: {
|
|
48
59
|
ref: termId,
|
|
@@ -53,7 +64,8 @@ class Term extends Content_1.Content {
|
|
|
53
64
|
updated_at: now
|
|
54
65
|
},
|
|
55
66
|
payload: {
|
|
56
|
-
phrase_id:
|
|
67
|
+
phrase_id: resolvedPhraseId ?? null,
|
|
68
|
+
type: recordType,
|
|
57
69
|
text: phrase.text,
|
|
58
70
|
sense_rank: phrase.sense_rank,
|
|
59
71
|
hanzi_count: phrase.hanzi_count,
|
|
@@ -97,6 +109,7 @@ class Term extends Content_1.Content {
|
|
|
97
109
|
sense_rank: phrase.sense_rank,
|
|
98
110
|
difficulty: phrase.difficulty,
|
|
99
111
|
phrase_id: phrase.id,
|
|
112
|
+
type: phrase.type ?? (0, RecordType_1.getRecordTypeFromId)(phrase.id), // Use phrase.type if available, else derive from ID
|
|
100
113
|
starred_at: null,
|
|
101
114
|
user_tags: [],
|
|
102
115
|
user_tag_count: 0,
|
|
@@ -128,6 +141,10 @@ class Term extends Content_1.Content {
|
|
|
128
141
|
this.checkDisposed("Term.difficulty");
|
|
129
142
|
return this.payload.difficulty;
|
|
130
143
|
}
|
|
144
|
+
get type() {
|
|
145
|
+
this.checkDisposed("Term.type");
|
|
146
|
+
return this.payload.type;
|
|
147
|
+
}
|
|
131
148
|
get alpha() {
|
|
132
149
|
this.checkDisposed("Term.alpha");
|
|
133
150
|
return this.payload.alpha;
|
package/dist/models/index.d.ts
CHANGED
package/dist/models/index.js
CHANGED
|
@@ -37,6 +37,7 @@ __exportStar(require("./Permissions"), exports);
|
|
|
37
37
|
__exportStar(require("./Phrase"), exports);
|
|
38
38
|
__exportStar(require("./Profile"), exports);
|
|
39
39
|
__exportStar(require("./Progress"), exports);
|
|
40
|
+
__exportStar(require("./RecordType"), exports);
|
|
40
41
|
__exportStar(require("./Review"), exports);
|
|
41
42
|
__exportStar(require("./SearchState"), exports);
|
|
42
43
|
__exportStar(require("./Session"), exports);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CompactDateTime } from "@shaxpir/shaxpir-common";
|
|
2
2
|
import { AnyCondition, Conditions } from '../models/Condition';
|
|
3
|
+
import { RecordType } from '../models/RecordType';
|
|
3
4
|
/**
|
|
4
5
|
* Interface for objects that can be matched against Conditions.
|
|
5
6
|
* This is the minimum set of fields needed for condition matching.
|
|
@@ -18,6 +19,8 @@ export interface ConditionMatchable {
|
|
|
18
19
|
updated_at?: CompactDateTime;
|
|
19
20
|
review_count?: number;
|
|
20
21
|
implied_review_count?: number;
|
|
22
|
+
type?: RecordType | null;
|
|
23
|
+
phrase_id?: number;
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
26
|
* Utility for checking if objects match Conditions.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ConditionMatcher = void 0;
|
|
4
|
+
const RecordType_1 = require("../models/RecordType");
|
|
4
5
|
/**
|
|
5
6
|
* Utility for checking if objects match Conditions.
|
|
6
7
|
*
|
|
@@ -118,6 +119,26 @@ exports.ConditionMatcher = {
|
|
|
118
119
|
// suitable for in-memory filtering. Return true (don't filter out).
|
|
119
120
|
return true;
|
|
120
121
|
}
|
|
122
|
+
case 'record_type': {
|
|
123
|
+
const rtCond = condition;
|
|
124
|
+
// Try to get type directly, or derive from phrase_id
|
|
125
|
+
const itemType = item.type ?? (item.phrase_id !== undefined ? (0, RecordType_1.getRecordTypeFromId)(item.phrase_id) : null);
|
|
126
|
+
if (itemType === null) {
|
|
127
|
+
// Cannot determine type - user-created term without type, return true (don't filter out)
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
return itemType === rtCond.recordType;
|
|
131
|
+
}
|
|
132
|
+
case 'exclude_record_type': {
|
|
133
|
+
const ertCond = condition;
|
|
134
|
+
// Try to get type directly, or derive from phrase_id
|
|
135
|
+
const itemType = item.type ?? (item.phrase_id !== undefined ? (0, RecordType_1.getRecordTypeFromId)(item.phrase_id) : null);
|
|
136
|
+
if (itemType === null) {
|
|
137
|
+
// Cannot determine type - user-created term without type, return true (don't filter out)
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return itemType !== ertCond.recordType;
|
|
141
|
+
}
|
|
121
142
|
default:
|
|
122
143
|
// For unsupported conditions, default to true (don't filter out)
|
|
123
144
|
return true;
|