@neutron.co.id/pendidikan-operation 1.27.8 → 1.27.11

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.
@@ -14,6 +14,8 @@ const syncStudentReport = operation.Action.define({
14
14
  execute: async (input, stream) => {
15
15
  utils.guard(stream, "streamRequired");
16
16
  utils.guard(input, "inputRequired");
17
+ const printedBy = stream.context.identitas.userId;
18
+ const printedAt = (/* @__PURE__ */ new Date()).toISOString();
17
19
  return useTelemetry.useTelemetry(stream, "syncStudentReport", { userId: stream.context.identitas.userId, ...input }, async () => {
18
20
  const sessionIds = [];
19
21
  const actions = stream.actions.data;
@@ -43,7 +45,9 @@ const syncStudentReport = operation.Action.define({
43
45
  { _id: input.studentId },
44
46
  {
45
47
  $set: {
46
- totalGrading: gradingCount
48
+ totalGrading: gradingCount,
49
+ printedAt,
50
+ printedBy
47
51
  }
48
52
  }
49
53
  );
@@ -52,7 +56,9 @@ const syncStudentReport = operation.Action.define({
52
56
  { _id: input.studentId },
53
57
  {
54
58
  $set: {
55
- totalGrading: gradingCount
59
+ totalGrading: gradingCount,
60
+ printedAt,
61
+ printedBy
56
62
  }
57
63
  }
58
64
  );
@@ -95,7 +101,9 @@ const syncStudentReport = operation.Action.define({
95
101
  $set: {
96
102
  totalAttendance: attendanceCount,
97
103
  attendanceOffline: totalAttendanceOffline,
98
- attendanceOnline: totalAttendanceOnline
104
+ attendanceOnline: totalAttendanceOnline,
105
+ printedAt,
106
+ printedBy
99
107
  }
100
108
  }
101
109
  );
@@ -106,7 +114,9 @@ const syncStudentReport = operation.Action.define({
106
114
  $set: {
107
115
  totalAttendance: attendanceCount,
108
116
  attendanceOffline: totalAttendanceOffline,
109
- attendanceOnline: totalAttendanceOnline
117
+ attendanceOnline: totalAttendanceOnline,
118
+ printedAt,
119
+ printedBy
110
120
  }
111
121
  }
112
122
  );
@@ -125,7 +135,9 @@ const syncStudentReport = operation.Action.define({
125
135
  { _id: input.studentId },
126
136
  {
127
137
  $set: {
128
- totalCheckin: checkInCount
138
+ totalCheckin: checkInCount,
139
+ printedAt,
140
+ printedBy
129
141
  }
130
142
  }
131
143
  );
@@ -134,7 +146,9 @@ const syncStudentReport = operation.Action.define({
134
146
  { _id: input.studentId },
135
147
  {
136
148
  $set: {
137
- totalCheckin: checkInCount
149
+ totalCheckin: checkInCount,
150
+ printedAt,
151
+ printedBy
138
152
  }
139
153
  }
140
154
  );
@@ -155,7 +169,9 @@ const syncStudentReport = operation.Action.define({
155
169
  { _id: input.studentId },
156
170
  {
157
171
  $set: {
158
- totalConsultation: consultationCount
172
+ totalConsultation: consultationCount,
173
+ printedAt,
174
+ printedBy
159
175
  }
160
176
  }
161
177
  );
@@ -164,7 +180,9 @@ const syncStudentReport = operation.Action.define({
164
180
  { _id: input.studentId },
165
181
  {
166
182
  $set: {
167
- totalConsultation: consultationCount
183
+ totalConsultation: consultationCount,
184
+ printedAt,
185
+ printedBy
168
186
  }
169
187
  }
170
188
  );
@@ -184,7 +202,9 @@ const syncStudentReport = operation.Action.define({
184
202
  { _id: input.studentId },
185
203
  {
186
204
  $set: {
187
- totalQuestion: questionCount
205
+ totalQuestion: questionCount,
206
+ printedAt,
207
+ printedBy
188
208
  }
189
209
  }
190
210
  );
@@ -193,7 +213,9 @@ const syncStudentReport = operation.Action.define({
193
213
  { _id: input.studentId },
194
214
  {
195
215
  $set: {
196
- totalQuestion: questionCount
216
+ totalQuestion: questionCount,
217
+ printedAt,
218
+ printedBy
197
219
  }
198
220
  }
199
221
  );
@@ -280,7 +302,9 @@ const syncStudentReport = operation.Action.define({
280
302
  sessionOffline: totalOffline,
281
303
  sessionOnline: totalOnline,
282
304
  sessionHybrid: totalHybrid,
283
- daySession: scheduleCount
305
+ daySession: scheduleCount,
306
+ printedAt,
307
+ printedBy
284
308
  }
285
309
  }
286
310
  );
@@ -293,7 +317,9 @@ const syncStudentReport = operation.Action.define({
293
317
  sessionOffline: totalOffline,
294
318
  sessionOnline: totalOnline,
295
319
  sessionHybrid: totalHybrid,
296
- daySession: scheduleCount
320
+ daySession: scheduleCount,
321
+ printedAt,
322
+ printedBy
297
323
  }
298
324
  }
299
325
  );
@@ -332,7 +358,9 @@ const syncStudentReport = operation.Action.define({
332
358
  { _id: input.studentId },
333
359
  {
334
360
  $set: {
335
- totalActivity: totalActivities
361
+ totalActivity: totalActivities,
362
+ printedAt,
363
+ printedBy
336
364
  }
337
365
  }
338
366
  );
@@ -341,7 +369,9 @@ const syncStudentReport = operation.Action.define({
341
369
  { _id: input.studentId },
342
370
  {
343
371
  $set: {
344
- totalActivity: totalActivities
372
+ totalActivity: totalActivities,
373
+ printedAt,
374
+ printedBy
345
375
  }
346
376
  }
347
377
  );
@@ -12,6 +12,8 @@ const syncStudentReport = Action.define({
12
12
  execute: async (input, stream) => {
13
13
  guard(stream, "streamRequired");
14
14
  guard(input, "inputRequired");
15
+ const printedBy = stream.context.identitas.userId;
16
+ const printedAt = (/* @__PURE__ */ new Date()).toISOString();
15
17
  return useTelemetry(stream, "syncStudentReport", { userId: stream.context.identitas.userId, ...input }, async () => {
16
18
  const sessionIds = [];
17
19
  const actions = stream.actions.data;
@@ -41,7 +43,9 @@ const syncStudentReport = Action.define({
41
43
  { _id: input.studentId },
42
44
  {
43
45
  $set: {
44
- totalGrading: gradingCount
46
+ totalGrading: gradingCount,
47
+ printedAt,
48
+ printedBy
45
49
  }
46
50
  }
47
51
  );
@@ -50,7 +54,9 @@ const syncStudentReport = Action.define({
50
54
  { _id: input.studentId },
51
55
  {
52
56
  $set: {
53
- totalGrading: gradingCount
57
+ totalGrading: gradingCount,
58
+ printedAt,
59
+ printedBy
54
60
  }
55
61
  }
56
62
  );
@@ -93,7 +99,9 @@ const syncStudentReport = Action.define({
93
99
  $set: {
94
100
  totalAttendance: attendanceCount,
95
101
  attendanceOffline: totalAttendanceOffline,
96
- attendanceOnline: totalAttendanceOnline
102
+ attendanceOnline: totalAttendanceOnline,
103
+ printedAt,
104
+ printedBy
97
105
  }
98
106
  }
99
107
  );
@@ -104,7 +112,9 @@ const syncStudentReport = Action.define({
104
112
  $set: {
105
113
  totalAttendance: attendanceCount,
106
114
  attendanceOffline: totalAttendanceOffline,
107
- attendanceOnline: totalAttendanceOnline
115
+ attendanceOnline: totalAttendanceOnline,
116
+ printedAt,
117
+ printedBy
108
118
  }
109
119
  }
110
120
  );
@@ -123,7 +133,9 @@ const syncStudentReport = Action.define({
123
133
  { _id: input.studentId },
124
134
  {
125
135
  $set: {
126
- totalCheckin: checkInCount
136
+ totalCheckin: checkInCount,
137
+ printedAt,
138
+ printedBy
127
139
  }
128
140
  }
129
141
  );
@@ -132,7 +144,9 @@ const syncStudentReport = Action.define({
132
144
  { _id: input.studentId },
133
145
  {
134
146
  $set: {
135
- totalCheckin: checkInCount
147
+ totalCheckin: checkInCount,
148
+ printedAt,
149
+ printedBy
136
150
  }
137
151
  }
138
152
  );
@@ -153,7 +167,9 @@ const syncStudentReport = Action.define({
153
167
  { _id: input.studentId },
154
168
  {
155
169
  $set: {
156
- totalConsultation: consultationCount
170
+ totalConsultation: consultationCount,
171
+ printedAt,
172
+ printedBy
157
173
  }
158
174
  }
159
175
  );
@@ -162,7 +178,9 @@ const syncStudentReport = Action.define({
162
178
  { _id: input.studentId },
163
179
  {
164
180
  $set: {
165
- totalConsultation: consultationCount
181
+ totalConsultation: consultationCount,
182
+ printedAt,
183
+ printedBy
166
184
  }
167
185
  }
168
186
  );
@@ -182,7 +200,9 @@ const syncStudentReport = Action.define({
182
200
  { _id: input.studentId },
183
201
  {
184
202
  $set: {
185
- totalQuestion: questionCount
203
+ totalQuestion: questionCount,
204
+ printedAt,
205
+ printedBy
186
206
  }
187
207
  }
188
208
  );
@@ -191,7 +211,9 @@ const syncStudentReport = Action.define({
191
211
  { _id: input.studentId },
192
212
  {
193
213
  $set: {
194
- totalQuestion: questionCount
214
+ totalQuestion: questionCount,
215
+ printedAt,
216
+ printedBy
195
217
  }
196
218
  }
197
219
  );
@@ -278,7 +300,9 @@ const syncStudentReport = Action.define({
278
300
  sessionOffline: totalOffline,
279
301
  sessionOnline: totalOnline,
280
302
  sessionHybrid: totalHybrid,
281
- daySession: scheduleCount
303
+ daySession: scheduleCount,
304
+ printedAt,
305
+ printedBy
282
306
  }
283
307
  }
284
308
  );
@@ -291,7 +315,9 @@ const syncStudentReport = Action.define({
291
315
  sessionOffline: totalOffline,
292
316
  sessionOnline: totalOnline,
293
317
  sessionHybrid: totalHybrid,
294
- daySession: scheduleCount
318
+ daySession: scheduleCount,
319
+ printedAt,
320
+ printedBy
295
321
  }
296
322
  }
297
323
  );
@@ -330,7 +356,9 @@ const syncStudentReport = Action.define({
330
356
  { _id: input.studentId },
331
357
  {
332
358
  $set: {
333
- totalActivity: totalActivities
359
+ totalActivity: totalActivities,
360
+ printedAt,
361
+ printedBy
334
362
  }
335
363
  }
336
364
  );
@@ -339,7 +367,9 @@ const syncStudentReport = Action.define({
339
367
  { _id: input.studentId },
340
368
  {
341
369
  $set: {
342
- totalActivity: totalActivities
370
+ totalActivity: totalActivities,
371
+ printedAt,
372
+ printedBy
343
373
  }
344
374
  }
345
375
  );
@@ -21,12 +21,19 @@ const importData = operation.Action.define({
21
21
  data
22
22
  }, async () => {
23
23
  const { getActorId } = operation.useStream(stream);
24
- const { prepareGradingMapper } = useImportData.useImportData(stream);
24
+ const { prepareGradingMapper, prepareClassSessionMapper } = useImportData.useImportData(stream);
25
25
  try {
26
26
  getActorId({ throw: true });
27
- await prepareGradingMapper(stream, {
28
- datas: data?.csvData
29
- });
27
+ if (data?.type === "classSession") {
28
+ await prepareClassSessionMapper(stream, {
29
+ datas: data?.csvData,
30
+ classGroupName: data?.classGroupName
31
+ });
32
+ } else {
33
+ await prepareGradingMapper(stream, {
34
+ datas: data?.csvData
35
+ });
36
+ }
30
37
  return core.Result.ok({
31
38
  state: "importData",
32
39
  message: "Import Data has been sucessfully executed. CSV has been processed on background.",
@@ -7,6 +7,8 @@ export interface ImportDataOutput {
7
7
  export interface ImportDataMeta {
8
8
  }
9
9
  export declare const importData: Action<"importData", {
10
+ type: string;
10
11
  csvData?: any[] | undefined;
12
+ classGroupName?: string | undefined;
11
13
  }, ImportDataOutput, ImportDataMeta>;
12
14
  export type ImportDataAction = typeof importData;
@@ -19,12 +19,19 @@ const importData = Action.define({
19
19
  data
20
20
  }, async () => {
21
21
  const { getActorId } = useStream(stream);
22
- const { prepareGradingMapper } = useImportData(stream);
22
+ const { prepareGradingMapper, prepareClassSessionMapper } = useImportData(stream);
23
23
  try {
24
24
  getActorId({ throw: true });
25
- await prepareGradingMapper(stream, {
26
- datas: data?.csvData
27
- });
25
+ if (data?.type === "classSession") {
26
+ await prepareClassSessionMapper(stream, {
27
+ datas: data?.csvData,
28
+ classGroupName: data?.classGroupName
29
+ });
30
+ } else {
31
+ await prepareGradingMapper(stream, {
32
+ datas: data?.csvData
33
+ });
34
+ }
28
35
  return Result.ok({
29
36
  state: "importData",
30
37
  message: "Import Data has been sucessfully executed. CSV has been processed on background.",
@@ -3,7 +3,9 @@
3
3
  const z = require('@neon.id/z');
4
4
 
5
5
  const ImportDataSchema = z.z.object({
6
- csvData: z.z.array(z.z.any()).optional().explain({ label: "Data" })
6
+ csvData: z.z.array(z.z.any()).optional().explain({ label: "Data" }),
7
+ classGroupName: z.z.string().optional(),
8
+ type: z.z.string().default("grading")
7
9
  });
8
10
 
9
11
  exports.ImportDataSchema = ImportDataSchema;
@@ -1,8 +1,14 @@
1
1
  import { z } from '@neon.id/z';
2
2
  export declare const ImportDataSchema: z.ZodObject<{
3
3
  csvData: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
4
+ classGroupName: z.ZodOptional<z.ZodString>;
5
+ type: z.ZodDefault<z.ZodString>;
4
6
  }, "strip", z.ZodTypeAny, {
7
+ type: string;
5
8
  csvData?: any[] | undefined;
9
+ classGroupName?: string | undefined;
6
10
  }, {
11
+ type?: string | undefined;
7
12
  csvData?: any[] | undefined;
13
+ classGroupName?: string | undefined;
8
14
  }>;
@@ -1,7 +1,9 @@
1
1
  import { z } from '@neon.id/z';
2
2
 
3
3
  const ImportDataSchema = z.object({
4
- csvData: z.array(z.any()).optional().explain({ label: "Data" })
4
+ csvData: z.array(z.any()).optional().explain({ label: "Data" }),
5
+ classGroupName: z.string().optional(),
6
+ type: z.string().default("grading")
5
7
  });
6
8
 
7
9
  export { ImportDataSchema };
package/build/index.d.ts CHANGED
@@ -108,7 +108,9 @@ export declare const actions: {
108
108
  eventId?: string | undefined;
109
109
  }, import("./actions").PrepareMediaScanterGradingInsertOutput, import("./actions").PrepareMediaScanterGradingInsertMeta>;
110
110
  importData: import("@neon.id/operation").Action<"importData", {
111
+ type: string;
111
112
  csvData?: any[] | undefined;
113
+ classGroupName?: string | undefined;
112
114
  }, import("./actions").ImportDataOutput, import("./actions").ImportDataMeta>;
113
115
  updateReportStudent: import("@neon.id/operation").Action<"updateReportStudent", {
114
116
  reportBranchId: string;
@@ -121,7 +121,266 @@ function useImportData(stream) {
121
121
  await _gradingMapper(stream2, { gradings: batch });
122
122
  await new Promise((resolve) => setTimeout(resolve, 5e3));
123
123
  }
124
- return { prepareGradingMapper };
124
+ async function prepareClassSessionMapper(stream2, input) {
125
+ utils.guard(stream2, "streamRequired");
126
+ if (!input.classGroupName) {
127
+ throw new Error("Class Group Name is required for import.");
128
+ }
129
+ const classGroup = await findClassGroupByName(stream2, input.classGroupName);
130
+ if (!classGroup) {
131
+ console.error(`[ImportClassSession] Class Group '${input.classGroupName}' not found.`);
132
+ throw new Error(`Class Group '${input.classGroupName}' not found.`);
133
+ }
134
+ const groupId = classGroup._id || classGroup.id;
135
+ const stageId = classGroup.stageId;
136
+ void queueArrayInChunks(stream2, input.datas, 100, async (s, batch) => {
137
+ await _classSessionMapper(s, { sessions: batch, classGroupId: groupId, stageId });
138
+ });
139
+ return { status: "CSV has being processed" };
140
+ }
141
+ async function findClassGroupByName(stream2, name) {
142
+ const result = await stream2.actions.data.getMany.execute({
143
+ model: "neu:jadwal:classGroup",
144
+ query: query.Query.define({
145
+ filter: { name },
146
+ fields: { _id: 1, id: 1, stageId: 1 },
147
+ limit: 1
148
+ })
149
+ }, stream2);
150
+ return result.isOk && result.payload?.data?.[0] || null;
151
+ }
152
+ async function _classSessionMapper(stream2, input) {
153
+ const operations = [];
154
+ const subjectCodes = [...new Set(input.sessions.flatMap(
155
+ (s) => (s.pelajaran || "").split(";").map((code) => code.trim()).filter(Boolean)
156
+ ))];
157
+ const subjectMap = await findSubjectsByCodes(stream2, subjectCodes);
158
+ const stageNames = [...new Set(input.sessions.flatMap(
159
+ (s) => s.jenjangStudi ? [s.jenjangStudi.trim()] : []
160
+ ))];
161
+ const stageMap = await findStages(stream2, stageNames);
162
+ const teacherIdentifiers = [];
163
+ input.sessions.forEach((s) => {
164
+ if (s.idIdentitasPengajar)
165
+ teacherIdentifiers.push({ type: "idIdentitas", value: s.idIdentitasPengajar });
166
+ if (s.nomorIndukPengajar)
167
+ teacherIdentifiers.push({ type: "nip", value: s.nomorIndukPengajar });
168
+ if (s.idPengajar)
169
+ teacherIdentifiers.push({ type: "idPengajar", value: s.idPengajar });
170
+ });
171
+ const teacherMap = await findTeachers(stream2, teacherIdentifiers);
172
+ for (const session of input.sessions) {
173
+ const title = session.judulJadwal || null;
174
+ let status = "draft";
175
+ if (session.status?.toLowerCase() === "tayang")
176
+ status = "published";
177
+ else if (session.status?.toLowerCase() === "arsip")
178
+ status = "archived";
179
+ let type = "offline";
180
+ if (session.tipe?.toLowerCase() === "live")
181
+ type = "online";
182
+ else if (session.tipe?.toLowerCase() === "hybrid")
183
+ type = "hybrid";
184
+ let startedAt = null;
185
+ let endedAt = null;
186
+ try {
187
+ const parseDate = (dateStr, timeStr) => {
188
+ if (!dateStr || !timeStr)
189
+ return null;
190
+ const parts = dateStr.split("/");
191
+ let formattedDate = dateStr;
192
+ if (parts.length === 3) {
193
+ formattedDate = `${parts[2]}-${parts[1]}-${parts[0]}`;
194
+ }
195
+ return (/* @__PURE__ */ new Date(`${formattedDate}T${timeStr}:00+07:00`)).toISOString();
196
+ };
197
+ if (session.tanggal && session.mulai)
198
+ startedAt = parseDate(session.tanggal, session.mulai);
199
+ if (session.tanggal && session.selesai)
200
+ endedAt = parseDate(session.tanggal, session.selesai);
201
+ } catch (e) {
202
+ console.error("[ImportClassSession] Date parsing error", e);
203
+ }
204
+ const teacherIds = [];
205
+ let teacherIdStr;
206
+ if (session.idIdentitasPengajar)
207
+ teacherIdStr = teacherMap.get(`idIdentitas:${session.idIdentitasPengajar}`);
208
+ else if (session.nomorIndukPengajar)
209
+ teacherIdStr = teacherMap.get(`nip:${session.nomorIndukPengajar}`);
210
+ else if (session.idPengajar)
211
+ teacherIdStr = teacherMap.get(`idPengajar:${session.idPengajar}`);
212
+ if (teacherIdStr) {
213
+ teacherIds.push(new core.ObjectId(teacherIdStr));
214
+ }
215
+ const subjectIds = [];
216
+ if (session.pelajaran) {
217
+ const codes = session.pelajaran.split(";").map((c) => c.trim()).filter(Boolean);
218
+ codes.forEach((code) => {
219
+ const sId = subjectMap.get(code);
220
+ if (sId)
221
+ subjectIds.push(new core.ObjectId(sId));
222
+ });
223
+ }
224
+ let sessionStageId = input.stageId;
225
+ if (session.jenjangStudi) {
226
+ const sName = session.jenjangStudi.trim();
227
+ const mappedStageId = stageMap.get(sName);
228
+ if (mappedStageId)
229
+ sessionStageId = mappedStageId;
230
+ }
231
+ const newId = new core.ObjectId();
232
+ operations.push({
233
+ insertOne: {
234
+ document: {
235
+ name: title,
236
+ description: session.subjudul,
237
+ note: session.catatanLokasi,
238
+ title,
239
+ subtitle: session.subjudul || null,
240
+ locationText: session.catatanLokasi || null,
241
+ map: session.peta || null,
242
+ link: session.link || null,
243
+ password: session.password || null,
244
+ startedAt,
245
+ endedAt,
246
+ _id: newId,
247
+ id: newId.toString(),
248
+ groupIds: [input.classGroupId],
249
+ stageId: sessionStageId,
250
+ teacherIds,
251
+ subjectIds,
252
+ status,
253
+ isTeacherShown: true,
254
+ isTeacherSubtituted: false,
255
+ type,
256
+ locationType: type === "online" ? null : "building",
257
+ isTitleSession: !!(session.judulJadwal && session.judulJadwal.trim()),
258
+ createdAt: /* @__PURE__ */ new Date(),
259
+ updatedAt: /* @__PURE__ */ new Date(),
260
+ meta: {
261
+ importData: session,
262
+ source: "import-csv"
263
+ }
264
+ }
265
+ }
266
+ });
267
+ }
268
+ if (operations.length > 0) {
269
+ await stream2.core.dbs["neu-jadwal"].models["neu:jadwal:classSession"].bulkWrite(operations);
270
+ }
271
+ }
272
+ async function findSubjectsByCodes(stream2, codes) {
273
+ if (codes.length === 0) {
274
+ return /* @__PURE__ */ new Map();
275
+ }
276
+ console.log("[ImportClassSession] findSubjectsByCodes input:", codes);
277
+ const result = await stream2.actions.data.getMany.execute({
278
+ model: "neu:akademik:academicSubject",
279
+ query: query.Query.define({
280
+ filter: { code: { $in: codes } },
281
+ fields: { _id: 1, id: 1, code: 1, name: 1 },
282
+ limit: constants.LIMIT_INFINITY
283
+ })
284
+ }, stream2);
285
+ const map = /* @__PURE__ */ new Map();
286
+ if (result.isOk && result.payload?.data) {
287
+ console.log(`[ImportClassSession] Found ${result.payload.data.length} subjects`);
288
+ result.payload.data.forEach((subject) => {
289
+ console.log(`[ImportClassSession] Mapping Subject Code: '${subject.code}' -> ID: '${subject.id || subject._id}'`);
290
+ map.set(subject.code, subject.id || subject._id);
291
+ });
292
+ } else {
293
+ console.log("[ImportClassSession] findSubjectsByCodes failed or empty:", result.message);
294
+ }
295
+ return map;
296
+ }
297
+ async function findStages(stream2, namesOrCodes) {
298
+ if (namesOrCodes.length === 0) {
299
+ return /* @__PURE__ */ new Map();
300
+ }
301
+ const result = await stream2.actions.data.getMany.execute({
302
+ model: "neo:akademik:stage",
303
+ query: query.Query.define({
304
+ filter: {
305
+ $or: [
306
+ { name: { $in: namesOrCodes } },
307
+ { code: { $in: namesOrCodes } }
308
+ ]
309
+ },
310
+ fields: { _id: 1, id: 1, name: 1, code: 1 },
311
+ limit: constants.LIMIT_INFINITY
312
+ })
313
+ }, stream2);
314
+ const map = /* @__PURE__ */ new Map();
315
+ if (result.isOk && result.payload?.data) {
316
+ result.payload.data.forEach((st) => {
317
+ if (st.name) {
318
+ map.set(st.name, st.id || st._id);
319
+ }
320
+ if (st.code) {
321
+ map.set(st.code, st.id || st._id);
322
+ }
323
+ });
324
+ }
325
+ return map;
326
+ }
327
+ async function findTeachers(stream2, identifiers) {
328
+ if (identifiers.length === 0) {
329
+ return /* @__PURE__ */ new Map();
330
+ }
331
+ const teacherIds = identifiers.filter((i) => i.type === "idPengajar").map((i) => i.value.trim());
332
+ const userIds = identifiers.filter((i) => i.type === "idIdentitas").map((i) => i.value.trim());
333
+ const nips = identifiers.filter((i) => i.type === "nip").map((i) => i.value.trim());
334
+ const conditions = [];
335
+ if (userIds.length) {
336
+ conditions.push({ userId: { $in: userIds } });
337
+ }
338
+ if (nips.length) {
339
+ conditions.push({ nip: { $in: nips } });
340
+ }
341
+ if (teacherIds.length) {
342
+ const validIds = teacherIds.filter((id) => /^[0-9a-f]{24}$/i.test(id)).map((id) => new core.ObjectId(id));
343
+ if (validIds.length > 0) {
344
+ conditions.push({ _id: { $in: validIds } });
345
+ }
346
+ conditions.push({ id: { $in: teacherIds } });
347
+ if (validIds.length === 0 && teacherIds.length > 0) {
348
+ console.warn("[ImportClassSession] No valid Hex ObjectIds found for idPengajar:", teacherIds);
349
+ }
350
+ }
351
+ if (conditions.length === 0) {
352
+ console.warn("[ImportClassSession] findTeachers: No valid query conditions.");
353
+ return /* @__PURE__ */ new Map();
354
+ }
355
+ const result = await stream2.actions.data.getMany.execute({
356
+ model: "neu:akademik:teacher",
357
+ query: query.Query.define({
358
+ filter: { $or: conditions },
359
+ fields: { _id: 1, id: 1, nip: 1, userId: 1 },
360
+ limit: constants.LIMIT_INFINITY
361
+ })
362
+ }, stream2);
363
+ const map = /* @__PURE__ */ new Map();
364
+ if (result.isOk && result.payload?.data) {
365
+ result.payload.data.forEach((t) => {
366
+ const id = t.id || t._id?.toString();
367
+ const { userId, nip } = t;
368
+ if (userId) {
369
+ map.set(`idIdentitas:${userId}`, id);
370
+ }
371
+ if (id) {
372
+ map.set(`idPengajar:${id}`, id);
373
+ }
374
+ if (nip) {
375
+ map.set(`nip:${nip}`, id);
376
+ }
377
+ });
378
+ } else {
379
+ console.error("[ImportClassSession] findTeachers failed:", result.message);
380
+ }
381
+ return map;
382
+ }
383
+ return { prepareGradingMapper, prepareClassSessionMapper };
125
384
  }
126
385
 
127
386
  exports.useImportData = useImportData;
@@ -5,4 +5,10 @@ export declare function useImportData(stream: Stream | undefined): {
5
5
  }) => Promise<{
6
6
  status: string;
7
7
  }>;
8
+ prepareClassSessionMapper: (stream: NStream.Stream, input: {
9
+ datas: [];
10
+ classGroupName: string;
11
+ }) => Promise<{
12
+ status: string;
13
+ }>;
8
14
  };
@@ -119,7 +119,266 @@ function useImportData(stream) {
119
119
  await _gradingMapper(stream2, { gradings: batch });
120
120
  await new Promise((resolve) => setTimeout(resolve, 5e3));
121
121
  }
122
- return { prepareGradingMapper };
122
+ async function prepareClassSessionMapper(stream2, input) {
123
+ guard(stream2, "streamRequired");
124
+ if (!input.classGroupName) {
125
+ throw new Error("Class Group Name is required for import.");
126
+ }
127
+ const classGroup = await findClassGroupByName(stream2, input.classGroupName);
128
+ if (!classGroup) {
129
+ console.error(`[ImportClassSession] Class Group '${input.classGroupName}' not found.`);
130
+ throw new Error(`Class Group '${input.classGroupName}' not found.`);
131
+ }
132
+ const groupId = classGroup._id || classGroup.id;
133
+ const stageId = classGroup.stageId;
134
+ void queueArrayInChunks(stream2, input.datas, 100, async (s, batch) => {
135
+ await _classSessionMapper(s, { sessions: batch, classGroupId: groupId, stageId });
136
+ });
137
+ return { status: "CSV has being processed" };
138
+ }
139
+ async function findClassGroupByName(stream2, name) {
140
+ const result = await stream2.actions.data.getMany.execute({
141
+ model: "neu:jadwal:classGroup",
142
+ query: Query.define({
143
+ filter: { name },
144
+ fields: { _id: 1, id: 1, stageId: 1 },
145
+ limit: 1
146
+ })
147
+ }, stream2);
148
+ return result.isOk && result.payload?.data?.[0] || null;
149
+ }
150
+ async function _classSessionMapper(stream2, input) {
151
+ const operations = [];
152
+ const subjectCodes = [...new Set(input.sessions.flatMap(
153
+ (s) => (s.pelajaran || "").split(";").map((code) => code.trim()).filter(Boolean)
154
+ ))];
155
+ const subjectMap = await findSubjectsByCodes(stream2, subjectCodes);
156
+ const stageNames = [...new Set(input.sessions.flatMap(
157
+ (s) => s.jenjangStudi ? [s.jenjangStudi.trim()] : []
158
+ ))];
159
+ const stageMap = await findStages(stream2, stageNames);
160
+ const teacherIdentifiers = [];
161
+ input.sessions.forEach((s) => {
162
+ if (s.idIdentitasPengajar)
163
+ teacherIdentifiers.push({ type: "idIdentitas", value: s.idIdentitasPengajar });
164
+ if (s.nomorIndukPengajar)
165
+ teacherIdentifiers.push({ type: "nip", value: s.nomorIndukPengajar });
166
+ if (s.idPengajar)
167
+ teacherIdentifiers.push({ type: "idPengajar", value: s.idPengajar });
168
+ });
169
+ const teacherMap = await findTeachers(stream2, teacherIdentifiers);
170
+ for (const session of input.sessions) {
171
+ const title = session.judulJadwal || null;
172
+ let status = "draft";
173
+ if (session.status?.toLowerCase() === "tayang")
174
+ status = "published";
175
+ else if (session.status?.toLowerCase() === "arsip")
176
+ status = "archived";
177
+ let type = "offline";
178
+ if (session.tipe?.toLowerCase() === "live")
179
+ type = "online";
180
+ else if (session.tipe?.toLowerCase() === "hybrid")
181
+ type = "hybrid";
182
+ let startedAt = null;
183
+ let endedAt = null;
184
+ try {
185
+ const parseDate = (dateStr, timeStr) => {
186
+ if (!dateStr || !timeStr)
187
+ return null;
188
+ const parts = dateStr.split("/");
189
+ let formattedDate = dateStr;
190
+ if (parts.length === 3) {
191
+ formattedDate = `${parts[2]}-${parts[1]}-${parts[0]}`;
192
+ }
193
+ return (/* @__PURE__ */ new Date(`${formattedDate}T${timeStr}:00+07:00`)).toISOString();
194
+ };
195
+ if (session.tanggal && session.mulai)
196
+ startedAt = parseDate(session.tanggal, session.mulai);
197
+ if (session.tanggal && session.selesai)
198
+ endedAt = parseDate(session.tanggal, session.selesai);
199
+ } catch (e) {
200
+ console.error("[ImportClassSession] Date parsing error", e);
201
+ }
202
+ const teacherIds = [];
203
+ let teacherIdStr;
204
+ if (session.idIdentitasPengajar)
205
+ teacherIdStr = teacherMap.get(`idIdentitas:${session.idIdentitasPengajar}`);
206
+ else if (session.nomorIndukPengajar)
207
+ teacherIdStr = teacherMap.get(`nip:${session.nomorIndukPengajar}`);
208
+ else if (session.idPengajar)
209
+ teacherIdStr = teacherMap.get(`idPengajar:${session.idPengajar}`);
210
+ if (teacherIdStr) {
211
+ teacherIds.push(new ObjectId(teacherIdStr));
212
+ }
213
+ const subjectIds = [];
214
+ if (session.pelajaran) {
215
+ const codes = session.pelajaran.split(";").map((c) => c.trim()).filter(Boolean);
216
+ codes.forEach((code) => {
217
+ const sId = subjectMap.get(code);
218
+ if (sId)
219
+ subjectIds.push(new ObjectId(sId));
220
+ });
221
+ }
222
+ let sessionStageId = input.stageId;
223
+ if (session.jenjangStudi) {
224
+ const sName = session.jenjangStudi.trim();
225
+ const mappedStageId = stageMap.get(sName);
226
+ if (mappedStageId)
227
+ sessionStageId = mappedStageId;
228
+ }
229
+ const newId = new ObjectId();
230
+ operations.push({
231
+ insertOne: {
232
+ document: {
233
+ name: title,
234
+ description: session.subjudul,
235
+ note: session.catatanLokasi,
236
+ title,
237
+ subtitle: session.subjudul || null,
238
+ locationText: session.catatanLokasi || null,
239
+ map: session.peta || null,
240
+ link: session.link || null,
241
+ password: session.password || null,
242
+ startedAt,
243
+ endedAt,
244
+ _id: newId,
245
+ id: newId.toString(),
246
+ groupIds: [input.classGroupId],
247
+ stageId: sessionStageId,
248
+ teacherIds,
249
+ subjectIds,
250
+ status,
251
+ isTeacherShown: true,
252
+ isTeacherSubtituted: false,
253
+ type,
254
+ locationType: type === "online" ? null : "building",
255
+ isTitleSession: !!(session.judulJadwal && session.judulJadwal.trim()),
256
+ createdAt: /* @__PURE__ */ new Date(),
257
+ updatedAt: /* @__PURE__ */ new Date(),
258
+ meta: {
259
+ importData: session,
260
+ source: "import-csv"
261
+ }
262
+ }
263
+ }
264
+ });
265
+ }
266
+ if (operations.length > 0) {
267
+ await stream2.core.dbs["neu-jadwal"].models["neu:jadwal:classSession"].bulkWrite(operations);
268
+ }
269
+ }
270
+ async function findSubjectsByCodes(stream2, codes) {
271
+ if (codes.length === 0) {
272
+ return /* @__PURE__ */ new Map();
273
+ }
274
+ console.log("[ImportClassSession] findSubjectsByCodes input:", codes);
275
+ const result = await stream2.actions.data.getMany.execute({
276
+ model: "neu:akademik:academicSubject",
277
+ query: Query.define({
278
+ filter: { code: { $in: codes } },
279
+ fields: { _id: 1, id: 1, code: 1, name: 1 },
280
+ limit: LIMIT_INFINITY
281
+ })
282
+ }, stream2);
283
+ const map = /* @__PURE__ */ new Map();
284
+ if (result.isOk && result.payload?.data) {
285
+ console.log(`[ImportClassSession] Found ${result.payload.data.length} subjects`);
286
+ result.payload.data.forEach((subject) => {
287
+ console.log(`[ImportClassSession] Mapping Subject Code: '${subject.code}' -> ID: '${subject.id || subject._id}'`);
288
+ map.set(subject.code, subject.id || subject._id);
289
+ });
290
+ } else {
291
+ console.log("[ImportClassSession] findSubjectsByCodes failed or empty:", result.message);
292
+ }
293
+ return map;
294
+ }
295
+ async function findStages(stream2, namesOrCodes) {
296
+ if (namesOrCodes.length === 0) {
297
+ return /* @__PURE__ */ new Map();
298
+ }
299
+ const result = await stream2.actions.data.getMany.execute({
300
+ model: "neo:akademik:stage",
301
+ query: Query.define({
302
+ filter: {
303
+ $or: [
304
+ { name: { $in: namesOrCodes } },
305
+ { code: { $in: namesOrCodes } }
306
+ ]
307
+ },
308
+ fields: { _id: 1, id: 1, name: 1, code: 1 },
309
+ limit: LIMIT_INFINITY
310
+ })
311
+ }, stream2);
312
+ const map = /* @__PURE__ */ new Map();
313
+ if (result.isOk && result.payload?.data) {
314
+ result.payload.data.forEach((st) => {
315
+ if (st.name) {
316
+ map.set(st.name, st.id || st._id);
317
+ }
318
+ if (st.code) {
319
+ map.set(st.code, st.id || st._id);
320
+ }
321
+ });
322
+ }
323
+ return map;
324
+ }
325
+ async function findTeachers(stream2, identifiers) {
326
+ if (identifiers.length === 0) {
327
+ return /* @__PURE__ */ new Map();
328
+ }
329
+ const teacherIds = identifiers.filter((i) => i.type === "idPengajar").map((i) => i.value.trim());
330
+ const userIds = identifiers.filter((i) => i.type === "idIdentitas").map((i) => i.value.trim());
331
+ const nips = identifiers.filter((i) => i.type === "nip").map((i) => i.value.trim());
332
+ const conditions = [];
333
+ if (userIds.length) {
334
+ conditions.push({ userId: { $in: userIds } });
335
+ }
336
+ if (nips.length) {
337
+ conditions.push({ nip: { $in: nips } });
338
+ }
339
+ if (teacherIds.length) {
340
+ const validIds = teacherIds.filter((id) => /^[0-9a-f]{24}$/i.test(id)).map((id) => new ObjectId(id));
341
+ if (validIds.length > 0) {
342
+ conditions.push({ _id: { $in: validIds } });
343
+ }
344
+ conditions.push({ id: { $in: teacherIds } });
345
+ if (validIds.length === 0 && teacherIds.length > 0) {
346
+ console.warn("[ImportClassSession] No valid Hex ObjectIds found for idPengajar:", teacherIds);
347
+ }
348
+ }
349
+ if (conditions.length === 0) {
350
+ console.warn("[ImportClassSession] findTeachers: No valid query conditions.");
351
+ return /* @__PURE__ */ new Map();
352
+ }
353
+ const result = await stream2.actions.data.getMany.execute({
354
+ model: "neu:akademik:teacher",
355
+ query: Query.define({
356
+ filter: { $or: conditions },
357
+ fields: { _id: 1, id: 1, nip: 1, userId: 1 },
358
+ limit: LIMIT_INFINITY
359
+ })
360
+ }, stream2);
361
+ const map = /* @__PURE__ */ new Map();
362
+ if (result.isOk && result.payload?.data) {
363
+ result.payload.data.forEach((t) => {
364
+ const id = t.id || t._id?.toString();
365
+ const { userId, nip } = t;
366
+ if (userId) {
367
+ map.set(`idIdentitas:${userId}`, id);
368
+ }
369
+ if (id) {
370
+ map.set(`idPengajar:${id}`, id);
371
+ }
372
+ if (nip) {
373
+ map.set(`nip:${nip}`, id);
374
+ }
375
+ });
376
+ } else {
377
+ console.error("[ImportClassSession] findTeachers failed:", result.message);
378
+ }
379
+ return map;
380
+ }
381
+ return { prepareGradingMapper, prepareClassSessionMapper };
123
382
  }
124
383
 
125
384
  export { useImportData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neutron.co.id/pendidikan-operation",
3
- "version": "1.27.8",
3
+ "version": "1.27.11",
4
4
  "description": "Operation package of Neutron Pendidikan.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "contributors": [
@@ -39,9 +39,9 @@
39
39
  "@neon.id/types": "^1.69.0",
40
40
  "@neon.id/utils": "^1.52.0",
41
41
  "@neon.id/z": "^1.16.0",
42
- "@neutron.co.id/akademik-models": "^1.21.3",
42
+ "@neutron.co.id/akademik-models": "^1.21.5-beta.1",
43
43
  "@neutron.co.id/jadwal-models": "^1.19.13",
44
- "@neutron.co.id/pendidikan-types": "^1.23.2",
44
+ "@neutron.co.id/pendidikan-types": "^1.23.3",
45
45
  "@neutron.co.id/penilaian-models": "^1.17.8",
46
46
  "@neutron.co.id/personalia-models": "^1.11.6",
47
47
  "@neutron.co.id/tanya-models": "^1.13.4",
@@ -75,9 +75,9 @@
75
75
  "@neon.id/types": "^1.69.0",
76
76
  "@neon.id/utils": "^1.52.0",
77
77
  "@neon.id/z": "^1.16.0",
78
- "@neutron.co.id/akademik-models": "^1.21.3",
78
+ "@neutron.co.id/akademik-models": "^1.21.5-beta.1",
79
79
  "@neutron.co.id/jadwal-models": "^1.19.13",
80
- "@neutron.co.id/pendidikan-types": "^1.23.2",
80
+ "@neutron.co.id/pendidikan-types": "^1.23.3",
81
81
  "@neutron.co.id/penilaian-models": "^1.17.8",
82
82
  "@neutron.co.id/personalia-models": "^1.11.6",
83
83
  "@neutron.co.id/tanya-models": "^1.13.4",
@@ -86,5 +86,5 @@
86
86
  "publishConfig": {
87
87
  "access": "public"
88
88
  },
89
- "build": 133
89
+ "build": 136
90
90
  }