Rapikan tampilan nilai, ganti NPM->NIM, fitur ganti password, kunci RPS view-only
- Tampilan "Isi Per Komponen Penilaian" diganti dari tombol bertumpuk teks jadi tabel (Komponen/Sub-CPMK/Bobot/Status/Aksi) dengan status berupa stempel, lebih mudah dipindai. - Filter pencarian MK (ketik kode/nama) di kotak penugasan dosen /admin. - Istilah NPM diseragamkan jadi NIM di seluruh aplikasi (label, header, template Excel, dokumen portofolio), termasuk migrasi kolom database mahasiswa.npm -> nim yang aman (ALTER TABLE RENAME COLUMN, otomatis saat startup, tanpa kehilangan data; parser Excel tetap terima file lama berheader NPM). - Fitur ganti password mandiri (/akun): verifikasi password lama, sesi lain otomatis keluar setelah berhasil, sesi yang dipakai tetap aktif. - RPS: dosen RPS+MK kini bisa menyesuaikan redaksi kalimat Sub-CPMK (bagian D) untuk MK yang diampu -- kode & kaitan CPMK/CPL tetap dari kurikulum, hanya teksnya yang bisa disesuaikan. Penyesuaian otomatis konsisten di dropdown mingguan (F), checklist komponen (G), serta unduhan RPS & portofolio, tanpa menyentuh data kurikulum induk atau halaman nilai/ dashboard prodi. - Form RPS dibungkus <fieldset disabled> untuk dosen tanpa hak edit -- seluruh field benar-benar terkunci (bukan cuma tombol simpan hilang).
This commit is contained in:
+12
-12
@@ -50,7 +50,7 @@ def skor_sub(nilai_data: dict, ambang: float) -> dict:
|
||||
"""Skor per Sub-CPMK dari data nilai tersimpan.
|
||||
|
||||
nilai_data = {"mode": "per_mahasiswa"|"agregat",
|
||||
"mahasiswa": [{"npm","nama","skor":{sub: nilai}}] |
|
||||
"mahasiswa": [{"nim","nama","skor":{sub: nilai}}] |
|
||||
"agregat": {sub: rata}}
|
||||
→ {sub: {"rata": x, "n": jumlah_mhs, "lulus_pct": y|None}}
|
||||
"""
|
||||
@@ -92,7 +92,7 @@ def bobot_sub_dari_rps(rps_data: dict | None) -> dict[str, float]:
|
||||
|
||||
|
||||
def skor_dari_sesi(komponen_daftar: list[dict], sesi: dict) -> dict[str, dict[str, float]]:
|
||||
"""npm -> {sub: skor}, dihitung dari log sesi input per-komponen (tombol
|
||||
"""nim -> {sub: skor}, dihitung dari log sesi input per-komponen (tombol
|
||||
'isi per komponen' di halaman nilai): rata sederhana antar sesi dalam
|
||||
komponen yang sama (mis. beberapa kali Kuis), lalu rata tertimbang bobot
|
||||
antar-komponen bila satu Sub-CPMK diukur oleh >1 komponen (mis. Kuis dan
|
||||
@@ -108,12 +108,12 @@ def skor_dari_sesi(komponen_daftar: list[dict], sesi: dict) -> dict[str, dict[st
|
||||
continue
|
||||
if i not in komponen_by_i or not daftar_sesi:
|
||||
continue
|
||||
per_npm: dict[str, list[float]] = {}
|
||||
per_nim: dict[str, list[float]] = {}
|
||||
for s in daftar_sesi:
|
||||
for npm, v in (s.get("nilai") or {}).items():
|
||||
for nim, v in (s.get("nilai") or {}).items():
|
||||
if v is not None:
|
||||
per_npm.setdefault(npm, []).append(float(v))
|
||||
per_komponen_avg[i] = {npm: _rata(xs) for npm, xs in per_npm.items()}
|
||||
per_nim.setdefault(nim, []).append(float(v))
|
||||
per_komponen_avg[i] = {nim: _rata(xs) for nim, xs in per_nim.items()}
|
||||
|
||||
kontribusi_per_sub: dict[str, list[tuple[float, dict]]] = {}
|
||||
for i, avg_map in per_komponen_avg.items():
|
||||
@@ -129,15 +129,15 @@ def skor_dari_sesi(komponen_daftar: list[dict], sesi: dict) -> dict[str, dict[st
|
||||
|
||||
out: dict[str, dict[str, float]] = {}
|
||||
for sub, kontribusi in kontribusi_per_sub.items():
|
||||
npms: set[str] = set()
|
||||
nims: set[str] = set()
|
||||
for _, avg_map in kontribusi:
|
||||
npms |= avg_map.keys()
|
||||
for npm in npms:
|
||||
pasangan = [(w, avg_map[npm]) for w, avg_map in kontribusi if npm in avg_map]
|
||||
nims |= avg_map.keys()
|
||||
for nim in nims:
|
||||
pasangan = [(w, avg_map[nim]) for w, avg_map in kontribusi if nim in avg_map]
|
||||
total_w = sum(w for w, _ in pasangan)
|
||||
nilai = (sum(w * v for w, v in pasangan) / total_w) if total_w \
|
||||
else _rata([v for _, v in pasangan])
|
||||
out.setdefault(npm, {})[sub] = nilai
|
||||
out.setdefault(nim, {})[sub] = nilai
|
||||
return out
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ def nilai_akhir_mahasiswa(nilai_data: dict, bobot_sub: dict[str, float]) -> list
|
||||
else: # RPS belum mengisi bobot → rata-rata sederhana
|
||||
akhir = _rata([v for _, v in pasangan])
|
||||
if akhir is not None:
|
||||
out.append({"npm": m.get("npm", ""), "nama": m.get("nama", ""),
|
||||
out.append({"nim": m.get("nim", ""), "nama": m.get("nama", ""),
|
||||
"akhir": akhir})
|
||||
return out
|
||||
|
||||
|
||||
Reference in New Issue
Block a user