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