@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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @eeplatform/basic-edu
2
2
 
3
+ ## 1.8.8
4
+
5
+ ### Patch Changes
6
+
7
+ - cd82d95: Section Mgmt - revised generation
8
+
9
+ ## 1.8.7
10
+
11
+ ### Patch Changes
12
+
13
+ - 24c7ff6: Fix enrollment status update
14
+
3
15
  ## 1.8.6
4
16
 
5
17
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -473,6 +473,7 @@ declare function useGradeLevelController(): {
473
473
  updateById: (req: Request, res: Response, next: NextFunction) => Promise<void>;
474
474
  deleteById: (req: Request, res: Response, next: NextFunction) => Promise<void>;
475
475
  getByEducationLevel: (req: Request, res: Response, next: NextFunction) => Promise<void>;
476
+ getByGradeLevel: (req: Request, res: Response, next: NextFunction) => Promise<void>;
476
477
  };
477
478
 
478
479
  type TRegion = {
@@ -870,6 +871,7 @@ declare function useLearnerRepo(): {
870
871
  school: string | ObjectId;
871
872
  schoolYear: string;
872
873
  gradeLevel: string;
874
+ specialProgram?: string | ObjectId;
873
875
  status?: string;
874
876
  }, session?: ClientSession) => Promise<number>;
