@eeplatform/basic-edu 1.5.2 → 1.7.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/CHANGELOG.md +12 -0
- package/CONVENTION.md +632 -0
- package/dist/index.d.ts +324 -2
- package/dist/index.js +3111 -319
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3134 -322
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3559,8 +3559,11 @@ function useLearnerRepo() {
|
|
|
3559
3559
|
}
|
|
3560
3560
|
}
|
|
3561
3561
|
async function getByGradeLevel(value, session) {
|
|
3562
|
+
value.limit = value.limit && value.limit > 0 ? value.limit : 50;
|
|
3563
|
+
value.skip = value.skip && value.skip > 0 ? value.skip - 1 : 0;
|
|
3562
3564
|
const validation = Joi7.object({
|
|
3563
3565
|
school: Joi7.string().hex().required(),
|
|
3566
|
+
schoolYear: Joi7.string().optional(),
|
|
3564
3567
|
gradeLevel: Joi7.string().required(),
|
|
3565
3568
|
status: Joi7.string().optional().allow("", null),
|
|
3566
3569
|
limit: Joi7.number().integer().required(),
|
|
@@ -3577,8 +3580,14 @@ function useLearnerRepo() {
|
|
|
3577
3580
|
};
|
|
3578
3581
|
const cacheKeyOptions = {
|
|
3579
3582
|
...query,
|
|
3580
|
-
tag: "byGradeLevel"
|
|
3583
|
+
tag: "byGradeLevel",
|
|
3584
|
+
limit: value.limit,
|
|
3585
|
+
skip: value.skip
|
|
3581
3586
|
};
|
|
3587
|
+
if (value.schoolYear) {
|
|
3588
|
+
query.schoolYear = value.schoolYear;
|
|
3589
|
+
cacheKeyOptions.schoolYear = value.schoolYear;
|
|
3590
|
+
}
|
|
3582
3591
|
if (value.school && typeof value.school === "string") {
|
|
3583
3592
|
try {
|
|
3584
3593
|
query.school = new ObjectId7(value.school);
|
|
@@ -3598,11 +3607,7 @@ function useLearnerRepo() {
|
|
|
3598
3607
|
}
|
|
3599
3608
|
try {
|
|
3600
3609
|
const data = await collection.aggregate(
|
|
3601
|
-
[
|
|
3602
|
-
{ $match: query },
|
|
3603
|
-
{ $skip: value.skip ?? 0 },
|
|
3604
|
-
{ $limit: value.limit ?? 100 }
|
|
3605
|
-
],
|
|
3610
|
+
[{ $match: query }, { $skip: value.skip }, { $limit: value.limit }],
|
|
3606
3611
|
{ session }
|
|
3607
3612
|
).toArray();
|
|
3608
3613
|
setCache(cacheKey, data, 600).then(() => {
|
|
@@ -37885,15 +37890,15 @@ function useSectionRepo() {
|
|
|
37885
37890
|
}
|
|
37886
37891
|
|
|
37887
37892
|
// src/resources/section/section.controller.ts
|
|
37888
|
-
import { BadRequestError as
|
|
37889
|
-
import
|
|
37893
|
+
import { BadRequestError as BadRequestError50 } from "@eeplatform/nodejs-utils";
|
|
37894
|
+
import Joi32 from "joi";
|
|
37890
37895
|
|
|
37891
37896
|
// src/resources/section/section.service.ts
|
|
37892
37897
|
import {
|
|
37893
|
-
AppError as
|
|
37894
|
-
BadRequestError as
|
|
37895
|
-
InternalServerError as
|
|
37896
|
-
useAtlas as
|
|
37898
|
+
AppError as AppError18,
|
|
37899
|
+
BadRequestError as BadRequestError49,
|
|
37900
|
+
InternalServerError as InternalServerError16,
|
|
37901
|
+
useAtlas as useAtlas23
|
|
37897
37902
|
} from "@eeplatform/nodejs-utils";
|
|
37898
37903
|
|
|
37899
37904
|
// src/resources/section-student/section.student.repository.ts
|
|
@@ -37902,6 +37907,8 @@ import {
|
|
|
37902
37907
|
BadRequestError as BadRequestError45,
|
|
37903
37908
|
InternalServerError as InternalServerError13,
|
|
37904
37909
|
logger as logger26,
|
|
37910
|
+
makeCacheKey as makeCacheKey15,
|
|
37911
|
+
paginate as paginate14,
|
|
37905
37912
|
useAtlas as useAtlas21,
|
|
37906
37913
|
useCache as useCache15
|
|
37907
37914
|
} from "@eeplatform/nodejs-utils";
|
|
@@ -37918,9 +37925,15 @@ var allowedSectionStudentStatuses = [
|
|
|
37918
37925
|
];
|
|
37919
37926
|
var schemaSectionStudent = Joi28.object({
|
|
37920
37927
|
_id: Joi28.string().hex().optional().allow(null, ""),
|
|
37928
|
+
lrn: Joi28.string().required(),
|
|
37929
|
+
school: Joi28.string().hex().required(),
|
|
37930
|
+
schoolName: Joi28.string().optional().allow(null, ""),
|
|
37921
37931
|
section: Joi28.string().hex().required(),
|
|
37922
37932
|
student: Joi28.string().required(),
|
|
37923
37933
|
studentName: Joi28.string().required(),
|
|
37934
|
+
gradeLevel: Joi28.string().optional().allow(null, ""),
|
|
37935
|
+
educationLevel: Joi28.string().optional().allow(null, ""),
|
|
37936
|
+
schoolYear: Joi28.string().optional().allow(null, ""),
|
|
37924
37937
|
status: Joi28.string().valid(...allowedSectionStudentStatuses).optional().allow(null, ""),
|
|
37925
37938
|
assignedAt: Joi28.string().isoDate().optional().allow(null, ""),
|
|
37926
37939
|
updatedAt: Joi28.string().isoDate().optional().allow(null, "")
|
|
@@ -37944,11 +37957,24 @@ function modelSectionStudent(value) {
|
|
|
37944
37957
|
throw new Error("Invalid section ID.");
|
|
37945
37958
|
}
|
|
37946
37959
|
}
|
|
37960
|
+
if (value.school && typeof value.school === "string") {
|
|
37961
|
+
try {
|
|
37962
|
+
value.school = new ObjectId27(value.school);
|
|
37963
|
+
} catch (error2) {
|
|
37964
|
+
throw new Error("Invalid school ID.");
|
|
37965
|
+
}
|
|
37966
|
+
}
|
|
37947
37967
|
return {
|
|
37948
37968
|
_id: value._id,
|
|
37969
|
+
lrn: value.lrn,
|
|
37970
|
+
school: value.school,
|
|
37971
|
+
schoolName: value.schoolName,
|
|
37949
37972
|
section: value.section,
|
|
37950
37973
|
student: value.student,
|
|
37951
37974
|
studentName: value.studentName,
|
|
37975
|
+
gradeLevel: value.gradeLevel,
|
|
37976
|
+
educationLevel: value.educationLevel,
|
|
37977
|
+
schoolYear: value.schoolYear,
|
|
37952
37978
|
status: value.status ?? "active",
|
|
37953
37979
|
assignedAt: value.assignedAt ?? "",
|
|
37954
37980
|
updatedAt: value.updatedAt ?? ""
|
|
@@ -37956,6 +37982,8 @@ function modelSectionStudent(value) {
|
|
|
37956
37982
|
}
|
|
37957
37983
|
|
|
37958
37984
|
// src/resources/section-student/section.student.repository.ts
|
|
37985
|
+
import { ObjectId as ObjectId28 } from "mongodb";
|
|
37986
|
+
import Joi29 from "joi";
|
|
37959
37987
|
function useSectionStudentRepo() {
|
|
37960
37988
|
const db = useAtlas21.getDb();
|
|
37961
37989
|
if (!db) {
|
|
@@ -38019,377 +38047,909 @@ function useSectionStudentRepo() {
|
|
|
38019
38047
|
}
|
|
38020
38048
|
}
|
|
38021
38049
|
}
|
|
38050
|
+
async function getAll({
|
|
38051
|
+
page = 1,
|
|
38052
|
+
limit = 10,
|
|
38053
|
+
search = "",
|
|
38054
|
+
school = "",
|
|
38055
|
+
status = "",
|
|
38056
|
+
gradeLevel = "",
|
|
38057
|
+
section = "",
|
|
38058
|
+
schoolYear = ""
|
|
38059
|
+
} = {}) {
|
|
38060
|
+
const { error } = Joi29.object({
|
|
38061
|
+
page: Joi29.number().min(1).default(1),
|
|
38062
|
+
limit: Joi29.number().min(1).max(100).default(10),
|
|
38063
|
+
search: Joi29.string().allow("").default(""),
|
|
38064
|
+
status: Joi29.string().allow("").default(""),
|
|
38065
|
+
school: Joi29.string().hex().allow("").default(""),
|
|
38066
|
+
gradeLevel: Joi29.string().allow("").default(""),
|
|
38067
|
+
section: Joi29.string().hex().allow("").default(""),
|
|
38068
|
+
schoolYear: Joi29.string().allow("").default("")
|
|
38069
|
+
}).validate({ page, limit, search, status, school, gradeLevel, section });
|
|
38070
|
+
if (error) {
|
|
38071
|
+
throw new BadRequestError45(error.details[0].message);
|
|
38072
|
+
}
|
|
38073
|
+
const query = { status: { $ne: "deleted" } };
|
|
38074
|
+
const cacheKeyOptions = { page, limit };
|
|
38075
|
+
if (status) {
|
|
38076
|
+
query.status = status;
|
|
38077
|
+
cacheKeyOptions.status = status;
|
|
38078
|
+
}
|
|
38079
|
+
if (school && typeof school === "string") {
|
|
38080
|
+
try {
|
|
38081
|
+
query.school = new ObjectId28(school);
|
|
38082
|
+
} catch (error2) {
|
|
38083
|
+
throw new BadRequestError45("Invalid school ID format.");
|
|
38084
|
+
}
|
|
38085
|
+
cacheKeyOptions.school = school;
|
|
38086
|
+
}
|
|
38087
|
+
if (gradeLevel) {
|
|
38088
|
+
query.gradeLevel = gradeLevel;
|
|
38089
|
+
cacheKeyOptions.gradeLevel = gradeLevel;
|
|
38090
|
+
}
|
|
38091
|
+
if (search) {
|
|
38092
|
+
query.$text = { $search: search };
|
|
38093
|
+
cacheKeyOptions.search = search;
|
|
38094
|
+
}
|
|
38095
|
+
if (section && typeof section === "string") {
|
|
38096
|
+
try {
|
|
38097
|
+
query.section = new ObjectId28(section);
|
|
38098
|
+
} catch (error2) {
|
|
38099
|
+
throw new BadRequestError45("Invalid section ID format.");
|
|
38100
|
+
}
|
|
38101
|
+
cacheKeyOptions.section = section;
|
|
38102
|
+
}
|
|
38103
|
+
if (schoolYear) {
|
|
38104
|
+
query.schoolYear = schoolYear;
|
|
38105
|
+
cacheKeyOptions.schoolYear = schoolYear;
|
|
38106
|
+
}
|
|
38107
|
+
const cacheKey = makeCacheKey15(namespace_collection, cacheKeyOptions);
|
|
38108
|
+
logger26.log({
|
|
38109
|
+
level: "info",
|
|
38110
|
+
message: `Cache key for getAll sections: ${cacheKey}`
|
|
38111
|
+
});
|
|
38112
|
+
const cachedData = await getCache(cacheKey);
|
|
38113
|
+
if (cachedData) {
|
|
38114
|
+
logger26.log({
|
|
38115
|
+
level: "info",
|
|
38116
|
+
message: `Cache hit for getAll sections: ${cacheKey}`
|
|
38117
|
+
});
|
|
38118
|
+
return cachedData;
|
|
38119
|
+
}
|
|
38120
|
+
page = page > 0 ? page - 1 : 0;
|
|
38121
|
+
const items = await collection.aggregate([
|
|
38122
|
+
{ $match: query },
|
|
38123
|
+
{ $skip: page * limit },
|
|
38124
|
+
{ $limit: limit }
|
|
38125
|
+
]).toArray();
|
|
38126
|
+
const length = await collection.countDocuments(query);
|
|
38127
|
+
const data = paginate14(items, page, limit, length);
|
|
38128
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
38129
|
+
logger26.log({
|
|
38130
|
+
level: "info",
|
|
38131
|
+
message: `Cache set for getAll section students: ${cacheKey}`
|
|
38132
|
+
});
|
|
38133
|
+
}).catch((err) => {
|
|
38134
|
+
logger26.log({
|
|
38135
|
+
level: "error",
|
|
38136
|
+
message: `Failed to set cache for getAll section students: ${err.message}`
|
|
38137
|
+
});
|
|
38138
|
+
});
|
|
38139
|
+
return data;
|
|
38140
|
+
}
|
|
38022
38141
|
return {
|
|
38023
38142
|
createIndexes,
|
|
38024
38143
|
delCachedData,
|
|
38025
|
-
add
|
|
38144
|
+
add,
|
|
38145
|
+
getAll
|
|
38026
38146
|
};
|
|
38027
38147
|
}
|
|
38028
38148
|
|
|
38029
|
-
// src/resources/section/section.
|
|
38030
|
-
|
|
38031
|
-
|
|
38032
|
-
|
|
38033
|
-
|
|
38034
|
-
|
|
38035
|
-
|
|
38036
|
-
|
|
38037
|
-
|
|
38038
|
-
|
|
38039
|
-
|
|
38040
|
-
|
|
38041
|
-
|
|
38042
|
-
|
|
38043
|
-
|
|
38044
|
-
|
|
38045
|
-
|
|
38046
|
-
|
|
38047
|
-
|
|
38048
|
-
|
|
38049
|
-
|
|
38050
|
-
|
|
38051
|
-
|
|
38052
|
-
|
|
38053
|
-
|
|
38054
|
-
|
|
38055
|
-
|
|
38056
|
-
|
|
38057
|
-
|
|
38149
|
+
// src/resources/section-subject/section.subject.model.ts
|
|
38150
|
+
import { BadRequestError as BadRequestError46 } from "@eeplatform/nodejs-utils";
|
|
38151
|
+
import Joi30 from "joi";
|
|
38152
|
+
import { ObjectId as ObjectId29 } from "mongodb";
|
|
38153
|
+
var schemaSectionSubject = Joi30.object({
|
|
38154
|
+
_id: Joi30.string().hex().optional().allow(null, ""),
|
|
38155
|
+
school: Joi30.string().hex().required(),
|
|
38156
|
+
schoolName: Joi30.string().optional().allow(null, ""),
|
|
38157
|
+
section: Joi30.string().hex().required(),
|
|
38158
|
+
sectionName: Joi30.string().required(),
|
|
38159
|
+
gradeLevel: Joi30.string().required(),
|
|
38160
|
+
educationLevel: Joi30.string().required(),
|
|
38161
|
+
schoolYear: Joi30.string().required(),
|
|
38162
|
+
subjectCode: Joi30.string().required(),
|
|
38163
|
+
subjectName: Joi30.string().required(),
|
|
38164
|
+
teacher: Joi30.string().hex().optional().allow(null, ""),
|
|
38165
|
+
teacherName: Joi30.string().optional().allow(null, ""),
|
|
38166
|
+
classroom: Joi30.string().optional().allow(null, ""),
|
|
38167
|
+
classroomName: Joi30.string().optional().allow(null, ""),
|
|
38168
|
+
daysOfWeek: Joi30.array().items(Joi30.string()).optional().allow(null),
|
|
38169
|
+
schedule: Joi30.string().optional().allow(null, ""),
|
|
38170
|
+
sessionDuration: Joi30.number().optional().allow(null, 0),
|
|
38171
|
+
sessionFrequency: Joi30.number().optional().allow(null, 0),
|
|
38172
|
+
status: Joi30.string().valid("active", "draft").optional(),
|
|
38173
|
+
createdAt: Joi30.string().isoDate().optional().allow(null, ""),
|
|
38174
|
+
updatedAt: Joi30.string().isoDate().optional().allow(null, ""),
|
|
38175
|
+
deletedAt: Joi30.string().isoDate().optional().allow(null, "")
|
|
38176
|
+
});
|
|
38177
|
+
var schemaSectionSubjectSetup = Joi30.object({
|
|
38178
|
+
teacher: Joi30.string().hex().optional().allow(null, ""),
|
|
38179
|
+
teacherName: Joi30.string().optional().allow(null, ""),
|
|
38180
|
+
classroom: Joi30.string().optional().allow(null, ""),
|
|
38181
|
+
classroomName: Joi30.string().optional().allow(null, ""),
|
|
38182
|
+
daysOfWeek: Joi30.array().items(Joi30.string()).optional().allow(null),
|
|
38183
|
+
schedule: Joi30.string().optional().allow(null, "")
|
|
38184
|
+
});
|
|
38185
|
+
function modelSectionSubject(value) {
|
|
38186
|
+
const { error } = schemaSectionSubject.validate(value);
|
|
38187
|
+
if (error) {
|
|
38188
|
+
throw new BadRequestError46(`Invalid section subject data: ${error.message}`);
|
|
38189
|
+
}
|
|
38190
|
+
if (value._id && typeof value._id === "string") {
|
|
38191
|
+
try {
|
|
38192
|
+
value._id = new ObjectId29(value._id);
|
|
38193
|
+
} catch (error2) {
|
|
38194
|
+
throw new Error("Invalid _id.");
|
|
38058
38195
|
}
|
|
38059
|
-
|
|
38060
|
-
|
|
38061
|
-
|
|
38062
|
-
|
|
38063
|
-
|
|
38064
|
-
|
|
38196
|
+
}
|
|
38197
|
+
if (value.school && typeof value.school === "string") {
|
|
38198
|
+
try {
|
|
38199
|
+
value.school = new ObjectId29(value.school);
|
|
38200
|
+
} catch (error2) {
|
|
38201
|
+
throw new Error("Invalid school ID.");
|
|
38065
38202
|
}
|
|
38066
|
-
return sizes;
|
|
38067
38203
|
}
|
|
38068
|
-
|
|
38069
|
-
|
|
38070
|
-
|
|
38071
|
-
|
|
38072
|
-
|
|
38073
|
-
);
|
|
38204
|
+
if (value.section && typeof value.section === "string") {
|
|
38205
|
+
try {
|
|
38206
|
+
value.section = new ObjectId29(value.section);
|
|
38207
|
+
} catch (error2) {
|
|
38208
|
+
throw new Error("Invalid section ID.");
|
|
38074
38209
|
}
|
|
38075
|
-
|
|
38076
|
-
|
|
38077
|
-
|
|
38210
|
+
}
|
|
38211
|
+
if (value.teacher && typeof value.teacher === "string") {
|
|
38212
|
+
try {
|
|
38213
|
+
value.teacher = new ObjectId29(value.teacher);
|
|
38214
|
+
} catch (error2) {
|
|
38215
|
+
throw new Error("Invalid teacher ID.");
|
|
38078
38216
|
}
|
|
38217
|
+
}
|
|
38218
|
+
return {
|
|
38219
|
+
_id: value._id,
|
|
38220
|
+
school: value.school,
|
|
38221
|
+
schoolName: value.schoolName,
|
|
38222
|
+
section: value.section,
|
|
38223
|
+
sectionName: value.sectionName,
|
|
38224
|
+
gradeLevel: value.gradeLevel,
|
|
38225
|
+
educationLevel: value.educationLevel,
|
|
38226
|
+
schoolYear: value.schoolYear,
|
|
38227
|
+
subjectCode: value.subjectCode,
|
|
38228
|
+
subjectName: value.subjectName,
|
|
38229
|
+
teacher: value.teacher,
|
|
38230
|
+
teacherName: value.teacherName,
|
|
38231
|
+
classroom: value.classroom ?? "",
|
|
38232
|
+
classroomName: value.classroomName ?? "",
|
|
38233
|
+
daysOfWeek: value.daysOfWeek ?? [],
|
|
38234
|
+
schedule: value.schedule ?? "",
|
|
38235
|
+
sessionDuration: value.sessionDuration ?? 0,
|
|
38236
|
+
sessionFrequency: value.sessionFrequency ?? 0,
|
|
38237
|
+
status: value.status ?? "draft",
|
|
38238
|
+
createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
|
|
38239
|
+
updatedAt: value.updatedAt ?? "",
|
|
38240
|
+
deletedAt: value.deletedAt ?? ""
|
|
38241
|
+
};
|
|
38242
|
+
}
|
|
38243
|
+
|
|
38244
|
+
// src/resources/section-subject/section.subject.repository.ts
|
|
38245
|
+
import {
|
|
38246
|
+
AppError as AppError16,
|
|
38247
|
+
BadRequestError as BadRequestError47,
|
|
38248
|
+
InternalServerError as InternalServerError14,
|
|
38249
|
+
logger as logger27,
|
|
38250
|
+
makeCacheKey as makeCacheKey16,
|
|
38251
|
+
paginate as paginate15,
|
|
38252
|
+
useAtlas as useAtlas22,
|
|
38253
|
+
useCache as useCache16
|
|
38254
|
+
} from "@eeplatform/nodejs-utils";
|
|
38255
|
+
import { ObjectId as ObjectId30 } from "mongodb";
|
|
38256
|
+
function useSectionSubjectRepo() {
|
|
38257
|
+
const db = useAtlas22.getDb();
|
|
38258
|
+
if (!db) {
|
|
38259
|
+
throw new Error("Unable to connect to server.");
|
|
38260
|
+
}
|
|
38261
|
+
const namespace_collection = "deped.section.subjects";
|
|
38262
|
+
const collection = db.collection(namespace_collection);
|
|
38263
|
+
const { getCache, setCache, delNamespace } = useCache16(namespace_collection);
|
|
38264
|
+
async function createIndexes() {
|
|
38079
38265
|
try {
|
|
38080
|
-
await
|
|
38081
|
-
|
|
38266
|
+
await collection.createIndexes([
|
|
38267
|
+
{ key: { school: 1 } },
|
|
38268
|
+
{ key: { section: 1 } },
|
|
38269
|
+
{ key: { teacher: 1 } },
|
|
38270
|
+
{ key: { subjectCode: 1 } },
|
|
38271
|
+
{ key: { schoolYear: 1 } },
|
|
38272
|
+
{ key: { gradeLevel: 1 } },
|
|
38273
|
+
{ key: { createdAt: 1 } },
|
|
38082
38274
|
{
|
|
38083
|
-
|
|
38084
|
-
|
|
38085
|
-
|
|
38275
|
+
key: {
|
|
38276
|
+
subjectName: "text",
|
|
38277
|
+
subjectCode: "text",
|
|
38278
|
+
teacherName: "text"
|
|
38279
|
+
}
|
|
38086
38280
|
},
|
|
38087
|
-
session
|
|
38088
|
-
);
|
|
38089
|
-
if (studentCount === 0) {
|
|
38090
|
-
throw new BadRequestError46("No learners found for this grade level.");
|
|
38091
|
-
}
|
|
38092
|
-
const gradeLevelData = await getByGradeLevel(
|
|
38093
38281
|
{
|
|
38094
|
-
|
|
38095
|
-
|
|
38096
|
-
|
|
38097
|
-
|
|
38098
|
-
|
|
38099
|
-
|
|
38100
|
-
throw new BadRequestError46("Grade level not found.");
|
|
38101
|
-
}
|
|
38102
|
-
const minPerSection = gradeLevelData.minNumberOfLearners;
|
|
38103
|
-
const maxPerSection = gradeLevelData.maxNumberOfLearners;
|
|
38104
|
-
const sectionsNeeded = Math.ceil(studentCount / minPerSection);
|
|
38105
|
-
if (sectionsNeeded > value.set.length) {
|
|
38106
|
-
throw new BadRequestError46(
|
|
38107
|
-
"Insufficient number of section names in set[]."
|
|
38108
|
-
);
|
|
38109
|
-
}
|
|
38110
|
-
const sectionSizes = distributeStudents(
|
|
38111
|
-
studentCount,
|
|
38112
|
-
minPerSection,
|
|
38113
|
-
maxPerSection
|
|
38114
|
-
);
|
|
38115
|
-
if (sectionSizes.length === 0) {
|
|
38116
|
-
throw new BadRequestError46("Unable to compute section sizes.");
|
|
38117
|
-
}
|
|
38118
|
-
let pointer = 0;
|
|
38119
|
-
for (let i = 0; i < sectionSizes.length; i++) {
|
|
38120
|
-
const size = sectionSizes[i];
|
|
38121
|
-
const sectionName = value.set[i];
|
|
38122
|
-
const section = await createSection(
|
|
38123
|
-
{
|
|
38124
|
-
school: value.school,
|
|
38125
|
-
schoolYear: value.schoolYear,
|
|
38126
|
-
gradeLevel: value.gradeLevel,
|
|
38127
|
-
name: sectionName,
|
|
38128
|
-
students: size
|
|
38129
|
-
},
|
|
38130
|
-
session
|
|
38131
|
-
);
|
|
38132
|
-
const learners = await getLeanerByGradeLevel(
|
|
38133
|
-
{
|
|
38134
|
-
school: value.school,
|
|
38135
|
-
gradeLevel: value.gradeLevel,
|
|
38136
|
-
skip: pointer,
|
|
38137
|
-
limit: size
|
|
38282
|
+
key: {
|
|
38283
|
+
school: 1,
|
|
38284
|
+
section: 1,
|
|
38285
|
+
subjectCode: 1,
|
|
38286
|
+
schoolYear: 1,
|
|
38287
|
+
status: 1
|
|
38138
38288
|
},
|
|
38139
|
-
|
|
38140
|
-
|
|
38141
|
-
if (!learners.length) {
|
|
38142
|
-
throw new BadRequestError46(`No learners found for section #${i + 1}.`);
|
|
38143
|
-
}
|
|
38144
|
-
pointer += size;
|
|
38145
|
-
for (const student of learners) {
|
|
38146
|
-
if (!student._id) {
|
|
38147
|
-
throw new BadRequestError46("Learner ID is missing.");
|
|
38148
|
-
}
|
|
38149
|
-
await assignStudent(
|
|
38150
|
-
{
|
|
38151
|
-
section: section.toString(),
|
|
38152
|
-
student: student._id?.toString(),
|
|
38153
|
-
studentName: `${student.learnerInfo.firstName} ${student.learnerInfo.lastName}`,
|
|
38154
|
-
status: "active"
|
|
38155
|
-
},
|
|
38156
|
-
session
|
|
38157
|
-
);
|
|
38289
|
+
unique: true,
|
|
38290
|
+
name: "unique_section_subject"
|
|
38158
38291
|
}
|
|
38159
|
-
|
|
38160
|
-
|
|
38161
|
-
|
|
38162
|
-
} catch (error2) {
|
|
38163
|
-
await session.abortTransaction();
|
|
38164
|
-
if (error2 instanceof AppError16) {
|
|
38165
|
-
throw error2;
|
|
38166
|
-
} else {
|
|
38167
|
-
throw new InternalServerError14("Failed to generate sections.");
|
|
38168
|
-
}
|
|
38169
|
-
} finally {
|
|
38170
|
-
await session?.endSession();
|
|
38292
|
+
]);
|
|
38293
|
+
} catch (error) {
|
|
38294
|
+
throw new Error("Failed to create index on section subjects.");
|
|
38171
38295
|
}
|
|
38172
38296
|
}
|
|
38173
|
-
|
|
38174
|
-
|
|
38175
|
-
|
|
38176
|
-
|
|
38177
|
-
|
|
38178
|
-
const {
|
|
38179
|
-
add: _add,
|
|
38180
|
-
getAll: _getAll,
|
|
38181
|
-
getById: _getById,
|
|
38182
|
-
getByName: _getByName,
|
|
38183
|
-
getBySchool: _getBySchool,
|
|
38184
|
-
updateFieldById: _updateFieldById,
|
|
38185
|
-
addStudentToSection: _addStudentToSection,
|
|
38186
|
-
removeStudentFromSection: _removeStudentFromSection,
|
|
38187
|
-
deleteById: _deleteById
|
|
38188
|
-
} = useSectionRepo();
|
|
38189
|
-
const { generateSections: _generateSections } = useSectionService();
|
|
38190
|
-
async function add(req, res, next) {
|
|
38191
|
-
const value = req.body;
|
|
38192
|
-
const { error } = schemaSection.validate(value);
|
|
38193
|
-
if (error) {
|
|
38194
|
-
next(new BadRequestError47(error.message));
|
|
38195
|
-
return;
|
|
38196
|
-
}
|
|
38197
|
-
try {
|
|
38198
|
-
const data = await _add(value);
|
|
38199
|
-
res.json({
|
|
38200
|
-
message: "Successfully created section.",
|
|
38201
|
-
data
|
|
38297
|
+
function delCachedData() {
|
|
38298
|
+
delNamespace().then(() => {
|
|
38299
|
+
logger27.log({
|
|
38300
|
+
level: "info",
|
|
38301
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
38202
38302
|
});
|
|
38203
|
-
|
|
38204
|
-
|
|
38205
|
-
|
|
38206
|
-
|
|
38303
|
+
}).catch((err) => {
|
|
38304
|
+
logger27.log({
|
|
38305
|
+
level: "error",
|
|
38306
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
38307
|
+
});
|
|
38308
|
+
});
|
|
38207
38309
|
}
|
|
38208
|
-
async function
|
|
38209
|
-
const value = req.body;
|
|
38210
|
-
const { error } = schemaGenerateSections.validate(value);
|
|
38211
|
-
if (error) {
|
|
38212
|
-
next(new BadRequestError47(error.message));
|
|
38213
|
-
return;
|
|
38214
|
-
}
|
|
38310
|
+
async function add(value, session) {
|
|
38215
38311
|
try {
|
|
38216
|
-
|
|
38217
|
-
res.
|
|
38218
|
-
|
|
38219
|
-
|
|
38312
|
+
value = modelSectionSubject(value);
|
|
38313
|
+
const res = await collection.insertOne(value, { session });
|
|
38314
|
+
delCachedData();
|
|
38315
|
+
return res.insertedId;
|
|
38316
|
+
} catch (error) {
|
|
38317
|
+
logger27.log({
|
|
38318
|
+
level: "error",
|
|
38319
|
+
message: error.message
|
|
38220
38320
|
});
|
|
38221
|
-
|
|
38222
|
-
|
|
38223
|
-
|
|
38321
|
+
if (error instanceof AppError16) {
|
|
38322
|
+
throw error;
|
|
38323
|
+
} else {
|
|
38324
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
38325
|
+
if (isDuplicated) {
|
|
38326
|
+
throw new BadRequestError47("Section subject already exists.");
|
|
38327
|
+
}
|
|
38328
|
+
throw new Error("Failed to create section subject.");
|
|
38329
|
+
}
|
|
38224
38330
|
}
|
|
38225
38331
|
}
|
|
38226
|
-
async function getAll(
|
|
38227
|
-
|
|
38228
|
-
|
|
38229
|
-
|
|
38230
|
-
|
|
38231
|
-
|
|
38232
|
-
|
|
38233
|
-
|
|
38234
|
-
|
|
38235
|
-
|
|
38236
|
-
|
|
38237
|
-
|
|
38238
|
-
|
|
38239
|
-
|
|
38240
|
-
const
|
|
38241
|
-
const
|
|
38242
|
-
|
|
38243
|
-
|
|
38244
|
-
|
|
38245
|
-
|
|
38246
|
-
if (
|
|
38247
|
-
|
|
38248
|
-
|
|
38332
|
+
async function getAll({
|
|
38333
|
+
search = "",
|
|
38334
|
+
page = 1,
|
|
38335
|
+
limit = 10,
|
|
38336
|
+
sort = {},
|
|
38337
|
+
status = "active",
|
|
38338
|
+
school = "",
|
|
38339
|
+
section = "",
|
|
38340
|
+
teacher = "",
|
|
38341
|
+
schoolYear = "",
|
|
38342
|
+
gradeLevel = "",
|
|
38343
|
+
subjectCode = ""
|
|
38344
|
+
} = {}) {
|
|
38345
|
+
page = page > 0 ? page - 1 : 0;
|
|
38346
|
+
const query = { status: { $ne: "deleted" } };
|
|
38347
|
+
const cacheKeyOptions = {
|
|
38348
|
+
page,
|
|
38349
|
+
limit,
|
|
38350
|
+
sort: JSON.stringify(sort)
|
|
38351
|
+
};
|
|
38352
|
+
if (status) {
|
|
38353
|
+
query.status = status;
|
|
38354
|
+
cacheKeyOptions.status = status;
|
|
38249
38355
|
}
|
|
38250
|
-
|
|
38251
|
-
|
|
38252
|
-
|
|
38253
|
-
|
|
38356
|
+
if (school) {
|
|
38357
|
+
try {
|
|
38358
|
+
query.school = new ObjectId30(school);
|
|
38359
|
+
} catch (error) {
|
|
38360
|
+
throw new BadRequestError47("Invalid school ID.");
|
|
38361
|
+
}
|
|
38362
|
+
cacheKeyOptions.school = school;
|
|
38254
38363
|
}
|
|
38255
|
-
if (
|
|
38256
|
-
|
|
38257
|
-
|
|
38364
|
+
if (section) {
|
|
38365
|
+
try {
|
|
38366
|
+
query.section = new ObjectId30(section);
|
|
38367
|
+
} catch (error) {
|
|
38368
|
+
throw new BadRequestError47("Invalid section ID.");
|
|
38369
|
+
}
|
|
38370
|
+
cacheKeyOptions.section = section;
|
|
38258
38371
|
}
|
|
38259
|
-
|
|
38260
|
-
|
|
38261
|
-
|
|
38262
|
-
|
|
38263
|
-
|
|
38264
|
-
|
|
38265
|
-
|
|
38266
|
-
schoolYear,
|
|
38267
|
-
gradeLevel
|
|
38268
|
-
});
|
|
38269
|
-
res.json(data);
|
|
38270
|
-
return;
|
|
38271
|
-
} catch (error2) {
|
|
38272
|
-
next(error2);
|
|
38372
|
+
if (teacher) {
|
|
38373
|
+
try {
|
|
38374
|
+
query.teacher = new ObjectId30(teacher);
|
|
38375
|
+
} catch (error) {
|
|
38376
|
+
throw new BadRequestError47("Invalid teacher ID.");
|
|
38377
|
+
}
|
|
38378
|
+
cacheKeyOptions.teacher = teacher;
|
|
38273
38379
|
}
|
|
38274
|
-
|
|
38275
|
-
|
|
38276
|
-
|
|
38277
|
-
|
|
38278
|
-
|
|
38279
|
-
|
|
38280
|
-
|
|
38281
|
-
|
|
38282
|
-
|
|
38283
|
-
|
|
38380
|
+
if (schoolYear) {
|
|
38381
|
+
query.schoolYear = schoolYear;
|
|
38382
|
+
cacheKeyOptions.schoolYear = schoolYear;
|
|
38383
|
+
}
|
|
38384
|
+
if (gradeLevel) {
|
|
38385
|
+
query.gradeLevel = gradeLevel;
|
|
38386
|
+
cacheKeyOptions.gradeLevel = gradeLevel;
|
|
38387
|
+
}
|
|
38388
|
+
if (subjectCode) {
|
|
38389
|
+
query.subjectCode = subjectCode;
|
|
38390
|
+
cacheKeyOptions.subjectCode = subjectCode;
|
|
38391
|
+
}
|
|
38392
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: 1 };
|
|
38393
|
+
if (search) {
|
|
38394
|
+
query.$text = { $search: search };
|
|
38395
|
+
cacheKeyOptions.search = search;
|
|
38284
38396
|
}
|
|
38397
|
+
const cacheKey = makeCacheKey16(namespace_collection, cacheKeyOptions);
|
|
38398
|
+
logger27.log({
|
|
38399
|
+
level: "info",
|
|
38400
|
+
message: `Cache key for getAll section subjects: ${cacheKey}`
|
|
38401
|
+
});
|
|
38285
38402
|
try {
|
|
38286
|
-
const
|
|
38287
|
-
|
|
38288
|
-
|
|
38289
|
-
|
|
38403
|
+
const cached = await getCache(cacheKey);
|
|
38404
|
+
if (cached) {
|
|
38405
|
+
logger27.log({
|
|
38406
|
+
level: "info",
|
|
38407
|
+
message: `Cache hit for getAll section subjects: ${cacheKey}`
|
|
38408
|
+
});
|
|
38409
|
+
return cached;
|
|
38410
|
+
}
|
|
38411
|
+
const items = await collection.aggregate([
|
|
38412
|
+
{ $match: query },
|
|
38413
|
+
{ $sort: sort },
|
|
38414
|
+
{ $skip: page * limit },
|
|
38415
|
+
{ $limit: limit }
|
|
38416
|
+
]).toArray();
|
|
38417
|
+
const length = await collection.countDocuments(query);
|
|
38418
|
+
const data = paginate15(items, page, limit, length);
|
|
38419
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
38420
|
+
logger27.log({
|
|
38421
|
+
level: "info",
|
|
38422
|
+
message: `Cache set for getAll section subjects: ${cacheKey}`
|
|
38423
|
+
});
|
|
38424
|
+
}).catch((err) => {
|
|
38425
|
+
logger27.log({
|
|
38426
|
+
level: "error",
|
|
38427
|
+
message: `Failed to set cache for getAll section subjects: ${err.message}`
|
|
38428
|
+
});
|
|
38290
38429
|
});
|
|
38291
|
-
return;
|
|
38292
|
-
} catch (
|
|
38293
|
-
|
|
38430
|
+
return data;
|
|
38431
|
+
} catch (error) {
|
|
38432
|
+
logger27.log({ level: "error", message: `${error}` });
|
|
38433
|
+
throw error;
|
|
38294
38434
|
}
|
|
38295
38435
|
}
|
|
38296
|
-
async function
|
|
38297
|
-
|
|
38298
|
-
|
|
38299
|
-
|
|
38300
|
-
|
|
38301
|
-
const { error } = validation.validate({ name });
|
|
38302
|
-
if (error) {
|
|
38303
|
-
next(new BadRequestError47(error.message));
|
|
38304
|
-
return;
|
|
38436
|
+
async function getById(_id) {
|
|
38437
|
+
try {
|
|
38438
|
+
_id = new ObjectId30(_id);
|
|
38439
|
+
} catch (error) {
|
|
38440
|
+
throw new BadRequestError47("Invalid ID.");
|
|
38305
38441
|
}
|
|
38442
|
+
const cacheKey = makeCacheKey16(namespace_collection, { _id: String(_id) });
|
|
38306
38443
|
try {
|
|
38307
|
-
const
|
|
38308
|
-
|
|
38309
|
-
|
|
38310
|
-
|
|
38444
|
+
const cached = await getCache(cacheKey);
|
|
38445
|
+
if (cached) {
|
|
38446
|
+
logger27.log({
|
|
38447
|
+
level: "info",
|
|
38448
|
+
message: `Cache hit for getById section subject: ${cacheKey}`
|
|
38449
|
+
});
|
|
38450
|
+
return cached;
|
|
38451
|
+
}
|
|
38452
|
+
const result = await collection.findOne({
|
|
38453
|
+
_id,
|
|
38454
|
+
deletedAt: { $in: ["", null] }
|
|
38311
38455
|
});
|
|
38312
|
-
|
|
38313
|
-
|
|
38314
|
-
|
|
38456
|
+
if (!result) {
|
|
38457
|
+
throw new BadRequestError47("Section subject not found.");
|
|
38458
|
+
}
|
|
38459
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
38460
|
+
logger27.log({
|
|
38461
|
+
level: "info",
|
|
38462
|
+
message: `Cache set for section subject by id: ${cacheKey}`
|
|
38463
|
+
});
|
|
38464
|
+
}).catch((err) => {
|
|
38465
|
+
logger27.log({
|
|
38466
|
+
level: "error",
|
|
38467
|
+
message: `Failed to set cache for section subject by id: ${err.message}`
|
|
38468
|
+
});
|
|
38469
|
+
});
|
|
38470
|
+
return result;
|
|
38471
|
+
} catch (error) {
|
|
38472
|
+
if (error instanceof AppError16) {
|
|
38473
|
+
throw error;
|
|
38474
|
+
} else {
|
|
38475
|
+
throw new InternalServerError14("Failed to get section subject.");
|
|
38476
|
+
}
|
|
38315
38477
|
}
|
|
38316
38478
|
}
|
|
38317
|
-
async function
|
|
38318
|
-
|
|
38319
|
-
|
|
38320
|
-
|
|
38321
|
-
|
|
38322
|
-
const { error } = validation.validate({ school });
|
|
38323
|
-
if (error) {
|
|
38324
|
-
next(new BadRequestError47(error.message));
|
|
38325
|
-
return;
|
|
38479
|
+
async function getBySection(section) {
|
|
38480
|
+
try {
|
|
38481
|
+
section = new ObjectId30(section);
|
|
38482
|
+
} catch (error) {
|
|
38483
|
+
throw new BadRequestError47("Invalid section ID.");
|
|
38326
38484
|
}
|
|
38485
|
+
const cacheKey = makeCacheKey16(namespace_collection, {
|
|
38486
|
+
section: String(section)
|
|
38487
|
+
});
|
|
38327
38488
|
try {
|
|
38328
|
-
const
|
|
38329
|
-
|
|
38330
|
-
|
|
38331
|
-
|
|
38489
|
+
const cached = await getCache(cacheKey);
|
|
38490
|
+
if (cached) {
|
|
38491
|
+
logger27.log({
|
|
38492
|
+
level: "info",
|
|
38493
|
+
message: `Cache hit for getBySection section subjects: ${cacheKey}`
|
|
38494
|
+
});
|
|
38495
|
+
return cached;
|
|
38496
|
+
}
|
|
38497
|
+
const result = await collection.find({
|
|
38498
|
+
section,
|
|
38499
|
+
deletedAt: { $in: ["", null] }
|
|
38500
|
+
}).toArray();
|
|
38501
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
38502
|
+
logger27.log({
|
|
38503
|
+
level: "info",
|
|
38504
|
+
message: `Cache set for section subjects by section: ${cacheKey}`
|
|
38505
|
+
});
|
|
38506
|
+
}).catch((err) => {
|
|
38507
|
+
logger27.log({
|
|
38508
|
+
level: "error",
|
|
38509
|
+
message: `Failed to set cache for section subjects by section: ${err.message}`
|
|
38510
|
+
});
|
|
38332
38511
|
});
|
|
38333
|
-
return;
|
|
38334
|
-
} catch (
|
|
38335
|
-
|
|
38512
|
+
return result;
|
|
38513
|
+
} catch (error) {
|
|
38514
|
+
if (error instanceof AppError16) {
|
|
38515
|
+
throw error;
|
|
38516
|
+
} else {
|
|
38517
|
+
throw new InternalServerError14(
|
|
38518
|
+
"Failed to get section subjects by section."
|
|
38519
|
+
);
|
|
38520
|
+
}
|
|
38336
38521
|
}
|
|
38337
38522
|
}
|
|
38338
|
-
async function
|
|
38339
|
-
|
|
38340
|
-
|
|
38341
|
-
|
|
38342
|
-
|
|
38343
|
-
field: Joi29.string().valid("name", "schoolYear", "gradeLevel", "adviser", "adviserName").required(),
|
|
38344
|
-
value: Joi29.string().required()
|
|
38345
|
-
});
|
|
38346
|
-
const { error } = validation.validate({ _id, field, value });
|
|
38347
|
-
if (error) {
|
|
38348
|
-
next(new BadRequestError47(error.message));
|
|
38349
|
-
return;
|
|
38523
|
+
async function getByTeacher(teacher) {
|
|
38524
|
+
try {
|
|
38525
|
+
teacher = new ObjectId30(teacher);
|
|
38526
|
+
} catch (error) {
|
|
38527
|
+
throw new BadRequestError47("Invalid teacher ID.");
|
|
38350
38528
|
}
|
|
38529
|
+
const cacheKey = makeCacheKey16(namespace_collection, {
|
|
38530
|
+
teacher: String(teacher)
|
|
38531
|
+
});
|
|
38351
38532
|
try {
|
|
38352
|
-
const
|
|
38353
|
-
|
|
38533
|
+
const cached = await getCache(cacheKey);
|
|
38534
|
+
if (cached) {
|
|
38535
|
+
logger27.log({
|
|
38536
|
+
level: "info",
|
|
38537
|
+
message: `Cache hit for getByTeacher section subjects: ${cacheKey}`
|
|
38538
|
+
});
|
|
38539
|
+
return cached;
|
|
38540
|
+
}
|
|
38541
|
+
const result = await collection.find({
|
|
38542
|
+
teacher,
|
|
38543
|
+
deletedAt: { $in: ["", null] }
|
|
38544
|
+
}).toArray();
|
|
38545
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
38546
|
+
logger27.log({
|
|
38547
|
+
level: "info",
|
|
38548
|
+
message: `Cache set for section subjects by teacher: ${cacheKey}`
|
|
38549
|
+
});
|
|
38550
|
+
}).catch((err) => {
|
|
38551
|
+
logger27.log({
|
|
38552
|
+
level: "error",
|
|
38553
|
+
message: `Failed to set cache for section subjects by teacher: ${err.message}`
|
|
38554
|
+
});
|
|
38555
|
+
});
|
|
38556
|
+
return result;
|
|
38557
|
+
} catch (error) {
|
|
38558
|
+
if (error instanceof AppError16) {
|
|
38559
|
+
throw error;
|
|
38560
|
+
} else {
|
|
38561
|
+
throw new InternalServerError14(
|
|
38562
|
+
"Failed to get section subjects by teacher."
|
|
38563
|
+
);
|
|
38564
|
+
}
|
|
38565
|
+
}
|
|
38566
|
+
}
|
|
38567
|
+
async function getBySchool(school) {
|
|
38568
|
+
try {
|
|
38569
|
+
school = new ObjectId30(school);
|
|
38570
|
+
} catch (error) {
|
|
38571
|
+
throw new BadRequestError47("Invalid school ID.");
|
|
38572
|
+
}
|
|
38573
|
+
const cacheKey = makeCacheKey16(namespace_collection, {
|
|
38574
|
+
school: String(school)
|
|
38575
|
+
});
|
|
38576
|
+
try {
|
|
38577
|
+
const cached = await getCache(cacheKey);
|
|
38578
|
+
if (cached) {
|
|
38579
|
+
logger27.log({
|
|
38580
|
+
level: "info",
|
|
38581
|
+
message: `Cache hit for getBySchool section subjects: ${cacheKey}`
|
|
38582
|
+
});
|
|
38583
|
+
return cached;
|
|
38584
|
+
}
|
|
38585
|
+
const result = await collection.find({
|
|
38586
|
+
school,
|
|
38587
|
+
deletedAt: { $in: ["", null] }
|
|
38588
|
+
}).toArray();
|
|
38589
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
38590
|
+
logger27.log({
|
|
38591
|
+
level: "info",
|
|
38592
|
+
message: `Cache set for section subjects by school: ${cacheKey}`
|
|
38593
|
+
});
|
|
38594
|
+
}).catch((err) => {
|
|
38595
|
+
logger27.log({
|
|
38596
|
+
level: "error",
|
|
38597
|
+
message: `Failed to set cache for section subjects by school: ${err.message}`
|
|
38598
|
+
});
|
|
38599
|
+
});
|
|
38600
|
+
return result;
|
|
38601
|
+
} catch (error) {
|
|
38602
|
+
if (error instanceof AppError16) {
|
|
38603
|
+
throw error;
|
|
38604
|
+
} else {
|
|
38605
|
+
throw new InternalServerError14(
|
|
38606
|
+
"Failed to get section subjects by school."
|
|
38607
|
+
);
|
|
38608
|
+
}
|
|
38609
|
+
}
|
|
38610
|
+
}
|
|
38611
|
+
async function updateFieldById({ _id, field, value } = {}, session) {
|
|
38612
|
+
const allowedFields = [
|
|
38613
|
+
"teacher",
|
|
38614
|
+
"teacherName",
|
|
38615
|
+
"classroom",
|
|
38616
|
+
"schedule",
|
|
38617
|
+
"daysOfWeek"
|
|
38618
|
+
];
|
|
38619
|
+
if (!allowedFields.includes(field)) {
|
|
38620
|
+
throw new BadRequestError47(
|
|
38621
|
+
`Field "${field}" is not allowed to be updated.`
|
|
38622
|
+
);
|
|
38623
|
+
}
|
|
38624
|
+
try {
|
|
38625
|
+
_id = new ObjectId30(_id);
|
|
38626
|
+
} catch (error) {
|
|
38627
|
+
throw new BadRequestError47("Invalid ID.");
|
|
38628
|
+
}
|
|
38629
|
+
if (field === "teacher" && value) {
|
|
38630
|
+
try {
|
|
38631
|
+
value = new ObjectId30(value).toString();
|
|
38632
|
+
} catch (error) {
|
|
38633
|
+
throw new BadRequestError47("Invalid teacher ID.");
|
|
38634
|
+
}
|
|
38635
|
+
}
|
|
38636
|
+
try {
|
|
38637
|
+
const updateValue = field === "teacher" ? new ObjectId30(value) : value;
|
|
38638
|
+
await collection.updateOne(
|
|
38639
|
+
{ _id, deletedAt: { $in: ["", null] } },
|
|
38640
|
+
{ $set: { [field]: updateValue, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
|
|
38641
|
+
{ session }
|
|
38642
|
+
);
|
|
38643
|
+
delCachedData();
|
|
38644
|
+
return `Successfully updated section subject ${field}.`;
|
|
38645
|
+
} catch (error) {
|
|
38646
|
+
throw new InternalServerError14(
|
|
38647
|
+
`Failed to update section subject ${field}.`
|
|
38648
|
+
);
|
|
38649
|
+
}
|
|
38650
|
+
}
|
|
38651
|
+
async function setupById(_id, value, session) {
|
|
38652
|
+
const { error } = schemaSectionSubjectSetup.validate(value);
|
|
38653
|
+
if (error) {
|
|
38654
|
+
throw new BadRequestError47(
|
|
38655
|
+
`Invalid section subject data: ${error.message}`
|
|
38656
|
+
);
|
|
38657
|
+
}
|
|
38658
|
+
try {
|
|
38659
|
+
_id = new ObjectId30(_id);
|
|
38660
|
+
} catch (error2) {
|
|
38661
|
+
throw new BadRequestError47("Invalid ID.");
|
|
38662
|
+
}
|
|
38663
|
+
try {
|
|
38664
|
+
await collection.updateOne(
|
|
38665
|
+
{ _id, deletedAt: { $in: ["", null] } },
|
|
38666
|
+
{ $set: { ...value, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
|
|
38667
|
+
{ session }
|
|
38668
|
+
);
|
|
38669
|
+
delCachedData();
|
|
38670
|
+
return `Successfully updated section subject.`;
|
|
38671
|
+
} catch (error2) {
|
|
38672
|
+
throw new InternalServerError14(`Failed to update section subject.`);
|
|
38673
|
+
}
|
|
38674
|
+
}
|
|
38675
|
+
async function deleteById(_id) {
|
|
38676
|
+
try {
|
|
38677
|
+
_id = new ObjectId30(_id);
|
|
38678
|
+
} catch (error) {
|
|
38679
|
+
throw new BadRequestError47("Invalid ID.");
|
|
38680
|
+
}
|
|
38681
|
+
try {
|
|
38682
|
+
await collection.updateOne(
|
|
38683
|
+
{ _id },
|
|
38684
|
+
{ $set: { status: "deleted", deletedAt: (/* @__PURE__ */ new Date()).toISOString() } }
|
|
38685
|
+
);
|
|
38686
|
+
delCachedData();
|
|
38687
|
+
return "Successfully deleted section subject.";
|
|
38688
|
+
} catch (error) {
|
|
38689
|
+
throw new InternalServerError14("Failed to delete section subject.");
|
|
38690
|
+
}
|
|
38691
|
+
}
|
|
38692
|
+
return {
|
|
38693
|
+
createIndexes,
|
|
38694
|
+
add,
|
|
38695
|
+
getAll,
|
|
38696
|
+
getById,
|
|
38697
|
+
getBySection,
|
|
38698
|
+
getByTeacher,
|
|
38699
|
+
getBySchool,
|
|
38700
|
+
updateFieldById,
|
|
38701
|
+
setupById,
|
|
38702
|
+
deleteById
|
|
38703
|
+
};
|
|
38704
|
+
}
|
|
38705
|
+
|
|
38706
|
+
// src/resources/section-subject/section.subject.service.ts
|
|
38707
|
+
import { AppError as AppError17, InternalServerError as InternalServerError15 } from "@eeplatform/nodejs-utils";
|
|
38708
|
+
function useSectionSubjectService() {
|
|
38709
|
+
const { getById: getSchoolById } = useSchoolRepo();
|
|
38710
|
+
const { add: _add } = useSectionSubjectRepo();
|
|
38711
|
+
async function add(value) {
|
|
38712
|
+
const { error } = schemaSectionSubject.validate(value);
|
|
38713
|
+
if (error) {
|
|
38714
|
+
throw new Error(`Invalid section subject data: ${error.message}`);
|
|
38715
|
+
}
|
|
38716
|
+
try {
|
|
38717
|
+
const school = await getSchoolById(value.school);
|
|
38718
|
+
if (!school) {
|
|
38719
|
+
throw new Error("School not found.");
|
|
38720
|
+
}
|
|
38721
|
+
if (!school.name) {
|
|
38722
|
+
throw new Error("School name is missing.");
|
|
38723
|
+
}
|
|
38724
|
+
value.schoolName = school.name;
|
|
38725
|
+
await _add(value);
|
|
38726
|
+
return "Successfully created section subject.";
|
|
38727
|
+
} catch (error2) {
|
|
38728
|
+
if (error2 instanceof AppError17) {
|
|
38729
|
+
throw error2;
|
|
38730
|
+
} else {
|
|
38731
|
+
throw new InternalServerError15("Failed to create section subject.");
|
|
38732
|
+
}
|
|
38733
|
+
}
|
|
38734
|
+
}
|
|
38735
|
+
return {
|
|
38736
|
+
add
|
|
38737
|
+
};
|
|
38738
|
+
}
|
|
38739
|
+
|
|
38740
|
+
// src/resources/section-subject/section.subject.controller.ts
|
|
38741
|
+
import { BadRequestError as BadRequestError48 } from "@eeplatform/nodejs-utils";
|
|
38742
|
+
import Joi31 from "joi";
|
|
38743
|
+
function useSectionSubjectController() {
|
|
38744
|
+
const {
|
|
38745
|
+
getAll: _getAll,
|
|
38746
|
+
getById: _getById,
|
|
38747
|
+
getBySection: _getBySection,
|
|
38748
|
+
getByTeacher: _getByTeacher,
|
|
38749
|
+
getBySchool: _getBySchool,
|
|
38750
|
+
updateFieldById: _updateFieldById,
|
|
38751
|
+
deleteById: _deleteById,
|
|
38752
|
+
setupById: _setupById
|
|
38753
|
+
} = useSectionSubjectRepo();
|
|
38754
|
+
const { add: _add } = useSectionSubjectService();
|
|
38755
|
+
async function add(req, res, next) {
|
|
38756
|
+
const value = req.body;
|
|
38757
|
+
const { error } = schemaSectionSubject.validate(value);
|
|
38758
|
+
if (error) {
|
|
38759
|
+
next(new BadRequestError48(error.message));
|
|
38760
|
+
return;
|
|
38761
|
+
}
|
|
38762
|
+
try {
|
|
38763
|
+
const data = await _add(value);
|
|
38764
|
+
res.json({
|
|
38765
|
+
message: "Successfully created section subject.",
|
|
38766
|
+
data
|
|
38767
|
+
});
|
|
38354
38768
|
return;
|
|
38355
38769
|
} catch (error2) {
|
|
38356
38770
|
next(error2);
|
|
38357
38771
|
}
|
|
38358
38772
|
}
|
|
38359
|
-
async function
|
|
38773
|
+
async function getAll(req, res, next) {
|
|
38774
|
+
const query = req.query;
|
|
38775
|
+
const validation = Joi31.object({
|
|
38776
|
+
page: Joi31.number().min(1).optional().allow("", null),
|
|
38777
|
+
limit: Joi31.number().min(1).optional().allow("", null),
|
|
38778
|
+
search: Joi31.string().optional().allow("", null),
|
|
38779
|
+
status: Joi31.string().optional().allow("", null),
|
|
38780
|
+
school: Joi31.string().hex().optional().allow("", null),
|
|
38781
|
+
section: Joi31.string().hex().optional().allow("", null),
|
|
38782
|
+
teacher: Joi31.string().hex().optional().allow("", null),
|
|
38783
|
+
schoolYear: Joi31.string().optional().allow("", null),
|
|
38784
|
+
gradeLevel: Joi31.string().optional().allow("", null),
|
|
38785
|
+
subjectCode: Joi31.string().optional().allow("", null)
|
|
38786
|
+
});
|
|
38787
|
+
const { error } = validation.validate(query);
|
|
38788
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
38789
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
38790
|
+
const search = req.query.search ?? "";
|
|
38791
|
+
const status = req.query.status ?? "";
|
|
38792
|
+
const school = req.query.school ?? "";
|
|
38793
|
+
const section = req.query.section ?? "";
|
|
38794
|
+
const teacher = req.query.teacher ?? "";
|
|
38795
|
+
const schoolYear = req.query.schoolYear ?? "";
|
|
38796
|
+
const gradeLevel = req.query.gradeLevel ?? "";
|
|
38797
|
+
const subjectCode = req.query.subjectCode ?? "";
|
|
38798
|
+
const isPageNumber = isFinite(page);
|
|
38799
|
+
if (!isPageNumber) {
|
|
38800
|
+
next(new BadRequestError48("Invalid page number."));
|
|
38801
|
+
return;
|
|
38802
|
+
}
|
|
38803
|
+
const isLimitNumber = isFinite(limit);
|
|
38804
|
+
if (!isLimitNumber) {
|
|
38805
|
+
next(new BadRequestError48("Invalid limit number."));
|
|
38806
|
+
return;
|
|
38807
|
+
}
|
|
38808
|
+
if (error) {
|
|
38809
|
+
next(new BadRequestError48(error.message));
|
|
38810
|
+
return;
|
|
38811
|
+
}
|
|
38812
|
+
try {
|
|
38813
|
+
const data = await _getAll({
|
|
38814
|
+
page,
|
|
38815
|
+
limit,
|
|
38816
|
+
search,
|
|
38817
|
+
status,
|
|
38818
|
+
school,
|
|
38819
|
+
section,
|
|
38820
|
+
teacher,
|
|
38821
|
+
schoolYear,
|
|
38822
|
+
gradeLevel,
|
|
38823
|
+
subjectCode
|
|
38824
|
+
});
|
|
38825
|
+
res.json(data);
|
|
38826
|
+
return;
|
|
38827
|
+
} catch (error2) {
|
|
38828
|
+
next(error2);
|
|
38829
|
+
}
|
|
38830
|
+
}
|
|
38831
|
+
async function getById(req, res, next) {
|
|
38832
|
+
const id = req.params.id;
|
|
38833
|
+
const validation = Joi31.object({
|
|
38834
|
+
id: Joi31.string().hex().required()
|
|
38835
|
+
});
|
|
38836
|
+
const { error } = validation.validate({ id });
|
|
38837
|
+
if (error) {
|
|
38838
|
+
next(new BadRequestError48(error.message));
|
|
38839
|
+
return;
|
|
38840
|
+
}
|
|
38841
|
+
try {
|
|
38842
|
+
const data = await _getById(id);
|
|
38843
|
+
res.json({
|
|
38844
|
+
message: "Successfully retrieved section subject.",
|
|
38845
|
+
data
|
|
38846
|
+
});
|
|
38847
|
+
return;
|
|
38848
|
+
} catch (error2) {
|
|
38849
|
+
next(error2);
|
|
38850
|
+
}
|
|
38851
|
+
}
|
|
38852
|
+
async function getBySection(req, res, next) {
|
|
38853
|
+
const section = req.params.section;
|
|
38854
|
+
const validation = Joi31.object({
|
|
38855
|
+
section: Joi31.string().hex().required()
|
|
38856
|
+
});
|
|
38857
|
+
const { error } = validation.validate({ section });
|
|
38858
|
+
if (error) {
|
|
38859
|
+
next(new BadRequestError48(error.message));
|
|
38860
|
+
return;
|
|
38861
|
+
}
|
|
38862
|
+
try {
|
|
38863
|
+
const data = await _getBySection(section);
|
|
38864
|
+
res.json({
|
|
38865
|
+
message: "Successfully retrieved section subjects.",
|
|
38866
|
+
data
|
|
38867
|
+
});
|
|
38868
|
+
return;
|
|
38869
|
+
} catch (error2) {
|
|
38870
|
+
next(error2);
|
|
38871
|
+
}
|
|
38872
|
+
}
|
|
38873
|
+
async function getByTeacher(req, res, next) {
|
|
38874
|
+
const teacher = req.params.teacher;
|
|
38875
|
+
const validation = Joi31.object({
|
|
38876
|
+
teacher: Joi31.string().hex().required()
|
|
38877
|
+
});
|
|
38878
|
+
const { error } = validation.validate({ teacher });
|
|
38879
|
+
if (error) {
|
|
38880
|
+
next(new BadRequestError48(error.message));
|
|
38881
|
+
return;
|
|
38882
|
+
}
|
|
38883
|
+
try {
|
|
38884
|
+
const data = await _getByTeacher(teacher);
|
|
38885
|
+
res.json({
|
|
38886
|
+
message: "Successfully retrieved section subjects.",
|
|
38887
|
+
data
|
|
38888
|
+
});
|
|
38889
|
+
return;
|
|
38890
|
+
} catch (error2) {
|
|
38891
|
+
next(error2);
|
|
38892
|
+
}
|
|
38893
|
+
}
|
|
38894
|
+
async function getBySchool(req, res, next) {
|
|
38895
|
+
const school = req.params.school;
|
|
38896
|
+
const validation = Joi31.object({
|
|
38897
|
+
school: Joi31.string().hex().required()
|
|
38898
|
+
});
|
|
38899
|
+
const { error } = validation.validate({ school });
|
|
38900
|
+
if (error) {
|
|
38901
|
+
next(new BadRequestError48(error.message));
|
|
38902
|
+
return;
|
|
38903
|
+
}
|
|
38904
|
+
try {
|
|
38905
|
+
const data = await _getBySchool(school);
|
|
38906
|
+
res.json({
|
|
38907
|
+
message: "Successfully retrieved section subjects.",
|
|
38908
|
+
data
|
|
38909
|
+
});
|
|
38910
|
+
return;
|
|
38911
|
+
} catch (error2) {
|
|
38912
|
+
next(error2);
|
|
38913
|
+
}
|
|
38914
|
+
}
|
|
38915
|
+
async function updateField(req, res, next) {
|
|
38360
38916
|
const _id = req.params.id;
|
|
38361
|
-
const {
|
|
38362
|
-
const validation =
|
|
38363
|
-
_id:
|
|
38364
|
-
|
|
38917
|
+
const { field, value } = req.body;
|
|
38918
|
+
const validation = Joi31.object({
|
|
38919
|
+
_id: Joi31.string().hex().required(),
|
|
38920
|
+
field: Joi31.string().valid(
|
|
38921
|
+
"subjectCode",
|
|
38922
|
+
"subjectName",
|
|
38923
|
+
"teacher",
|
|
38924
|
+
"teacherName",
|
|
38925
|
+
"classroom",
|
|
38926
|
+
"schedule"
|
|
38927
|
+
).required(),
|
|
38928
|
+
value: Joi31.string().required()
|
|
38365
38929
|
});
|
|
38366
|
-
const { error } = validation.validate({ _id,
|
|
38930
|
+
const { error } = validation.validate({ _id, field, value });
|
|
38367
38931
|
if (error) {
|
|
38368
|
-
next(new
|
|
38932
|
+
next(new BadRequestError48(error.message));
|
|
38369
38933
|
return;
|
|
38370
38934
|
}
|
|
38371
38935
|
try {
|
|
38372
|
-
const message = await
|
|
38936
|
+
const message = await _updateFieldById({ _id, field, value });
|
|
38373
38937
|
res.json({ message });
|
|
38374
38938
|
return;
|
|
38375
38939
|
} catch (error2) {
|
|
38376
38940
|
next(error2);
|
|
38377
38941
|
}
|
|
38378
38942
|
}
|
|
38379
|
-
async function
|
|
38943
|
+
async function setupById(req, res, next) {
|
|
38380
38944
|
const _id = req.params.id;
|
|
38381
|
-
const
|
|
38382
|
-
const
|
|
38383
|
-
_id: Joi29.string().hex().required(),
|
|
38384
|
-
studentId: Joi29.string().required()
|
|
38385
|
-
});
|
|
38386
|
-
const { error } = validation.validate({ _id, studentId });
|
|
38945
|
+
const data = req.body;
|
|
38946
|
+
const { error } = schemaSectionSubjectSetup.validate(data);
|
|
38387
38947
|
if (error) {
|
|
38388
|
-
next(new
|
|
38948
|
+
next(new BadRequestError48(error.message));
|
|
38389
38949
|
return;
|
|
38390
38950
|
}
|
|
38391
38951
|
try {
|
|
38392
|
-
const message = await
|
|
38952
|
+
const message = await _setupById(_id, data);
|
|
38393
38953
|
res.json({ message });
|
|
38394
38954
|
return;
|
|
38395
38955
|
} catch (error2) {
|
|
@@ -38398,12 +38958,12 @@ function useSectionController() {
|
|
|
38398
38958
|
}
|
|
38399
38959
|
async function deleteById(req, res, next) {
|
|
38400
38960
|
const _id = req.params.id;
|
|
38401
|
-
const validation =
|
|
38402
|
-
_id:
|
|
38961
|
+
const validation = Joi31.object({
|
|
38962
|
+
_id: Joi31.string().hex().required()
|
|
38403
38963
|
});
|
|
38404
38964
|
const { error } = validation.validate({ _id });
|
|
38405
38965
|
if (error) {
|
|
38406
|
-
next(new
|
|
38966
|
+
next(new BadRequestError48(error.message));
|
|
38407
38967
|
return;
|
|
38408
38968
|
}
|
|
38409
38969
|
try {
|
|
@@ -38416,27 +38976,2260 @@ function useSectionController() {
|
|
|
38416
38976
|
}
|
|
38417
38977
|
return {
|
|
38418
38978
|
add,
|
|
38419
|
-
generateSections,
|
|
38420
38979
|
getAll,
|
|
38421
38980
|
getById,
|
|
38422
|
-
|
|
38981
|
+
getBySection,
|
|
38982
|
+
getByTeacher,
|
|
38423
38983
|
getBySchool,
|
|
38424
38984
|
updateField,
|
|
38425
|
-
|
|
38426
|
-
removeStudent,
|
|
38985
|
+
setupById,
|
|
38427
38986
|
deleteById
|
|
38428
38987
|
};
|
|
38429
38988
|
}
|
|
38430
38989
|
|
|
38990
|
+
// src/resources/section/section.service.ts
|
|
38991
|
+
function useSectionService() {
|
|
38992
|
+
const { getCountByGradeLevel, getByGradeLevel: getLeanerByGradeLevel } = useLearnerRepo();
|
|
38993
|
+
const { getByGradeLevel } = useGradeLevelRepo();
|
|
38994
|
+
const { add: createSection } = useSectionRepo();
|
|
38995
|
+
const { add: assignStudent } = useSectionStudentRepo();
|
|
38996
|
+
const { getAll: getAllCurriculumSubjects } = useCurriculumSubjectRepo();
|
|
38997
|
+
const { add: addSectionSubject } = useSectionSubjectRepo();
|
|
38998
|
+
const { getById: getSchoolById } = useSchoolRepo();
|
|
38999
|
+
function distributeStudents(total, minPer, maxPer) {
|
|
39000
|
+
if (total <= 0)
|
|
39001
|
+
return [];
|
|
39002
|
+
if (minPer <= 0 || maxPer <= 0)
|
|
39003
|
+
return [];
|
|
39004
|
+
if (minPer > maxPer) {
|
|
39005
|
+
throw new BadRequestError49(
|
|
39006
|
+
"Minimum students per section cannot be greater than maximum."
|
|
39007
|
+
);
|
|
39008
|
+
}
|
|
39009
|
+
const minSections = Math.ceil(total / maxPer);
|
|
39010
|
+
const maxSections = Math.floor(total / minPer);
|
|
39011
|
+
let sectionCount;
|
|
39012
|
+
if (minSections <= maxSections) {
|
|
39013
|
+
sectionCount = minSections;
|
|
39014
|
+
} else {
|
|
39015
|
+
sectionCount = minSections;
|
|
39016
|
+
}
|
|
39017
|
+
const base = Math.floor(total / sectionCount);
|
|
39018
|
+
const extra = total % sectionCount;
|
|
39019
|
+
const sizes = new Array(sectionCount).fill(base);
|
|
39020
|
+
for (let i = 0; i < extra; i++) {
|
|
39021
|
+
sizes[i] += 1;
|
|
39022
|
+
}
|
|
39023
|
+
for (const size of sizes) {
|
|
39024
|
+
if (size > maxPer) {
|
|
39025
|
+
throw new BadRequestError49(
|
|
39026
|
+
`Generated section exceeds max limit of ${maxPer}.`
|
|
39027
|
+
);
|
|
39028
|
+
}
|
|
39029
|
+
}
|
|
39030
|
+
return sizes;
|
|
39031
|
+
}
|
|
39032
|
+
async function generateSections(value) {
|
|
39033
|
+
const { error } = schemaGenerateSections.validate(value);
|
|
39034
|
+
if (error) {
|
|
39035
|
+
throw new BadRequestError49(
|
|
39036
|
+
`Invalid section generation data: ${error.message}`
|
|
39037
|
+
);
|
|
39038
|
+
}
|
|
39039
|
+
const session = useAtlas23.getClient()?.startSession();
|
|
39040
|
+
if (!session) {
|
|
39041
|
+
throw new Error("Unable to start database session.");
|
|
39042
|
+
}
|
|
39043
|
+
try {
|
|
39044
|
+
await session.startTransaction();
|
|
39045
|
+
const studentCount = await getCountByGradeLevel(
|
|
39046
|
+
{
|
|
39047
|
+
school: value.school,
|
|
39048
|
+
schoolYear: value.schoolYear,
|
|
39049
|
+
gradeLevel: value.gradeLevel
|
|
39050
|
+
},
|
|
39051
|
+
session
|
|
39052
|
+
);
|
|
39053
|
+
if (studentCount === 0) {
|
|
39054
|
+
throw new BadRequestError49("No learners found for this grade level.");
|
|
39055
|
+
}
|
|
39056
|
+
const gradeLevelData = await getByGradeLevel(
|
|
39057
|
+
{
|
|
39058
|
+
school: value.school,
|
|
39059
|
+
gradeLevel: value.gradeLevel
|
|
39060
|
+
},
|
|
39061
|
+
session
|
|
39062
|
+
);
|
|
39063
|
+
if (!gradeLevelData) {
|
|
39064
|
+
throw new BadRequestError49("Grade level not found.");
|
|
39065
|
+
}
|
|
39066
|
+
const minPerSection = gradeLevelData.minNumberOfLearners;
|
|
39067
|
+
const maxPerSection = gradeLevelData.maxNumberOfLearners;
|
|
39068
|
+
const sectionsNeeded = Math.ceil(studentCount / minPerSection);
|
|
39069
|
+
if (sectionsNeeded > value.set.length) {
|
|
39070
|
+
throw new BadRequestError49(
|
|
39071
|
+
"Insufficient number of section names in set[]."
|
|
39072
|
+
);
|
|
39073
|
+
}
|
|
39074
|
+
const sectionSizes = distributeStudents(
|
|
39075
|
+
studentCount,
|
|
39076
|
+
minPerSection,
|
|
39077
|
+
maxPerSection
|
|
39078
|
+
);
|
|
39079
|
+
if (sectionSizes.length === 0) {
|
|
39080
|
+
throw new BadRequestError49("Unable to compute section sizes.");
|
|
39081
|
+
}
|
|
39082
|
+
const schoolData = await getSchoolById(value.school);
|
|
39083
|
+
if (!schoolData) {
|
|
39084
|
+
throw new BadRequestError49("School not found.");
|
|
39085
|
+
}
|
|
39086
|
+
let totalStudentsProcessed = 0;
|
|
39087
|
+
for (let i = 0; i < sectionSizes.length; i++) {
|
|
39088
|
+
const size = sectionSizes[i];
|
|
39089
|
+
const sectionName = value.set[i];
|
|
39090
|
+
const section = await createSection(
|
|
39091
|
+
{
|
|
39092
|
+
school: value.school,
|
|
39093
|
+
schoolYear: value.schoolYear,
|
|
39094
|
+
gradeLevel: value.gradeLevel,
|
|
39095
|
+
name: sectionName,
|
|
39096
|
+
students: size
|
|
39097
|
+
},
|
|
39098
|
+
session
|
|
39099
|
+
);
|
|
39100
|
+
const skip = totalStudentsProcessed;
|
|
39101
|
+
const learners = await getLeanerByGradeLevel(
|
|
39102
|
+
{
|
|
39103
|
+
school: value.school,
|
|
39104
|
+
gradeLevel: value.gradeLevel,
|
|
39105
|
+
skip,
|
|
39106
|
+
limit: size
|
|
39107
|
+
},
|
|
39108
|
+
session
|
|
39109
|
+
);
|
|
39110
|
+
if (!learners.length) {
|
|
39111
|
+
throw new BadRequestError49(`No learners found for section #${i + 1}.`);
|
|
39112
|
+
}
|
|
39113
|
+
totalStudentsProcessed += learners.length;
|
|
39114
|
+
for (const student of learners) {
|
|
39115
|
+
if (!student._id) {
|
|
39116
|
+
throw new BadRequestError49("Learner ID is missing.");
|
|
39117
|
+
}
|
|
39118
|
+
if (!student.learnerInfo.lrn) {
|
|
39119
|
+
throw new BadRequestError49("Learner LRN is missing.");
|
|
39120
|
+
}
|
|
39121
|
+
await assignStudent(
|
|
39122
|
+
{
|
|
39123
|
+
section: section.toString(),
|
|
39124
|
+
lrn: student.learnerInfo.lrn,
|
|
39125
|
+
student: student._id?.toString(),
|
|
39126
|
+
studentName: `${student.learnerInfo.firstName} ${student.learnerInfo.lastName}`,
|
|
39127
|
+
school: value.school,
|
|
39128
|
+
schoolName: schoolData.name,
|
|
39129
|
+
gradeLevel: value.gradeLevel,
|
|
39130
|
+
educationLevel: gradeLevelData.educationLevel,
|
|
39131
|
+
schoolYear: value.schoolYear,
|
|
39132
|
+
status: "active"
|
|
39133
|
+
},
|
|
39134
|
+
session
|
|
39135
|
+
);
|
|
39136
|
+
}
|
|
39137
|
+
const curriculumSubjects = await getAllCurriculumSubjects({
|
|
39138
|
+
schoolYear: Number(value.schoolYear),
|
|
39139
|
+
gradeLevel: value.gradeLevel,
|
|
39140
|
+
limit: 20
|
|
39141
|
+
});
|
|
39142
|
+
for (const curriculumSubject of curriculumSubjects.items) {
|
|
39143
|
+
await addSectionSubject(
|
|
39144
|
+
{
|
|
39145
|
+
school: value.school,
|
|
39146
|
+
schoolName: schoolData.name,
|
|
39147
|
+
gradeLevel: value.gradeLevel,
|
|
39148
|
+
educationLevel: gradeLevelData.educationLevel,
|
|
39149
|
+
schoolYear: value.schoolYear,
|
|
39150
|
+
section: section.toString(),
|
|
39151
|
+
sectionName,
|
|
39152
|
+
subjectCode: curriculumSubject.subjectCode,
|
|
39153
|
+
subjectName: curriculumSubject.subjectName,
|
|
39154
|
+
teacher: "",
|
|
39155
|
+
teacherName: "",
|
|
39156
|
+
schedule: "",
|
|
39157
|
+
daysOfWeek: [],
|
|
39158
|
+
classroom: "",
|
|
39159
|
+
classroomName: "",
|
|
39160
|
+
sessionDuration: curriculumSubject.sessionDuration,
|
|
39161
|
+
sessionFrequency: curriculumSubject.sessionFrequency,
|
|
39162
|
+
status: "draft"
|
|
39163
|
+
},
|
|
39164
|
+
session
|
|
39165
|
+
);
|
|
39166
|
+
}
|
|
39167
|
+
}
|
|
39168
|
+
await session.commitTransaction();
|
|
39169
|
+
return "Sections generated successfully.";
|
|
39170
|
+
} catch (error2) {
|
|
39171
|
+
await session.abortTransaction();
|
|
39172
|
+
if (error2 instanceof AppError18) {
|
|
39173
|
+
throw error2;
|
|
39174
|
+
} else {
|
|
39175
|
+
throw new InternalServerError16("Failed to generate sections.");
|
|
39176
|
+
}
|
|
39177
|
+
} finally {
|
|
39178
|
+
await session?.endSession();
|
|
39179
|
+
}
|
|
39180
|
+
}
|
|
39181
|
+
return { generateSections };
|
|
39182
|
+
}
|
|
39183
|
+
|
|
39184
|
+
// src/resources/section/section.controller.ts
|
|
39185
|
+
function useSectionController() {
|
|
39186
|
+
const {
|
|
39187
|
+
add: _add,
|
|
39188
|
+
getAll: _getAll,
|
|
39189
|
+
getById: _getById,
|
|
39190
|
+
getByName: _getByName,
|
|
39191
|
+
getBySchool: _getBySchool,
|
|
39192
|
+
updateFieldById: _updateFieldById,
|
|
39193
|
+
addStudentToSection: _addStudentToSection,
|
|
39194
|
+
removeStudentFromSection: _removeStudentFromSection,
|
|
39195
|
+
deleteById: _deleteById
|
|
39196
|
+
} = useSectionRepo();
|
|
39197
|
+
const { generateSections: _generateSections } = useSectionService();
|
|
39198
|
+
async function add(req, res, next) {
|
|
39199
|
+
const value = req.body;
|
|
39200
|
+
const { error } = schemaSection.validate(value);
|
|
39201
|
+
if (error) {
|
|
39202
|
+
next(new BadRequestError50(error.message));
|
|
39203
|
+
return;
|
|
39204
|
+
}
|
|
39205
|
+
try {
|
|
39206
|
+
const data = await _add(value);
|
|
39207
|
+
res.json({
|
|
39208
|
+
message: "Successfully created section.",
|
|
39209
|
+
data
|
|
39210
|
+
});
|
|
39211
|
+
return;
|
|
39212
|
+
} catch (error2) {
|
|
39213
|
+
next(error2);
|
|
39214
|
+
}
|
|
39215
|
+
}
|
|
39216
|
+
async function generateSections(req, res, next) {
|
|
39217
|
+
const value = req.body;
|
|
39218
|
+
const { error } = schemaGenerateSections.validate(value);
|
|
39219
|
+
if (error) {
|
|
39220
|
+
next(new BadRequestError50(error.message));
|
|
39221
|
+
return;
|
|
39222
|
+
}
|
|
39223
|
+
try {
|
|
39224
|
+
const data = await _generateSections(value);
|
|
39225
|
+
res.json({
|
|
39226
|
+
message: "Successfully created section.",
|
|
39227
|
+
data
|
|
39228
|
+
});
|
|
39229
|
+
return;
|
|
39230
|
+
} catch (error2) {
|
|
39231
|
+
next(error2);
|
|
39232
|
+
}
|
|
39233
|
+
}
|
|
39234
|
+
async function getAll(req, res, next) {
|
|
39235
|
+
const query = req.query;
|
|
39236
|
+
const validation = Joi32.object({
|
|
39237
|
+
page: Joi32.number().min(1).optional().allow("", null),
|
|
39238
|
+
limit: Joi32.number().min(1).optional().allow("", null),
|
|
39239
|
+
search: Joi32.string().optional().allow("", null),
|
|
39240
|
+
status: Joi32.string().optional().allow("", null),
|
|
39241
|
+
school: Joi32.string().hex().optional().allow("", null),
|
|
39242
|
+
schoolYear: Joi32.string().optional().allow("", null),
|
|
39243
|
+
gradeLevel: Joi32.string().optional().allow("", null)
|
|
39244
|
+
});
|
|
39245
|
+
const { error } = validation.validate(query);
|
|
39246
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
39247
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
39248
|
+
const search = req.query.search ?? "";
|
|
39249
|
+
const status = req.query.status ?? "active";
|
|
39250
|
+
const school = req.query.school ?? "";
|
|
39251
|
+
const schoolYear = req.query.schoolYear ?? "";
|
|
39252
|
+
const gradeLevel = req.query.gradeLevel ?? "";
|
|
39253
|
+
const isPageNumber = isFinite(page);
|
|
39254
|
+
if (!isPageNumber) {
|
|
39255
|
+
next(new BadRequestError50("Invalid page number."));
|
|
39256
|
+
return;
|
|
39257
|
+
}
|
|
39258
|
+
const isLimitNumber = isFinite(limit);
|
|
39259
|
+
if (!isLimitNumber) {
|
|
39260
|
+
next(new BadRequestError50("Invalid limit number."));
|
|
39261
|
+
return;
|
|
39262
|
+
}
|
|
39263
|
+
if (error) {
|
|
39264
|
+
next(new BadRequestError50(error.message));
|
|
39265
|
+
return;
|
|
39266
|
+
}
|
|
39267
|
+
try {
|
|
39268
|
+
const data = await _getAll({
|
|
39269
|
+
page,
|
|
39270
|
+
limit,
|
|
39271
|
+
search,
|
|
39272
|
+
status,
|
|
39273
|
+
school,
|
|
39274
|
+
schoolYear,
|
|
39275
|
+
gradeLevel
|
|
39276
|
+
});
|
|
39277
|
+
res.json(data);
|
|
39278
|
+
return;
|
|
39279
|
+
} catch (error2) {
|
|
39280
|
+
next(error2);
|
|
39281
|
+
}
|
|
39282
|
+
}
|
|
39283
|
+
async function getById(req, res, next) {
|
|
39284
|
+
const id = req.params.id;
|
|
39285
|
+
const validation = Joi32.object({
|
|
39286
|
+
id: Joi32.string().hex().required()
|
|
39287
|
+
});
|
|
39288
|
+
const { error } = validation.validate({ id });
|
|
39289
|
+
if (error) {
|
|
39290
|
+
next(new BadRequestError50(error.message));
|
|
39291
|
+
return;
|
|
39292
|
+
}
|
|
39293
|
+
try {
|
|
39294
|
+
const data = await _getById(id);
|
|
39295
|
+
res.json(data);
|
|
39296
|
+
return;
|
|
39297
|
+
} catch (error2) {
|
|
39298
|
+
next(error2);
|
|
39299
|
+
}
|
|
39300
|
+
}
|
|
39301
|
+
async function getByName(req, res, next) {
|
|
39302
|
+
const name = req.params.name;
|
|
39303
|
+
const validation = Joi32.object({
|
|
39304
|
+
name: Joi32.string().required()
|
|
39305
|
+
});
|
|
39306
|
+
const { error } = validation.validate({ name });
|
|
39307
|
+
if (error) {
|
|
39308
|
+
next(new BadRequestError50(error.message));
|
|
39309
|
+
return;
|
|
39310
|
+
}
|
|
39311
|
+
try {
|
|
39312
|
+
const data = await _getByName(name);
|
|
39313
|
+
res.json({
|
|
39314
|
+
message: "Successfully retrieved section.",
|
|
39315
|
+
data
|
|
39316
|
+
});
|
|
39317
|
+
return;
|
|
39318
|
+
} catch (error2) {
|
|
39319
|
+
next(error2);
|
|
39320
|
+
}
|
|
39321
|
+
}
|
|
39322
|
+
async function getBySchool(req, res, next) {
|
|
39323
|
+
const school = req.params.school;
|
|
39324
|
+
const validation = Joi32.object({
|
|
39325
|
+
school: Joi32.string().hex().required()
|
|
39326
|
+
});
|
|
39327
|
+
const { error } = validation.validate({ school });
|
|
39328
|
+
if (error) {
|
|
39329
|
+
next(new BadRequestError50(error.message));
|
|
39330
|
+
return;
|
|
39331
|
+
}
|
|
39332
|
+
try {
|
|
39333
|
+
const data = await _getBySchool(school);
|
|
39334
|
+
res.json({
|
|
39335
|
+
message: "Successfully retrieved sections.",
|
|
39336
|
+
data
|
|
39337
|
+
});
|
|
39338
|
+
return;
|
|
39339
|
+
} catch (error2) {
|
|
39340
|
+
next(error2);
|
|
39341
|
+
}
|
|
39342
|
+
}
|
|
39343
|
+
async function updateField(req, res, next) {
|
|
39344
|
+
const _id = req.params.id;
|
|
39345
|
+
const { field, value } = req.body;
|
|
39346
|
+
const validation = Joi32.object({
|
|
39347
|
+
_id: Joi32.string().hex().required(),
|
|
39348
|
+
field: Joi32.string().valid("name", "schoolYear", "gradeLevel", "adviser", "adviserName").required(),
|
|
39349
|
+
value: Joi32.string().required()
|
|
39350
|
+
});
|
|
39351
|
+
const { error } = validation.validate({ _id, field, value });
|
|
39352
|
+
if (error) {
|
|
39353
|
+
next(new BadRequestError50(error.message));
|
|
39354
|
+
return;
|
|
39355
|
+
}
|
|
39356
|
+
try {
|
|
39357
|
+
const message = await _updateFieldById({ _id, field, value });
|
|
39358
|
+
res.json({ message });
|
|
39359
|
+
return;
|
|
39360
|
+
} catch (error2) {
|
|
39361
|
+
next(error2);
|
|
39362
|
+
}
|
|
39363
|
+
}
|
|
39364
|
+
async function addStudent(req, res, next) {
|
|
39365
|
+
const _id = req.params.id;
|
|
39366
|
+
const { studentId } = req.body;
|
|
39367
|
+
const validation = Joi32.object({
|
|
39368
|
+
_id: Joi32.string().hex().required(),
|
|
39369
|
+
studentId: Joi32.string().required()
|
|
39370
|
+
});
|
|
39371
|
+
const { error } = validation.validate({ _id, studentId });
|
|
39372
|
+
if (error) {
|
|
39373
|
+
next(new BadRequestError50(error.message));
|
|
39374
|
+
return;
|
|
39375
|
+
}
|
|
39376
|
+
try {
|
|
39377
|
+
const message = await _addStudentToSection(_id, studentId);
|
|
39378
|
+
res.json({ message });
|
|
39379
|
+
return;
|
|
39380
|
+
} catch (error2) {
|
|
39381
|
+
next(error2);
|
|
39382
|
+
}
|
|
39383
|
+
}
|
|
39384
|
+
async function removeStudent(req, res, next) {
|
|
39385
|
+
const _id = req.params.id;
|
|
39386
|
+
const { studentId } = req.body;
|
|
39387
|
+
const validation = Joi32.object({
|
|
39388
|
+
_id: Joi32.string().hex().required(),
|
|
39389
|
+
studentId: Joi32.string().required()
|
|
39390
|
+
});
|
|
39391
|
+
const { error } = validation.validate({ _id, studentId });
|
|
39392
|
+
if (error) {
|
|
39393
|
+
next(new BadRequestError50(error.message));
|
|
39394
|
+
return;
|
|
39395
|
+
}
|
|
39396
|
+
try {
|
|
39397
|
+
const message = await _removeStudentFromSection(_id, studentId);
|
|
39398
|
+
res.json({ message });
|
|
39399
|
+
return;
|
|
39400
|
+
} catch (error2) {
|
|
39401
|
+
next(error2);
|
|
39402
|
+
}
|
|
39403
|
+
}
|
|
39404
|
+
async function deleteById(req, res, next) {
|
|
39405
|
+
const _id = req.params.id;
|
|
39406
|
+
const validation = Joi32.object({
|
|
39407
|
+
_id: Joi32.string().hex().required()
|
|
39408
|
+
});
|
|
39409
|
+
const { error } = validation.validate({ _id });
|
|
39410
|
+
if (error) {
|
|
39411
|
+
next(new BadRequestError50(error.message));
|
|
39412
|
+
return;
|
|
39413
|
+
}
|
|
39414
|
+
try {
|
|
39415
|
+
const message = await _deleteById(_id);
|
|
39416
|
+
res.json({ message });
|
|
39417
|
+
return;
|
|
39418
|
+
} catch (error2) {
|
|
39419
|
+
next(error2);
|
|
39420
|
+
}
|
|
39421
|
+
}
|
|
39422
|
+
return {
|
|
39423
|
+
add,
|
|
39424
|
+
generateSections,
|
|
39425
|
+
getAll,
|
|
39426
|
+
getById,
|
|
39427
|
+
getByName,
|
|
39428
|
+
getBySchool,
|
|
39429
|
+
updateField,
|
|
39430
|
+
addStudent,
|
|
39431
|
+
removeStudent,
|
|
39432
|
+
deleteById
|
|
39433
|
+
};
|
|
39434
|
+
}
|
|
39435
|
+
|
|
39436
|
+
// src/resources/section-student/section.student.controller.ts
|
|
39437
|
+
import { BadRequestError as BadRequestError51 } from "@eeplatform/nodejs-utils";
|
|
39438
|
+
import Joi33 from "joi";
|
|
39439
|
+
function useSectionStudentController() {
|
|
39440
|
+
const { add: _add, getAll: _getAll } = useSectionStudentRepo();
|
|
39441
|
+
async function add(req, res, next) {
|
|
39442
|
+
const value = req.body;
|
|
39443
|
+
const { error } = schemaSectionStudent.validate(value);
|
|
39444
|
+
if (error) {
|
|
39445
|
+
next(new BadRequestError51(error.message));
|
|
39446
|
+
return;
|
|
39447
|
+
}
|
|
39448
|
+
try {
|
|
39449
|
+
const data = await _add(value);
|
|
39450
|
+
res.json({
|
|
39451
|
+
message: "Successfully created section student.",
|
|
39452
|
+
data
|
|
39453
|
+
});
|
|
39454
|
+
return;
|
|
39455
|
+
} catch (error2) {
|
|
39456
|
+
next(error2);
|
|
39457
|
+
}
|
|
39458
|
+
}
|
|
39459
|
+
async function getAll(req, res, next) {
|
|
39460
|
+
const query = req.query;
|
|
39461
|
+
const validation = Joi33.object({
|
|
39462
|
+
page: Joi33.number().min(1).optional().allow("", null),
|
|
39463
|
+
limit: Joi33.number().min(1).optional().allow("", null),
|
|
39464
|
+
search: Joi33.string().optional().allow("", null),
|
|
39465
|
+
status: Joi33.string().optional().allow("", null),
|
|
39466
|
+
school: Joi33.string().optional().allow("", null),
|
|
39467
|
+
gradeLevel: Joi33.string().optional().allow("", null),
|
|
39468
|
+
section: Joi33.string().optional().allow("", null),
|
|
39469
|
+
schoolYear: Joi33.string().optional().allow("", null)
|
|
39470
|
+
});
|
|
39471
|
+
const { error } = validation.validate(query);
|
|
39472
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
39473
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
39474
|
+
const search = req.query.search ?? "";
|
|
39475
|
+
const status = req.query.status ?? "active";
|
|
39476
|
+
const school = req.query.school ?? "";
|
|
39477
|
+
const gradeLevel = req.query.gradeLevel ?? "";
|
|
39478
|
+
const section = req.query.section ?? "";
|
|
39479
|
+
const schoolYear = req.query.schoolYear ?? "";
|
|
39480
|
+
const isPageNumber = isFinite(page);
|
|
39481
|
+
if (!isPageNumber) {
|
|
39482
|
+
next(new BadRequestError51("Invalid page number."));
|
|
39483
|
+
return;
|
|
39484
|
+
}
|
|
39485
|
+
const isLimitNumber = isFinite(limit);
|
|
39486
|
+
if (!isLimitNumber) {
|
|
39487
|
+
next(new BadRequestError51("Invalid limit number."));
|
|
39488
|
+
return;
|
|
39489
|
+
}
|
|
39490
|
+
if (error) {
|
|
39491
|
+
next(new BadRequestError51(error.message));
|
|
39492
|
+
return;
|
|
39493
|
+
}
|
|
39494
|
+
try {
|
|
39495
|
+
const data = await _getAll({
|
|
39496
|
+
page,
|
|
39497
|
+
limit,
|
|
39498
|
+
search,
|
|
39499
|
+
status,
|
|
39500
|
+
school,
|
|
39501
|
+
gradeLevel,
|
|
39502
|
+
section,
|
|
39503
|
+
schoolYear
|
|
39504
|
+
});
|
|
39505
|
+
res.json(data);
|
|
39506
|
+
return;
|
|
39507
|
+
} catch (error2) {
|
|
39508
|
+
next(error2);
|
|
39509
|
+
}
|
|
39510
|
+
}
|
|
39511
|
+
return {
|
|
39512
|
+
add,
|
|
39513
|
+
getAll
|
|
39514
|
+
};
|
|
39515
|
+
}
|
|
39516
|
+
|
|
39517
|
+
// src/resources/building/building.model.ts
|
|
39518
|
+
import { BadRequestError as BadRequestError52, logger as logger28 } from "@eeplatform/nodejs-utils";
|
|
39519
|
+
import Joi34 from "joi";
|
|
39520
|
+
import { ObjectId as ObjectId31 } from "mongodb";
|
|
39521
|
+
var schemaBuilding = Joi34.object({
|
|
39522
|
+
_id: Joi34.string().hex().optional(),
|
|
39523
|
+
school: Joi34.string().hex().required(),
|
|
39524
|
+
serial: Joi34.string().optional().allow("", null),
|
|
39525
|
+
name: Joi34.string().required(),
|
|
39526
|
+
levels: Joi34.number().integer().min(1).required(),
|
|
39527
|
+
createdAt: Joi34.date().optional().allow("", null),
|
|
39528
|
+
updatedAt: Joi34.date().optional().allow("", null),
|
|
39529
|
+
deletedAt: Joi34.date().optional().allow("", null),
|
|
39530
|
+
status: Joi34.string().optional().allow("", null)
|
|
39531
|
+
});
|
|
39532
|
+
var schemaBuildingUnit = Joi34.object({
|
|
39533
|
+
_id: Joi34.string().hex().optional(),
|
|
39534
|
+
school: Joi34.string().hex().required(),
|
|
39535
|
+
name: Joi34.string().optional().allow("", null),
|
|
39536
|
+
building: Joi34.string().hex().required(),
|
|
39537
|
+
buildingName: Joi34.string().optional().allow("", null),
|
|
39538
|
+
level: Joi34.number().integer().min(1).required(),
|
|
39539
|
+
category: Joi34.string().required(),
|
|
39540
|
+
type: Joi34.string().required(),
|
|
39541
|
+
seating_capacity: Joi34.number().integer().min(0).required(),
|
|
39542
|
+
standing_capacity: Joi34.number().integer().min(0).required(),
|
|
39543
|
+
description: Joi34.string().optional().allow("", null),
|
|
39544
|
+
unit_of_measurement: Joi34.string().valid("sqm").required(),
|
|
39545
|
+
area: Joi34.number().positive().required(),
|
|
39546
|
+
status: Joi34.string().optional().allow("", null)
|
|
39547
|
+
});
|
|
39548
|
+
var schemaUpdateOptions = Joi34.object({
|
|
39549
|
+
name: Joi34.string().optional().allow("", null),
|
|
39550
|
+
building: Joi34.string().hex().optional().allow("", null),
|
|
39551
|
+
buildingName: Joi34.string().optional().allow("", null),
|
|
39552
|
+
level: Joi34.number().integer().min(1).optional().allow("", null),
|
|
39553
|
+
category: Joi34.string().optional().allow("", null),
|
|
39554
|
+
type: Joi34.string().optional().allow("", null),
|
|
39555
|
+
seating_capacity: Joi34.number().integer().min(0).optional().allow("", null),
|
|
39556
|
+
standing_capacity: Joi34.number().integer().min(0).optional().allow("", null),
|
|
39557
|
+
area: Joi34.number().positive().optional().allow("", null)
|
|
39558
|
+
});
|
|
39559
|
+
function MBuilding(value) {
|
|
39560
|
+
const { error } = schemaBuilding.validate(value);
|
|
39561
|
+
if (error) {
|
|
39562
|
+
logger28.info(`Building Model: ${error.message}`);
|
|
39563
|
+
throw new BadRequestError52(error.message);
|
|
39564
|
+
}
|
|
39565
|
+
if (value._id && typeof value._id === "string") {
|
|
39566
|
+
try {
|
|
39567
|
+
value._id = new ObjectId31(value._id);
|
|
39568
|
+
} catch (error2) {
|
|
39569
|
+
throw new BadRequestError52("Invalid _id format");
|
|
39570
|
+
}
|
|
39571
|
+
}
|
|
39572
|
+
try {
|
|
39573
|
+
value.school = new ObjectId31(value.school);
|
|
39574
|
+
} catch (error2) {
|
|
39575
|
+
throw new BadRequestError52("Invalid school format");
|
|
39576
|
+
}
|
|
39577
|
+
return {
|
|
39578
|
+
_id: value._id ?? void 0,
|
|
39579
|
+
school: value.school,
|
|
39580
|
+
serial: value.serial ?? "",
|
|
39581
|
+
name: value.name ?? "",
|
|
39582
|
+
levels: value.levels ?? 0,
|
|
39583
|
+
status: value.status ?? "active",
|
|
39584
|
+
createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
|
|
39585
|
+
updatedAt: value.updatedAt ?? "",
|
|
39586
|
+
deletedAt: value.deletedAt ?? ""
|
|
39587
|
+
};
|
|
39588
|
+
}
|
|
39589
|
+
function MBuildingUnit(value) {
|
|
39590
|
+
const { error } = schemaBuildingUnit.validate(value);
|
|
39591
|
+
if (error) {
|
|
39592
|
+
logger28.info(`Building Unit Model: ${error.message}`);
|
|
39593
|
+
throw new BadRequestError52(error.message);
|
|
39594
|
+
}
|
|
39595
|
+
if (value._id && typeof value._id === "string") {
|
|
39596
|
+
try {
|
|
39597
|
+
value._id = new ObjectId31(value._id);
|
|
39598
|
+
} catch (error2) {
|
|
39599
|
+
throw new BadRequestError52("Invalid ID");
|
|
39600
|
+
}
|
|
39601
|
+
}
|
|
39602
|
+
try {
|
|
39603
|
+
value.school = new ObjectId31(value.school);
|
|
39604
|
+
} catch (error2) {
|
|
39605
|
+
throw new BadRequestError52("Invalid school ID");
|
|
39606
|
+
}
|
|
39607
|
+
try {
|
|
39608
|
+
value.building = new ObjectId31(value.building);
|
|
39609
|
+
} catch (error2) {
|
|
39610
|
+
throw new BadRequestError52("Invalid building ID");
|
|
39611
|
+
}
|
|
39612
|
+
return {
|
|
39613
|
+
_id: value._id ?? void 0,
|
|
39614
|
+
school: value.school,
|
|
39615
|
+
name: value.name ?? "",
|
|
39616
|
+
building: value.building,
|
|
39617
|
+
buildingName: value.buildingName ?? "",
|
|
39618
|
+
level: value.level ?? 0,
|
|
39619
|
+
category: value.category ?? "",
|
|
39620
|
+
type: value.type ?? "",
|
|
39621
|
+
seating_capacity: value.seating_capacity ?? 0,
|
|
39622
|
+
standing_capacity: value.standing_capacity ?? 0,
|
|
39623
|
+
description: value.description ?? "",
|
|
39624
|
+
unit_of_measurement: value.unit_of_measurement ?? "sqm",
|
|
39625
|
+
area: value.area ?? 0,
|
|
39626
|
+
status: value.status ?? "active",
|
|
39627
|
+
createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
|
|
39628
|
+
updatedAt: value.updatedAt ?? "",
|
|
39629
|
+
deletedAt: value.deletedAt ?? ""
|
|
39630
|
+
};
|
|
39631
|
+
}
|
|
39632
|
+
|
|
39633
|
+
// src/resources/building/building.repository.ts
|
|
39634
|
+
import {
|
|
39635
|
+
AppError as AppError19,
|
|
39636
|
+
BadRequestError as BadRequestError53,
|
|
39637
|
+
InternalServerError as InternalServerError17,
|
|
39638
|
+
logger as logger29,
|
|
39639
|
+
makeCacheKey as makeCacheKey17,
|
|
39640
|
+
paginate as paginate16,
|
|
39641
|
+
useAtlas as useAtlas24,
|
|
39642
|
+
useCache as useCache17
|
|
39643
|
+
} from "@eeplatform/nodejs-utils";
|
|
39644
|
+
import { ObjectId as ObjectId32 } from "mongodb";
|
|
39645
|
+
function useBuildingRepo() {
|
|
39646
|
+
const db = useAtlas24.getDb();
|
|
39647
|
+
if (!db) {
|
|
39648
|
+
throw new Error("Unable to connect to server.");
|
|
39649
|
+
}
|
|
39650
|
+
const namespace_collection = "school.buildings";
|
|
39651
|
+
const collection = db.collection(namespace_collection);
|
|
39652
|
+
const { getCache, setCache, delNamespace } = useCache17(namespace_collection);
|
|
39653
|
+
async function createIndexes() {
|
|
39654
|
+
try {
|
|
39655
|
+
await collection.createIndexes([
|
|
39656
|
+
{ key: { name: 1 }, unique: true, name: "unique_name_index" },
|
|
39657
|
+
{ key: { school: 1 } },
|
|
39658
|
+
{ key: { status: 1 } }
|
|
39659
|
+
]);
|
|
39660
|
+
} catch (error) {
|
|
39661
|
+
throw new Error("Failed to create index on buildings.");
|
|
39662
|
+
}
|
|
39663
|
+
}
|
|
39664
|
+
async function add(value, session) {
|
|
39665
|
+
try {
|
|
39666
|
+
value = MBuilding(value);
|
|
39667
|
+
const res = await collection.insertOne(value, { session });
|
|
39668
|
+
delCachedData();
|
|
39669
|
+
return res.insertedId;
|
|
39670
|
+
} catch (error) {
|
|
39671
|
+
logger29.log({
|
|
39672
|
+
level: "error",
|
|
39673
|
+
message: error.message
|
|
39674
|
+
});
|
|
39675
|
+
if (error instanceof AppError19) {
|
|
39676
|
+
throw error;
|
|
39677
|
+
} else {
|
|
39678
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
39679
|
+
if (isDuplicated) {
|
|
39680
|
+
throw new BadRequestError53("Building already exists.");
|
|
39681
|
+
}
|
|
39682
|
+
throw new Error("Failed to create building.");
|
|
39683
|
+
}
|
|
39684
|
+
}
|
|
39685
|
+
}
|
|
39686
|
+
async function updateById(_id, value, session) {
|
|
39687
|
+
try {
|
|
39688
|
+
_id = new ObjectId32(_id);
|
|
39689
|
+
} catch (error) {
|
|
39690
|
+
throw new BadRequestError53("Invalid ID.");
|
|
39691
|
+
}
|
|
39692
|
+
try {
|
|
39693
|
+
const res = await collection.updateOne(
|
|
39694
|
+
{ _id },
|
|
39695
|
+
{ $set: value },
|
|
39696
|
+
{ session }
|
|
39697
|
+
);
|
|
39698
|
+
delCachedData();
|
|
39699
|
+
return res;
|
|
39700
|
+
} catch (error) {
|
|
39701
|
+
logger29.log({
|
|
39702
|
+
level: "error",
|
|
39703
|
+
message: error.message
|
|
39704
|
+
});
|
|
39705
|
+
if (error instanceof AppError19) {
|
|
39706
|
+
throw error;
|
|
39707
|
+
} else {
|
|
39708
|
+
throw new Error("Failed to update building.");
|
|
39709
|
+
}
|
|
39710
|
+
}
|
|
39711
|
+
}
|
|
39712
|
+
async function getAll({
|
|
39713
|
+
search = "",
|
|
39714
|
+
page = 1,
|
|
39715
|
+
limit = 10,
|
|
39716
|
+
sort = {},
|
|
39717
|
+
school = "",
|
|
39718
|
+
status = "active"
|
|
39719
|
+
} = {}) {
|
|
39720
|
+
page = page > 0 ? page - 1 : 0;
|
|
39721
|
+
const query = {
|
|
39722
|
+
status
|
|
39723
|
+
};
|
|
39724
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
39725
|
+
if (search) {
|
|
39726
|
+
query.$text = { $search: search };
|
|
39727
|
+
}
|
|
39728
|
+
if (school) {
|
|
39729
|
+
try {
|
|
39730
|
+
query.school = new ObjectId32(school);
|
|
39731
|
+
} catch (error) {
|
|
39732
|
+
throw new BadRequestError53("Invalid school ID.");
|
|
39733
|
+
}
|
|
39734
|
+
}
|
|
39735
|
+
const cacheParams = {
|
|
39736
|
+
page,
|
|
39737
|
+
limit,
|
|
39738
|
+
sort: JSON.stringify(sort)
|
|
39739
|
+
};
|
|
39740
|
+
if (search)
|
|
39741
|
+
cacheParams.search = search;
|
|
39742
|
+
if (school)
|
|
39743
|
+
cacheParams.school = school;
|
|
39744
|
+
if (status !== "active")
|
|
39745
|
+
cacheParams.status = status;
|
|
39746
|
+
const cacheKey = makeCacheKey17(namespace_collection, cacheParams);
|
|
39747
|
+
logger29.log({
|
|
39748
|
+
level: "info",
|
|
39749
|
+
message: `Cache key for getAll buildings: ${cacheKey}`
|
|
39750
|
+
});
|
|
39751
|
+
try {
|
|
39752
|
+
const cached = await getCache(cacheKey);
|
|
39753
|
+
if (cached) {
|
|
39754
|
+
logger29.log({
|
|
39755
|
+
level: "info",
|
|
39756
|
+
message: `Cache hit for getAll buildings: ${cacheKey}`
|
|
39757
|
+
});
|
|
39758
|
+
return cached;
|
|
39759
|
+
}
|
|
39760
|
+
const items = await collection.aggregate([
|
|
39761
|
+
{ $match: query },
|
|
39762
|
+
{ $sort: sort },
|
|
39763
|
+
{ $skip: page * limit },
|
|
39764
|
+
{ $limit: limit }
|
|
39765
|
+
]).toArray();
|
|
39766
|
+
const length = await collection.countDocuments(query);
|
|
39767
|
+
const data = paginate16(items, page, limit, length);
|
|
39768
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
39769
|
+
logger29.log({
|
|
39770
|
+
level: "info",
|
|
39771
|
+
message: `Cache set for getAll buildings: ${cacheKey}`
|
|
39772
|
+
});
|
|
39773
|
+
}).catch((err) => {
|
|
39774
|
+
logger29.log({
|
|
39775
|
+
level: "error",
|
|
39776
|
+
message: `Failed to set cache for getAll buildings: ${err.message}`
|
|
39777
|
+
});
|
|
39778
|
+
});
|
|
39779
|
+
return data;
|
|
39780
|
+
} catch (error) {
|
|
39781
|
+
logger29.log({ level: "error", message: `${error}` });
|
|
39782
|
+
throw error;
|
|
39783
|
+
}
|
|
39784
|
+
}
|
|
39785
|
+
async function getById(_id) {
|
|
39786
|
+
try {
|
|
39787
|
+
_id = new ObjectId32(_id);
|
|
39788
|
+
} catch (error) {
|
|
39789
|
+
throw new BadRequestError53("Invalid ID.");
|
|
39790
|
+
}
|
|
39791
|
+
const cacheKey = makeCacheKey17(namespace_collection, { _id: String(_id) });
|
|
39792
|
+
try {
|
|
39793
|
+
const cached = await getCache(cacheKey);
|
|
39794
|
+
if (cached) {
|
|
39795
|
+
logger29.log({
|
|
39796
|
+
level: "info",
|
|
39797
|
+
message: `Cache hit for getById building: ${cacheKey}`
|
|
39798
|
+
});
|
|
39799
|
+
return cached;
|
|
39800
|
+
}
|
|
39801
|
+
const result = await collection.findOne({
|
|
39802
|
+
_id
|
|
39803
|
+
});
|
|
39804
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
39805
|
+
logger29.log({
|
|
39806
|
+
level: "info",
|
|
39807
|
+
message: `Cache set for building by id: ${cacheKey}`
|
|
39808
|
+
});
|
|
39809
|
+
}).catch((err) => {
|
|
39810
|
+
logger29.log({
|
|
39811
|
+
level: "error",
|
|
39812
|
+
message: `Failed to set cache for building by id: ${err.message}`
|
|
39813
|
+
});
|
|
39814
|
+
});
|
|
39815
|
+
return result;
|
|
39816
|
+
} catch (error) {
|
|
39817
|
+
if (error instanceof AppError19) {
|
|
39818
|
+
throw error;
|
|
39819
|
+
} else {
|
|
39820
|
+
throw new InternalServerError17("Failed to get building.");
|
|
39821
|
+
}
|
|
39822
|
+
}
|
|
39823
|
+
}
|
|
39824
|
+
async function deleteById(_id, session) {
|
|
39825
|
+
try {
|
|
39826
|
+
_id = new ObjectId32(_id);
|
|
39827
|
+
} catch (error) {
|
|
39828
|
+
throw new BadRequestError53("Invalid ID.");
|
|
39829
|
+
}
|
|
39830
|
+
try {
|
|
39831
|
+
const res = await collection.updateOne(
|
|
39832
|
+
{ _id },
|
|
39833
|
+
{ $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } }
|
|
39834
|
+
);
|
|
39835
|
+
delCachedData();
|
|
39836
|
+
return res;
|
|
39837
|
+
} catch (error) {
|
|
39838
|
+
logger29.log({
|
|
39839
|
+
level: "error",
|
|
39840
|
+
message: error.message
|
|
39841
|
+
});
|
|
39842
|
+
if (error instanceof AppError19) {
|
|
39843
|
+
throw error;
|
|
39844
|
+
} else {
|
|
39845
|
+
throw new InternalServerError17("Failed to delete building.");
|
|
39846
|
+
}
|
|
39847
|
+
}
|
|
39848
|
+
}
|
|
39849
|
+
function delCachedData() {
|
|
39850
|
+
delNamespace().then(() => {
|
|
39851
|
+
logger29.log({
|
|
39852
|
+
level: "info",
|
|
39853
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
39854
|
+
});
|
|
39855
|
+
}).catch((err) => {
|
|
39856
|
+
logger29.log({
|
|
39857
|
+
level: "error",
|
|
39858
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
39859
|
+
});
|
|
39860
|
+
});
|
|
39861
|
+
}
|
|
39862
|
+
return {
|
|
39863
|
+
createIndexes,
|
|
39864
|
+
add,
|
|
39865
|
+
getAll,
|
|
39866
|
+
getById,
|
|
39867
|
+
updateById,
|
|
39868
|
+
deleteById
|
|
39869
|
+
};
|
|
39870
|
+
}
|
|
39871
|
+
|
|
39872
|
+
// src/resources/building/building.service.ts
|
|
39873
|
+
import {
|
|
39874
|
+
BadRequestError as BadRequestError55,
|
|
39875
|
+
NotFoundError as NotFoundError3,
|
|
39876
|
+
useAtlas as useAtlas26
|
|
39877
|
+
} from "@eeplatform/nodejs-utils";
|
|
39878
|
+
|
|
39879
|
+
// src/resources/building/building-unit.repository.ts
|
|
39880
|
+
import {
|
|
39881
|
+
AppError as AppError20,
|
|
39882
|
+
BadRequestError as BadRequestError54,
|
|
39883
|
+
InternalServerError as InternalServerError18,
|
|
39884
|
+
logger as logger30,
|
|
39885
|
+
makeCacheKey as makeCacheKey18,
|
|
39886
|
+
paginate as paginate17,
|
|
39887
|
+
useAtlas as useAtlas25,
|
|
39888
|
+
useCache as useCache18
|
|
39889
|
+
} from "@eeplatform/nodejs-utils";
|
|
39890
|
+
import { ObjectId as ObjectId33 } from "mongodb";
|
|
39891
|
+
function useBuildingUnitRepo() {
|
|
39892
|
+
const db = useAtlas25.getDb();
|
|
39893
|
+
if (!db) {
|
|
39894
|
+
throw new Error("Unable to connect to server.");
|
|
39895
|
+
}
|
|
39896
|
+
const namespace_collection = "school.building-units";
|
|
39897
|
+
const collection = db.collection(namespace_collection);
|
|
39898
|
+
const { getCache, setCache, delNamespace } = useCache18(namespace_collection);
|
|
39899
|
+
async function createIndexes() {
|
|
39900
|
+
try {
|
|
39901
|
+
await collection.createIndexes([
|
|
39902
|
+
{
|
|
39903
|
+
key: { name: 1, building: 1, level: 1 },
|
|
39904
|
+
unique: true,
|
|
39905
|
+
name: "unique_name_index"
|
|
39906
|
+
},
|
|
39907
|
+
{ key: { school: 1 } },
|
|
39908
|
+
{ key: { building: 1 } },
|
|
39909
|
+
{ key: { status: 1 } },
|
|
39910
|
+
{ key: { createdAt: 1 } },
|
|
39911
|
+
{
|
|
39912
|
+
key: {
|
|
39913
|
+
name: "text",
|
|
39914
|
+
buildingName: "text",
|
|
39915
|
+
category: "text",
|
|
39916
|
+
type: "text"
|
|
39917
|
+
}
|
|
39918
|
+
}
|
|
39919
|
+
]);
|
|
39920
|
+
} catch (error) {
|
|
39921
|
+
throw new Error("Failed to create index on building units.");
|
|
39922
|
+
}
|
|
39923
|
+
}
|
|
39924
|
+
function delCachedData() {
|
|
39925
|
+
delNamespace().then(() => {
|
|
39926
|
+
logger30.log({
|
|
39927
|
+
level: "info",
|
|
39928
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
39929
|
+
});
|
|
39930
|
+
}).catch((err) => {
|
|
39931
|
+
logger30.log({
|
|
39932
|
+
level: "error",
|
|
39933
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
39934
|
+
});
|
|
39935
|
+
});
|
|
39936
|
+
}
|
|
39937
|
+
async function add(value, session) {
|
|
39938
|
+
try {
|
|
39939
|
+
value = MBuildingUnit(value);
|
|
39940
|
+
const res = await collection.insertOne(value, { session });
|
|
39941
|
+
delCachedData();
|
|
39942
|
+
return res.insertedId;
|
|
39943
|
+
} catch (error) {
|
|
39944
|
+
logger30.log({
|
|
39945
|
+
level: "error",
|
|
39946
|
+
message: error.message
|
|
39947
|
+
});
|
|
39948
|
+
if (error instanceof AppError20) {
|
|
39949
|
+
throw error;
|
|
39950
|
+
} else {
|
|
39951
|
+
throw new Error("Failed to create building unit.");
|
|
39952
|
+
}
|
|
39953
|
+
}
|
|
39954
|
+
}
|
|
39955
|
+
async function updateById(_id, value, session) {
|
|
39956
|
+
const { error } = schemaUpdateOptions.validate(value);
|
|
39957
|
+
if (error) {
|
|
39958
|
+
throw new BadRequestError54(error.message);
|
|
39959
|
+
}
|
|
39960
|
+
try {
|
|
39961
|
+
_id = new ObjectId33(_id);
|
|
39962
|
+
} catch (error2) {
|
|
39963
|
+
throw new BadRequestError54("Invalid ID.");
|
|
39964
|
+
}
|
|
39965
|
+
try {
|
|
39966
|
+
const res = await collection.updateOne(
|
|
39967
|
+
{ _id },
|
|
39968
|
+
{ $set: value },
|
|
39969
|
+
{ session }
|
|
39970
|
+
);
|
|
39971
|
+
delCachedData();
|
|
39972
|
+
return res;
|
|
39973
|
+
} catch (error2) {
|
|
39974
|
+
logger30.log({
|
|
39975
|
+
level: "error",
|
|
39976
|
+
message: error2.message
|
|
39977
|
+
});
|
|
39978
|
+
if (error2 instanceof AppError20) {
|
|
39979
|
+
throw error2;
|
|
39980
|
+
} else {
|
|
39981
|
+
throw new Error("Failed to create building unit.");
|
|
39982
|
+
}
|
|
39983
|
+
}
|
|
39984
|
+
}
|
|
39985
|
+
async function updateByBuildingId(building, value, session) {
|
|
39986
|
+
const { error } = schemaUpdateOptions.validate(value);
|
|
39987
|
+
if (error) {
|
|
39988
|
+
throw new BadRequestError54(error.message);
|
|
39989
|
+
}
|
|
39990
|
+
try {
|
|
39991
|
+
building = new ObjectId33(building);
|
|
39992
|
+
} catch (error2) {
|
|
39993
|
+
throw new BadRequestError54("Invalid building ID.");
|
|
39994
|
+
}
|
|
39995
|
+
try {
|
|
39996
|
+
const res = await collection.updateMany(
|
|
39997
|
+
{ building },
|
|
39998
|
+
{ $set: value },
|
|
39999
|
+
{ session }
|
|
40000
|
+
);
|
|
40001
|
+
delCachedData();
|
|
40002
|
+
return res;
|
|
40003
|
+
} catch (error2) {
|
|
40004
|
+
logger30.log({
|
|
40005
|
+
level: "error",
|
|
40006
|
+
message: error2.message
|
|
40007
|
+
});
|
|
40008
|
+
if (error2 instanceof AppError20) {
|
|
40009
|
+
throw error2;
|
|
40010
|
+
} else {
|
|
40011
|
+
throw new Error("Failed to update building unit.");
|
|
40012
|
+
}
|
|
40013
|
+
}
|
|
40014
|
+
}
|
|
40015
|
+
async function getAll({
|
|
40016
|
+
search = "",
|
|
40017
|
+
page = 1,
|
|
40018
|
+
limit = 10,
|
|
40019
|
+
sort = {},
|
|
40020
|
+
school = "",
|
|
40021
|
+
building = "",
|
|
40022
|
+
status = "active"
|
|
40023
|
+
} = {}) {
|
|
40024
|
+
page = page > 0 ? page - 1 : 0;
|
|
40025
|
+
const query = {
|
|
40026
|
+
deletedAt: { $in: ["", null] },
|
|
40027
|
+
status: { $in: [status, "", null] }
|
|
40028
|
+
};
|
|
40029
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
40030
|
+
if (search) {
|
|
40031
|
+
query.$text = { $search: search };
|
|
40032
|
+
}
|
|
40033
|
+
if (school) {
|
|
40034
|
+
try {
|
|
40035
|
+
query.school = new ObjectId33(school);
|
|
40036
|
+
} catch (error) {
|
|
40037
|
+
throw new BadRequestError54("Invalid school ID.");
|
|
40038
|
+
}
|
|
40039
|
+
}
|
|
40040
|
+
if (building) {
|
|
40041
|
+
try {
|
|
40042
|
+
query.building = new ObjectId33(building);
|
|
40043
|
+
} catch (error) {
|
|
40044
|
+
throw new BadRequestError54("Invalid building ID.");
|
|
40045
|
+
}
|
|
40046
|
+
}
|
|
40047
|
+
const cacheParams = {
|
|
40048
|
+
page,
|
|
40049
|
+
limit,
|
|
40050
|
+
sort: JSON.stringify(sort)
|
|
40051
|
+
};
|
|
40052
|
+
if (search)
|
|
40053
|
+
cacheParams.search = search;
|
|
40054
|
+
if (school)
|
|
40055
|
+
cacheParams.school = school;
|
|
40056
|
+
if (building)
|
|
40057
|
+
cacheParams.building = building;
|
|
40058
|
+
if (status !== "active")
|
|
40059
|
+
cacheParams.status = status;
|
|
40060
|
+
const cacheKey = makeCacheKey18(namespace_collection, cacheParams);
|
|
40061
|
+
logger30.log({
|
|
40062
|
+
level: "info",
|
|
40063
|
+
message: `Cache key for getAll building units: ${cacheKey}`
|
|
40064
|
+
});
|
|
40065
|
+
try {
|
|
40066
|
+
const cached = await getCache(cacheKey);
|
|
40067
|
+
if (cached) {
|
|
40068
|
+
logger30.log({
|
|
40069
|
+
level: "info",
|
|
40070
|
+
message: `Cache hit for getAll building units: ${cacheKey}`
|
|
40071
|
+
});
|
|
40072
|
+
return cached;
|
|
40073
|
+
}
|
|
40074
|
+
const items = await collection.aggregate([
|
|
40075
|
+
{ $match: query },
|
|
40076
|
+
{ $sort: sort },
|
|
40077
|
+
{ $skip: page * limit },
|
|
40078
|
+
{ $limit: limit }
|
|
40079
|
+
]).toArray();
|
|
40080
|
+
const length = await collection.countDocuments(query);
|
|
40081
|
+
const data = paginate17(items, page, limit, length);
|
|
40082
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
40083
|
+
logger30.log({
|
|
40084
|
+
level: "info",
|
|
40085
|
+
message: `Cache set for getAll building units: ${cacheKey}`
|
|
40086
|
+
});
|
|
40087
|
+
}).catch((err) => {
|
|
40088
|
+
logger30.log({
|
|
40089
|
+
level: "error",
|
|
40090
|
+
message: `Failed to set cache for getAll building units: ${err.message}`
|
|
40091
|
+
});
|
|
40092
|
+
});
|
|
40093
|
+
return data;
|
|
40094
|
+
} catch (error) {
|
|
40095
|
+
logger30.log({ level: "error", message: `${error}` });
|
|
40096
|
+
throw error;
|
|
40097
|
+
}
|
|
40098
|
+
}
|
|
40099
|
+
async function getById(_id) {
|
|
40100
|
+
try {
|
|
40101
|
+
_id = new ObjectId33(_id);
|
|
40102
|
+
} catch (error) {
|
|
40103
|
+
throw new BadRequestError54("Invalid ID.");
|
|
40104
|
+
}
|
|
40105
|
+
const cacheKey = makeCacheKey18(namespace_collection, { _id: String(_id) });
|
|
40106
|
+
try {
|
|
40107
|
+
const cached = await getCache(cacheKey);
|
|
40108
|
+
if (cached) {
|
|
40109
|
+
logger30.log({
|
|
40110
|
+
level: "info",
|
|
40111
|
+
message: `Cache hit for getById building unit: ${cacheKey}`
|
|
40112
|
+
});
|
|
40113
|
+
return cached;
|
|
40114
|
+
}
|
|
40115
|
+
const result = await collection.findOne({
|
|
40116
|
+
_id,
|
|
40117
|
+
deletedAt: { $in: ["", null] }
|
|
40118
|
+
});
|
|
40119
|
+
if (!result) {
|
|
40120
|
+
throw new BadRequestError54("Building unit not found.");
|
|
40121
|
+
}
|
|
40122
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
40123
|
+
logger30.log({
|
|
40124
|
+
level: "info",
|
|
40125
|
+
message: `Cache set for building unit by id: ${cacheKey}`
|
|
40126
|
+
});
|
|
40127
|
+
}).catch((err) => {
|
|
40128
|
+
logger30.log({
|
|
40129
|
+
level: "error",
|
|
40130
|
+
message: `Failed to set cache for building unit by id: ${err.message}`
|
|
40131
|
+
});
|
|
40132
|
+
});
|
|
40133
|
+
return result;
|
|
40134
|
+
} catch (error) {
|
|
40135
|
+
if (error instanceof AppError20) {
|
|
40136
|
+
throw error;
|
|
40137
|
+
} else {
|
|
40138
|
+
throw new InternalServerError18("Failed to get building unit.");
|
|
40139
|
+
}
|
|
40140
|
+
}
|
|
40141
|
+
}
|
|
40142
|
+
async function getByBuildingLevel(building, level) {
|
|
40143
|
+
try {
|
|
40144
|
+
building = new ObjectId33(building);
|
|
40145
|
+
} catch (error) {
|
|
40146
|
+
throw new BadRequestError54("Invalid building ID.");
|
|
40147
|
+
}
|
|
40148
|
+
const cacheKey = makeCacheKey18(namespace_collection, {
|
|
40149
|
+
building: String(building),
|
|
40150
|
+
level
|
|
40151
|
+
});
|
|
40152
|
+
try {
|
|
40153
|
+
const cached = await getCache(cacheKey);
|
|
40154
|
+
if (cached) {
|
|
40155
|
+
logger30.log({
|
|
40156
|
+
level: "info",
|
|
40157
|
+
message: `Cache hit for getById building unit: ${cacheKey}`
|
|
40158
|
+
});
|
|
40159
|
+
return cached;
|
|
40160
|
+
}
|
|
40161
|
+
const result = await collection.findOne({
|
|
40162
|
+
building,
|
|
40163
|
+
level,
|
|
40164
|
+
status: "active"
|
|
40165
|
+
});
|
|
40166
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
40167
|
+
logger30.log({
|
|
40168
|
+
level: "info",
|
|
40169
|
+
message: `Cache set for building unit by id: ${cacheKey}`
|
|
40170
|
+
});
|
|
40171
|
+
}).catch((err) => {
|
|
40172
|
+
logger30.log({
|
|
40173
|
+
level: "error",
|
|
40174
|
+
message: `Failed to set cache for building unit by id: ${err.message}`
|
|
40175
|
+
});
|
|
40176
|
+
});
|
|
40177
|
+
return result;
|
|
40178
|
+
} catch (error) {
|
|
40179
|
+
if (error instanceof AppError20) {
|
|
40180
|
+
throw error;
|
|
40181
|
+
} else {
|
|
40182
|
+
throw new InternalServerError18("Failed to get building unit.");
|
|
40183
|
+
}
|
|
40184
|
+
}
|
|
40185
|
+
}
|
|
40186
|
+
async function getByBuilding(building) {
|
|
40187
|
+
try {
|
|
40188
|
+
building = new ObjectId33(building);
|
|
40189
|
+
} catch (error) {
|
|
40190
|
+
throw new BadRequestError54("Invalid building ID.");
|
|
40191
|
+
}
|
|
40192
|
+
const cacheKey = makeCacheKey18(namespace_collection, {
|
|
40193
|
+
building: String(building)
|
|
40194
|
+
});
|
|
40195
|
+
try {
|
|
40196
|
+
const cached = await getCache(cacheKey);
|
|
40197
|
+
if (cached) {
|
|
40198
|
+
logger30.log({
|
|
40199
|
+
level: "info",
|
|
40200
|
+
message: `Cache hit for getById building unit: ${cacheKey}`
|
|
40201
|
+
});
|
|
40202
|
+
return cached;
|
|
40203
|
+
}
|
|
40204
|
+
const result = await collection.findOne({
|
|
40205
|
+
building,
|
|
40206
|
+
status: "active"
|
|
40207
|
+
});
|
|
40208
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
40209
|
+
logger30.log({
|
|
40210
|
+
level: "info",
|
|
40211
|
+
message: `Cache set for building unit by id: ${cacheKey}`
|
|
40212
|
+
});
|
|
40213
|
+
}).catch((err) => {
|
|
40214
|
+
logger30.log({
|
|
40215
|
+
level: "error",
|
|
40216
|
+
message: `Failed to set cache for building unit by id: ${err.message}`
|
|
40217
|
+
});
|
|
40218
|
+
});
|
|
40219
|
+
return result;
|
|
40220
|
+
} catch (error) {
|
|
40221
|
+
if (error instanceof AppError20) {
|
|
40222
|
+
throw error;
|
|
40223
|
+
} else {
|
|
40224
|
+
throw new InternalServerError18("Failed to get building unit.");
|
|
40225
|
+
}
|
|
40226
|
+
}
|
|
40227
|
+
}
|
|
40228
|
+
async function deleteById(_id, session) {
|
|
40229
|
+
try {
|
|
40230
|
+
_id = new ObjectId33(_id);
|
|
40231
|
+
} catch (error) {
|
|
40232
|
+
throw new BadRequestError54("Invalid ID.");
|
|
40233
|
+
}
|
|
40234
|
+
try {
|
|
40235
|
+
const res = await collection.updateOne(
|
|
40236
|
+
{ _id },
|
|
40237
|
+
{ $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } },
|
|
40238
|
+
{ session }
|
|
40239
|
+
);
|
|
40240
|
+
delCachedData();
|
|
40241
|
+
return "Room/Facility deleted successfully.";
|
|
40242
|
+
} catch (error) {
|
|
40243
|
+
logger30.log({
|
|
40244
|
+
level: "error",
|
|
40245
|
+
message: error.message
|
|
40246
|
+
});
|
|
40247
|
+
if (error instanceof AppError20) {
|
|
40248
|
+
throw error;
|
|
40249
|
+
} else {
|
|
40250
|
+
throw new Error("Failed to deleted room/facility.");
|
|
40251
|
+
}
|
|
40252
|
+
}
|
|
40253
|
+
}
|
|
40254
|
+
return {
|
|
40255
|
+
createIndexes,
|
|
40256
|
+
add,
|
|
40257
|
+
getAll,
|
|
40258
|
+
getById,
|
|
40259
|
+
getByBuildingLevel,
|
|
40260
|
+
updateById,
|
|
40261
|
+
getByBuilding,
|
|
40262
|
+
deleteById,
|
|
40263
|
+
updateByBuildingId
|
|
40264
|
+
};
|
|
40265
|
+
}
|
|
40266
|
+
|
|
40267
|
+
// src/resources/building/building.service.ts
|
|
40268
|
+
function useBuildingService() {
|
|
40269
|
+
const {
|
|
40270
|
+
updateById: _updateById,
|
|
40271
|
+
getById: _getById,
|
|
40272
|
+
deleteById: _deleteById
|
|
40273
|
+
} = useBuildingRepo();
|
|
40274
|
+
const { getByBuildingLevel, getByBuilding, updateByBuildingId } = useBuildingUnitRepo();
|
|
40275
|
+
async function updateById(id, data) {
|
|
40276
|
+
data.levels = Number(data.levels);
|
|
40277
|
+
const session = useAtlas26.getClient()?.startSession();
|
|
40278
|
+
try {
|
|
40279
|
+
const building = await _getById(id);
|
|
40280
|
+
if (!building) {
|
|
40281
|
+
throw new NotFoundError3("Building not found.");
|
|
40282
|
+
}
|
|
40283
|
+
if (data.levels < building.levels) {
|
|
40284
|
+
const unit = await getByBuildingLevel(id, building.levels);
|
|
40285
|
+
if (unit) {
|
|
40286
|
+
throw new BadRequestError55(
|
|
40287
|
+
"Cannot reduce floors, there are existing building units at higher floors."
|
|
40288
|
+
);
|
|
40289
|
+
}
|
|
40290
|
+
}
|
|
40291
|
+
session?.startTransaction();
|
|
40292
|
+
if (building.name !== data.name) {
|
|
40293
|
+
await updateByBuildingId(id, { buildingName: data.name }, session);
|
|
40294
|
+
}
|
|
40295
|
+
const result = await _updateById(id, data, session);
|
|
40296
|
+
await session?.commitTransaction();
|
|
40297
|
+
return result;
|
|
40298
|
+
} catch (error) {
|
|
40299
|
+
await session?.abortTransaction();
|
|
40300
|
+
throw error;
|
|
40301
|
+
} finally {
|
|
40302
|
+
session?.endSession();
|
|
40303
|
+
}
|
|
40304
|
+
}
|
|
40305
|
+
async function deleteById(id) {
|
|
40306
|
+
const building = await getByBuilding(id);
|
|
40307
|
+
if (building) {
|
|
40308
|
+
throw new BadRequestError55(
|
|
40309
|
+
"Cannot delete building with existing room/facility. Please delete room/facility first."
|
|
40310
|
+
);
|
|
40311
|
+
}
|
|
40312
|
+
try {
|
|
40313
|
+
await _deleteById(id);
|
|
40314
|
+
return "Building deleted successfully.";
|
|
40315
|
+
} catch (error) {
|
|
40316
|
+
throw error;
|
|
40317
|
+
}
|
|
40318
|
+
}
|
|
40319
|
+
return {
|
|
40320
|
+
updateById,
|
|
40321
|
+
deleteById
|
|
40322
|
+
};
|
|
40323
|
+
}
|
|
40324
|
+
|
|
40325
|
+
// src/resources/building/building.controller.ts
|
|
40326
|
+
import { BadRequestError as BadRequestError56, logger as logger31 } from "@eeplatform/nodejs-utils";
|
|
40327
|
+
import Joi35 from "joi";
|
|
40328
|
+
function useBuildingController() {
|
|
40329
|
+
const { getAll: _getAll, getById: _getById, add: _add } = useBuildingRepo();
|
|
40330
|
+
const { updateById: _updateById, deleteById: _deleteById } = useBuildingService();
|
|
40331
|
+
async function createBuilding(req, res, next) {
|
|
40332
|
+
const value = req.body;
|
|
40333
|
+
const validation = Joi35.object({
|
|
40334
|
+
name: Joi35.string().required(),
|
|
40335
|
+
school: Joi35.string().hex().required(),
|
|
40336
|
+
levels: Joi35.number().integer().min(1).required(),
|
|
40337
|
+
serial: Joi35.string().optional().allow("", null),
|
|
40338
|
+
status: Joi35.string().optional().allow("", null)
|
|
40339
|
+
});
|
|
40340
|
+
const { error } = validation.validate(value);
|
|
40341
|
+
if (error) {
|
|
40342
|
+
next(new BadRequestError56(error.message));
|
|
40343
|
+
logger31.info(`Controller: ${error.message}`);
|
|
40344
|
+
return;
|
|
40345
|
+
}
|
|
40346
|
+
try {
|
|
40347
|
+
const result = await _add(value);
|
|
40348
|
+
res.json(result);
|
|
40349
|
+
return;
|
|
40350
|
+
} catch (error2) {
|
|
40351
|
+
next(error2);
|
|
40352
|
+
}
|
|
40353
|
+
}
|
|
40354
|
+
async function updateById(req, res, next) {
|
|
40355
|
+
const value = req.body;
|
|
40356
|
+
const id = req.params.id ?? "";
|
|
40357
|
+
const validation = Joi35.object({
|
|
40358
|
+
id: Joi35.string().hex().required(),
|
|
40359
|
+
value: Joi35.object({
|
|
40360
|
+
name: Joi35.string().required(),
|
|
40361
|
+
serial: Joi35.string().optional().allow("", null),
|
|
40362
|
+
levels: Joi35.number().integer().min(1).required()
|
|
40363
|
+
})
|
|
40364
|
+
});
|
|
40365
|
+
const { error } = validation.validate({ id, value });
|
|
40366
|
+
if (error) {
|
|
40367
|
+
next(new BadRequestError56(error.message));
|
|
40368
|
+
logger31.info(`Controller: ${error.message}`);
|
|
40369
|
+
return;
|
|
40370
|
+
}
|
|
40371
|
+
try {
|
|
40372
|
+
const result = await _updateById(id, value);
|
|
40373
|
+
res.json(result);
|
|
40374
|
+
return;
|
|
40375
|
+
} catch (error2) {
|
|
40376
|
+
next(error2);
|
|
40377
|
+
}
|
|
40378
|
+
}
|
|
40379
|
+
async function getAll(req, res, next) {
|
|
40380
|
+
const query = req.query;
|
|
40381
|
+
const validation = Joi35.object({
|
|
40382
|
+
page: Joi35.number().min(1).optional().allow("", null),
|
|
40383
|
+
limit: Joi35.number().min(1).optional().allow("", null),
|
|
40384
|
+
search: Joi35.string().optional().allow("", null),
|
|
40385
|
+
school: Joi35.string().hex().optional().allow("", null),
|
|
40386
|
+
status: Joi35.string().optional().allow("", null)
|
|
40387
|
+
});
|
|
40388
|
+
const { error } = validation.validate(query);
|
|
40389
|
+
if (error) {
|
|
40390
|
+
next(new BadRequestError56(error.message));
|
|
40391
|
+
return;
|
|
40392
|
+
}
|
|
40393
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
40394
|
+
let limit = parseInt(req.query.limit) ?? 20;
|
|
40395
|
+
limit = isNaN(limit) ? 20 : limit;
|
|
40396
|
+
const sort = req.query.sort ? String(req.query.sort).split(",") : "";
|
|
40397
|
+
const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
|
|
40398
|
+
const sortObj = {};
|
|
40399
|
+
if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
|
|
40400
|
+
sort.forEach((field, index) => {
|
|
40401
|
+
sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
|
|
40402
|
+
});
|
|
40403
|
+
}
|
|
40404
|
+
const status = req.query.status ?? "active";
|
|
40405
|
+
const school = req.query.school ?? "";
|
|
40406
|
+
const search = req.query.search ?? "";
|
|
40407
|
+
try {
|
|
40408
|
+
const buildings = await _getAll({
|
|
40409
|
+
page,
|
|
40410
|
+
limit,
|
|
40411
|
+
sort: sortObj,
|
|
40412
|
+
status,
|
|
40413
|
+
school,
|
|
40414
|
+
search
|
|
40415
|
+
});
|
|
40416
|
+
res.json(buildings);
|
|
40417
|
+
return;
|
|
40418
|
+
} catch (error2) {
|
|
40419
|
+
next(error2);
|
|
40420
|
+
}
|
|
40421
|
+
}
|
|
40422
|
+
async function getById(req, res, next) {
|
|
40423
|
+
const id = req.params.id;
|
|
40424
|
+
const validation = Joi35.object({
|
|
40425
|
+
id: Joi35.string().hex().required()
|
|
40426
|
+
});
|
|
40427
|
+
const { error } = validation.validate({ id });
|
|
40428
|
+
if (error) {
|
|
40429
|
+
next(new BadRequestError56(error.message));
|
|
40430
|
+
return;
|
|
40431
|
+
}
|
|
40432
|
+
try {
|
|
40433
|
+
const building = await _getById(id);
|
|
40434
|
+
res.json({
|
|
40435
|
+
message: "Successfully retrieved building.",
|
|
40436
|
+
data: { building }
|
|
40437
|
+
});
|
|
40438
|
+
return;
|
|
40439
|
+
} catch (error2) {
|
|
40440
|
+
next(error2);
|
|
40441
|
+
}
|
|
40442
|
+
}
|
|
40443
|
+
async function deleteById(req, res, next) {
|
|
40444
|
+
const id = req.params.id;
|
|
40445
|
+
const validation = Joi35.object({
|
|
40446
|
+
id: Joi35.string().hex().required()
|
|
40447
|
+
});
|
|
40448
|
+
const { error } = validation.validate({ id });
|
|
40449
|
+
if (error) {
|
|
40450
|
+
next(new BadRequestError56(error.message));
|
|
40451
|
+
return;
|
|
40452
|
+
}
|
|
40453
|
+
try {
|
|
40454
|
+
const message = await _deleteById(id);
|
|
40455
|
+
res.json(message);
|
|
40456
|
+
return;
|
|
40457
|
+
} catch (error2) {
|
|
40458
|
+
next(error2);
|
|
40459
|
+
}
|
|
40460
|
+
}
|
|
40461
|
+
return {
|
|
40462
|
+
createBuilding,
|
|
40463
|
+
getAll,
|
|
40464
|
+
getById,
|
|
40465
|
+
updateById,
|
|
40466
|
+
deleteById
|
|
40467
|
+
};
|
|
40468
|
+
}
|
|
40469
|
+
|
|
40470
|
+
// src/resources/building/building-unit.service.ts
|
|
40471
|
+
import { useAtlas as useAtlas27 } from "@eeplatform/nodejs-utils";
|
|
40472
|
+
function useBuildingUnitService() {
|
|
40473
|
+
const { add: _add } = useBuildingUnitRepo();
|
|
40474
|
+
async function add(value) {
|
|
40475
|
+
const session = useAtlas27.getClient()?.startSession();
|
|
40476
|
+
if (!session) {
|
|
40477
|
+
throw new Error("Unable to start session for building unit service.");
|
|
40478
|
+
}
|
|
40479
|
+
try {
|
|
40480
|
+
await session.startTransaction();
|
|
40481
|
+
for (let index = 0; index < value.qty; index++) {
|
|
40482
|
+
await _add(
|
|
40483
|
+
{ ...value.building, name: `${value.building.name} ${index + 1}` },
|
|
40484
|
+
session
|
|
40485
|
+
);
|
|
40486
|
+
}
|
|
40487
|
+
await session.commitTransaction();
|
|
40488
|
+
return "Building unit added successfully.";
|
|
40489
|
+
} catch (error) {
|
|
40490
|
+
await session.abortTransaction();
|
|
40491
|
+
throw error;
|
|
40492
|
+
} finally {
|
|
40493
|
+
session.endSession();
|
|
40494
|
+
}
|
|
40495
|
+
}
|
|
40496
|
+
return {
|
|
40497
|
+
add
|
|
40498
|
+
};
|
|
40499
|
+
}
|
|
40500
|
+
|
|
40501
|
+
// src/resources/building/building-unit.controller.ts
|
|
40502
|
+
import { BadRequestError as BadRequestError57 } from "@eeplatform/nodejs-utils";
|
|
40503
|
+
import Joi36 from "joi";
|
|
40504
|
+
function useBuildingUnitController() {
|
|
40505
|
+
const {
|
|
40506
|
+
getAll: _getAll,
|
|
40507
|
+
getById: _getById,
|
|
40508
|
+
updateById: _updateById,
|
|
40509
|
+
deleteById: _deleteById
|
|
40510
|
+
} = useBuildingUnitRepo();
|
|
40511
|
+
const { add: _add } = useBuildingUnitService();
|
|
40512
|
+
async function add(req, res, next) {
|
|
40513
|
+
const data = req.body;
|
|
40514
|
+
const validation = Joi36.object({
|
|
40515
|
+
building: Joi36.object({
|
|
40516
|
+
school: Joi36.string().hex().required(),
|
|
40517
|
+
name: Joi36.string().optional().allow("", null),
|
|
40518
|
+
building: Joi36.string().hex().required(),
|
|
40519
|
+
buildingName: Joi36.string().optional().allow("", null),
|
|
40520
|
+
level: Joi36.number().integer().min(1).required(),
|
|
40521
|
+
category: Joi36.string().required(),
|
|
40522
|
+
type: Joi36.string().required(),
|
|
40523
|
+
seating_capacity: Joi36.number().integer().min(0).required(),
|
|
40524
|
+
standing_capacity: Joi36.number().integer().min(0).required(),
|
|
40525
|
+
description: Joi36.string().optional().allow("", null),
|
|
40526
|
+
unit_of_measurement: Joi36.string().valid("sqm").required(),
|
|
40527
|
+
area: Joi36.number().positive().required(),
|
|
40528
|
+
status: Joi36.string().optional().allow("", null)
|
|
40529
|
+
}),
|
|
40530
|
+
qty: Joi36.number().integer().min(1).max(20).optional().default(1)
|
|
40531
|
+
});
|
|
40532
|
+
const { error } = validation.validate(data);
|
|
40533
|
+
if (error) {
|
|
40534
|
+
next(new BadRequestError57(error.message));
|
|
40535
|
+
return;
|
|
40536
|
+
}
|
|
40537
|
+
try {
|
|
40538
|
+
const buildingUnit = await _add(data);
|
|
40539
|
+
res.json({
|
|
40540
|
+
message: "Building unit added successfully.",
|
|
40541
|
+
data: { buildingUnit }
|
|
40542
|
+
});
|
|
40543
|
+
} catch (error2) {
|
|
40544
|
+
next(error2);
|
|
40545
|
+
}
|
|
40546
|
+
}
|
|
40547
|
+
async function updateById(req, res, next) {
|
|
40548
|
+
const data = req.body;
|
|
40549
|
+
const id = req.params.id ?? "";
|
|
40550
|
+
const validation = Joi36.object({
|
|
40551
|
+
id: Joi36.string().hex().required(),
|
|
40552
|
+
value: schemaUpdateOptions
|
|
40553
|
+
});
|
|
40554
|
+
const { error } = validation.validate({ id, value: data });
|
|
40555
|
+
if (error) {
|
|
40556
|
+
next(new BadRequestError57(error.message));
|
|
40557
|
+
return;
|
|
40558
|
+
}
|
|
40559
|
+
try {
|
|
40560
|
+
const buildingUnit = await _updateById(id, data);
|
|
40561
|
+
res.json({
|
|
40562
|
+
message: "Building unit updated successfully.",
|
|
40563
|
+
data: { buildingUnit }
|
|
40564
|
+
});
|
|
40565
|
+
} catch (error2) {
|
|
40566
|
+
next(error2);
|
|
40567
|
+
}
|
|
40568
|
+
}
|
|
40569
|
+
async function getAll(req, res, next) {
|
|
40570
|
+
const query = req.query;
|
|
40571
|
+
const validation = Joi36.object({
|
|
40572
|
+
page: Joi36.number().min(1).optional().allow("", null),
|
|
40573
|
+
limit: Joi36.number().min(1).optional().allow("", null),
|
|
40574
|
+
search: Joi36.string().optional().allow("", null),
|
|
40575
|
+
school: Joi36.string().hex().optional().allow("", null),
|
|
40576
|
+
building: Joi36.string().hex().optional().allow("", null),
|
|
40577
|
+
status: Joi36.string().optional().allow("", null)
|
|
40578
|
+
});
|
|
40579
|
+
const { error } = validation.validate(query);
|
|
40580
|
+
if (error) {
|
|
40581
|
+
next(new BadRequestError57(error.message));
|
|
40582
|
+
return;
|
|
40583
|
+
}
|
|
40584
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
40585
|
+
let limit = parseInt(req.query.limit) ?? 20;
|
|
40586
|
+
limit = isNaN(limit) ? 20 : limit;
|
|
40587
|
+
const sort = req.query.sort ? String(req.query.sort).split(",") : "";
|
|
40588
|
+
const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
|
|
40589
|
+
const sortObj = {};
|
|
40590
|
+
if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
|
|
40591
|
+
sort.forEach((field, index) => {
|
|
40592
|
+
sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
|
|
40593
|
+
});
|
|
40594
|
+
}
|
|
40595
|
+
const status = req.query.status ?? "active";
|
|
40596
|
+
const school = req.query.school ?? "";
|
|
40597
|
+
const building = req.query.building ?? "";
|
|
40598
|
+
const search = req.query.search ?? "";
|
|
40599
|
+
try {
|
|
40600
|
+
const buildings = await _getAll({
|
|
40601
|
+
page,
|
|
40602
|
+
limit,
|
|
40603
|
+
sort: sortObj,
|
|
40604
|
+
status,
|
|
40605
|
+
school,
|
|
40606
|
+
search,
|
|
40607
|
+
building
|
|
40608
|
+
});
|
|
40609
|
+
res.json(buildings);
|
|
40610
|
+
return;
|
|
40611
|
+
} catch (error2) {
|
|
40612
|
+
next(error2);
|
|
40613
|
+
}
|
|
40614
|
+
}
|
|
40615
|
+
async function getById(req, res, next) {
|
|
40616
|
+
const id = req.params.id;
|
|
40617
|
+
const validation = Joi36.object({
|
|
40618
|
+
id: Joi36.string().hex().required()
|
|
40619
|
+
});
|
|
40620
|
+
const { error } = validation.validate({ id });
|
|
40621
|
+
if (error) {
|
|
40622
|
+
next(new BadRequestError57(error.message));
|
|
40623
|
+
return;
|
|
40624
|
+
}
|
|
40625
|
+
try {
|
|
40626
|
+
const buildingUnit = await _getById(id);
|
|
40627
|
+
res.json({
|
|
40628
|
+
message: "Successfully retrieved building unit.",
|
|
40629
|
+
data: { buildingUnit }
|
|
40630
|
+
});
|
|
40631
|
+
return;
|
|
40632
|
+
} catch (error2) {
|
|
40633
|
+
next(error2);
|
|
40634
|
+
}
|
|
40635
|
+
}
|
|
40636
|
+
async function deleteById(req, res, next) {
|
|
40637
|
+
const id = req.params.id;
|
|
40638
|
+
const validation = Joi36.object({
|
|
40639
|
+
id: Joi36.string().hex().required()
|
|
40640
|
+
});
|
|
40641
|
+
const { error } = validation.validate({ id });
|
|
40642
|
+
if (error) {
|
|
40643
|
+
next(new BadRequestError57(error.message));
|
|
40644
|
+
return;
|
|
40645
|
+
}
|
|
40646
|
+
try {
|
|
40647
|
+
const message = await _deleteById(id);
|
|
40648
|
+
res.json({ message });
|
|
40649
|
+
return;
|
|
40650
|
+
} catch (error2) {
|
|
40651
|
+
next(error2);
|
|
40652
|
+
}
|
|
40653
|
+
}
|
|
40654
|
+
return {
|
|
40655
|
+
add,
|
|
40656
|
+
getAll,
|
|
40657
|
+
getById,
|
|
40658
|
+
updateById,
|
|
40659
|
+
deleteById
|
|
40660
|
+
};
|
|
40661
|
+
}
|
|
40662
|
+
|
|
40663
|
+
// src/resources/personnel/personnel.model.ts
|
|
40664
|
+
import { BadRequestError as BadRequestError58, logger as logger32 } from "@eeplatform/nodejs-utils";
|
|
40665
|
+
import Joi37 from "joi";
|
|
40666
|
+
import { ObjectId as ObjectId34 } from "mongodb";
|
|
40667
|
+
var schemaPersonnel = Joi37.object({
|
|
40668
|
+
_id: Joi37.string().hex().optional().allow("", null),
|
|
40669
|
+
school: Joi37.string().hex().required(),
|
|
40670
|
+
schoolName: Joi37.string().optional().allow("", null),
|
|
40671
|
+
firstName: Joi37.string().required(),
|
|
40672
|
+
lastName: Joi37.string().required(),
|
|
40673
|
+
middleName: Joi37.string().optional().allow("", null),
|
|
40674
|
+
suffix: Joi37.string().optional().allow("", null),
|
|
40675
|
+
title: Joi37.string().optional().allow("", null),
|
|
40676
|
+
classification: Joi37.string().required(),
|
|
40677
|
+
status: Joi37.string().optional().allow("", null),
|
|
40678
|
+
createdAt: Joi37.date().optional().allow("", null),
|
|
40679
|
+
updatedAt: Joi37.date().optional().allow("", null),
|
|
40680
|
+
deletedAt: Joi37.date().optional().allow("", null)
|
|
40681
|
+
});
|
|
40682
|
+
function MPersonnel(value) {
|
|
40683
|
+
const { error } = schemaPersonnel.validate(value);
|
|
40684
|
+
if (error) {
|
|
40685
|
+
logger32.info(`Personnel Model: ${error.message}`);
|
|
40686
|
+
throw new BadRequestError58(error.message);
|
|
40687
|
+
}
|
|
40688
|
+
if (value._id && typeof value._id === "string") {
|
|
40689
|
+
try {
|
|
40690
|
+
value._id = new ObjectId34(value._id);
|
|
40691
|
+
} catch (error2) {
|
|
40692
|
+
throw new BadRequestError58("Invalid _id format");
|
|
40693
|
+
}
|
|
40694
|
+
}
|
|
40695
|
+
if (value.school && typeof value.school === "string") {
|
|
40696
|
+
try {
|
|
40697
|
+
value.school = new ObjectId34(value.school);
|
|
40698
|
+
} catch (error2) {
|
|
40699
|
+
throw new BadRequestError58("Invalid school format");
|
|
40700
|
+
}
|
|
40701
|
+
}
|
|
40702
|
+
if (!value.status) {
|
|
40703
|
+
value.status = "active";
|
|
40704
|
+
}
|
|
40705
|
+
return {
|
|
40706
|
+
_id: value._id,
|
|
40707
|
+
school: value.school,
|
|
40708
|
+
schoolName: value.schoolName ?? "",
|
|
40709
|
+
firstName: value.firstName ?? "",
|
|
40710
|
+
lastName: value.lastName ?? "",
|
|
40711
|
+
middleName: value.middleName ?? "",
|
|
40712
|
+
suffix: value.suffix ?? "",
|
|
40713
|
+
title: value.title ?? "",
|
|
40714
|
+
classification: value.classification ?? "",
|
|
40715
|
+
status: value.status,
|
|
40716
|
+
createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
|
|
40717
|
+
updatedAt: value.updatedAt ?? "",
|
|
40718
|
+
deletedAt: value.deletedAt ?? ""
|
|
40719
|
+
};
|
|
40720
|
+
}
|
|
40721
|
+
|
|
40722
|
+
// src/resources/personnel/personnel.repository.ts
|
|
40723
|
+
import {
|
|
40724
|
+
AppError as AppError21,
|
|
40725
|
+
BadRequestError as BadRequestError59,
|
|
40726
|
+
InternalServerError as InternalServerError19,
|
|
40727
|
+
logger as logger33,
|
|
40728
|
+
makeCacheKey as makeCacheKey19,
|
|
40729
|
+
paginate as paginate18,
|
|
40730
|
+
useAtlas as useAtlas28,
|
|
40731
|
+
useCache as useCache19
|
|
40732
|
+
} from "@eeplatform/nodejs-utils";
|
|
40733
|
+
import { ObjectId as ObjectId35 } from "mongodb";
|
|
40734
|
+
function usePersonnelRepo() {
|
|
40735
|
+
const db = useAtlas28.getDb();
|
|
40736
|
+
if (!db) {
|
|
40737
|
+
throw new Error("Unable to connect to server.");
|
|
40738
|
+
}
|
|
40739
|
+
const namespace_collection = "deped.school.personnel";
|
|
40740
|
+
const collection = db.collection(namespace_collection);
|
|
40741
|
+
const { getCache, setCache, delNamespace } = useCache19(namespace_collection);
|
|
40742
|
+
async function createIndexes() {
|
|
40743
|
+
try {
|
|
40744
|
+
await collection.createIndexes([
|
|
40745
|
+
{
|
|
40746
|
+
key: {
|
|
40747
|
+
firstName: 1,
|
|
40748
|
+
lastName: 1,
|
|
40749
|
+
classification: 1,
|
|
40750
|
+
status: 1
|
|
40751
|
+
},
|
|
40752
|
+
name: "personnel_search_index"
|
|
40753
|
+
},
|
|
40754
|
+
{ key: { status: 1 } },
|
|
40755
|
+
{ key: { classification: 1 } },
|
|
40756
|
+
{ key: { firstName: "text", lastName: "text", middleName: "text" } }
|
|
40757
|
+
]);
|
|
40758
|
+
} catch (error) {
|
|
40759
|
+
throw new Error("Failed to create index on personnel.");
|
|
40760
|
+
}
|
|
40761
|
+
}
|
|
40762
|
+
async function add(value, session) {
|
|
40763
|
+
try {
|
|
40764
|
+
value = MPersonnel(value);
|
|
40765
|
+
await collection.insertOne(value, { session });
|
|
40766
|
+
delCachedData();
|
|
40767
|
+
return "Successfully added personnel.";
|
|
40768
|
+
} catch (error) {
|
|
40769
|
+
logger33.log({
|
|
40770
|
+
level: "error",
|
|
40771
|
+
message: error.message
|
|
40772
|
+
});
|
|
40773
|
+
if (error instanceof AppError21) {
|
|
40774
|
+
throw error;
|
|
40775
|
+
} else {
|
|
40776
|
+
throw new Error("Failed to create personnel.");
|
|
40777
|
+
}
|
|
40778
|
+
}
|
|
40779
|
+
}
|
|
40780
|
+
async function updateById(_id, value, session) {
|
|
40781
|
+
try {
|
|
40782
|
+
_id = new ObjectId35(_id);
|
|
40783
|
+
} catch (error) {
|
|
40784
|
+
throw new BadRequestError59("Invalid ID.");
|
|
40785
|
+
}
|
|
40786
|
+
try {
|
|
40787
|
+
const res = await collection.updateOne(
|
|
40788
|
+
{ _id },
|
|
40789
|
+
{ $set: { ...value, updatedAt: /* @__PURE__ */ new Date() } },
|
|
40790
|
+
{ session }
|
|
40791
|
+
);
|
|
40792
|
+
delCachedData();
|
|
40793
|
+
return res;
|
|
40794
|
+
} catch (error) {
|
|
40795
|
+
logger33.log({
|
|
40796
|
+
level: "error",
|
|
40797
|
+
message: error.message
|
|
40798
|
+
});
|
|
40799
|
+
if (error instanceof AppError21) {
|
|
40800
|
+
throw error;
|
|
40801
|
+
} else {
|
|
40802
|
+
throw new Error("Failed to update personnel.");
|
|
40803
|
+
}
|
|
40804
|
+
}
|
|
40805
|
+
}
|
|
40806
|
+
async function getAll({
|
|
40807
|
+
school = "",
|
|
40808
|
+
search = "",
|
|
40809
|
+
page = 1,
|
|
40810
|
+
limit = 10,
|
|
40811
|
+
sort = {},
|
|
40812
|
+
status = "active"
|
|
40813
|
+
} = {}) {
|
|
40814
|
+
page = page > 0 ? page - 1 : 0;
|
|
40815
|
+
const query = {
|
|
40816
|
+
status
|
|
40817
|
+
};
|
|
40818
|
+
const cacheParams = {
|
|
40819
|
+
page,
|
|
40820
|
+
limit,
|
|
40821
|
+
sort: JSON.stringify(sort)
|
|
40822
|
+
};
|
|
40823
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
40824
|
+
if (search) {
|
|
40825
|
+
query.$text = { $search: search };
|
|
40826
|
+
cacheParams.search = search;
|
|
40827
|
+
}
|
|
40828
|
+
if (school) {
|
|
40829
|
+
try {
|
|
40830
|
+
query.school = new ObjectId35(school);
|
|
40831
|
+
} catch (error) {
|
|
40832
|
+
throw new BadRequestError59("Invalid school ID.");
|
|
40833
|
+
}
|
|
40834
|
+
cacheParams.school = school;
|
|
40835
|
+
}
|
|
40836
|
+
const cacheKey = makeCacheKey19(namespace_collection, cacheParams);
|
|
40837
|
+
logger33.log({
|
|
40838
|
+
level: "info",
|
|
40839
|
+
message: `Cache key for getAll personnel: ${cacheKey}`
|
|
40840
|
+
});
|
|
40841
|
+
try {
|
|
40842
|
+
const cached = await getCache(cacheKey);
|
|
40843
|
+
if (cached) {
|
|
40844
|
+
logger33.log({
|
|
40845
|
+
level: "info",
|
|
40846
|
+
message: `Cache hit for getAll personnel: ${cacheKey}`
|
|
40847
|
+
});
|
|
40848
|
+
return cached;
|
|
40849
|
+
}
|
|
40850
|
+
const items = await collection.aggregate([
|
|
40851
|
+
{ $match: query },
|
|
40852
|
+
{ $sort: sort },
|
|
40853
|
+
{ $skip: page * limit },
|
|
40854
|
+
{ $limit: limit }
|
|
40855
|
+
]).toArray();
|
|
40856
|
+
const length = await collection.countDocuments(query);
|
|
40857
|
+
const data = paginate18(items, page, limit, length);
|
|
40858
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
40859
|
+
logger33.log({
|
|
40860
|
+
level: "info",
|
|
40861
|
+
message: `Cache set for getAll personnel: ${cacheKey}`
|
|
40862
|
+
});
|
|
40863
|
+
}).catch((err) => {
|
|
40864
|
+
logger33.log({
|
|
40865
|
+
level: "error",
|
|
40866
|
+
message: `Failed to set cache for getAll personnel: ${err.message}`
|
|
40867
|
+
});
|
|
40868
|
+
});
|
|
40869
|
+
return data;
|
|
40870
|
+
} catch (error) {
|
|
40871
|
+
logger33.log({ level: "error", message: `${error}` });
|
|
40872
|
+
throw error;
|
|
40873
|
+
}
|
|
40874
|
+
}
|
|
40875
|
+
async function getById(_id) {
|
|
40876
|
+
try {
|
|
40877
|
+
_id = new ObjectId35(_id);
|
|
40878
|
+
} catch (error) {
|
|
40879
|
+
throw new BadRequestError59("Invalid ID.");
|
|
40880
|
+
}
|
|
40881
|
+
const cacheKey = makeCacheKey19(namespace_collection, { _id: String(_id) });
|
|
40882
|
+
try {
|
|
40883
|
+
const cached = await getCache(cacheKey);
|
|
40884
|
+
if (cached) {
|
|
40885
|
+
logger33.log({
|
|
40886
|
+
level: "info",
|
|
40887
|
+
message: `Cache hit for getById personnel: ${cacheKey}`
|
|
40888
|
+
});
|
|
40889
|
+
return cached;
|
|
40890
|
+
}
|
|
40891
|
+
const result = await collection.findOne({
|
|
40892
|
+
_id
|
|
40893
|
+
});
|
|
40894
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
40895
|
+
logger33.log({
|
|
40896
|
+
level: "info",
|
|
40897
|
+
message: `Cache set for personnel by id: ${cacheKey}`
|
|
40898
|
+
});
|
|
40899
|
+
}).catch((err) => {
|
|
40900
|
+
logger33.log({
|
|
40901
|
+
level: "error",
|
|
40902
|
+
message: `Failed to set cache for personnel by id: ${err.message}`
|
|
40903
|
+
});
|
|
40904
|
+
});
|
|
40905
|
+
return result;
|
|
40906
|
+
} catch (error) {
|
|
40907
|
+
if (error instanceof AppError21) {
|
|
40908
|
+
throw error;
|
|
40909
|
+
} else {
|
|
40910
|
+
throw new InternalServerError19("Failed to get personnel.");
|
|
40911
|
+
}
|
|
40912
|
+
}
|
|
40913
|
+
}
|
|
40914
|
+
async function deleteById(_id, session) {
|
|
40915
|
+
try {
|
|
40916
|
+
_id = new ObjectId35(_id);
|
|
40917
|
+
} catch (error) {
|
|
40918
|
+
throw new BadRequestError59("Invalid ID.");
|
|
40919
|
+
}
|
|
40920
|
+
try {
|
|
40921
|
+
const res = await collection.updateOne(
|
|
40922
|
+
{ _id },
|
|
40923
|
+
{ $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } }
|
|
40924
|
+
);
|
|
40925
|
+
delCachedData();
|
|
40926
|
+
return res;
|
|
40927
|
+
} catch (error) {
|
|
40928
|
+
logger33.log({
|
|
40929
|
+
level: "error",
|
|
40930
|
+
message: error.message
|
|
40931
|
+
});
|
|
40932
|
+
if (error instanceof AppError21) {
|
|
40933
|
+
throw error;
|
|
40934
|
+
} else {
|
|
40935
|
+
throw new InternalServerError19("Failed to delete personnel.");
|
|
40936
|
+
}
|
|
40937
|
+
}
|
|
40938
|
+
}
|
|
40939
|
+
async function getByClassification(classification) {
|
|
40940
|
+
const query = {
|
|
40941
|
+
classification,
|
|
40942
|
+
status: "active"
|
|
40943
|
+
};
|
|
40944
|
+
const cacheKey = makeCacheKey19(namespace_collection, { classification });
|
|
40945
|
+
try {
|
|
40946
|
+
const cached = await getCache(cacheKey);
|
|
40947
|
+
if (cached) {
|
|
40948
|
+
logger33.log({
|
|
40949
|
+
level: "info",
|
|
40950
|
+
message: `Cache hit for getByClassification personnel: ${cacheKey}`
|
|
40951
|
+
});
|
|
40952
|
+
return cached;
|
|
40953
|
+
}
|
|
40954
|
+
const result = await collection.find(query).toArray();
|
|
40955
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
40956
|
+
logger33.log({
|
|
40957
|
+
level: "info",
|
|
40958
|
+
message: `Cache set for personnel by classification: ${cacheKey}`
|
|
40959
|
+
});
|
|
40960
|
+
}).catch((err) => {
|
|
40961
|
+
logger33.log({
|
|
40962
|
+
level: "error",
|
|
40963
|
+
message: `Failed to set cache for personnel by classification: ${err.message}`
|
|
40964
|
+
});
|
|
40965
|
+
});
|
|
40966
|
+
return result;
|
|
40967
|
+
} catch (error) {
|
|
40968
|
+
if (error instanceof AppError21) {
|
|
40969
|
+
throw error;
|
|
40970
|
+
} else {
|
|
40971
|
+
throw new InternalServerError19(
|
|
40972
|
+
"Failed to get personnel by classification."
|
|
40973
|
+
);
|
|
40974
|
+
}
|
|
40975
|
+
}
|
|
40976
|
+
}
|
|
40977
|
+
function delCachedData() {
|
|
40978
|
+
delNamespace().then(() => {
|
|
40979
|
+
logger33.log({
|
|
40980
|
+
level: "info",
|
|
40981
|
+
message: `Cache cleared for namespace: ${namespace_collection}`
|
|
40982
|
+
});
|
|
40983
|
+
}).catch((err) => {
|
|
40984
|
+
logger33.log({
|
|
40985
|
+
level: "error",
|
|
40986
|
+
message: `Failed to clear cache for namespace: ${namespace_collection}: ${err.message}`
|
|
40987
|
+
});
|
|
40988
|
+
});
|
|
40989
|
+
}
|
|
40990
|
+
return {
|
|
40991
|
+
createIndexes,
|
|
40992
|
+
add,
|
|
40993
|
+
updateById,
|
|
40994
|
+
getAll,
|
|
40995
|
+
getById,
|
|
40996
|
+
deleteById,
|
|
40997
|
+
getByClassification,
|
|
40998
|
+
delCachedData
|
|
40999
|
+
};
|
|
41000
|
+
}
|
|
41001
|
+
|
|
41002
|
+
// src/resources/personnel/personnel.controller.ts
|
|
41003
|
+
import { BadRequestError as BadRequestError60, logger as logger34 } from "@eeplatform/nodejs-utils";
|
|
41004
|
+
import Joi38 from "joi";
|
|
41005
|
+
function usePersonnelController() {
|
|
41006
|
+
const {
|
|
41007
|
+
getAll: _getAll,
|
|
41008
|
+
getById: _getById,
|
|
41009
|
+
add: _add,
|
|
41010
|
+
updateById: _updateById,
|
|
41011
|
+
deleteById: _deleteById,
|
|
41012
|
+
getByClassification: _getByClassification
|
|
41013
|
+
} = usePersonnelRepo();
|
|
41014
|
+
async function add(req, res, next) {
|
|
41015
|
+
const value = req.body;
|
|
41016
|
+
const { error } = schemaPersonnel.validate(value);
|
|
41017
|
+
if (error) {
|
|
41018
|
+
next(new BadRequestError60(error.message));
|
|
41019
|
+
logger34.info(`Controller: ${error.message}`);
|
|
41020
|
+
return;
|
|
41021
|
+
}
|
|
41022
|
+
try {
|
|
41023
|
+
const result = await _add(value);
|
|
41024
|
+
res.json(result);
|
|
41025
|
+
return;
|
|
41026
|
+
} catch (error2) {
|
|
41027
|
+
next(error2);
|
|
41028
|
+
}
|
|
41029
|
+
}
|
|
41030
|
+
async function updateById(req, res, next) {
|
|
41031
|
+
const value = req.body;
|
|
41032
|
+
const id = req.params.id ?? "";
|
|
41033
|
+
const validation = Joi38.object({
|
|
41034
|
+
id: Joi38.string().hex().required(),
|
|
41035
|
+
value: Joi38.object({
|
|
41036
|
+
firstName: Joi38.string().optional(),
|
|
41037
|
+
lastName: Joi38.string().optional(),
|
|
41038
|
+
middleName: Joi38.string().optional().allow("", null),
|
|
41039
|
+
classification: Joi38.string().optional(),
|
|
41040
|
+
status: Joi38.string().optional()
|
|
41041
|
+
}).min(1)
|
|
41042
|
+
});
|
|
41043
|
+
const { error } = validation.validate({ id, value });
|
|
41044
|
+
if (error) {
|
|
41045
|
+
next(new BadRequestError60(error.message));
|
|
41046
|
+
logger34.info(`Controller: ${error.message}`);
|
|
41047
|
+
return;
|
|
41048
|
+
}
|
|
41049
|
+
try {
|
|
41050
|
+
const result = await _updateById(id, value);
|
|
41051
|
+
res.json(result);
|
|
41052
|
+
return;
|
|
41053
|
+
} catch (error2) {
|
|
41054
|
+
next(error2);
|
|
41055
|
+
}
|
|
41056
|
+
}
|
|
41057
|
+
async function getAll(req, res, next) {
|
|
41058
|
+
const query = req.query;
|
|
41059
|
+
const validation = Joi38.object({
|
|
41060
|
+
page: Joi38.number().min(1).optional().allow("", null),
|
|
41061
|
+
limit: Joi38.number().min(1).optional().allow("", null),
|
|
41062
|
+
search: Joi38.string().optional().allow("", null),
|
|
41063
|
+
status: Joi38.string().optional().allow("", null),
|
|
41064
|
+
classification: Joi38.string().optional().allow("", null)
|
|
41065
|
+
});
|
|
41066
|
+
const { error } = validation.validate(query);
|
|
41067
|
+
if (error) {
|
|
41068
|
+
next(new BadRequestError60(error.message));
|
|
41069
|
+
return;
|
|
41070
|
+
}
|
|
41071
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
41072
|
+
let limit = parseInt(req.query.limit) ?? 20;
|
|
41073
|
+
limit = isNaN(limit) ? 20 : limit;
|
|
41074
|
+
const sort = req.query.sort ? String(req.query.sort).split(",") : "";
|
|
41075
|
+
const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
|
|
41076
|
+
const sortObj = {};
|
|
41077
|
+
if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
|
|
41078
|
+
sort.forEach((field, index) => {
|
|
41079
|
+
sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
|
|
41080
|
+
});
|
|
41081
|
+
}
|
|
41082
|
+
const status = req.query.status ?? "active";
|
|
41083
|
+
const search = req.query.search ?? "";
|
|
41084
|
+
try {
|
|
41085
|
+
const personnel = await _getAll({
|
|
41086
|
+
page,
|
|
41087
|
+
limit,
|
|
41088
|
+
sort: sortObj,
|
|
41089
|
+
status,
|
|
41090
|
+
search
|
|
41091
|
+
});
|
|
41092
|
+
res.json(personnel);
|
|
41093
|
+
return;
|
|
41094
|
+
} catch (error2) {
|
|
41095
|
+
next(error2);
|
|
41096
|
+
}
|
|
41097
|
+
}
|
|
41098
|
+
async function getAllBySchool(req, res, next) {
|
|
41099
|
+
const school = req.params.school;
|
|
41100
|
+
const schoolValidation = Joi38.string().hex().required();
|
|
41101
|
+
const { error: schoolError } = schoolValidation.validate(school);
|
|
41102
|
+
if (schoolError) {
|
|
41103
|
+
next(new BadRequestError60(schoolError.message));
|
|
41104
|
+
return;
|
|
41105
|
+
}
|
|
41106
|
+
const query = req.query;
|
|
41107
|
+
const validation = Joi38.object({
|
|
41108
|
+
page: Joi38.number().min(1).optional().allow("", null),
|
|
41109
|
+
limit: Joi38.number().min(1).optional().allow("", null),
|
|
41110
|
+
search: Joi38.string().optional().allow("", null),
|
|
41111
|
+
status: Joi38.string().optional().allow("", null),
|
|
41112
|
+
classification: Joi38.string().optional().allow("", null)
|
|
41113
|
+
});
|
|
41114
|
+
const { error } = validation.validate(query);
|
|
41115
|
+
if (error) {
|
|
41116
|
+
next(new BadRequestError60(error.message));
|
|
41117
|
+
return;
|
|
41118
|
+
}
|
|
41119
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
41120
|
+
let limit = parseInt(req.query.limit) ?? 20;
|
|
41121
|
+
limit = isNaN(limit) ? 20 : limit;
|
|
41122
|
+
const sort = req.query.sort ? String(req.query.sort).split(",") : "";
|
|
41123
|
+
const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
|
|
41124
|
+
const sortObj = {};
|
|
41125
|
+
if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
|
|
41126
|
+
sort.forEach((field, index) => {
|
|
41127
|
+
sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
|
|
41128
|
+
});
|
|
41129
|
+
}
|
|
41130
|
+
const status = req.query.status ?? "active";
|
|
41131
|
+
const search = req.query.search ?? "";
|
|
41132
|
+
try {
|
|
41133
|
+
const personnel = await _getAll({
|
|
41134
|
+
school,
|
|
41135
|
+
page,
|
|
41136
|
+
limit,
|
|
41137
|
+
sort: sortObj,
|
|
41138
|
+
status,
|
|
41139
|
+
search
|
|
41140
|
+
});
|
|
41141
|
+
res.json(personnel);
|
|
41142
|
+
return;
|
|
41143
|
+
} catch (error2) {
|
|
41144
|
+
next(error2);
|
|
41145
|
+
}
|
|
41146
|
+
}
|
|
41147
|
+
async function getById(req, res, next) {
|
|
41148
|
+
const id = req.params.id;
|
|
41149
|
+
const validation = Joi38.object({
|
|
41150
|
+
id: Joi38.string().hex().required()
|
|
41151
|
+
});
|
|
41152
|
+
const { error } = validation.validate({ id });
|
|
41153
|
+
if (error) {
|
|
41154
|
+
next(new BadRequestError60(error.message));
|
|
41155
|
+
return;
|
|
41156
|
+
}
|
|
41157
|
+
try {
|
|
41158
|
+
const personnel = await _getById(id);
|
|
41159
|
+
res.json({
|
|
41160
|
+
message: "Successfully retrieved personnel.",
|
|
41161
|
+
data: personnel
|
|
41162
|
+
});
|
|
41163
|
+
return;
|
|
41164
|
+
} catch (error2) {
|
|
41165
|
+
next(error2);
|
|
41166
|
+
}
|
|
41167
|
+
}
|
|
41168
|
+
async function deleteById(req, res, next) {
|
|
41169
|
+
const id = req.params.id;
|
|
41170
|
+
const validation = Joi38.object({
|
|
41171
|
+
id: Joi38.string().hex().required()
|
|
41172
|
+
});
|
|
41173
|
+
const { error } = validation.validate({ id });
|
|
41174
|
+
if (error) {
|
|
41175
|
+
next(new BadRequestError60(error.message));
|
|
41176
|
+
return;
|
|
41177
|
+
}
|
|
41178
|
+
try {
|
|
41179
|
+
const result = await _deleteById(id);
|
|
41180
|
+
res.json({
|
|
41181
|
+
message: "Successfully deleted personnel.",
|
|
41182
|
+
data: result
|
|
41183
|
+
});
|
|
41184
|
+
return;
|
|
41185
|
+
} catch (error2) {
|
|
41186
|
+
next(error2);
|
|
41187
|
+
}
|
|
41188
|
+
}
|
|
41189
|
+
async function getByClassification(req, res, next) {
|
|
41190
|
+
const classification = req.params.classification;
|
|
41191
|
+
const validation = Joi38.object({
|
|
41192
|
+
classification: Joi38.string().required()
|
|
41193
|
+
});
|
|
41194
|
+
const { error } = validation.validate({ classification });
|
|
41195
|
+
if (error) {
|
|
41196
|
+
next(new BadRequestError60(error.message));
|
|
41197
|
+
return;
|
|
41198
|
+
}
|
|
41199
|
+
try {
|
|
41200
|
+
const personnel = await _getByClassification(classification);
|
|
41201
|
+
res.json({
|
|
41202
|
+
message: "Successfully retrieved personnel by classification.",
|
|
41203
|
+
data: personnel
|
|
41204
|
+
});
|
|
41205
|
+
return;
|
|
41206
|
+
} catch (error2) {
|
|
41207
|
+
next(error2);
|
|
41208
|
+
}
|
|
41209
|
+
}
|
|
41210
|
+
return {
|
|
41211
|
+
add,
|
|
41212
|
+
updateById,
|
|
41213
|
+
getAll,
|
|
41214
|
+
getAllBySchool,
|
|
41215
|
+
getById,
|
|
41216
|
+
deleteById,
|
|
41217
|
+
getByClassification
|
|
41218
|
+
};
|
|
41219
|
+
}
|
|
41220
|
+
|
|
38431
41221
|
// src/config.ts
|
|
38432
41222
|
import * as dotenv from "dotenv";
|
|
38433
41223
|
dotenv.config();
|
|
38434
41224
|
export {
|
|
38435
41225
|
MAsset,
|
|
41226
|
+
MBuilding,
|
|
41227
|
+
MBuildingUnit,
|
|
38436
41228
|
MCurriculum,
|
|
38437
41229
|
MCurriculumSubject,
|
|
38438
41230
|
MGradeLevel,
|
|
38439
41231
|
MLearner,
|
|
41232
|
+
MPersonnel,
|
|
38440
41233
|
MPlantilla,
|
|
38441
41234
|
MStockCard,
|
|
38442
41235
|
allowedSectionStudentStatuses,
|
|
@@ -38447,9 +41240,12 @@ export {
|
|
|
38447
41240
|
modelSection,
|
|
38448
41241
|
modelSectionPreset,
|
|
38449
41242
|
modelSectionStudent,
|
|
41243
|
+
modelSectionSubject,
|
|
38450
41244
|
schemaAsset,
|
|
38451
41245
|
schemaAssetUpdateOption,
|
|
38452
41246
|
schemaBasicEduCount,
|
|
41247
|
+
schemaBuilding,
|
|
41248
|
+
schemaBuildingUnit,
|
|
38453
41249
|
schemaCurriculum,
|
|
38454
41250
|
schemaCurriculumSubject,
|
|
38455
41251
|
schemaCurriculumSubjectAdd,
|
|
@@ -38458,6 +41254,7 @@ export {
|
|
|
38458
41254
|
schemaEnrollment,
|
|
38459
41255
|
schemaGenerateSections,
|
|
38460
41256
|
schemaGradeLevel,
|
|
41257
|
+
schemaPersonnel,
|
|
38461
41258
|
schemaPlantilla,
|
|
38462
41259
|
schemaRegion,
|
|
38463
41260
|
schemaSchool,
|
|
@@ -38465,11 +41262,20 @@ export {
|
|
|
38465
41262
|
schemaSection,
|
|
38466
41263
|
schemaSectionPreset,
|
|
38467
41264
|
schemaSectionStudent,
|
|
41265
|
+
schemaSectionSubject,
|
|
41266
|
+
schemaSectionSubjectSetup,
|
|
38468
41267
|
schemaStockCard,
|
|
41268
|
+
schemaUpdateOptions,
|
|
38469
41269
|
schemaUpdateStatus,
|
|
38470
41270
|
useAssetController,
|
|
38471
41271
|
useAssetRepo,
|
|
38472
41272
|
useBasicEduCountRepo,
|
|
41273
|
+
useBuildingController,
|
|
41274
|
+
useBuildingRepo,
|
|
41275
|
+
useBuildingService,
|
|
41276
|
+
useBuildingUnitController,
|
|
41277
|
+
useBuildingUnitRepo,
|
|
41278
|
+
useBuildingUnitService,
|
|
38473
41279
|
useCurriculumController,
|
|
38474
41280
|
useCurriculumRepo,
|
|
38475
41281
|
useCurriculumSubjectController,
|
|
@@ -38485,6 +41291,8 @@ export {
|
|
|
38485
41291
|
useGradeLevelRepo,
|
|
38486
41292
|
useLearnerController,
|
|
38487
41293
|
useLearnerRepo,
|
|
41294
|
+
usePersonnelController,
|
|
41295
|
+
usePersonnelRepo,
|
|
38488
41296
|
usePlantillaController,
|
|
38489
41297
|
usePlantillaRepo,
|
|
38490
41298
|
usePlantillaService,
|
|
@@ -38497,7 +41305,11 @@ export {
|
|
|
38497
41305
|
useSectionPresetController,
|
|
38498
41306
|
useSectionPresetRepo,
|
|
38499
41307
|
useSectionRepo,
|
|
41308
|
+
useSectionStudentController,
|
|
38500
41309
|
useSectionStudentRepo,
|
|
41310
|
+
useSectionSubjectController,
|
|
41311
|
+
useSectionSubjectRepo,
|
|
41312
|
+
useSectionSubjectService,
|
|
38501
41313
|
useStockCardController,
|
|
38502
41314
|
useStockCardRepository,
|
|
38503
41315
|
useStockCardService
|