@neutron.co.id/pendidikan-operation 1.27.9 → 1.27.12

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.
@@ -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 };
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ const core = require('@neon.id/core');
4
+ const operation = require('@neon.id/operation');
5
+ const utils = require('@neon.id/utils');
6
+ const useTelemetry = require('../../providers/useTelemetry.cjs');
7
+
8
+ const bulkDeleteSession = operation.Action.define({
9
+ key: "bulkDeleteSession",
10
+ name: "Bulk Delete Session",
11
+ type: "command",
12
+ category: "domain",
13
+ execute: async (input, stream) => {
14
+ utils.guard(stream, "streamRequired");
15
+ utils.guard(input, "inputRequired");
16
+ return useTelemetry.useTelemetry(stream, "bulkDeleteSession", {
17
+ userId: stream.context.identitas.userId,
18
+ ...input
19
+ }, async () => {
20
+ const dbs = stream.core.dbs;
21
+ const dbSession = dbs["neu-jadwal"].models["neu:jadwal:classSession"];
22
+ await dbSession.updateMany(
23
+ {
24
+ _id: { $in: input.ids }
25
+ },
26
+ {
27
+ $set: {
28
+ deletedAt: /* @__PURE__ */ new Date()
29
+ }
30
+ }
31
+ );
32
+ return core.Result.ok({
33
+ state: "bulkSessionDelete",
34
+ message: "Sessions have been deleted.",
35
+ data: {
36
+ code: 1
37
+ }
38
+ });
39
+ });
40
+ }
41
+ });
42
+
43
+ exports.bulkDeleteSession = bulkDeleteSession;
@@ -0,0 +1,11 @@
1
+ import { Action } from '@neon.id/operation';
2
+ export interface BulkDeleteSessionInput {
3
+ ids: string[];
4
+ }
5
+ export interface BulkDeleteSessionOutput {
6
+ code: number;
7
+ }
8
+ export interface BulkDeleteSessionMeta {
9
+ }
10
+ export declare const bulkDeleteSession: Action<"bulkDeleteSession", BulkDeleteSessionInput, BulkDeleteSessionOutput, BulkDeleteSessionMeta>;
11
+ export type BulkDeleteSessionAction = typeof bulkDeleteSession;
@@ -0,0 +1,41 @@
1
+ import { Result } from '@neon.id/core';
2
+ import { Action } from '@neon.id/operation';
3
+ import { guard } from '@neon.id/utils';
4
+ import { useTelemetry } from '../../providers/useTelemetry.mjs';
5
+
6
+ const bulkDeleteSession = Action.define({
7
+ key: "bulkDeleteSession",
8
+ name: "Bulk Delete Session",
9
+ type: "command",
10
+ category: "domain",
11
+ execute: async (input, stream) => {
12
+ guard(stream, "streamRequired");
13
+ guard(input, "inputRequired");
14
+ return useTelemetry(stream, "bulkDeleteSession", {
15
+ userId: stream.context.identitas.userId,
16
+ ...input
17
+ }, async () => {
18
+ const dbs = stream.core.dbs;
19
+ const dbSession = dbs["neu-jadwal"].models["neu:jadwal:classSession"];
20
+ await dbSession.updateMany(
21
+ {
22
+ _id: { $in: input.ids }
23
+ },
24
+ {
25
+ $set: {
26
+ deletedAt: /* @__PURE__ */ new Date()
27
+ }
28
+ }
29
+ );
30
+ return Result.ok({
31
+ state: "bulkSessionDelete",
32
+ message: "Sessions have been deleted.",
33
+ data: {
34
+ code: 1
35
+ }
36
+ });
37
+ });
38
+ }
39
+ });
40
+
41
+ export { bulkDeleteSession };
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ const core = require('@neon.id/core');
4
+ const operation = require('@neon.id/operation');
5
+ const utils = require('@neon.id/utils');
6
+ const useTelemetry = require('../../providers/useTelemetry.cjs');
7
+
8
+ const bulkUpdateSession = operation.Action.define({
9
+ key: "bulkUpdateSession",
10
+ name: "Bulk Update Session",
11
+ type: "command",
12
+ category: "domain",
13
+ execute: async (input, stream) => {
14
+ utils.guard(stream, "streamRequired");
15
+ utils.guard(input, "inputRequired");
16
+ return useTelemetry.useTelemetry(stream, "bulkUpdateSession", {
17
+ userId: stream.context.identitas.userId,
18
+ ...input
19
+ }, async () => {
20
+ const dbs = stream.core.dbs;
21
+ const dbSession = dbs["neu-jadwal"].models["neu:jadwal:classSession"];
22
+ const updateData = {};
23
+ if (input.data.teacherRepeaterIds)
24
+ updateData.teacherIds = input.data.teacherRepeaterIds;
25
+ if (input.data.teacherIds)
26
+ updateData.teacherIds = input.data.teacherIds;
27
+ if (input.data.subjectIds)
28
+ updateData.subjectIds = input.data.subjectIds;
29
+ if (input.data.roomId)
30
+ updateData.roomId = input.data.roomId;
31
+ if (input.data.buildingId)
32
+ updateData.buildingId = input.data.buildingId;
33
+ if (input.data.status)
34
+ updateData.status = input.data.status;
35
+ if (input.data.classSessionPurposeId)
36
+ updateData.classSessionPurposeId = input.data.classSessionPurposeId;
37
+ await dbSession.updateMany(
38
+ {
39
+ _id: { $in: input.ids }
40
+ },
41
+ {
42
+ $set: updateData
43
+ }
44
+ );
45
+ return core.Result.ok({
46
+ state: "bulkSessionUpdate",
47
+ message: "Sessions have been updated.",
48
+ data: {
49
+ code: 1
50
+ }
51
+ });
52
+ });
53
+ }
54
+ });
55
+
56
+ exports.bulkUpdateSession = bulkUpdateSession;
@@ -0,0 +1,20 @@
1
+ import { Action } from '@neon.id/operation';
2
+ export interface BulkUpdateSessionInput {
3
+ ids: string[];
4
+ data: {
5
+ teacherRepeaterIds?: string[];
6
+ teacherIds?: string[];
7
+ subjectIds?: string[];
8
+ roomId?: string;
9
+ buildingId?: string;
10
+ status?: string;
11
+ classSessionPurposeId?: string;
12
+ };
13
+ }
14
+ export interface BulkUpdateSessionOutput {
15
+ code: number;
16
+ }
17
+ export interface BulkUpdateSessionMeta {
18
+ }
19
+ export declare const bulkUpdateSession: Action<"bulkUpdateSession", BulkUpdateSessionInput, BulkUpdateSessionOutput, BulkUpdateSessionMeta>;
20
+ export type BulkUpdateSessionAction = typeof bulkUpdateSession;
@@ -0,0 +1,54 @@
1
+ import { Result } from '@neon.id/core';
2
+ import { Action } from '@neon.id/operation';
3
+ import { guard } from '@neon.id/utils';
4
+ import { useTelemetry } from '../../providers/useTelemetry.mjs';
5
+
6
+ const bulkUpdateSession = Action.define({
7
+ key: "bulkUpdateSession",
8
+ name: "Bulk Update Session",
9
+ type: "command",
10
+ category: "domain",
11
+ execute: async (input, stream) => {
12
+ guard(stream, "streamRequired");
13
+ guard(input, "inputRequired");
14
+ return useTelemetry(stream, "bulkUpdateSession", {
15
+ userId: stream.context.identitas.userId,
16
+ ...input
17
+ }, async () => {
18
+ const dbs = stream.core.dbs;
19
+ const dbSession = dbs["neu-jadwal"].models["neu:jadwal:classSession"];
20
+ const updateData = {};
21
+ if (input.data.teacherRepeaterIds)
22
+ updateData.teacherIds = input.data.teacherRepeaterIds;
23
+ if (input.data.teacherIds)
24
+ updateData.teacherIds = input.data.teacherIds;
25
+ if (input.data.subjectIds)
26
+ updateData.subjectIds = input.data.subjectIds;
27
+ if (input.data.roomId)
28
+ updateData.roomId = input.data.roomId;
29
+ if (input.data.buildingId)
30
+ updateData.buildingId = input.data.buildingId;
31
+ if (input.data.status)
32
+ updateData.status = input.data.status;
33
+ if (input.data.classSessionPurposeId)
34
+ updateData.classSessionPurposeId = input.data.classSessionPurposeId;
35
+ await dbSession.updateMany(
36
+ {
37
+ _id: { $in: input.ids }
38
+ },
39
+ {
40
+ $set: updateData
41
+ }
42
+ );
43
+ return Result.ok({
44
+ state: "bulkSessionUpdate",
45
+ message: "Sessions have been updated.",
46
+ data: {
47
+ code: 1
48
+ }
49
+ });
50
+ });
51
+ }
52
+ });
53
+
54
+ export { bulkUpdateSession };
@@ -14,3 +14,5 @@ export * from './action.customSaveOneClassSession';
14
14
  export * from './syncClassGroups';
