@eeplatform/basic-edu 1.8.6 → 1.8.8

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/dist/index.mjs CHANGED
@@ -2875,7 +2875,7 @@ var learnerInfoSchema = Joi5.object({
2875
2875
  });
2876
2876
  var schemaUpdateStatus = Joi5.object({
2877
2877
  _id: Joi5.string().hex().length(24).required(),
2878
- status: Joi5.string().valid("pending", "accepted", "rejected").required()
2878
+ status: Joi5.string().valid("pending", "accepted", "rejected", "cancelled").required()
2879
2879
  });
2880
2880
  var gradeLevels = [
2881
2881
  "K1",
@@ -3580,17 +3580,16 @@ function useLearnerRepo() {
3580
3580
  school: Joi7.string().hex().required(),
3581
3581
  schoolYear: Joi7.string().required(),
3582
3582
  gradeLevel: Joi7.string().required(),
3583
+ specialProgram: Joi7.string().hex().optional().allow(null, ""),
3583
3584
  status: Joi7.string().optional()
3584
3585
  });
3585
3586
  const { error } = validation.validate(value);
3586
3587
  if (error) {
3587
3588
  throw new BadRequestError9(`Invalid data: ${error.message}`);
3588
3589
  }
3589
- const status = value.status ?? "active";
3590
3590
  const query = {
3591
3591
  schoolYear: value.schoolYear,
3592
- gradeLevel: value.gradeLevel,
3593
- status
3592
+ gradeLevel: value.gradeLevel
3594
3593
  };
3595
3594
  const cacheKeyOptions = {
3596
3595
  ...query,
@@ -3604,6 +3603,16 @@ function useLearnerRepo() {
3604
3603
  }
3605
3604
  cacheKeyOptions.school = value.school.toString();
3606
3605
  }
3606
+ if (value.specialProgram) {
3607
+ try {
3608
+ query["learnerInfo.specialProgram"] = new ObjectId7(
3609
+ value.specialProgram
3610
+ );
3611
+ } catch (error2) {
3612
+ throw new BadRequestError9("Invalid special program ID.");
3613
+ }
3614
+ cacheKeyOptions.specialProgram = value.specialProgram.toString();
3615
+ }
3607
3616
  const cacheKey = makeCacheKey4(namespace_collection, cacheKeyOptions);
3608
3617
  const cachedData = await getCache(cacheKey);
3609
3618
  if (cachedData !== void 0 && cachedData !== null) {
@@ -4337,6 +4346,51 @@ function useProgramScreeningRepo() {
4337
4346
  }
4338
4347
  }
4339
4348
  }
4349
+ async function getByApplicantId(_id) {
4350
+ try {
4351
+ _id = new ObjectId10(_id);
4352
+ } catch (error) {
4353
+ throw new BadRequestError14("Invalid ID.");
4354
+ }
4355
+ const cacheKey = makeCacheKey6(namespace_collection, {
4356
+ applicant: String(_id)
4357
+ });
4358
+ try {
4359
+ const cached = await getCache(cacheKey);
4360
+ if (cached) {
4361
+ logger11.log({
4362
+ level: "info",
4363
+ message: `Cache hit for getById program screening: ${cacheKey}`
4364
+ });
4365
+ return cached;
4366
+ }
4367
+ const result = await collection.findOne({
4368
+ applicant: _id,
4369
+ status: { $ne: "deleted" }
4370
+ });
4371
+ if (!result) {
4372
+ throw new BadRequestError14("Program screening not found.");
4373
+ }
4374
+ setCache(cacheKey, result, 300).then(() => {
4375
+ logger11.log({
4376
+ level: "info",
4377
+ message: `Cache set for program screening by id: ${cacheKey}`
4378
+ });
4379
+ }).catch((err) => {
4380
+ logger11.log({
4381
+ level: "error",
4382
+ message: `Failed to set cache for program screening by id: ${err.message}`
4383
+ });
4384
+ });
4385
+ return result;
4386
+ } catch (error) {
4387
+ if (error instanceof AppError7) {
4388
+ throw error;
4389
+ } else {
4390
+ throw new InternalServerError5("Failed to get program screening.");
4391
+ }
4392
+ }
4393
+ }
4340
4394
  async function getByCode(code, school) {
4341
4395
  try {
4342
4396
  school = new ObjectId10(school);
@@ -4460,6 +4514,26 @@ function useProgramScreeningRepo() {
4460
4514
  );
4461
4515
  }
4462
4516
  }
