Files
OBE-Mapping/app/templates/form_nilai.html
T
Power BI Dev afb9e72ba2 Tambah asesmen per komponen, batasi Dasbor/Rekap ke kaprodi, penugasan per kelas
- Halaman nilai: tombol isi-cepat per komponen penilaian (bagian G RPS) yang
  menyalin nilai ke seluruh Sub-CPMK terkait; komponen berulang (Kuis, Tugas,
  Presentasi, Tes Lisan) mendukung banyak sesi dengan rata-rata otomatis,
  komponen sekali-jalan (UTS/UAS) mengedit sesi yang sama. Sub-CPMK yang
  diukur >1 komponen digulung rata tertimbang bobot (hitung.skor_dari_sesi).
- Hapus jalur unggah Excel & mode "ketik rata-rata kelas" (agregat) dari
  input nilai — proses diseragamkan: hanya grid manual + tombol komponen,
  keduanya bersumber dari daftar mahasiswa impor kaprodi.
- Dasbor Monitoring, Rekap CPL/CPMK, dan Riwayat kini khusus kaprodi (dosen
  tidak lagi melihat rollup lintas-MK yang bukan miliknya).
- Penugasan dosen ke MK bisa dibatasi per kelas (kolom kelas baru di tabel
  penugasan, migrasi otomatis & aman untuk DB yang sudah berjalan) — dosen
  MK dengan banyak kelas paralel hanya melihat & menilai kelasnya sendiri;
  hapus nilai MK penuh dikunci untuk dosen yang dibatasi kelas.
2026-07-21 12:44:57 +07:00