15
15
  export * from './sendClassConsultationNotification';
16
16
  export * from './sendClassSessionNotification';
17
+ export * from './action.bulkDeleteSession';
18
+ export * from './action.bulkUpdateSession';
package/build/index.cjs CHANGED
@@ -30,6 +30,8 @@ const setAksesModulSiswaDiClassGroup_action = require('./actions/jadwal/setAkses
30
30
  const getClassSessionConflicts_action = require('./actions/jadwal/getClassSessionConflicts/getClassSessionConflicts.action.cjs');
31
31
  const setTravelWageSessions_action = require('./actions/jadwal/setTravelWageSessions/setTravelWageSessions.action.cjs');
32
32
  const action_customSaveOneClassSession = require('./actions/jadwal/action.customSaveOneClassSession.cjs');
33
+ const action_bulkDeleteSession = require('./actions/jadwal/action.bulkDeleteSession.cjs');
34
+ const action_bulkUpdateSession = require('./actions/jadwal/action.bulkUpdateSession.cjs');
33
35
  const action_getGradingCount = require('./actions/penilaian/getGradingCount/action.getGradingCount.cjs');
34
36
  const refreshGrading_action = require('./actions/penilaian/refreshGrading/refreshGrading.action.cjs');
35
37
  const refreshManyGrading_action = require('./actions/penilaian/refreshManyGrading/refreshManyGrading.action.cjs');
@@ -118,6 +120,8 @@ const actions = {
118
120
  getClassSessionConflicts: getClassSessionConflicts_action.getClassSessionConflicts,
119
121
  setTravelWageSessions: setTravelWageSessions_action.setTravelWageSessions,
120
122
  customSaveOneClassSession: action_customSaveOneClassSession.customSaveOneClassSession,
123
+ bulkDeleteSession: action_bulkDeleteSession.bulkDeleteSession,
124
+ bulkUpdateSession: action_bulkUpdateSession.bulkUpdateSession,
121
125
  // Penilaian
122
126
  getGradingCount: action_getGradingCount.getGradingCount,
123
127
  refreshGrading: refreshGrading_action.refreshGrading,
@@ -184,6 +188,8 @@ exports.setAksesModulSiswaDiClassGroup = setAksesModulSiswaDiClassGroup_action.s
184
188
  exports.getClassSessionConflicts = getClassSessionConflicts_action.getClassSessionConflicts;
185
189
  exports.setTravelWageSessions = setTravelWageSessions_action.setTravelWageSessions;
186
190
  exports.customSaveOneClassSession = action_customSaveOneClassSession.customSaveOneClassSession;
191
+ exports.bulkDeleteSession = action_bulkDeleteSession.bulkDeleteSession;
192
+ exports.bulkUpdateSession = action_bulkUpdateSession.bulkUpdateSession;
187
193
  exports.getGradingCount = action_getGradingCount.getGradingCount;
188
194
  exports.refreshGrading = refreshGrading_action.refreshGrading;
189
195
  exports.refreshManyGrading = refreshManyGrading_action.refreshManyGrading;
package/build/index.d.ts CHANGED
@@ -78,6 +78,8 @@ export declare const actions: {
78
78
  classSessionIds?: string[] | undefined;
79
79
  }, import("./actions").SetTravelWageSessionsOutput, import("./actions").SetTravelWageSessionsMeta>;
80
80
  customSaveOneClassSession: import("@neon.id/operation").Action<"customSaveOneClassSession", import("./actions").CustomSaveOneClassSessionInput, import("./actions").CustomSaveOneClassSessionOutput, import("./actions").CustomSaveOneClassSessionMeta>;
81
+ bulkDeleteSession: import("@neon.id/operation").Action<"bulkDeleteSession", import("./actions").BulkDeleteSessionInput, import("./actions").BulkDeleteSessionOutput, import("./actions").BulkDeleteSessionMeta>;
82
+ bulkUpdateSession: import("@neon.id/operation").Action<"bulkUpdateSession", import("./actions").BulkUpdateSessionInput, import("./actions").BulkUpdateSessionOutput, import("./actions").BulkUpdateSessionMeta>;
81
83
  getGradingCount: import("@neon.id/operation").Action<"getGradingCount", import("./actions").GetGradingCountInput, import("./actions").GetGradingCountOutput, import("./actions").GetGradingCountMeta>;
82
84
  refreshGrading: import("@neon.id/operation").Action<"refreshGrading", {
83
85
  gradingId?: string | undefined;
@@ -108,7 +110,9 @@ export declare const actions: {
108
110
  eventId?: string | undefined;
109
111
  }, import("./actions").PrepareMediaScanterGradingInsertOutput, import("./actions").PrepareMediaScanterGradingInsertMeta>;
110
112
  importData: import("@neon.id/operation").Action<"importData", {
113
+ type: string;
111
114
  csvData?: any[] | undefined;
115
+ classGroupName?: string | undefined;
112
116
  }, import("./actions").ImportDataOutput, import("./actions").ImportDataMeta>;
113
117
  updateReportStudent: import("@neon.id/operation").Action<"updateReportStudent", {
114
118
  reportBranchId: string;
package/build/index.mjs CHANGED
@@ -28,6 +28,8 @@ import { setAksesModulSiswaDiClassGroup } from './actions/jadwal/setAksesModulSi
28
28
  import { getClassSessionConflicts } from './actions/jadwal/getClassSessionConflicts/getClassSessionConflicts.action.mjs';
29
29
  import { setTravelWageSessions } from './actions/jadwal/setTravelWageSessions/setTravelWageSessions.action.mjs';
30
30
  import { customSaveOneClassSession } from './actions/jadwal/action.customSaveOneClassSession.mjs';
31
+ import { bulkDeleteSession } from './actions/jadwal/action.bulkDeleteSession.mjs';
32
+ import { bulkUpdateSession } from './actions/jadwal/action.bulkUpdateSession.mjs';
31
33
  import { getGradingCount } from './actions/penilaian/getGradingCount/action.getGradingCount.mjs';
32
34
  import { refreshGrading } from './actions/penilaian/refreshGrading/refreshGrading.action.mjs';
33
35
  import { refreshManyGrading } from './actions/penilaian/refreshManyGrading/refreshManyGrading.action.mjs';
@@ -118,6 +120,8 @@ const actions = {
118
120
  getClassSessionConflicts,
119
121
  setTravelWageSessions,
120
122
  customSaveOneClassSession,
123
+ bulkDeleteSession,
124
+ bulkUpdateSession,
121
125
  // Penilaian
122
126
  getGradingCount,
123
127
  refreshGrading,
@@ -154,4 +158,4 @@ const actions = {
154
158
  syncStudents
155
159
  };
156
160
 
157
- export { acceptQuestion, actions, addManyGradingComponent, allConflict, calculateGrading, calculateManyComparator, calculateOneComparator, checkClassAttendance, classSessionInventoryOccurs, classSessionInventoryPreparation, clearAllOverrides, clearGrading, clearOneOverrides, createGradingAndScores, createManySession, customSaveOneClassSession, deleteManySession, editAnswer, generateGrading, getClassSessionConflicts, getGradingCount, getQuestionCount, getStaffId, getStaffPoint, getStudentId, getStudentPoint, getTeacherPoint, hasUnderstand, importData, notUnderstand, prepareConflict, prepareExperience, prepareMediaScanterGradingInsert, presenceSessionStudent, presenceSessionTeacher, rasionalizeGrading, refreshGrading, refreshManyGrading, refreshModuleAccess, reminderQuestions, replaceModuleAccess, replaceModuleAccessManyStudent, sendAnswer, sendClassConsultationNotification, sendClassSessionNotification, sendQuestion, sendScoreNotification, setAksesModulSiswaDiClassGroup, setTravelWageSessions, syncClassGroups, syncClassSessions, syncCommitment, syncStudentAdmisi, syncStudentBranch, syncStudentInformation, syncStudentReport, syncStudents, updateGradingAndScores, updateMany, updateReportStudent, userCountStats };
161
+ export { acceptQuestion, actions, addManyGradingComponent, allConflict, bulkDeleteSession, bulkUpdateSession, calculateGrading, calculateManyComparator, calculateOneComparator, checkClassAttendance, classSessionInventoryOccurs, classSessionInventoryPreparation, clearAllOverrides, clearGrading, clearOneOverrides, createGradingAndScores, createManySession, customSaveOneClassSession, deleteManySession, editAnswer, generateGrading, getClassSessionConflicts, getGradingCount, getQuestionCount, getStaffId, getStaffPoint, getStudentId, getStudentPoint, getTeacherPoint, hasUnderstand, importData, notUnderstand, prepareConflict, prepareExperience, prepareMediaScanterGradingInsert, presenceSessionStudent, presenceSessionTeacher, rasionalizeGrading, refreshGrading, refreshManyGrading, refreshModuleAccess, reminderQuestions, replaceModuleAccess, replaceModuleAccessManyStudent, sendAnswer, sendClassConsultationNotification, sendClassSessionNotification, sendQuestion, sendScoreNotification, setAksesModulSiswaDiClassGroup, setTravelWageSessions, syncClassGroups, syncClassSessions, syncCommitment, syncStudentAdmisi, syncStudentBranch, syncStudentInformation, syncStudentReport, syncStudents, updateGradingAndScores, updateMany, updateReportStudent, userCountStats };
@@ -121,7 +121,268 @@ 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
+ const branchIds = classGroup.branchIds || [];
137
+ void queueArrayInChunks(stream2, input.datas, 100, async (s, batch) => {
138
+ await _classSessionMapper(s, { sessions: batch, classGroupId: groupId, stageId, branchIds });
139
+ });
140
+ return { status: "CSV has being processed" };
141
+ }
142
+ async function findClassGroupByName(stream2, name) {
143
+ const result = await stream2.actions.data.getMany.execute({
144
+ model: "neu:jadwal:classGroup",
145
+ query: query.Query.define({
146
+ filter: { name },
147
+ fields: { _id: 1, id: 1, stageId: 1, branchIds: 1 },
148
+ limit: 1
149
+ })
150
+ }, stream2);
151
+ return result.isOk && result.payload?.data?.[0] || null;
152
+ }
153
+ async function _classSessionMapper(stream2, input) {
154
+ const operations = [];
155
+ const subjectCodes = [...new Set(input.sessions.flatMap(
156
+ (s) => (s.pelajaran || "").split(";").map((code) => code.trim()).filter(Boolean)
157
+ ))];
158
+ const subjectMap = await findSubjectsByCodes(stream2, subjectCodes);
159
+ const stageNames = [...new Set(input.sessions.flatMap(
160
+ (s) => s.jenjangStudi ? [s.jenjangStudi.trim()] : []
161
+ ))];
162
+ const stageMap = await findStages(stream2, stageNames);
163
+ const teacherIdentifiers = [];
164
+ input.sessions.forEach((s) => {
165
+ if (s.idIdentitasPengajar)
166
+ teacherIdentifiers.push({ type: "idIdentitas", value: s.idIdentitasPengajar });
167
+ if (s.nomorIndukPengajar)
168
+ teacherIdentifiers.push({ type: "nip", value: s.nomorIndukPengajar });
169
+ if (s.idPengajar)
170
+ teacherIdentifiers.push({ type: "idPengajar", value: s.idPengajar });
171
+ });
172
+ const teacherMap = await findTeachers(stream2, teacherIdentifiers);
173
+ for (const session of input.sessions) {
174
+ const title = session.judulJadwal || null;
175
+ let status = "draft";
176
+ if (session.status?.toLowerCase() === "tayang")
177
+ status = "published";
178
+ else if (session.status?.toLowerCase() === "arsip")
179
+ status = "archived";
180
+ let type = "offline";
181
+ if (session.tipe?.toLowerCase() === "live")
182
+ type = "online";
183
+ else if (session.tipe?.toLowerCase() === "hybrid")
184
+ type = "hybrid";
185
+ let startedAt = null;
186
+ let endedAt = null;
187
+ try {
188
+ const parseDate = (dateStr, timeStr) => {
189
+ if (!dateStr || !timeStr)
190
+ return null;
191
+ const parts = dateStr.split("/");
192
+ let formattedDate = dateStr;
193
+ if (parts.length === 3) {
194
+ formattedDate = `${parts[2]}-${parts[1]}-${parts[0]}`;
195
+ }
196
+ return (/* @__PURE__ */ new Date(`${formattedDate}T${timeStr}:00+07:00`)).toISOString();
197
+ };
198
+ if (session.tanggal && session.mulai)
199
+ startedAt = parseDate(session.tanggal, session.mulai);
200
+ if (session.tanggal && session.selesai)
201
+ endedAt = parseDate(session.tanggal, session.selesai);
202
+ } catch (e) {
203
+ console.error("[ImportClassSession] Date parsing error", e);
204
+ }
205
+ const teacherIds = [];
206
+ let teacherIdStr;
207
+ if (session.idIdentitasPengajar)
208
+ teacherIdStr = teacherMap.get(`idIdentitas:${session.idIdentitasPengajar}`);
209
+ else if (session.nomorIndukPengajar)
210
+ teacherIdStr = teacherMap.get(`nip:${session.nomorIndukPengajar}`);
211
+ else if (session.idPengajar)
212
+ teacherIdStr = teacherMap.get(`idPengajar:${session.idPengajar}`);
213
+ if (teacherIdStr) {
214
+ teacherIds.push(new core.ObjectId(teacherIdStr));
215
+ }
216
+ const subjectIds = [];
217
+ if (session.pelajaran) {
218
+ const codes = session.pelajaran.split(";").map((c) => c.trim()).filter(Boolean);
219
+ codes.forEach((code) => {
220
+ const sId = subjectMap.get(code);
221
+ if (sId)
222
+ subjectIds.push(new core.ObjectId(sId));
223
+ });
224
+ }
225
+ let sessionStageId = input.stageId;
226
+ if (session.jenjangStudi) {
227
+ const sName = session.jenjangStudi.trim();
228
+ const mappedStageId = stageMap.get(sName);
229
+ if (mappedStageId)
230
+ sessionStageId = mappedStageId;
231
+ }
232
+ const newId = new core.ObjectId();
233
+ operations.push({
234
+ insertOne: {
235
+ document: {
236
+ name: title,
237
+ description: session.subjudul,
238
+ note: session.catatanLokasi,
239
+ title,
240
+ subtitle: session.subjudul || null,
241
+ locationText: session.catatanLokasi || null,
242
+ map: session.peta || null,
243
+ link: session.link || null,
244
+ password: session.password || null,
245
+ startedAt,
246
+ endedAt,
247
+ _id: newId,
248
+ id: newId.toString(),
249
+ groupIds: [input.classGroupId],
250
+ stageId: sessionStageId,
251
+ branchIds: input.branchIds.map((id) => new core.ObjectId(id)),
252
+ teacherIds,
253
+ subjectIds,
254
+ status,
255
+ isTeacherShown: true,
256
+ isTeacherSubtituted: false,
257
+ type,
258
+ locationType: type === "online" ? null : "building",
259
+ isTitleSession: !!(session.judulJadwal && session.judulJadwal.trim()),
260
+ createdAt: /* @__PURE__ */ new Date(),
261
+ updatedAt: /* @__PURE__ */ new Date(),
262
+ meta: {
263
+ importData: session,
264
+ source: "import-csv"
265
+ }
266
+ }
267
+ }
268
+ });
269
+ }
270
+ if (operations.length > 0) {
271
+ await stream2.core.dbs["neu-jadwal"].models["neu:jadwal:classSession"].bulkWrite(operations);
272
+ }
273
+ }
274
+ async function findSubjectsByCodes(stream2, codes) {
275
+ if (codes.length === 0) {
276
+ return /* @__PURE__ */ new Map();
277
+ }
278
+ console.log("[ImportClassSession] findSubjectsByCodes input:", codes);
279
+ const result = await stream2.actions.data.getMany.execute({
280
+ model: "neu:akademik:academicSubject",
281
+ query: query.Query.define({
282
+ filter: { code: { $in: codes } },
283
+ fields: { _id: 1, id: 1, code: 1, name: 1 },
284
+ limit: constants.LIMIT_INFINITY
285
+ })
286
+ }, stream2);
287
+ const map = /* @__PURE__ */ new Map();
288
+ if (result.isOk && result.payload?.data) {
289
+ console.log(`[ImportClassSession] Found ${result.payload.data.length} subjects`);
290
+ result.payload.data.forEach((subject) => {
291
+ console.log(`[ImportClassSession] Mapping Subject Code: '${subject.code}' -> ID: '${subject.id || subject._id}'`);
292
+ map.set(subject.code, subject.id || subject._id);
293
+ });
294
+ } else {
295
+ console.log("[ImportClassSession] findSubjectsByCodes failed or empty:", result.message);
296
+ }
297
+ return map;
298
+ }
299
+ async function findStages(stream2, namesOrCodes) {
300
+ if (namesOrCodes.length === 0) {
301
+ return /* @__PURE__ */ new Map();
302
+ }
303
+ const result = await stream2.actions.data.getMany.execute({
304
+ model: "neo:akademik:stage",
305
+ query: query.Query.define({
306
+ filter: {
307
+ $or: [
308
+ { name: { $in: namesOrCodes } },
309
+ { code: { $in: namesOrCodes } }
310
+ ]
311
+ },
312
+ fields: { _id: 1, id: 1, name: 1, code: 1 },
313
+ limit: constants.LIMIT_INFINITY
314
+ })
315
+ }, stream2);
316
+ const map = /* @__PURE__ */ new Map();
317
+ if (result.isOk && result.payload?.data) {
318
+ result.payload.data.forEach((st) => {
319
+ if (st.name) {
320
+ map.set(st.name, st.id || st._id);
321
+ }
322
+ if (st.code) {
323
+ map.set(st.code, st.id || st._id);
324
+ }
325
+ });
326
+ }
327
+ return map;
328
+ }
329
+ async function findTeachers(stream2, identifiers) {
330
+ if (identifiers.length === 0) {
331
+ return /* @__PURE__ */ new Map();
332
+ }
333
+ const teacherIds = identifiers.filter((i) => i.type === "idPengajar").map((i) => i.value.trim());
334
+ const userIds = identifiers.filter((i) => i.type === "idIdentitas").map((i) => i.value.trim());
335
+ const nips = identifiers.filter((i) => i.type === "nip").map((i) => i.value.trim());
336
+ const conditions = [];
337
+ if (userIds.length) {
338
+ conditions.push({ userId: { $in: userIds } });
339
+ }
340
+ if (nips.length) {
341
+ conditions.push({ nip: { $in: nips } });
342
+ }
343
+ if (teacherIds.length) {
344
+ const validIds = teacherIds.filter((id) => /^[0-9a-f]{24}$/i.test(id)).map((id) => new core.ObjectId(id));
345
+ if (validIds.length > 0) {
346
+ conditions.push({ _id: { $in: validIds } });
347
+ }
348
+ conditions.push({ id: { $in: teacherIds } });
349
+ if (validIds.length === 0 && teacherIds.length > 0) {
350
+ console.warn("[ImportClassSession] No valid Hex ObjectIds found for idPengajar:", teacherIds);
351
+ }
352
+ }
353
+ if (conditions.length === 0) {
354
+ console.warn("[ImportClassSession] findTeachers: No valid query conditions.");
355
+ return /* @__PURE__ */ new Map();
356
+ }
357
+ const result = await stream2.actions.data.getMany.execute({
358
+ model: "neu:akademik:teacher",
359
+ query: query.Query.define({
360
+ filter: { $or: conditions },
361
+ fields: { _id: 1, id: 1, nip: 1, userId: 1 },
362
+ limit: constants.LIMIT_INFINITY
363
+ })
364
+ }, stream2);
365
+ const map = /* @__PURE__ */ new Map();
366
+ if (result.isOk && result.payload?.data) {
367
+ result.payload.data.forEach((t) => {
368
+ const id = t.id || t._id?.toString();
369
+ const { userId, nip } = t;
370
+ if (userId) {
371
+ map.set(`idIdentitas:${userId}`, id);
372
+ }
373
+ if (id) {
374
+ map.set(`idPengajar:${id}`, id);
375
+ }
376
+ if (nip) {
377
+ map.set(`nip:${nip}`, id);
378
+ }
379
+ });
380
+ } else {
381
+ console.error("[ImportClassSession] findTeachers failed:", result.message);
382
+ }
383
+ return map;
384
+ }
385
+ return { prepareGradingMapper, prepareClassSessionMapper };
125
386
  }
126
387
 
127
388
  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,268 @@ 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
+ const branchIds = classGroup.branchIds || [];
135
+ void queueArrayInChunks(stream2, input.datas, 100, async (s, batch) => {
136
+ await _classSessionMapper(s, { sessions: batch, classGroupId: groupId, stageId, branchIds });
137
+ });
138
+ return { status: "CSV has being processed" };
139
+ }
140
+ async function findClassGroupByName(stream2, name) {
141
+ const result = await stream2.actions.data.getMany.execute({
142
+ model: "neu:jadwal:classGroup",
143
+ query: Query.define({
144
+ filter: { name },
145
+ fields: { _id: 1, id: 1, stageId: 1, branchIds: 1 },
146
+ limit: 1
147
+ })
148
+ }, stream2);
149
+ return result.isOk && result.payload?.data?.[0] || null;
150
+ }
151
+ async function _classSessionMapper(stream2, input) {
152
+ const operations = [];
153
+ const subjectCodes = [...new Set(input.sessions.flatMap(
154
+ (s) => (s.pelajaran || "").split(";").map((code) => code.trim()).filter(Boolean)
155
+ ))];
156
+ const subjectMap = await findSubjectsByCodes(stream2, subjectCodes);
157
+ const stageNames = [...new Set(input.sessions.flatMap(
158
+ (s) => s.jenjangStudi ? [s.jenjangStudi.trim()] : []
159
+ ))];
160
+ const stageMap = await findStages(stream2, stageNames);
161
+ const teacherIdentifiers = [];
162
+ input.sessions.forEach((s) => {
163
+ if (s.idIdentitasPengajar)
164
+ teacherIdentifiers.push({ type: "idIdentitas", value: s.idIdentitasPengajar });
165
+ if (s.nomorIndukPengajar)
166
+ teacherIdentifiers.push({ type: "nip", value: s.nomorIndukPengajar });
167
+ if (s.idPengajar)
168
+ teacherIdentifiers.push({ type: "idPengajar", value: s.idPengajar });
169
+ });
170
+ const teacherMap = await findTeachers(stream2, teacherIdentifiers);
171
+ for (const session of input.sessions) {
172
+ const title = session.judulJadwal || null;
173
+ let status = "draft";
174
+ if (session.status?.toLowerCase() === "tayang")
175
+ status = "published";
176
+ else if (session.status?.toLowerCase() === "arsip")
177
+ status = "archived";
178
+ let type = "offline";
179
+ if (session.tipe?.toLowerCase() === "live")
180
+ type = "online";
181
+ else if (session.tipe?.toLowerCase() === "hybrid")
182
+ type = "hybrid";
183
+ let startedAt = null;
184
+ let endedAt = null;
185
+ try {
186
+ const parseDate = (dateStr, timeStr) => {
187
+ if (!dateStr || !timeStr)
188
+ return null;
189
+ const parts = dateStr.split("/");
190
+ let formattedDate = dateStr;
191
+ if (parts.length === 3) {
192
+ formattedDate = `${parts[2]}-${parts[1]}-${parts[0]}`;
193
+ }
194
+ return (/* @__PURE__ */ new Date(`${formattedDate}T${timeStr}:00+07:00`)).toISOString();
195
+ };
196
+ if (session.tanggal && session.mulai)
197
+ startedAt = parseDate(session.tanggal, session.mulai);
198
+ if (session.tanggal && session.selesai)
199
+ endedAt = parseDate(session.tanggal, session.selesai);
200
+ } catch (e) {
201
+ console.error("[ImportClassSession] Date parsing error", e);
202
+ }
203
+ const teacherIds = [];
204
+ let teacherIdStr;
205
+ if (session.idIdentitasPengajar)
206
+ teacherIdStr = teacherMap.get(`idIdentitas:${session.idIdentitasPengajar}`);
207
+ else if (session.nomorIndukPengajar)
208
+ teacherIdStr = teacherMap.get(`nip:${session.nomorIndukPengajar}`);
209
+ else if (session.idPengajar)
210
+ teacherIdStr = teacherMap.get(`idPengajar:${session.idPengajar}`);
211
+ if (teacherIdStr) {
212
+ teacherIds.push(new ObjectId(teacherIdStr));
213
+ }
214
+ const subjectIds = [];
215
+ if (session.pelajaran) {
216
+ const codes = session.pelajaran.split(";").map((c) => c.trim()).filter(Boolean);
217
+ codes.forEach((code) => {
218
+ const sId = subjectMap.get(code);
219
+ if (sId)
220
+ subjectIds.push(new ObjectId(sId));
221
+ });
222
+ }
223
+ let sessionStageId = input.stageId;
224
+ if (session.jenjangStudi) {
225
+ const sName = session.jenjangStudi.trim();
226
+ const mappedStageId = stageMap.get(sName);
227
+ if (mappedStageId)
228
+ sessionStageId = mappedStageId;
229
+ }
230
+ const newId = new ObjectId();
231
+ operations.push({
232
+ insertOne: {
233
+ document: {
234
+ name: title,
235
+ description: session.subjudul,
236
+ note: session.catatanLokasi,
237
+ title,
238
+ subtitle: session.subjudul || null,
239
+ locationText: session.catatanLokasi || null,
240
+ map: session.peta || null,
241
+ link: session.link || null,
242
+ password: session.password || null,
243
+ startedAt,
244
+ endedAt,
245
+ _id: newId,
246
+ id: newId.toString(),
247
+ groupIds: [input.classGroupId],
248
+ stageId: sessionStageId,
249
+ branchIds: input.branchIds.map((id) => new ObjectId(id)),
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.define({
280
+ filter: { code: { $in: codes } },
281
+ fields: { _id: 1, id: 1, code: 1, name: 1 },
282
+ limit: 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.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: 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 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.define({
358
+ filter: { $or: conditions },
359
+ fields: { _id: 1, id: 1, nip: 1, userId: 1 },
360
+ limit: 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 };
123
384
  }
124
385
 
125
386
  export { useImportData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neutron.co.id/pendidikan-operation",
3
- "version": "1.27.9",
3
+ "version": "1.27.12",
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.4",
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.4",
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": 134
89
+ "build": 137
90
90
  }