4517
+ async function updateStatusByApplicantId(_id, status, session) {
4518
+ try {
4519
+ _id = new ObjectId10(_id);
4520
+ } catch (error) {
4521
+ throw new BadRequestError14("Invalid ID.");
4522
+ }
4523
+ try {
4524
+ await collection.updateOne(
4525
+ { applicant: _id },
4526
+ { $set: { status, updatedAt: /* @__PURE__ */ new Date() } },
4527
+ { session }
4528
+ );
4529
+ delCachedData();
4530
+ return "Successfully updated program screening status.";
4531
+ } catch (error) {
4532
+ throw new InternalServerError5(
4533
+ "Failed to update program screening status."
4534
+ );
4535
+ }
4536
+ }
4463
4537
  return {
4464
4538
  createIndexes,
4465
4539
  add,
@@ -4468,7 +4542,9 @@ function useProgramScreeningRepo() {
4468
4542
  getByCode,
4469
4543
  updateFieldById,
4470
4544
  deleteById,
4471
- updateById
4545
+ updateById,
4546
+ updateStatusByApplicantId,
4547
+ getByApplicantId
4472
4548
  };
4473
4549
  }
4474
4550
 
@@ -5364,7 +5440,11 @@ function useEnrollmentService() {
5364
5440
  getById: _getById,
5365
5441
  updateStatusById: _updateStatusById
5366
5442
  } = useEnrollmentRepo();
5367
- const { add: addProgramScreening } = useProgramScreeningRepo();
5443
+ const {
5444
+ add: addProgramScreening,
5445
+ updateStatusByApplicantId,
5446
+ getByApplicantId
5447
+ } = useProgramScreeningRepo();
5368
5448
  const { getById: getProgramById } = useProgramRepo();
