again n again

This commit is contained in:
Randa Firman Putra
2025-12-04 22:15:37 +07:00
parent 82bec8eecc
commit 700a153b86
33 changed files with 1437 additions and 58 deletions

View File

@@ -0,0 +1,156 @@
import { NextRequest, NextResponse } from 'next/server';
import supabase from '@/lib/db';
interface MKBelumDiambilData {
tahun_angkatan: number;
jenis_mk: string;
jumlah_belum_diambil: number;
total_mata_kuliah: number;
total_mahasiswa: number;
}
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
// 1. Ambil semua mata kuliah
const { data: mataKuliah, error: errMK } = await supabase
.from('mata_kuliah')
.select('id_mk, jenis_mk');
if (errMK) {
console.error('Error fetching mata kuliah:', errMK);
throw errMK;
}
// 2. Hitung total mata kuliah per kategori (total_mk CTE)
const totalMKPerJenis = new Map<string, number>();
mataKuliah?.forEach((mk) => {
const jenis = mk.jenis_mk;
totalMKPerJenis.set(jenis, (totalMKPerJenis.get(jenis) || 0) + 1);
});
// 3. Ambil data mahasiswa
let mahasiswaQuery = supabase
.from('mahasiswa')
.select('id_mahasiswa, tahun_angkatan');
// Jika ada filter tahun angkatan, terapkan filter
if (tahunAngkatan && tahunAngkatan !== 'all') {
mahasiswaQuery = mahasiswaQuery.eq('tahun_angkatan', parseInt(tahunAngkatan));
}
const { data: mahasiswa, error: errMhs } = await mahasiswaQuery;
if (errMhs) {
console.error('Error fetching mahasiswa:', errMhs);
throw errMhs;
}
// 4. Hitung total mahasiswa per angkatan (total_mhs CTE)
const totalMhsPerAngkatan = new Map<number, number>();
mahasiswa?.forEach((m) => {
const tahun = m.tahun_angkatan;
if (tahun) {
totalMhsPerAngkatan.set(tahun, (totalMhsPerAngkatan.get(tahun) || 0) + 1);
}
});
// 5. Ambil semua nilai mahasiswa
const { data: nilaiMahasiswa, error: errNilai } = await supabase
.from('nilai_mahasiswa')
.select('id_mahasiswa, id_mk, nilai_huruf');
if (errNilai) {
console.error('Error fetching nilai mahasiswa:', errNilai);
throw errNilai;
}
// 6. Buat Map untuk cek apakah mahasiswa sudah mengambil MK (dengan nilai != E)
// Key: "id_mahasiswa-id_mk", Value: true jika sudah diambil dan nilai != E
const sudahAmbilMap = new Map<string, boolean>();
nilaiMahasiswa?.forEach((nilai) => {
// Hanya dianggap sudah ambil jika nilai != E
if (nilai.nilai_huruf !== 'E') {
const key = `${nilai.id_mahasiswa}-${nilai.id_mk}`;
sudahAmbilMap.set(key, true);
}
});
// 7. Hitung mata kuliah belum diambil per angkatan per jenis MK (belum_ambil CTE)
// Map dengan key yang unik menggunakan object
const belumAmbilMap = new Map<string, { tahun: number; jenis: string; jumlah: number }>();
// CROSS JOIN mahasiswa dengan mata_kuliah
mahasiswa?.forEach((mhs) => {
if (!mhs.tahun_angkatan) return;
mataKuliah?.forEach((mk) => {
const key = `${mhs.id_mahasiswa}-${mk.id_mk}`;
// WHERE NOT EXISTS: jika tidak ada di sudahAmbilMap, berarti belum diambil
if (!sudahAmbilMap.has(key)) {
// Gunakan separator yang aman (||) untuk menghindari konflik dengan nama jenis_mk
const groupKey = `${mhs.tahun_angkatan}||${mk.jenis_mk}`;
const existing = belumAmbilMap.get(groupKey);
if (existing) {
existing.jumlah += 1;
} else {
belumAmbilMap.set(groupKey, {
tahun: mhs.tahun_angkatan,
jenis: mk.jenis_mk,
jumlah: 1
});
}
}
});
});
// 8. Gabungkan semua data (JOIN)
const results: MKBelumDiambilData[] = [];
belumAmbilMap.forEach((value) => {
const tahun_angkatan = value.tahun;
const jenis_mk = value.jenis;
const jumlah_belum_diambil = value.jumlah;
const total_mata_kuliah = totalMKPerJenis.get(jenis_mk) || 0;
const total_mahasiswa = totalMhsPerAngkatan.get(tahun_angkatan) || 0;
results.push({
tahun_angkatan,
jenis_mk,
jumlah_belum_diambil,
total_mata_kuliah,
total_mahasiswa,
});
});
// 9. Urutkan berdasarkan tahun_angkatan dan jenis_mk
results.sort((a, b) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return a.tahun_angkatan - b.tahun_angkatan;
}
// Urutkan jenis MK: Wajib, Pilihan Wajib, Pilihan
const jenisOrder = ['Wajib', 'Pilihan Wajib', 'Pilihan'];
return jenisOrder.indexOf(a.jenis_mk) - jenisOrder.indexOf(b.jenis_mk);
});
return NextResponse.json(results, {
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
},
});
} catch (error) {
console.error('Error in mkbelumdiambil API:', error);
return NextResponse.json(
{ error: 'Internal Server Error', message: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
);
}
}

