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.
This commit is contained in:
@@ -8,7 +8,10 @@
|
||||
<p class="keterangan"><b>Dosen RPS + MK</b> dapat mengedit RPS, nilai, dan portofolio
|
||||
pada MK yang ditugaskan. <b>Dosen MK</b> hanya dapat mengedit nilai & portofolio
|
||||
pada MK yang ditugaskan — halaman RPS terbuka hanya untuk dibaca. Kaprodi dapat
|
||||
mengakses semuanya.</p>
|
||||
mengakses semuanya. Kosongkan kolom kelas bila dosen mengampu <b>seluruh kelas</b>
|
||||
MK itu; isi (mis. "A" atau "A,B") bila dosen hanya boleh melihat & menilai
|
||||
mahasiswa kelas tertentu — cocok untuk MK dengan beberapa kelas paralel yang
|
||||
diampu dosen berbeda.</p>
|
||||
<div class="gulir">
|
||||
<table class="grid">
|
||||
<thead>
|
||||
@@ -30,7 +33,9 @@
|
||||
{% if p.role == 'kaprodi' %}
|
||||
<i style="color:var(--grey)">semua MK</i>
|
||||
{% else %}
|
||||
{{ p.mk|join(', ') if p.mk else '—' }}
|
||||
{% if p.mk %}
|
||||
{% for kode in p.mk %}{{ kode }}{% if p.kelas_map.get(kode) %} (kelas {{ p.kelas_map[kode] }}){% endif %}{{ ', ' if not loop.last }}{% endfor %}
|
||||
{% else %}—{% endif %}
|
||||
<details style="margin-top:6px">
|
||||
<summary style="cursor:pointer;color:var(--blue);font-size:13px">Ubah penugasan…</summary>
|
||||
<form method="post" action="/admin/pengguna/{{ p.id }}/tugas">
|
||||
@@ -38,6 +43,8 @@
|
||||
{% for mk in daftar %}
|
||||
<label><input type="checkbox" name="mk" value="{{ mk.kode }}"
|
||||
{% if mk.kode in p.mk %}checked{% endif %}> {{ mk.kode }} — {{ mk.nama }}</label>
|
||||
<input type="text" name="kelas_{{ mk.kode }}" placeholder="kelas (kosong = semua)"
|
||||
value="{{ p.kelas_map.get(mk.kode, '') }}" style="width:150px;margin:0 0 4px 22px;font-size:12px">
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button class="tombol garis" type="submit" style="padding:4px 12px;font-size:13px">Simpan Penugasan</button>
|
||||
|
||||
@@ -28,9 +28,11 @@
|
||||
<nav class="menu-baris">
|
||||
<span class="menu-kiri">
|
||||
{{ tautan('/', 'Beranda') }}
|
||||
{{ tautan('/pantau', 'Dasbor Monitoring') }}
|
||||
{{ tautan('/rekap', 'Rekap CPL/CPMK') }}
|
||||
{{ tautan('/rekap/riwayat', 'Riwayat') }}
|
||||
{% if user.role == 'kaprodi' %}
|
||||
{{ tautan('/pantau', 'Dasbor Monitoring') }}
|
||||
{{ tautan('/rekap', 'Rekap CPL/CPMK') }}
|
||||
{{ tautan('/rekap/riwayat', 'Riwayat') }}
|
||||
{% endif %}
|
||||
{{ tautan('/metodologi', 'Metodologi') }}
|
||||
{{ tautan('/dashboard', 'Pemetaan CPMK') }}
|
||||
{% if user.role == 'kaprodi' %}{{ tautan('/admin', 'Kelola Akun & Mahasiswa') }}{% endif %}
|
||||
|
||||
@@ -7,20 +7,66 @@
|
||||
|
||||
<h1>Input Nilai — {{ peta.nama }} <span style="color:var(--grey)">({{ peta.kode }})</span></h1>
|
||||
|
||||
<p class="keterangan">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>). Pilih <b>satu</b> cara
|
||||
di bawah — menyimpan lewat cara mana pun menggantikan data nilai MK ini.</p>
|
||||
<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 & 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 unggahan:
|
||||
{% 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.')">
|
||||
@@ -100,56 +146,6 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<details class="cara-alt"{% if not grid %} open{% endif %}>
|
||||
<summary>{{ 'ALTERNATIF — ' if grid }}UNGGAH EXCEL PER MAHASISWA <span style="font-weight:normal;font-style:italic;color:var(--grey)">(cocok bila nilai sudah direkap di Excel, atau team teaching mengisi terpisah)</span></summary>
|
||||
<p class="keterangan">
|
||||
Unduh template berkolom Sub-CPMK MK ini, isi nilai 0–100 per mahasiswa, lalu unggah kembali.
|
||||
{% if terdaftar %}Template sudah berisi <b>{{ terdaftar|length }} mahasiswa</b> yang
|
||||
mengontrak MK ini (dari daftar impor kaprodi).
|
||||
{% else %}<b>Daftar mahasiswa MK ini belum diimpor kaprodi</b> — template kosong,
|
||||
isi NPM & nama secara manual.{% endif %}
|
||||
<br><b>Team teaching:</b> tiap dosen cukup mengunggah bagiannya sendiri — kolom
|
||||
Sub-CPMK yang diampunya (mis. sebelum/sesudah UTS) atau baris mahasiswa kelasnya.
|
||||
Unggahan <b>digabung</b>: sel terisi memperbarui nilai, sel kosong tidak menghapus
|
||||
nilai yang sudah diunggah dosen lain.
|
||||
</p>
|
||||
<div class="aksi-baris" style="border-top:none;margin-top:0;padding-top:0">
|
||||
<a class="tombol garis" href="/nilai/{{ peta.kode }}/template.xlsx">Unduh Template Excel</a>
|
||||
</div>
|
||||
<form method="post" action="/nilai/{{ peta.kode }}" enctype="multipart/form-data">
|
||||
<div class="aksi-baris" style="border-top:none">
|
||||
<input type="file" name="berkas" accept=".xlsx" style="width:auto">
|
||||
<button class="tombol" type="submit" name="aksi" value="unggah">Unggah & Hitung</button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<details class="cara-alt"{% if tersimpan and tersimpan.data.mode == 'agregat' %} open{% endif %}>
|
||||
<summary>ALTERNATIF — KETIK RATA-RATA KELAS PER SUB-CPMK <span style="font-weight:normal;font-style:italic;color:var(--grey)">(hanya bila nilai per mahasiswa tidak tersedia; ditandai "bukti agregat" pada rekap)</span></summary>
|
||||
<form method="post" action="/nilai/{{ peta.kode }}">
|
||||
<div class="gulir" style="max-height:420px;overflow-y:auto">
|
||||
<table class="grid">
|
||||
<thead><tr><th style="width:120px">Sub-CPMK</th><th>Uraian</th><th class="tengah" style="width:130px">Rata-rata (0–100)</th></tr></thead>
|
||||
<tbody>
|
||||
{% for s in peta.subs %}
|
||||
<tr>
|
||||
<td class="tengah">{{ s.kode }}</td>
|
||||
<td>{{ s.uraian[:110] }}{% if s.uraian|length > 110 %}…{% endif %}</td>
|
||||
<td class="isian"><input type="number" name="rata_{{ s.kode }}" min="0" max="100" step="0.01"
|
||||
value="{% if tersimpan and tersimpan.data.mode == 'agregat' %}{{ tersimpan.data.agregat.get(s.kode, '') }}{% endif %}"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</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 garis" type="submit" name="aksi" value="agregat">Simpan Rata-rata</button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
{% 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">
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
{% extends "base.html" %}
|
||||
{% block judul %}Nilai {{ komponen.nama }} — {{ peta.nama }} — Informatika UNTAN{% endblock %}
|
||||
{% block isi %}
|
||||
<div class="nav-baris">
|
||||
<div>MK ini: <a href="/rps/{{ peta.kode }}">RPS</a> · <a href="/nilai/{{ peta.kode }}">Input Nilai</a> · <b>{{ komponen.nama }}</b> · <a href="/portofolio/{{ peta.kode }}">Portofolio</a></div>
|
||||
</div>
|
||||
|
||||
<h1>Isi Nilai — {{ komponen.nama }} <span style="color:var(--grey)">({{ peta.nama }})</span></h1>
|
||||
|
||||
<p class="keterangan">
|
||||
Nilai di sini digabung ke Sub-CPMK yang diukur komponen ini:
|
||||
{% for s in komponen.subs %}<b>{{ s }}</b>{{ ', ' if not loop.last }}{% endfor %}
|
||||
{% if komponen.bobot %}(bobot komponen {{ komponen.bobot }}%){% endif %}.
|
||||
{% if komponen.ulang %}
|
||||
Komponen ini <b>boleh diisi berkali-kali</b> (mis. beberapa kali kuis sepanjang
|
||||
semester) — nilai tiap sesi dirata-rata otomatis per Sub-CPMK. Bila satu Sub-CPMK
|
||||
juga diukur komponen lain, hasil akhirnya dirata tertimbang bobot antar-komponen.
|
||||
{% else %}
|
||||
Komponen ini <b>hanya satu sesi per semester</b> — menyimpan lagi memperbarui
|
||||
(menimpa) sesi yang sudah ada, bukan menambah sesi baru.
|
||||
{% endif %}
|
||||
Jika mahasiswa perlu nilai berbeda antar Sub-CPMK dalam komponen yang sama
|
||||
(mis. UTS dengan beberapa kelompok soal), isi lewat
|
||||
<a href="/nilai/{{ peta.kode }}">grid per Sub-CPMK</a> di halaman utama nilai, bukan di sini.
|
||||
</p>
|
||||
|
||||
{% if pesan %}<div class="info">{{ pesan }}</div>{% endif %}
|
||||
|
||||
{% if komponen.ulang and daftar_sesi %}
|
||||
<h2 class="bagian">SESI TERSIMPAN</h2>
|
||||
<table class="grid" style="max-width:560px">
|
||||
<thead><tr><th>Sesi</th><th class="tengah" style="width:120px">Mahasiswa Terisi</th><th style="width:60px"></th></tr></thead>
|
||||
<tbody>
|
||||
{% for s in daftar_sesi %}
|
||||
<tr{% if loop.index0 == edit_id %} style="background:var(--blue-fill)"{% endif %}>
|
||||
<td>{{ s.label }}</td>
|
||||
<td class="tengah">{{ (s.nilai or {})|length }}</td>
|
||||
<td><a href="/nilai/{{ peta.kode }}/komponen/{{ i }}?sesi_id={{ loop.index0 }}">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<h2 class="bagian">{{ 'EDIT SESI' if edit_id < daftar_sesi|length else 'SESI BARU' }} — {{ label_awal }}</h2>
|
||||
<form method="post" action="/nilai/{{ peta.kode }}/komponen/{{ i }}">
|
||||
<input type="hidden" name="edit_id" value="{{ edit_id }}">
|
||||
{% if komponen.ulang %}
|
||||
<label style="display:block;margin-bottom:10px">Nama sesi
|
||||
<input type="text" name="label" value="{{ label_awal }}" style="display:block;margin-top:4px;max-width:280px">
|
||||
</label>
|
||||
{% else %}
|
||||
<input type="hidden" name="label" value="{{ label_awal }}">
|
||||
{% endif %}
|
||||
<div class="gulir" style="max-height:480px;overflow-y:auto">
|
||||
<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>
|
||||
<th class="tengah" style="width:140px">Nilai (0–100)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for m in grid %}
|
||||
<tr>
|
||||
<td class="tengah">{{ loop.index }}
|
||||
<input type="hidden" name="npm_{{ loop.index0 }}" value="{{ m.npm }}">
|
||||
<input type="hidden" name="nama_{{ loop.index0 }}" value="{{ m.nama }}"></td>
|
||||
<td>{{ m.npm }}</td>
|
||||
<td>{{ m.nama }}</td>
|
||||
<td class="isian"><input type="number" name="nilai_{{ loop.index0 }}"
|
||||
min="0" max="100" step="0.01"
|
||||
value="{{ '%g'|format(m.nilai_awal) if m.nilai_awal is not none else '' }}"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</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">Simpan Sesi Ini</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user