5369
5449
  async function add(value) {
5370
5450
  const session = useAtlas10.getClient()?.startSession();
@@ -5495,6 +5575,11 @@ function useEnrollmentService() {
5495
5575
  if (status === "rejected" && enrollment.status == "accepted") {
5496
5576
  throw new BadRequestError19("Accepted enrollments cannot be rejected.");
5497
5577
  }
5578
+ if (status === "withdrawn" && enrollment.status !== "screening") {
5579
+ throw new BadRequestError19(
5580
+ "Only enrollments in screening status can be withdrawn."
5581
+ );
5582
+ }
5498
5583
  const result = await _updateStatusById(_id, status, session);
5499
5584
  if (result.modifiedCount === 0) {
5500
5585
  throw new InternalServerError7("Failed to accept enrollment");
@@ -5534,6 +5619,14 @@ function useEnrollmentService() {
5534
5619
  enrollment.createdBy = enrollment.createdBy?.toString();
5535
5620
  await addLearner(enrollment, session);
5536
5621
  }
5622
+ const programScreening = await getByApplicantId(_id);
5623
+ if (programScreening) {
5624
+ if (["cancelled", "withdrawn"].includes(status)) {
5625
+ await updateStatusByApplicantId(_id, "cancelled", session);
5626
+ } else {
5627
+ await updateStatusByApplicantId(_id, status, session);
5628
+ }
5629
+ }
5537
5630
  await session.commitTransaction();
5538
5631
  return "Enrollment accepted successfully";
5539
5632
  } catch (error2) {
@@ -6168,7 +6261,8 @@ function useGradeLevelController() {
6168
6261
  add: _add,
6169
6262
  updateById: _updateById,
6170
6263
  deleteById: _deleteById,
6171
- getByEducationLevel: _getByEducationLevel
6264
+ getByEducationLevel: _getByEducationLevel,
6265
+ getByGradeLevel: _getByGradeLevel
6172
6266
  } = useGradeLevelRepo();
6173
6267
  async function add(req, res, next) {
6174
6268
  const value = req.body;
@@ -6339,13 +6433,34 @@ function useGradeLevelController() {
6339
6433
  next(error2);
6340
6434
  }
6341
6435
  }
6436
+ async function getByGradeLevel(req, res, next) {
6437
+ const gradeLevel = req.params.gradeLevel;
6438
+ const school = req.params.school;
6439
+ const validation = Joi16.object({
6440
+ gradeLevel: Joi16.string().required(),
6441
+ school: Joi16.string().hex().required()
6442
+ });
6443
+ const { error } = validation.validate({ gradeLevel, school });
6444
+ if (error) {
6445
+ next(new BadRequestError23(error.message));
6446
+ return;
6447
+ }
6448
+ try {
6449
+ const data = await _getByGradeLevel({ gradeLevel, school });
6450
+ res.json(data);
6451
+ return;
6452
+ } catch (error2) {
6453
+ next(error2);
6454
+ }
6455
+ }
6342
6456
  return {
6343
6457
  add,
6344
6458
  getAll,
6345
6459
  getById,
6346
6460
  updateById,
6347
6461
  deleteById,
6348
- getByEducationLevel
6462
+ getByEducationLevel,
6463
+ getByGradeLevel
6349
6464
  };
6350
6465
  }
6351
6466
 
@@ -38966,6 +39081,9 @@ var schemaGenerateSections = Joi31.object({
38966
39081
  school: Joi31.string().hex().required(),
38967
39082
  schoolYear: Joi31.string().required(),
38968
39083
  gradeLevel: Joi31.string().required(),
39084
+ minStudents: Joi31.number().integer().min(1).required(),
39085
+ maxStudents: Joi31.number().integer().min(Joi31.ref("minStudents")).required(),
39086
+ specialProgram: Joi31.string().hex().optional().allow(null, ""),
38969
39087
  set: Joi31.array().items(Joi31.string().min(1).max(100)).required()
38970
39088
  });
38971
39089
  function modelSection(value) {
@@ -42435,7 +42553,8 @@ function useSectionService() {
42435
42553
  {
42436
42554
  school: value.school,
42437
42555
  schoolYear: value.schoolYear,
42438
- gradeLevel: value.gradeLevel
42556
+ gradeLevel: value.gradeLevel,
42557
+ specialProgram: value.specialProgram
42439
42558
  },
42440
42559
  session
42441
42560
  );
@@ -42452,8 +42571,8 @@ function useSectionService() {
42452
42571
  if (!gradeLevelData) {
42453
42572
  throw new BadRequestError64("Grade level not found.");
42454
42573
  }
42455
- const minPerSection = gradeLevelData.minNumberOfLearners;
42456
- const maxPerSection = gradeLevelData.maxNumberOfLearners;
42574
+ const minPerSection = value.minStudents ?? gradeLevelData.minNumberOfLearners;
42575
+ const maxPerSection = value.maxStudents ?? gradeLevelData.maxNumberOfLearners;
42457
42576
  const sectionsNeeded = Math.ceil(studentCount / minPerSection);
42458
42577
  if (sectionsNeeded > value.set.length) {
42459
42578
  throw new BadRequestError64(
@@ -42599,7 +42718,64 @@ function useSectionService() {
42599
42718
  await session?.endSession();
42600
42719
  }
42601
42720
  }
42602
- return { generateSections };
42721
+ async function generateSectionPreview(value) {
42722
+ const { error } = schemaGenerateSections.validate(value);
42723
+ if (error) {
42724
+ throw new BadRequestError64(
42725
+ `Invalid section generation data: ${error.message}`
42726
+ );
42727
+ }
42728
+ try {
42729
+ const studentCount = await getCountByGradeLevel({
42730
+ school: value.school,
42731
+ schoolYear: value.schoolYear,
42732
+ gradeLevel: value.gradeLevel,
42733
+ specialProgram: value.specialProgram
42734
+ });
42735
+ if (studentCount === 0) {
42736
+ throw new BadRequestError64("No learners found for this grade level.");
42737
+ }
42738
+ const gradeLevelData = await getByGradeLevel({
42739
+ school: value.school,
42740
+ gradeLevel: value.gradeLevel
42741
+ });
42742
+ if (!gradeLevelData) {
42743
+ throw new BadRequestError64("Grade level not found.");
42744
+ }
42745
+ const minPerSection = value.minStudents ?? gradeLevelData.minNumberOfLearners;
42746
+ const maxPerSection = value.maxStudents ?? gradeLevelData.maxNumberOfLearners;
42747
+ const sectionsNeeded = Math.ceil(studentCount / minPerSection);
42748
+ if (sectionsNeeded > value.set.length) {
42749
+ throw new BadRequestError64(
42750
+ "Insufficient number of section names in set[]."
42751
+ );
42752
+ }
42753
+ const sectionSizes = distributeStudents(
42754
+ studentCount,
42755
+ minPerSection,
42756
+ maxPerSection
42757
+ );
42758
+ if (sectionSizes.length === 0) {
42759
+ throw new BadRequestError64("Unable to compute section sizes.");
42760
+ }
42761
+ const sections = sectionSizes.map((size, index) => ({
42762
+ name: value.set[index],
42763
+ value: size
42764
+ }));
42765
+ return {
42766
+ totalSectionsGenerated: sectionSizes.length,
42767
+ totalStudentsAssigned: studentCount,
42768
+ sections
42769
+ };
42770
+ } catch (error2) {
42771
+ if (error2 instanceof AppError25) {
42772
+ throw error2;
42773
+ } else {
42774
+ throw new InternalServerError21("Failed to generate section preview.");
42775
+ }
42776
+ }
42777
+ }
42778
+ return { generateSections, generateSectionPreview };
42603
42779
  }
42604
42780
 
42605
42781
  // src/resources/section/section.controller.ts
@@ -42615,7 +42791,10 @@ function useSectionController() {
42615
42791
  removeStudentFromSection: _removeStudentFromSection,
42616
42792
  deleteById: _deleteById
42617
42793
  } = useSectionRepo();
42618
- const { generateSections: _generateSections } = useSectionService();
42794
+ const {
42795
+ generateSections: _generateSections,
42796
+ generateSectionPreview: _generateSectionPreview
42797
+ } = useSectionService();
42619
42798
  async function add(req, res, next) {
42620
42799
  const value = req.body;
42621
42800
  const { error } = schemaSection.validate(value);
@@ -42652,6 +42831,21 @@ function useSectionController() {
42652
42831
  next(error2);
42653
42832
  }
42654
42833
  }
42834
+ async function generateSectionPreview(req, res, next) {
42835
+ const value = req.body;
42836
+ const { error } = schemaGenerateSections.validate(value);
42837
+ if (error) {
42838
+ next(new BadRequestError65(error.message));
42839
+ return;
42840
+ }
42841
+ try {
42842
+ const data = await _generateSectionPreview(value);
42843
+ res.json(data);
42844
+ return;
42845
+ } catch (error2) {
42846
+ next(error2);
42847
+ }
42848
+ }
42655
42849
  async function getAll(req, res, next) {
42656
42850
  const query = req.query;
42657
42851
  const validation = Joi43.object({
@@ -42843,6 +43037,7 @@ function useSectionController() {
42843
43037
  return {
42844
43038
  add,
42845
43039
  generateSections,
43040
+ generateSectionPreview,
42846
43041
  getAll,
42847
43042
  getById,
42848
43043
  getByName,