View File

@@ -122,11 +122,11 @@ export async function GET() {
// Di SQL, jika nilai NULL, kondisi akan menghasilkan NULL (dianggap FALSE dalam CASE WHEN)
let isDO = 0;
// Evaluasi 4 semester: semester BETWEEN 4 AND 7 AND (sks_total < 40 OR ipk <= 2.50)
// Evaluasi 4 semester: semester BETWEEN 3 AND 6 AND (sks_total < 40 OR ipk <= 2.50)
// Jika sksTotal NULL (tidak ada nilai), kondisi sksTotal < 40 akan NULL (FALSE)
if (
sem >= 4 &&
sem <= 7 &&
sem >= 3 &&
sem <= 6 &&
sksTotal !== null && // Pastikan ada data nilai
(
sksTotal < 40 ||
@@ -135,10 +135,10 @@ export async function GET() {
) {
isDO = 1;
}
// Evaluasi 8 semester: semester BETWEEN 8 AND 13 AND (sks_total < 80 OR ipk <= 2.50)
// Evaluasi 8 semester: semester BETWEEN 7 AND 11 AND (sks_total < 80 OR ipk <= 2.50)
else if (
sem >= 8 &&
sem <= 13 &&
sem >= 7 &&
sem <= 11 &&
sksTotal !== null && // Pastikan ada data nilai
(
sksTotal < 80 ||
@@ -147,10 +147,10 @@ export async function GET() {
) {
isDO = 1;
}
// Evaluasi akhir masa studi: semester = 14 AND (sks_total < 144 OR ipk <= 2.00 OR jumlah_e > 0 OR sks_d > 14 OR min_wajib < 2.00 OR lulus_ta1 = 0 OR lulus_ta2 = 0)
// Evaluasi akhir masa studi: semester = 12 AND (sks_total < 144 OR ipk <= 2.00 OR jumlah_e > 0 OR sks_d > 14 OR min_wajib < 2.00 OR lulus_ta1 = 0 OR lulus_ta2 = 0)
// Di SQL: lulus_ta1 = 0 akan TRUE jika lulus_ta1 adalah 0, FALSE jika NULL atau 1
else if (
sem === 14 &&
sem === 12 &&
sksTotal !== null && // Pastikan ada data nilai
(
sksTotal < 144 ||

View File

@@ -0,0 +1,154 @@
import { NextRequest, NextResponse } from 'next/server';
import supabase from '@/lib/db';
interface MataKuliahBelumDiambil {
nama_mk: string;
jenis_mk: string;
kode_mk: string;
sks: number;
semester: number;
}
interface MahasiswaMKBelumDiambil {
nim: string;
nama: string;
tahun_angkatan: number;
sks_total: number;
mk_belum_diambil: MataKuliahBelumDiambil[];
}
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
const jenisMK = searchParams.get('jenis_mk');
// 1. Ambil data mahasiswa
let mahasiswaQuery = supabase
.from('mahasiswa')
.select('id_mahasiswa, nim, nama, tahun_angkatan')
.order('tahun_angkatan', { ascending: true })
.order('nim', { ascending: true });
// Jika ada filter tahun angkatan, terapkan filter
if (tahunAngkatan && tahunAngkatan !== 'all') {
mahasiswaQuery = mahasiswaQuery.eq('tahun_angkatan', parseInt(tahunAngkatan));
}
const { data: mahasiswa, error: errMhs } = await mahasiswaQuery;
if (errMhs) {
console.error('Error fetching mahasiswa:', errMhs);
throw errMhs;
}
// 2. Ambil semua mata kuliah
let mkQuery = supabase
.from('mata_kuliah')
.select('id_mk, kode_mk, nama_mk, jenis_mk, sks, semester')
.order('semester', { ascending: true })
.order('nama_mk', { ascending: true });
// Filter jenis MK jika ada
if (jenisMK && jenisMK !== 'all') {
mkQuery = mkQuery.eq('jenis_mk', jenisMK);
}
const { data: mataKuliah, error: errMK } = await mkQuery;
if (errMK) {
console.error('Error fetching mata kuliah:', errMK);
throw errMK;
}
// 3. Ambil semua nilai mahasiswa (hanya yang nilai != E, karena E dianggap belum lulus)
const { data: nilaiMahasiswa, error: errNilai } = await supabase
.from('nilai_mahasiswa')
.select('id_mahasiswa, id_mk, nilai_huruf');
if (errNilai) {
console.error('Error fetching nilai mahasiswa:', errNilai);
throw errNilai;
}
// 4. Buat Map untuk cek MK yang sudah diambil (nilai != E)
// Key: "id_mahasiswa-id_mk", Value: true
const sudahAmbilMap = new Map<string, boolean>();
nilaiMahasiswa?.forEach((nilai) => {
// Hanya dianggap sudah ambil jika nilai != E
if (nilai.nilai_huruf !== 'E') {
const key = `${nilai.id_mahasiswa}-${nilai.id_mk}`;
sudahAmbilMap.set(key, true);
}
});
// 5. Hitung SKS total per mahasiswa (dari MK yang sudah lulus, nilai != E)
const sksTotalMap = new Map<number, number>();
nilaiMahasiswa?.forEach((nilai) => {
if (nilai.nilai_huruf !== 'E') {
const mk = mataKuliah?.find(m => m.id_mk === nilai.id_mk);
if (mk) {
const currentSKS = sksTotalMap.get(nilai.id_mahasiswa) || 0;
sksTotalMap.set(nilai.id_mahasiswa, currentSKS + mk.sks);
}
}
});
// 6. Proses data mahasiswa dan MK belum diambil
const results: MahasiswaMKBelumDiambil[] = [];
mahasiswa?.forEach((mhs) => {
const mkBelumDiambil: MataKuliahBelumDiambil[] = [];
// CROSS JOIN: cek setiap MK untuk mahasiswa ini
mataKuliah?.forEach((mk) => {
const key = `${mhs.id_mahasiswa}-${mk.id_mk}`;
// WHERE nm.id_mk IS NULL: jika tidak ada di sudahAmbilMap, berarti belum diambil
if (!sudahAmbilMap.has(key)) {
mkBelumDiambil.push({
nama_mk: mk.nama_mk,
jenis_mk: mk.jenis_mk,
kode_mk: mk.kode_mk,
sks: mk.sks,
semester: mk.semester
});
}
});
// Hanya tambahkan jika ada MK yang belum diambil
if (mkBelumDiambil.length > 0) {
results.push({
nim: mhs.nim,
nama: mhs.nama,
tahun_angkatan: mhs.tahun_angkatan,
sks_total: sksTotalMap.get(mhs.id_mahasiswa) || 0,
mk_belum_diambil: mkBelumDiambil
});
}
});
// 7. Urutkan berdasarkan tahun_angkatan dan nim
results.sort((a, b) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return a.tahun_angkatan - b.tahun_angkatan;
}
return a.nim.localeCompare(b.nim);
});
return NextResponse.json(results, {
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
},
});
} catch (error) {
console.error('Error in mk-belum-diambil API:', error);
return NextResponse.json(
{ error: 'Internal Server Error', message: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
);
}
}

View File

@@ -132,10 +132,10 @@ export async function GET(request: NextRequest) {
const alasanList: string[] = [];
let isDO = false;
// Evaluasi 4 semester: semester BETWEEN 4 AND 7 AND (sks_total < 40 OR ipk <= 2.50)
// Evaluasi 4 semester: semester BETWEEN 3 AND 6 AND (sks_total < 40 OR ipk <= 2.50)
if (
sem >= 4 &&
sem <= 7 &&
sem >= 3 &&
sem <= 6 &&
sksTotal !== null &&
(
sksTotal < 40 ||
@@ -144,16 +144,16 @@ export async function GET(request: NextRequest) {
) {
isDO = true;
if (sksTotal < 40) {
alasanList.push('SKS kurang dari 40 pada evaluasi semester 4');
alasanList.push('SKS kurang dari 40 pada evaluasi semester 3');
}
if (ipk !== null && ipk <= 2.50) {
alasanList.push('IPK kurang dari 2.50 pada evaluasi semester 4');
alasanList.push('IPK kurang dari 2.50 pada evaluasi semester 3');
}
}
// Evaluasi 8 semester: semester BETWEEN 8 AND 13 AND (sks_total < 80 OR ipk <= 2.50)
// Evaluasi 8 semester: semester BETWEEN 7 AND 11 AND (sks_total < 80 OR ipk <= 2.50)
else if (
sem >= 8 &&
sem <= 13 &&
sem >= 7 &&
sem <= 11 &&
sksTotal !== null &&
(
sksTotal < 80 ||
@@ -162,15 +162,15 @@ export async function GET(request: NextRequest) {
) {
isDO = true;
if (sksTotal < 80) {
alasanList.push('SKS kurang dari 80 pada evaluasi semester 8');
alasanList.push('SKS kurang dari 80 pada evaluasi semester 7');
}
if (ipk !== null && ipk <= 2.50) {
alasanList.push('IPK kurang dari 2.50 pada evaluasi semester 8');
alasanList.push('IPK kurang dari 2.50 pada evaluasi semester 7');
}
}
// Evaluasi akhir masa studi: semester = 14
else if (
sem === 14 &&
sem === 12 &&
sksTotal !== null &&
(
sksTotal < 144 ||
@@ -184,25 +184,25 @@ export async function GET(request: NextRequest) {
) {
isDO = true;
if (sksTotal < 144) {
alasanList.push('Belum mencapai 144 SKS pada akhir masa studi');
alasanList.push('Belum mencapai 144 SKS pada evaluasi semester 12');
}
if (ipk !== null && ipk <= 2.00) {
alasanList.push('IPK di bawah 2.00 pada akhir masa studi');
alasanList.push('IPK di bawah 2.00 pada evaluasi semester 12');
}
if (jumlahE !== null && jumlahE > 0) {
alasanList.push('Memiliki nilai E pada akhir masa studi');
alasanList.push('Memiliki nilai E pada evaluasi semester 12');
}
if (sksD !== null && sksD > 14) {
alasanList.push('Total SKS dari nilai D lebih dari 14 SKS');
alasanList.push('Total SKS dari nilai D lebih dari 14 SKS pada evaluasi semester 12');
}
if (minWajib !== null && minWajib < 2.00) {
alasanList.push('Nilai minimal mata kuliah wajib di bawah C');
alasanList.push('Nilai minimal mata kuliah wajib di bawah C pada evaluasi semester 12');
}
if (lulusTA1 === 0) {
alasanList.push('Belum lulus Tugas Akhir 1 (INF-55201-406)');
alasanList.push('Belum lulus Tugas Akhir 1 (INF-55201-406) pada evaluasi semester 12');
}
if (lulusTA2 === 0) {
alasanList.push('Belum lulus Tugas Akhir 2 (INF-55201-407)');
alasanList.push('Belum lulus Tugas Akhir 2 (INF-55201-407) pada evaluasi semester 12');
}
}