fix: universal Anna University 2021 Regulation credit engine (IC=1, VA=1, Lab=1, Mandatory=0)

This commit is contained in:
Shanmuga Krishnan S M
2026-08-19 14:10:01 +05:30
parent 5ea7de558b
commit 2f69b0a29d

View File

@@ -906,7 +906,7 @@ export async function fetchImsCatMarks(_token: string, _regNumber: string, _forc
export function getExactSubjectCredits(deptCodeOrName: string, _semester: number, subjectCode: string, subjectName: string): number { export function getExactSubjectCredits(deptCodeOrName: string, _semester: number, subjectCode: string, subjectName: string): number {
const deptKey = DEPARTMENT_CODE_MAP[deptCodeOrName] || deptCodeOrName.toUpperCase(); const deptKey = DEPARTMENT_CODE_MAP[deptCodeOrName] || deptCodeOrName.toUpperCase();
// Extract course code (e.g. GE23121 from subjectCode or parenthesized subjectName) // Extract course code (e.g. AD23IC2 from subjectCode or parenthesized subjectName)
let cleanCode = (subjectCode || '').replace(/[^A-Z0-9]/gi, '').toUpperCase(); let cleanCode = (subjectCode || '').replace(/[^A-Z0-9]/gi, '').toUpperCase();
if (!cleanCode && subjectName) { if (!cleanCode && subjectName) {
const codeInParen = subjectName.match(/\(([A-Z0-9]+)\)/i)?.[1]; const codeInParen = subjectName.match(/\(([A-Z0-9]+)\)/i)?.[1];
@@ -916,15 +916,17 @@ export function getExactSubjectCredits(deptCodeOrName: string, _semester: number
const rawName = (subjectName || '').toLowerCase().trim(); const rawName = (subjectName || '').toLowerCase().trim();
const isLabSubject = rawName.includes('lab') || rawName.includes('laboratory'); const isLabSubject = rawName.includes('lab') || rawName.includes('laboratory');
// 0. Priority check for Anna University Mandatory Non-Credit 0-credit courses // ─── RULE 0: MANDATORY NON-CREDIT COURSES (0 CREDITS) ───
if ( if (
cleanCode === 'GE23112' || cleanCode === 'GE23213' || cleanCode === 'GE2112' || cleanCode === 'GE2211' || cleanCode === 'GE23112' || cleanCode === 'GE23213' || cleanCode === 'GE2112' || cleanCode === 'GE2211' ||
cleanCode.startsWith('MX') || cleanCode.startsWith('MC') ||
rawName.includes('heritage of tamil') || rawName.includes('heritage of tamil') ||
rawName.includes('tamil & technology') || rawName.includes('tamil & technology') ||
rawName.includes('tamil and technology') || rawName.includes('tamil and technology') ||
rawName.includes('heritage') || rawName.includes('heritage') ||
rawName.includes('mandatory') || rawName.includes('mandatory') ||
rawName.includes('audit course') || rawName.includes('audit course') ||
rawName.includes('disaster risk') ||
rawName.includes('essence of indian') || rawName.includes('essence of indian') ||
rawName.includes('constitution of india') || rawName.includes('constitution of india') ||
rawName.includes('induction program') rawName.includes('induction program')
@@ -932,7 +934,27 @@ export function getExactSubjectCredits(deptCodeOrName: string, _semester: number
return 0; return 0;
} }
// 1. EXACT COURSE CODE MATCH across ALL department curricula (Codes like GE23121 vs GE23111 are unique!) // ─── RULE 1: INDUSTRY COURSES & VALUE-ADDED COURSES (1 CREDIT) ───
// Anna University 2021 Regulation at RIT:
// All 'IC' codes (AD23IC1, AD23IC2, AD23IC3, EC23IC1, CS23IC1, CB23IC1, EE23IC1...) = Industry Certified Courses = 1 CREDIT!
// All 'VA' / 'VAC' codes (AD23VA1, CS23VA1, EC23VA1...) = Value-Added Courses = 1 CREDIT!
if (
cleanCode.includes('IC') ||
cleanCode.includes('VAC') ||
cleanCode.includes('VA') ||
rawName.includes('industry course') ||
rawName.includes('value added')
) {
return 1;
}
// ─── RULE 2: LAB / PRACTICAL COURSES (1 CREDIT) ───
// Codes ending in 21, 22 or having 5th digit '2' for lab (e.g. AD23521, CS23521, GE23121, GE23122, PH23121)
if (isLabSubject || /[A-Z]{2,4}\d{2,3}2[1-9]$/i.test(cleanCode) || /21$|22$/i.test(cleanCode)) {
return 1;
}
// ─── RULE 3: EXACT COURSE CODE MATCH IN CURRICULUM REGISTRY ───
if (cleanCode && cleanCode.length >= 4) { if (cleanCode && cleanCode.length >= 4) {
const electiveMatch = PROFESSIONAL_ELECTIVES_LIST.find(e => e.code.toUpperCase() === cleanCode); const electiveMatch = PROFESSIONAL_ELECTIVES_LIST.find(e => e.code.toUpperCase() === cleanCode);
if (electiveMatch) return electiveMatch.credits; if (electiveMatch) return electiveMatch.credits;
@@ -952,7 +974,7 @@ export function getExactSubjectCredits(deptCodeOrName: string, _semester: number
} }
} }
// 2. SUBJECT TITLE MATCH (Strict Lab vs Theory isolation!) // ─── RULE 4: SUBJECT TITLE MATCH (Strict Lab vs Theory isolation) ───
if (rawName && rawName.length > 3) { if (rawName && rawName.length > 3) {
const electiveNameMatch = PROFESSIONAL_ELECTIVES_LIST.find(e => rawName.includes(e.name.toLowerCase())); const electiveNameMatch = PROFESSIONAL_ELECTIVES_LIST.find(e => rawName.includes(e.name.toLowerCase()));
if (electiveNameMatch) return electiveNameMatch.credits; if (electiveNameMatch) return electiveNameMatch.credits;
@@ -968,7 +990,6 @@ export function getExactSubjectCredits(deptCodeOrName: string, _semester: number
const itemNameClean = itemRaw.split('(')[0].trim(); const itemNameClean = itemRaw.split('(')[0].trim();
const itemIsLab = itemRaw.includes('lab') || itemRaw.includes('laboratory'); const itemIsLab = itemRaw.includes('lab') || itemRaw.includes('laboratory');
// CRITICAL: A Lab subject must NEVER match a Theory subject, and vice versa!
if (isLabSubject !== itemIsLab) continue; if (isLabSubject !== itemIsLab) continue;
if (itemNameClean.length > 3 && (rawName === itemNameClean || rawName.includes(itemNameClean) || itemNameClean.includes(rawName))) { if (itemNameClean.length > 3 && (rawName === itemNameClean || rawName.includes(itemNameClean) || itemNameClean.includes(rawName))) {
@@ -979,9 +1000,7 @@ export function getExactSubjectCredits(deptCodeOrName: string, _semester: number
} }
} }
// 3. Fallback Heuristics based on subject type // ─── RULE 5: 4-CREDIT SUBJECTS (MATH / ENGINEERING GRAPHICS / DESIGN) ───
if (isLabSubject) return 1;
if ( if (
rawName.includes('mathematics') || rawName.includes('mathematics') ||
rawName.includes('calculus') || rawName.includes('calculus') ||