Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { autoInjectable } from "tsyringe";
import BaseController from "../../base/contollers/Base.controller";
import { HttpHeaderEnum } from "~/api/shared/helpers/enums/HttpHeader.enum";
import { HttpMethodEnum } from "~/api/shared/helpers/enums/HttpMethod.enum";
import { HttpStatusCodeEnum } from "~/api/shared/helpers/enums/HttpStatusCode.enum";
import StudentTermResultReadService from "../services/StudentTermResultRead.service";
import ApplicationStatusEnum from "~/api/shared/helpers/enums/ApplicationStatus.enum";
import { HttpContentTypeEnum } from "~/api/shared/helpers/enums/HttpContentType.enum";
import { EntryPointHandler, INextFunction, IRequest, IResponse, IRouter } from "~/infrastructure/internal/types";

@autoInjectable()
export default class StudentTermResultReadController extends BaseController {
static controllerName: string;
private studentTermResultReadService: StudentTermResultReadService;
constructor(StudentTermResultReadService: StudentTermResultReadService) {
super();
this.controllerName = "StudentTermResultReadController";
this.studentTermResultReadService = StudentTermResultReadService;
}

read: EntryPointHandler = async (req: IRequest, res: IResponse, next: INextFunction): Promise<void> => {
return this.handleResultData(res, next, this.studentTermResultReadService.execute(res.trace, req), {
[HttpHeaderEnum.CONTENT_TYPE]: HttpContentTypeEnum.APPLICATION_JSON,
});
};

public initializeRoutes(router: IRouter): void {
this.setRouter(router());

this.addRoute({
method: HttpMethodEnum.POST,
path: "/student/termresult/list",
handlers: [this.read],
produces: [
{
applicationStatus: ApplicationStatusEnum.CREATED,
httpStatus: HttpStatusCodeEnum.CREATED,
},
],
description: "List of Student Term Results",
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import { InternalServerError } from "~/infrastructure/internal/exceptions/Intern
export default class StudentTermResultCreateProvider {
public async create(args: StudentTermResultCreateType, dbClient: PrismaTransactionClient = DbClient): Promise<StudentTermResult> {
try {
const { studentId, termId, tenantId, totalScore, averageScore, subjectCountGraded, subjectCountOffered, finalized } = args;
const { studentId, termId, tenantId, totalScore, averageScore, subjectCountGraded, subjectCountOffered, finalized, classId, classDivisionId } = args;

const result = await dbClient.studentTermResult.create({
data: {
termId,
classId,
tenantId,
studentId,
finalized,
totalScore,
averageScore,
classDivisionId,
subjectCountGraded,
subjectCountOffered,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StudentTermResult } from "@prisma/client";
import { StudentTermResultReadType } from "../types/StudentTypes";
import { userObjectWithoutPassword } from "~/api/shared/helpers/objects";
import DbClient, { PrismaTransactionClient } from "~/infrastructure/internal/database";
import { EnforceTenantId } from "~/api/modules/base/decorators/EnforceTenantId.decorator";
import { InternalServerError } from "~/infrastructure/internal/exceptions/InternalServerError";
Expand All @@ -8,13 +9,32 @@ import { InternalServerError } from "~/infrastructure/internal/exceptions/Intern
export default class StudentTermResultReadProvider {
public async getByCriteria(criteria: StudentTermResultReadType, dbClient: PrismaTransactionClient = DbClient): Promise<StudentTermResult[]> {
try {
const { studentId, termId, tenantId } = criteria;
const { studentId, termId, tenantId, classId, classDivisionId } = criteria;

const results = await dbClient.studentTermResult.findMany({
where: {
...(studentId && { studentId }),
...(termId && { termId }),
...(classId && { classId }),
...(tenantId && { tenantId }),
...(studentId && { studentId }),
...(classDivisionId && { classDivisionId }),
},
include: {
student: {
include: {
user: { select: userObjectWithoutPassword },
subjectGrades: {
include: {
subject: {
select: {
id: true,
name: true,
},
},
},
},
},
},
},
});

Expand All @@ -26,13 +46,22 @@ export default class StudentTermResultReadProvider {

public async getOneByCriteria(criteria: StudentTermResultReadType, dbClient: PrismaTransactionClient = DbClient): Promise<StudentTermResult | null> {
try {
const { studentId, termId, tenantId } = criteria;
const { studentId, termId, tenantId, classId, classDivisionId } = criteria;

const result = await dbClient.studentTermResult.findFirst({
where: {
...(studentId && { studentId }),
...(termId && { termId }),
...(classId && { classId }),
...(tenantId && { tenantId }),
...(studentId && { studentId }),
...(classDivisionId && { classDivisionId }),
},
include: {
student: {
include: {
user: { select: userObjectWithoutPassword },
},
},
},
});

Expand Down
46 changes: 46 additions & 0 deletions src/api/modules/student/services/StudentTermResultRead.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { autoInjectable } from "tsyringe";
import { IRequest } from "~/infrastructure/internal/types";
import { BaseService } from "../../base/services/Base.service";
import { IResult } from "~/api/shared/helpers/results/IResult";
import { ERROR } from "~/api/shared/helpers/messages/SystemMessages";
import { ServiceTrace } from "~/api/shared/helpers/trace/ServiceTrace";
import { ILoggingDriver } from "~/infrastructure/internal/logger/ILoggingDriver";
import { HttpStatusCodeEnum } from "~/api/shared/helpers/enums/HttpStatusCode.enum";
import StudentTermResultReadProvider from "../providers/StudentTermResultRead.provider";
import { SUCCESS, STUDENT_RESOURCE } from "~/api/shared/helpers/messages/SystemMessages";
import { LoggingProviderFactory } from "~/infrastructure/internal/logger/LoggingProviderFactory";
import { RESOURCE_FETCHED_SUCCESSFULLY } from "~/api/shared/helpers/messages/SystemMessagesFunction";

@autoInjectable()
export default class StudentTermResultReadService extends BaseService<IRequest> {
static serviceName = "StudentTermResultReadService";
loggingProvider: ILoggingDriver;
studentTermResultReadProvider: StudentTermResultReadProvider;

constructor(studentTermResultReadProvider: StudentTermResultReadProvider) {
super(StudentTermResultReadService.serviceName);
this.studentTermResultReadProvider = studentTermResultReadProvider;
this.loggingProvider = LoggingProviderFactory.build();
}

public async execute(trace: ServiceTrace, args: IRequest): Promise<IResult> {
try {
this.initializeServiceTrace(trace, args.query);

const { tenantId } = args.body;
const { termId, classId, classDivisionId } = args.query;

const termResults = await this.studentTermResultReadProvider.getByCriteria({ tenantId: Number(tenantId), termId: Number(termId), classId: Number(classId), classDivisionId: Number(classDivisionId) });

trace.setSuccessful();

this.result.setData(SUCCESS, HttpStatusCodeEnum.SUCCESS, RESOURCE_FETCHED_SUCCESSFULLY(STUDENT_RESOURCE), termResults);

return this.result;
} catch (error: any) {
this.loggingProvider.error(error);
this.result.setError(ERROR, error.httpStatusCode, error.description);
return this.result;
}
}
}
6 changes: 5 additions & 1 deletion src/api/modules/student/types/StudentTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ export interface StudentTermResultCreateType {
totalScore?: number;
finalized?: boolean;
averageScore?: number;
classId: number;
subjectCountGraded?: number;
subjectCountOffered?: number;
classDivisionId: number;
}

export interface StudentTermResultUpdateType {
Expand All @@ -163,7 +165,9 @@ export interface StudentTermResultUpdateType {
}

export interface StudentTermResultReadType {
studentId?: number;
termId?: number;
classId?: number;
tenantId?: number;
studentId?: number;
classDivisionId?: number;
}
48 changes: 37 additions & 11 deletions src/api/modules/subject/services/SubjectGradingCreate.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ export default class SubjectGradingCreateService extends BaseService<IRequest> {
// Update student's term results
await this.createOrUpdateStudentTermResult({
studentId,
classId: student?.classId,
classDivisionId: student?.classDivisionId,
tenantId,
calendarId,
termId,
Expand Down Expand Up @@ -149,6 +151,8 @@ export default class SubjectGradingCreateService extends BaseService<IRequest> {
this.createOrUpdateStudentTermResult({
termId,
calendarId,
classId: input.student?.classId,
classDivisionId: input.student?.classDivisionId,
tenantId,
subjectId,
studentId: input.subjectId,
Expand Down Expand Up @@ -362,22 +366,42 @@ export default class SubjectGradingCreateService extends BaseService<IRequest> {
}
}

private async createOrUpdateStudentTermResult({ studentId, tenantId, calendarId, termId, subjectId, newlyAddedScore }: { studentId: number; tenantId: number; calendarId: number; termId: number; subjectId: number; newlyAddedScore: number }): Promise<void> {
private async createOrUpdateStudentTermResult({
studentId,
tenantId,
calendarId,
termId,
subjectId,
newlyAddedScore,
classId,
classDivisionId,
}: {
studentId: number;
tenantId: number;
calendarId: number;
termId: number;
subjectId: number;
newlyAddedScore: number;
classId: number | null;
classDivisionId: number | null;
}): Promise<void> {
try {
// fetch existing term result
const existing = await this.studentTermResultReadProvider.getOneByCriteria({ studentId, termId, tenantId });
// fetch existingTermResult term result
const existingTermResult = await this.studentTermResultReadProvider.getOneByCriteria({ studentId, termId, tenantId });

if (existing?.finalized) {
if (existingTermResult?.finalized) {
throw new BadRequestError("Cannot update this student's result as it has already been finalized.");
}

// check if this subject was already graded
const prior = await this.subjectGradingReadProvider.getOneByCriteria({ studentId, subjectId, calendarId, termId, tenantId });
const incrementCount = prior ? 0 : 1;

if (existing) {
const newTotal = existing.totalScore + newlyAddedScore;
const newCount = existing.subjectCountGraded + incrementCount;
console.log("existingTermResult", !!existingTermResult, "newlyAddedScore", newlyAddedScore, "incrementCount", incrementCount, "studentId", studentId, "termId", termId, "tenantId", tenantId);

if (existingTermResult) {
const newTotal = existingTermResult.totalScore + newlyAddedScore;
const newCount = existingTermResult.subjectCountGraded + incrementCount;
const newAvg = newCount > 0 ? newTotal / newCount : 0;

await this.studentTermResultUpdateProvider.update({
Expand All @@ -401,16 +425,18 @@ export default class SubjectGradingCreateService extends BaseService<IRequest> {
studentId,
termId,
tenantId,
totalScore: newlyAddedScore,
averageScore: newlyAddedScore,
finalized: false,
classId: classId!,
subjectCountGraded: 1,
totalScore: newlyAddedScore,
subjectCountOffered: offered,
finalized: false,
averageScore: newlyAddedScore,
classDivisionId: classDivisionId!,
});
}
} catch (error: any) {
this.loggingProvider.error(error);
throw new NormalizedAppError(error.message);
throw new NormalizedAppError(error);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Warnings:

- You are about to drop the column `schoolCalendarId` on the `StudentTermResult` table. All the data in the column will be lost.

*/
-- DropForeignKey
ALTER TABLE "StudentTermResult" DROP CONSTRAINT "StudentTermResult_schoolCalendarId_fkey";

-- AlterTable
ALTER TABLE "StudentTermResult" DROP COLUMN "schoolCalendarId";
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- AlterTable
ALTER TABLE "StudentTermResult" ADD COLUMN "classDivisionId" INTEGER,
ADD COLUMN "classId" INTEGER;

-- AddForeignKey
ALTER TABLE "StudentTermResult" ADD CONSTRAINT "StudentTermResult_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "StudentTermResult" ADD CONSTRAINT "StudentTermResult_classDivisionId_fkey" FOREIGN KEY ("classDivisionId") REFERENCES "ClassDivision"("id") ON DELETE SET NULL ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Warnings:

- Made the column `classDivisionId` on table `StudentTermResult` required. This step will fail if there are existing NULL values in that column.
- Made the column `classId` on table `StudentTermResult` required. This step will fail if there are existing NULL values in that column.

*/
-- DropForeignKey
ALTER TABLE "StudentTermResult" DROP CONSTRAINT "StudentTermResult_classDivisionId_fkey";

-- DropForeignKey
ALTER TABLE "StudentTermResult" DROP CONSTRAINT "StudentTermResult_classId_fkey";

-- AlterTable
ALTER TABLE "StudentTermResult" ALTER COLUMN "classDivisionId" SET NOT NULL,
ALTER COLUMN "classId" SET NOT NULL;

-- AddForeignKey
ALTER TABLE "StudentTermResult" ADD CONSTRAINT "StudentTermResult_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "StudentTermResult" ADD CONSTRAINT "StudentTermResult_classDivisionId_fkey" FOREIGN KEY ("classDivisionId") REFERENCES "ClassDivision"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Loading