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:
Power BI Dev
2026-07-22 08:37:54 +07:00
parent afb9e72ba2
commit 5f615aa7e8
12 changed files with 199 additions and 87 deletions
+4 -4
View File
@@ -265,12 +265,12 @@ def build_portofolio(d: dict) -> bytes:
skor_sub = d.get("skor_sub", {})
if nilai.get("mode") == "per_mahasiswa":
mhs = nilai.get("mahasiswa", [])
akhir = {a["npm"]: a["akhir"] for a in d.get("akhir", [])}
akhir = {a["nim"]: a["akhir"] for a in d.get("akhir", [])}
kol_sub = [s["kode"] for s in subs]
tf = doc.add_table(rows=2 + len(mhs) + 3, cols=3 + len(kol_sub) + 1)
_borders(tf)
_hdr(tf.cell(0, 0), "No.")
_hdr(tf.cell(0, 1), "NPM")
_hdr(tf.cell(0, 1), "NIM")
_hdr(tf.cell(0, 2), "Nama")
for j, sk in enumerate(kol_sub):
_hdr(tf.cell(0, 3 + j), sk, size=7)
@@ -285,13 +285,13 @@ def build_portofolio(d: dict) -> bytes:
align=WD_ALIGN_PARAGRAPH.CENTER)
for i, m in enumerate(mhs, 2):
_ct(tf.cell(i, 0), str(i - 1), size=8, align=WD_ALIGN_PARAGRAPH.CENTER)
_ct(tf.cell(i, 1), m.get("npm", ""), size=8)
_ct(tf.cell(i, 1), m.get("nim", ""), size=8)
_ct(tf.cell(i, 2), m.get("nama", ""), size=8)
for j, sk in enumerate(kol_sub):
v = (m.get("skor") or {}).get(sk)
_ct(tf.cell(i, 3 + j), _f(v, 0) if v is not None else "",
size=8, align=WD_ALIGN_PARAGRAPH.CENTER)
_ct(tf.cell(i, 3 + len(kol_sub)), _f(akhir.get(m.get("npm", "")), 1),
_ct(tf.cell(i, 3 + len(kol_sub)), _f(akhir.get(m.get("nim", "")), 1),
size=8, bold=True, align=WD_ALIGN_PARAGRAPH.CENTER)
# baris statistik per sub
base = 2 + len(mhs)