186 lines
9.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block judul %}Nilai {{ peta.nama }} — Informatika UNTAN{% endblock %}
{% block isi %}
<div class="nav-baris">
<div>MK ini: <a href="/rps/{{ peta.kode }}">RPS</a> · <b>Input Nilai</b> · <a href="/portofolio/{{ peta.kode }}">Portofolio</a></div>
</div>
<h1>Input Nilai — {{ peta.nama }} <span style="color:var(--grey)">({{ peta.kode }})</span></h1>
<p class="keterangan">Daftar mahasiswa mengikuti impor kaprodi. Apa pun cara
inputnya, pengukuran memakai <b>satu metodologi yang sama</b>: skor per Sub-CPMK
digulung ke CPMK lalu CPL (rincian di halaman <a href="/metodologi">Metodologi</a>).</p>
{% if kelas_dosen is not none %}
<div class="info">Anda hanya mengampu kelas <b>{{ kelas_dosen|join(', ') }}</b> pada MK ini —
daftar &amp; tombol di bawah cuma menampilkan mahasiswa kelas tersebut.</div>
{% endif %}
{% if pesan %}<div class="info">{{ pesan }}</div>{% endif %}
{% for w in peringatan %}<div class="peringatan">{{ w }}</div>{% endfor %}
{% if komponen_daftar %}
<h2 class="bagian">ISI PER KOMPONEN PENILAIAN <span style="font-weight:normal;font-style:italic;color:var(--grey)">(cara cepat — nilai per mahasiswa digabung ke Sub-CPMK yang diukur komponen itu)</span></h2>
<p class="keterangan">
Komponen <b>berulang</b> (Kuis, Tugas, Presentasi, Tes Lisan) boleh diisi berkali-kali
sepanjang semester — tiap sesi dirata-rata otomatis. Komponen <b>sekali-jalan</b>
(UTS, UAS) hanya satu sesi; mengisi lagi memperbarui sesi yang sama, bukan menambah.
Bila satu instrumen (mis. UTS dengan beberapa kelompok soal) perlu nilai berbeda
antar Sub-CPMK, isi lewat grid per Sub-CPMK di bawah.</p>
<table class="grid" style="max-width:760px">
<thead>
<tr>
<th>Komponen</th>
<th class="tengah" style="width:90px">Sub-CPMK</th>
<th class="tengah" style="width:70px">Bobot</th>
<th class="tengah" style="width:150px">Status</th>
<th class="tengah" style="width:110px"></th>
</tr>
</thead>
<tbody>
{% for k in komponen_daftar %}
<tr>
<td>{{ k.nama }}<br><span style="font-size:12px;color:var(--grey)">{{ 'boleh diisi berkali-kali' if k.ulang else 'sekali-jalan' }}</span></td>
<td class="tengah">{{ k.n_subs }}</td>
<td class="tengah">{% if k.bobot %}{{ k.bobot }}%{% else %}—{% endif %}</td>
<td class="tengah">
{% if k.ulang %}
{% if k.n_sesi %}<span class="stamp stamp-ok">{{ k.n_sesi }} SESI</span>{% else %}<span class="stamp stamp-draft">BELUM ADA SESI</span>{% endif %}
{% else %}
{% if k.n_sesi %}<span class="stamp stamp-ok">SUDAH DIISI</span>{% else %}<span class="stamp stamp-draft">BELUM DIISI</span>{% endif %}
{% endif %}
</td>
<td class="tengah">
<a class="tombol garis" style="padding:4px 12px;font-size:13px" href="/nilai/{{ peta.kode }}/komponen/{{ k.i }}">
{{ 'Sesi Baru' if k.ulang else ('Edit' if k.n_sesi else 'Isi') }}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if tersimpan %}
<div class="info">
Data tersimpan: mode
<b>{% if tersimpan.data.mode == 'per_mahasiswa' %}PER MAHASISWA ({{ tersimpan.data.mahasiswa|length }} mahasiswa){% else %}BUKTI AGREGAT (rata-rata kelas){% endif %}</b>
— terakhir disimpan {{ tersimpan.updated_at }}{% if tersimpan.updated_by %} oleh {{ tersimpan.updated_by }}{% endif %}.
{% if tersimpan.data.sumber %}<br>Riwayat perubahan:
{{ tersimpan.data.sumber if tersimpan.data.sumber is string else tersimpan.data.sumber|join(' · ') }}.{% endif %}
<form method="post" action="/nilai/{{ peta.kode }}" style="display:inline;margin-left:8px"
onsubmit="return confirm('Hapus SELURUH nilai tersimpan MK ini untuk {{ tahun }}? Tindakan ini tidak bisa dibatalkan.')">
<button class="tombol garis" type="submit" name="aksi" value="hapus"
style="color:var(--red);border-color:var(--red);padding:2px 10px;font-size:13px">Mulai Ulang (hapus semua nilai)</button>
</form>
</div>
{% endif %}
{% if grid %}
{% set per_mhs = tersimpan and tersimpan.data.mode == 'per_mahasiswa' %}
<h2 class="bagian">NILAI PER MAHASISWA <span style="font-weight:normal;font-style:italic;color:var(--grey)">(ketik langsung di sel, 0100; kolom Nilai Akhir &amp; baris rata-rata dihitung dari data tersimpan)</span></h2>
<p class="keterangan">
Daftar mahasiswa dari impor kaprodi. Kosongkan sel yang belum dinilai;
<b>Simpan Nilai</b> menyimpan persis isi tabel. Nilai Akhir = tertimbang bobot
komponen RPS; angkanya diperbarui setelah disimpan.
</p>
<form method="post" action="/nilai/{{ peta.kode }}">
<div class="gulir" style="max-height:480px;overflow-y:auto">
{% set ada_kelas = grid|selectattr('kelas')|list|length > 0 %}
{% set n_kiri = 4 if ada_kelas else 3 %}
<table class="grid">
<thead>
<tr>
<th style="width:44px" class="tengah">No.</th>
<th style="width:112px">NPM</th>
<th style="min-width:150px">Nama</th>
{% if ada_kelas %}<th style="width:60px" class="tengah">Kelas</th>{% endif %}
{% for s in peta.subs %}<th class="tengah" style="font-size:11px;min-width:62px">{{ s.kode }}</th>{% endfor %}
<th class="tengah" style="width:80px">Nilai Akhir</th>
</tr>
</thead>
<tbody>
{% for m in grid %}
{% set i = loop.index0 %}
<tr>
<td class="tengah">{{ loop.index }}
<input type="hidden" name="npm_{{ i }}" value="{{ m.npm }}">
<input type="hidden" name="nama_{{ i }}" value="{{ m.nama }}"></td>
<td>{{ m.npm }}</td>
<td>{{ m.nama }}</td>
{% if ada_kelas %}<td class="tengah">{{ m.kelas or '—' }}</td>{% endif %}
{% for s in peta.subs %}
<td class="isian"><input type="number" name="skor_{{ i }}_{{ s.kode }}"
min="0" max="100" step="0.01"
value="{{ '%g'|format(m.skor[s.kode]) if s.kode in m.skor else '' }}"></td>
{% endfor %}
<td class="tengah"><b>{{ '%.1f'|format(akhir.get(m.npm)) if akhir.get(m.npm) is not none else '—' }}</b></td>
</tr>
{% endfor %}
{% if per_mhs and hasil %}
<tr class="total">
<td colspan="{{ n_kiri }}" style="text-align:right">Rata-rata kelas (tersimpan)</td>
{% for s in peta.subs %}
{% set info = hasil.sub.get(s.kode) %}
<td class="tengah">{{ '%.1f'|format(info.rata) if info else '—' }}</td>
{% endfor %}
<td class="tengah">{{ '%.1f'|format(akhir.values()|sum / akhir|length) if akhir else '—' }}</td>
</tr>
<tr class="total">
<td colspan="{{ n_kiri }}" style="text-align:right">% lulus (≥ {{ ambang|round(0)|int }})</td>
{% for s in peta.subs %}
{% set info = hasil.sub.get(s.kode) %}
<td class="tengah" {% if info and info.lulus_pct is not none and info.rata < ambang %}style="color:var(--red)"{% endif %}>{{ '%.0f'|format(info.lulus_pct) ~ '%' if info and info.lulus_pct is not none else '—' }}</td>
{% endfor %}
<td></td>
</tr>
{% endif %}
</tbody>
</table>
</div>
<div class="aksi-baris">
<span style="color:var(--grey);font-size:13px">Disimpan atas nama <b>{{ user.nama }}</b></span>
<span class="spacer"></span>
<button class="tombol" type="submit" name="aksi" value="grid">Simpan Nilai</button>
</div>
</form>
{% endif %}
{% if hasil %}
<h2 class="bagian">HASIL KETERCAPAIAN MK INI <span style="font-weight:normal;font-style:italic;color:var(--grey)">(ambang {{ ambang|round(0)|int }}; roll-up bagi rata)</span></h2>
<div class="gulir">
<table class="grid">
<thead><tr><th>CPMK</th><th class="tengah">Skor</th><th class="tengah">Sub Ternilai</th><th>CPL Didukung</th></tr></thead>
<tbody>
{% for kode_c, v in hasil.cpmk.items() %}
<tr>
<td>{{ kode_c }}</td>
<td class="tengah">{% if v.skor is not none %}<b>{{ '%.2f'|format(v.skor) }}</b>{% else %}—{% endif %}</td>
<td class="tengah">{{ v.n_ternilai }}/{{ v.n_sub }}</td>
<td>{{ v.cpl|join(', ') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="gulir" style="margin-top:10px">
<table class="grid">
<thead><tr><th>CPL (dalam MK ini)</th><th class="tengah">Skor</th><th class="tengah">Status</th></tr></thead>
<tbody>
{% for kode_c, v in hasil.cpl.items() %}
<tr>
<td>{{ kode_c }}</td>
<td class="tengah">{% if v.skor is not none %}<b>{{ '%.2f'|format(v.skor) }}</b>{% else %}—{% endif %}</td>
<td class="tengah">
{% if v.skor is none %}<span class="stamp stamp-draft">BELUM TERUKUR</span>
{% elif v.skor >= ambang %}<span class="stamp stamp-ok">TERCAPAI</span>
{% else %}<span class="stamp stamp-no">DI BAWAH AMBANG</span>{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endblock %}