875
877
  updateStatusById: (_id: string | ObjectId, status: string, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
@@ -1013,6 +1015,7 @@ declare function useSectionRepo(): {
1013
1015
  declare function useSectionController(): {
1014
1016
  add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
1015
1017
  generateSections: (req: Request, res: Response, next: NextFunction) => Promise<void>;
1018
+ generateSectionPreview: (req: Request, res: Response, next: NextFunction) => Promise<void>;
1016
1019
  getAll: (req: Request, res: Response, next: NextFunction) => Promise<void>;
1017
1020
  getById: (req: Request, res: Response, next: NextFunction) => Promise<void>;
1018
1021
  getByName: (req: Request, res: Response, next: NextFunction) => Promise<void>;
@@ -1636,6 +1639,8 @@ declare function useProgramScreeningRepo(): {
1636
1639
  }, session?: ClientSession) => Promise<string>;
1637
1640
  deleteById: (_id: string | ObjectId) => Promise<string>;
1638
1641
  updateById: (_id: string | ObjectId, value: Pick<TProgramScreening, "specialProgram" | "specialProgramName" | "status">) => Promise<string>;
1642
+ updateStatusByApplicantId: (_id: string | ObjectId, status: string, session?: ClientSession) => Promise<string>;
1643
+ getByApplicantId: (_id: string | ObjectId) => Promise<TProgramScreening>;
1639
1644
  };
1640
1645
 
1641
1646
  declare function useProgramScreeningController(): {
package/dist/index.js CHANGED
@@ -2968,7 +2968,7 @@ var learnerInfoSchema = import_joi5.default.object({
2968
2968
  });
2969
2969
  var schemaUpdateStatus = import_joi5.default.object({
2970
2970
  _id: import_joi5.default.string().hex().length(24).required(),
2971
- status: import_joi5.default.string().valid("pending", "accepted", "rejected").required()
2971
+ status: import_joi5.default.string().valid("pending", "accepted", "rejected", "cancelled").required()
2972
2972
  });
2973
2973
  var gradeLevels = [
2974
2974
  "K1",
@@ -3649,17 +3649,16 @@ function useLearnerRepo() {
3649
3649
  school: import_joi7.default.string().hex().required(),
3650
3650
  schoolYear: import_joi7.default.string().required(),
3651
3651
  gradeLevel: import_joi7.default.string().required(),
3652
+ specialProgram: import_joi7.default.string().hex().optional().allow(null, ""),
3652
3653
  status: import_joi7.default.string().optional()
3653
3654
  });
3654
3655
  const { error } = validation.validate(value);
3655
3656
  if (error) {
3656
3657
  throw new import_nodejs_utils10.BadRequestError(`Invalid data: ${error.message}`);
3657
3658
  }
3658
- const status = value.status ?? "active";
3659
3659
  const query = {
3660
3660
  schoolYear: value.schoolYear,
3661
- gradeLevel: value.gradeLevel,
3662
- status
3661
+ gradeLevel: value.gradeLevel
3663
3662
  };
3664
3663
  const cacheKeyOptions = {
3665
3664
  ...query,
@@ -3673,6 +3672,16 @@ function useLearnerRepo() {
3673
3672
  }
3674
3673
  cacheKeyOptions.school = value.school.toString();
3675
3674
  }
3675
+ if (value.specialProgram) {
3676
+ try {
3677
+ query["learnerInfo.specialProgram"] = new import_mongodb7.ObjectId(
3678
+ value.specialProgram
3679
+ );
3680
+ } catch (error2) {
3681
+ throw new import_nodejs_utils10.BadRequestError("Invalid special program ID.");
3682
+ }
3683
+ cacheKeyOptions.specialProgram = value.specialProgram.toString();
3684
+ }
3676
3685
  const cacheKey = (0, import_nodejs_utils10.makeCacheKey)(namespace_collection, cacheKeyOptions);
3677
3686
  const cachedData = await getCache(cacheKey);
3678
3687
  if (cachedData !== void 0 && cachedData !== null) {
@@ -4390,6 +4399,51 @@ function useProgramScreeningRepo() {
4390
4399
  }
4391
4400
  }
4392
4401
  }
4402
+ async function getByApplicantId(_id) {
4403
+ try {
4404
+ _id = new import_mongodb10.ObjectId(_id);
4405
+ } catch (error) {
4406
+ throw new import_nodejs_utils15.BadRequestError("Invalid ID.");
4407
+ }
4408
+ const cacheKey = (0, import_nodejs_utils15.makeCacheKey)(namespace_collection, {
4409
+ applicant: String(_id)
4410
+ });
4411
+ try {
4412
+ const cached = await getCache(cacheKey);
4413
+ if (cached) {
4414
+ import_nodejs_utils15.logger.log({
4415
+ level: "info",
4416
+ message: `Cache hit for getById program screening: ${cacheKey}`
4417
+ });
4418
+ return cached;
4419
+ }
4420
+ const result = await collection.findOne({
4421
+ applicant: _id,
4422
+ status: { $ne: "deleted" }
4423
+ });
4424
+ if (!result) {
4425
+ throw new import_nodejs_utils15.BadRequestError("Program screening not found.");
4426
+ }
4427
+ setCache(cacheKey, result, 300).then(() => {
4428
+ import_nodejs_utils15.logger.log({
4429
+ level: "info",
4430
+ message: `Cache set for program screening by id: ${cacheKey}`
4431
+ });
4432
+ }).catch((err) => {
4433
+ import_nodejs_utils15.logger.log({
4434
+ level: "error",
4435
+ message: `Failed to set cache for program screening by id: ${err.message}`
4436
+ });
4437
+ });
4438
+ return result;
4439
+ } catch (error) {
4440
+ if (error instanceof import_nodejs_utils15.AppError) {
4441
+ throw error;
4442
+ } else {
4443
+ throw new import_nodejs_utils15.InternalServerError("Failed to get program screening.");
4444
+ }
4445
+ }
4446
+ }
4393
4447
  async function getByCode(code, school) {
4394
4448
  try {
4395
4449
  school = new import_mongodb10.ObjectId(school);
@@ -4513,6 +4567,26 @@ function useProgramScreeningRepo() {
4513
4567
  );
4514
4568
  }
4515
4569
  }
4570
+ async function updateStatusByApplicantId(_id, status, session) {
4571
+ try {
4572
+ _id = new import_mongodb10.ObjectId(_id);
4573
+ } catch (error) {
4574
+ throw new import_nodejs_utils15.BadRequestError("Invalid ID.");
4575
+ }
4576
+ try {
4577
+ await collection.updateOne(
4578
+ { applicant: _id },
4579
+ { $set: { status, updatedAt: /* @__PURE__ */ new Date() } },
4580
+ { session }
4581
+ );
4582
+ delCachedData();
4583
+ return "Successfully updated program screening status.";
4584
+ } catch (error) {
4585
+ throw new import_nodejs_utils15.InternalServerError(
4586
+ "Failed to update program screening status."
4587
+ );
4588
+ }
4589
+ }
4516
4590
  return {
4517
4591
  createIndexes,
4518
4592
  add,
@@ -4521,7 +4595,9 @@ function useProgramScreeningRepo() {
4521
4595
  getByCode,
4522
4596
  updateFieldById,
4523
4597
  deleteById,
4524
- updateById
4598
+ updateById,
4599
+ updateStatusByApplicantId,
4600
+ getByApplicantId
4525
4601
  };
4526
4602
  }
4527
4603
 
@@ -5408,7 +5484,11 @@ function useEnrollmentService() {
5408
5484
  getById: _getById,
5409
5485
  updateStatusById: _updateStatusById
5410
5486
  } = useEnrollmentRepo();
5411
- const { add: addProgramScreening } = useProgramScreeningRepo();
5487
+ const {
5488
+ add: addProgramScreening,
5489
+ updateStatusByApplicantId,
5490
+ getByApplicantId
5491
+ } = useProgramScreeningRepo();
5412
5492
  const { getById: getProgramById } = useProgramRepo();
5413
5493
  async function add(value) {
5414
5494
  const session = import_nodejs_utils21.useAtlas.getClient()?.startSession();
@@ -5539,6 +5619,11 @@ function useEnrollmentService() {
5539
5619
  if (status === "rejected" && enrollment.status == "accepted") {
5540
5620
  throw new import_nodejs_utils21.BadRequestError("Accepted enrollments cannot be rejected.");
5541
5621
  }
5622
+ if (status === "withdrawn" && enrollment.status !== "screening") {
5623
+ throw new import_nodejs_utils21.BadRequestError(
5624
+ "Only enrollments in screening status can be withdrawn."
5625
+ );
5626
+ }
5542
5627
  const result = await _updateStatusById(_id, status, session);
5543
5628
  if (result.modifiedCount === 0) {
5544
5629
  throw new import_nodejs_utils21.InternalServerError("Failed to accept enrollment");
@@ -5578,6 +5663,14 @@ function useEnrollmentService() {
5578
5663
  enrollment.createdBy = enrollment.createdBy?.toString();
5579
5664
  await addLearner(enrollment, session);
5580
5665
  }
5666
+ const programScreening = await getByApplicantId(_id);
5667
+ if (programScreening) {
5668
+ if (["cancelled", "withdrawn"].includes(status)) {
5669
+ await updateStatusByApplicantId(_id, "cancelled", session);
5670
+ } else {
5671
+ await updateStatusByApplicantId(_id, status, session);
5672
+ }
5673
+ }
5581
5674
  await session.commitTransaction();
5582
5675
  return "Enrollment accepted successfully";
5583
5676
  } catch (error2) {
@@ -6203,7 +6296,8 @@ function useGradeLevelController() {
6203
6296
  add: _add,
6204
6297
  updateById: _updateById,
6205
6298
  deleteById: _deleteById,
6206
- getByEducationLevel: _getByEducationLevel
6299
+ getByEducationLevel: _getByEducationLevel,
6300
+ getByGradeLevel: _getByGradeLevel
6207
6301
  } = useGradeLevelRepo();
6208
6302
  async function add(req, res, next) {
6209
6303
  const value = req.body;
@@ -6374,13 +6468,34 @@ function useGradeLevelController() {
6374
6468
  next(error2);
6375
6469
  }
6376
6470
  }
6471
+ async function getByGradeLevel(req, res, next) {
6472
+ const gradeLevel = req.params.gradeLevel;
6473
+ const school = req.params.school;
6474
+ const validation = import_joi16.default.object({
6475
+ gradeLevel: import_joi16.default.string().required(),
6476
+ school: import_joi16.default.string().hex().required()
6477
+ });
6478
+ const { error } = validation.validate({ gradeLevel, school });
6479
+ if (error) {
6480
+ next(new import_nodejs_utils25.BadRequestError(error.message));
6481
+ return;
6482
+ }
6483
+ try {
6484
+ const data = await _getByGradeLevel({ gradeLevel, school });
6485
+ res.json(data);
6486
+ return;
6487
+ } catch (error2) {
6488
+ next(error2);
6489
+ }
6490
+ }
6377
6491
  return {
6378
6492
  add,
6379
6493
  getAll,
6380
6494
  getById,
6381
6495
  updateById,
6382
6496
  deleteById,
6383
- getByEducationLevel
6497
+ getByEducationLevel,
6498
+ getByGradeLevel
6384
6499
  };
6385
6500
  }
6386
6501
 
@@ -38933,6 +39048,9 @@ var schemaGenerateSections = import_joi31.default.object({
38933
39048
  school: import_joi31.default.string().hex().required(),
38934
39049
  schoolYear: import_joi31.default.string().required(),
38935
39050
  gradeLevel: import_joi31.default.string().required(),
39051
+ minStudents: import_joi31.default.number().integer().min(1).required(),
39052
+ maxStudents: import_joi31.default.number().integer().min(import_joi31.default.ref("minStudents")).required(),
39053
+ specialProgram: import_joi31.default.string().hex().optional().allow(null, ""),
38936
39054
  set: import_joi31.default.array().items(import_joi31.default.string().min(1).max(100)).required()
38937
39055
  });
38938
39056
  function modelSection(value) {
@@ -42343,7 +42461,8 @@ function useSectionService() {
42343
42461
  {
42344
42462
  school: value.school,
42345
42463
  schoolYear: value.schoolYear,
42346
- gradeLevel: value.gradeLevel
42464
+ gradeLevel: value.gradeLevel,
42465
+ specialProgram: value.specialProgram
42347
42466
  },
42348
42467
  session
42349
42468
  );
@@ -42360,8 +42479,8 @@ function useSectionService() {
42360
42479
  if (!gradeLevelData) {
42361
42480
  throw new import_nodejs_utils69.BadRequestError("Grade level not found.");
42362
42481
  }
42363
- const minPerSection = gradeLevelData.minNumberOfLearners;
42364
- const maxPerSection = gradeLevelData.maxNumberOfLearners;
42482
+ const minPerSection = value.minStudents ?? gradeLevelData.minNumberOfLearners;
42483
+ const maxPerSection = value.maxStudents ?? gradeLevelData.maxNumberOfLearners;
42365
42484
  const sectionsNeeded = Math.ceil(studentCount / minPerSection);
42366
42485
  if (sectionsNeeded > value.set.length) {
42367
42486
  throw new import_nodejs_utils69.BadRequestError(
@@ -42507,7 +42626,64 @@ function useSectionService() {
42507
42626
  await session?.endSession();
42508
42627
  }
42509
42628
  }
42510
- return { generateSections };
42629
+ async function generateSectionPreview(value) {
42630
+ const { error } = schemaGenerateSections.validate(value);
42631
+ if (error) {
42632
+ throw new import_nodejs_utils69.BadRequestError(
42633
+ `Invalid section generation data: ${error.message}`
42634
+ );
42635
+ }
42636
+ try {
42637
+ const studentCount = await getCountByGradeLevel({
42638
+ school: value.school,
42639
+ schoolYear: value.schoolYear,
42640
+ gradeLevel: value.gradeLevel,
42641
+ specialProgram: value.specialProgram
42642
+ });
42643
+ if (studentCount === 0) {
42644
+ throw new import_nodejs_utils69.BadRequestError("No learners found for this grade level.");
42645
+ }
42646
+ const gradeLevelData = await getByGradeLevel({
42647
+ school: value.school,
42648
+ gradeLevel: value.gradeLevel
42649
+ });
42650
+ if (!gradeLevelData) {
42651
+ throw new import_nodejs_utils69.BadRequestError("Grade level not found.");
42652
+ }
42653
+ const minPerSection = value.minStudents ?? gradeLevelData.minNumberOfLearners;
42654
+ const maxPerSection = value.maxStudents ?? gradeLevelData.maxNumberOfLearners;
42655
+ const sectionsNeeded = Math.ceil(studentCount / minPerSection);
42656
+ if (sectionsNeeded > value.set.length) {
42657
+ throw new import_nodejs_utils69.BadRequestError(
42658
+ "Insufficient number of section names in set[]."
42659
+ );
42660
+ }
42661
+ const sectionSizes = distributeStudents(
42662
+ studentCount,
42663
+ minPerSection,
42664
+ maxPerSection
42665
+ );
42666
+ if (sectionSizes.length === 0) {
42667
+ throw new import_nodejs_utils69.BadRequestError("Unable to compute section sizes.");
42668
+ }
42669
+ const sections = sectionSizes.map((size, index) => ({
42670
+ name: value.set[index],
42671
+ value: size
42672
+ }));
42673
+ return {
42674
+ totalSectionsGenerated: sectionSizes.length,
42675
+ totalStudentsAssigned: studentCount,
42676
+ sections
42677
+ };
42678
+ } catch (error2) {
42679
+ if (error2 instanceof import_nodejs_utils69.AppError) {
42680
+ throw error2;
42681
+ } else {
42682
+ throw new import_nodejs_utils69.InternalServerError("Failed to generate section preview.");
42683
+ }
42684
+ }
42685
+ }
42686
+ return { generateSections, generateSectionPreview };
42511
42687
  }
42512
42688
 
42513
42689
  // src/resources/section/section.controller.ts
@@ -42523,7 +42699,10 @@ function useSectionController() {
42523
42699
  removeStudentFromSection: _removeStudentFromSection,
42524
42700
  deleteById: _deleteById
42525
42701
  } = useSectionRepo();
42526
- const { generateSections: _generateSections } = useSectionService();
42702
+ const {
42703
+ generateSections: _generateSections,
42704
+ generateSectionPreview: _generateSectionPreview
42705
+ } = useSectionService();
42527
42706
  async function add(req, res, next) {
42528
42707
  const value = req.body;
42529
42708
  const { error } = schemaSection.validate(value);
@@ -42560,6 +42739,21 @@ function useSectionController() {
42560
42739
  next(error2);
42561
42740
  }
42562
42741
  }
42742
+ async function generateSectionPreview(req, res, next) {
42743
+ const value = req.body;
42744
+ const { error } = schemaGenerateSections.validate(value);
42745
+ if (error) {
42746
+ next(new import_nodejs_utils70.BadRequestError(error.message));
42747
+ return;
42748
+ }
42749
+ try {
42750
+ const data = await _generateSectionPreview(value);
42751
+ res.json(data);
42752
+ return;
42753
+ } catch (error2) {
42754
+ next(error2);
42755
+ }
42756
+ }
42563
42757
  async function getAll(req, res, next) {
42564
42758
  const query = req.query;
42565
42759
  const validation = import_joi43.default.object({
@@ -42751,6 +42945,7 @@ function useSectionController() {
42751
42945
  return {
42752
42946
  add,
42753
42947
  generateSections,
42948
+ generateSectionPreview,
42754
42949
  getAll,
42755
42950
  getById,
42756
42951
  getByName,