@openstax/ts-utils 1.30.4 → 1.31.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.
@@ -0,0 +1 @@
1
+ export declare const assertNoUndefined: (obj: any, path?: string[]) => void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertNoUndefined = void 0;
4
+ const assertNoUndefined = (obj, path = []) => {
5
+ if (obj === undefined) {
6
+ throw new Error(`unknown attribute type ${typeof obj} with value ${obj} at ${path.join('.') || 'root'}.`);
7
+ }
8
+ if (obj && typeof obj === 'object') {
9
+ for (const [key, value] of Object.entries(obj)) {
10
+ (0, exports.assertNoUndefined)(value, [...path, key]);
11
+ }
12
+ }
13
+ };
14
+ exports.assertNoUndefined = assertNoUndefined;
@@ -43,6 +43,7 @@ const __1 = require("../../..");
43
43
  const config_1 = require("../../../config");
44
44
  const errors_1 = require("../../../errors");
45
45
  const guards_1 = require("../../../guards");
46
+ const fileSystemAssert_1 = require("../fileSystemAssert");
46
47
  const fileSystemUnversionedDocumentStore = (initializer) => () => (configProvider) => (_, hashKey, options) => {
47
48
  const tableName = (0, config_1.resolveConfigValue)(configProvider[initializer.configSpace || 'fileSystem'].tableName);
48
49
  const tablePath = tableName.then((table) => path_1.default.join(initializer.dataDir, table));
@@ -81,6 +82,7 @@ const fileSystemUnversionedDocumentStore = (initializer) => () => (configProvide
81
82
  return {
82
83
  loadAllDocumentsTheBadWay,
83
84
  getItemsByField: async (key, value, pageKey) => {
85
+ (0, fileSystemAssert_1.assertNoUndefined)(value, [key]);
84
86
  const pageSize = 10;
85
87
  const items = await loadAllDocumentsTheBadWay();
86
88
  const filteredItems = items.filter((item) => item[key] === value);
@@ -92,11 +94,18 @@ const fileSystemUnversionedDocumentStore = (initializer) => () => (configProvide
92
94
  return { items: paginatedItems, nextPageToken };
93
95
  },
94
96
  batchGetItem: async (ids) => {
95
- const items = await Promise.all(ids.map((id) => load(hashFilename(id))));
97
+ const items = await Promise.all(ids.map((id) => {
98
+ (0, fileSystemAssert_1.assertNoUndefined)(id, ['id']);
99
+ return load(hashFilename(id));
100
+ }));
96
101
  return items.filter(guards_1.isDefined);
97
102
  },
98
- getItem: (id) => load(hashFilename(id)),
103
+ getItem: (id) => {
104
+ (0, fileSystemAssert_1.assertNoUndefined)(id, ['id']);
105
+ return load(hashFilename(id));
106
+ },
99
107
  incrementItemAttribute: async (id, attribute) => {
108
+ (0, fileSystemAssert_1.assertNoUndefined)(id, ['id']);
100
109
  const filename = hashFilename(id);
101
110
  const path = await filePath(filename);
102
111
  await mkTableDir;
@@ -127,11 +136,13 @@ const fileSystemUnversionedDocumentStore = (initializer) => () => (configProvide
127
136
  throw new errors_1.NotFoundError(`Item with ${hashKey.toString()} "${id}" does not exist`);
128
137
  }
129
138
  const newItem = { ...data, ...item };
139
+ (0, fileSystemAssert_1.assertNoUndefined)(newItem);
130
140
  return new Promise((resolve, reject) => {
131
141
  writeFile(path, JSON.stringify(newItem, null, 2), (err) => err ? reject(err) : resolve(newItem));
132
142
  });
133
143
  },
134
144
  putItem: async (item) => {
145
+ (0, fileSystemAssert_1.assertNoUndefined)(item);
135
146
  const path = await filePath(hashFilename(item[hashKey]));
136
147
  await mkTableDir;
137
148
  return new Promise((resolve, reject) => {
@@ -139,6 +150,7 @@ const fileSystemUnversionedDocumentStore = (initializer) => () => (configProvide
139
150
  });
140
151
  },
141
152
  createItem: async (item) => {
153
+ (0, fileSystemAssert_1.assertNoUndefined)(item);
142
154
  const hashed = hashFilename(item[hashKey]);
143
155
  const existingItem = await load(hashed);
144
156
  if (existingItem) {
@@ -159,6 +171,7 @@ const fileSystemUnversionedDocumentStore = (initializer) => () => (configProvide
159
171
  // Process items sequentially to ensure consistent conflict detection
160
172
  // Note: concurrency parameter is ignored for filesystem to avoid race conditions
161
173
  for (const item of items) {
174
+ (0, fileSystemAssert_1.assertNoUndefined)(item);
162
175
  try {
163
176
  const hashed = hashFilename(item[hashKey]);
164
177
  const existingItem = await load(hashed);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fileSystemVersionedDocumentStore = void 0;
4
+ const fileSystemAssert_1 = require("../fileSystemAssert");
4
5
  const file_system_1 = require("../unversioned/file-system");
5
6
  const PAGE_LIMIT = 5;
6
7
  const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider) => (_, hashKey, options) => {
@@ -12,6 +13,7 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
12
13
  }));
13
14
  },
14
15
  getVersions: async (id, startVersion) => {
16
+ (0, fileSystemAssert_1.assertNoUndefined)(id, ['id']);
15
17
  const item = await unversionedDocuments.getItem(id);
16
18
  const versions = item === null || item === void 0 ? void 0 : item.items.reverse();
17
19
  if (!versions) {
@@ -26,6 +28,7 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
26
28
  };
27
29
  },
28
30
  getItem: async (id, timestamp) => {
31
+ (0, fileSystemAssert_1.assertNoUndefined)(id, ['id']);
29
32
  const item = await unversionedDocuments.getItem(id);
30
33
  if (timestamp) {
31
34
  return item === null || item === void 0 ? void 0 : item.items.find(version => version.timestamp === timestamp);
@@ -41,6 +44,7 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
41
44
  save: async (changes) => {
42
45
  var _a;
43
46
  const document = { ...item, ...changes, timestamp, author };
47
+ (0, fileSystemAssert_1.assertNoUndefined)(document);
44
48
  const container = (_a = await unversionedDocuments.getItem(document[hashKey])) !== null && _a !== void 0 ? _a : { id: document[hashKey], items: [] };
45
49
  const updated = { ...container, items: [...container.items, document] };
46
50
  await unversionedDocuments.putItem(updated);
@@ -52,6 +56,7 @@ const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider)
52
56
  var _a;
53
57
  const author = (options === null || options === void 0 ? void 0 : options.getAuthor) ? await options.getAuthor(...authorArgs) : authorArgs[0];
54
58
  const document = { ...item, timestamp: new Date().getTime(), author };
59
+ (0, fileSystemAssert_1.assertNoUndefined)(document);
55
60
  const container = (_a = await unversionedDocuments.getItem(document[hashKey])) !== null && _a !== void 0 ? _a : { id: document[hashKey], items: [] };
56
61
  const updated = { ...container, items: [...container.items, document] };
57
62
  await unversionedDocuments.putItem(updated);
@@ -3,11 +3,15 @@ import { AuthProvider } from '../authProvider';
3
3
  import { ActivityState } from './attempt-utils';
4
4
  import { LrsGateway } from '.';
5
5
  export interface Grade {
6
- scoreGiven: number;
7
- scoreMaximum: number;
8
- comment?: string;
9
6
  activityProgress: 'Initialized' | 'Started' | 'inProgress' | 'Submitted' | 'Completed';
7
+ comment?: string;
10
8
  gradingProgress: 'FullyGraded' | 'Pending' | 'PendingManual' | 'Failed' | 'NotReady';
9
+ scoreGiven: number;
10
+ scoreMaximum: number;
11
+ submission?: {
12
+ startedAt?: string;
13
+ submittedAt?: string;
14
+ };
11
15
  userId?: string;
12
16
  }
13
17
  export declare const getRegistrationAttemptInfo: (lrs: LrsGateway, registration: string, options?: {
@@ -24,7 +28,12 @@ export declare const getScoreGrade: (score: {
24
28
  raw?: number;
25
29
  min?: number;
26
30
  max?: number;
27
- }, completed: boolean, userId?: string, maxScore?: number) => Grade;
31
+ }, options: {
32
+ maxScore?: number;
33
+ startedAt?: string;
34
+ submittedAt?: string;
35
+ userId?: string;
36
+ }) => Grade;
28
37
  export type Progress = {
29
38
  scaled: number;
30
39
  max?: number;
@@ -29,30 +29,45 @@ exports.getRegistrationAttemptInfo = getRegistrationAttemptInfo;
29
29
  // generates a payload that can be sent to the LMS, documentation here:
30
30
  // lti: http://www.imsglobal.org/spec/lti-ags/v2p0#score-publish-service
31
31
  // ltijs: https://cvmcosta.me/ltijs/#/grading
32
- const getScoreGrade = (score, completed, userId, maxScore) => {
32
+ // Note: "min" is currently completely ignored
33
+ const getScoreGrade = (score, options) => {
33
34
  const { raw, scaled, max } = score;
35
+ const { maxScore, startedAt, submittedAt, userId } = options;
34
36
  const scoreMaximum = maxScore !== null && maxScore !== void 0 ? maxScore : 100;
35
37
  const scoreGiven = raw && max
36
38
  ? scoreMaximum / max * raw
37
39
  : scaled
38
40
  ? scaled * scoreMaximum
39
41
  : 0;
42
+ const submission = {};
43
+ if (startedAt) {
44
+ submission.startedAt = startedAt;
45
+ }
46
+ if (submittedAt) {
47
+ submission.submittedAt = submittedAt;
48
+ }
40
49
  return {
41
50
  userId,
42
- activityProgress: completed ? 'Completed' : 'Started',
51
+ activityProgress: submittedAt ? 'Completed' : 'Started',
43
52
  // canvas assumes that anything that isn't 'FullyGraded' requires manual grading and displays a "needs grading" icon.
44
53
  // if you warp your mind you can consider the portion of the assignment which is completed to be fully graded.
45
54
  gradingProgress: 'FullyGraded',
46
55
  scoreMaximum,
47
56
  scoreGiven: (0, __1.roundToPrecision)(scoreGiven, -2),
57
+ submission,
48
58
  };
49
59
  };
50
60
  exports.getScoreGrade = getScoreGrade;
51
61
  // These methods assign 0's to incomplete activities
52
62
  const getCompletedActivityStateGradeAndProgress = ({ name, scoreMaximum, state, userId }) => {
53
- var _a, _b;
63
+ var _a, _b, _c, _d;
54
64
  return ({
55
- grade: (0, exports.getScoreGrade)(((_b = (_a = state.currentAttemptCompleted) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.score) || {}, !!state.currentAttemptCompleted, userId, scoreMaximum),
65
+ grade: (0, exports.getScoreGrade)(((_b = (_a = state.currentAttemptCompleted) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.score) || {}, {
66
+ maxScore: scoreMaximum,
67
+ startedAt: (_c = state.currentAttempt) === null || _c === void 0 ? void 0 : _c.timestamp,
68
+ submittedAt: (_d = state.currentAttemptCompleted) === null || _d === void 0 ? void 0 : _d.timestamp,
69
+ userId,
70
+ }),
56
71
  progress: {
57
72
  scaled: state.currentAttemptCompleted ? 1 : 0,
58
73
  },