# -*- coding: utf-8 -*- """ Generator Rancangan Tugas Mahasiswa (RTM) (.docx). Mengikuti format resmi `FORM RPS & RTM UNTAN (draft).docx`: tabel identitas (Mata Kuliah/Kode MK/Tugas ke/SKS/Dosen Pengampu/Semester) diikuti tabel RANCANGAN TUGAS MAHASISWA per tugas (Bentuk, Judul, Sub-CPMK, Deskripsi, Metode Pengerjaan, Bentuk & Format Luaran, Indikator/Kriteria/Bobot, Jadwal Pelaksanaan, Lain-lain, Daftar Rujukan). Dokumen ini berdiri sendiri, tak memuat data nilai/skor mahasiswa sama sekali. """ import io import os from docx import Document from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.oxml import OxmlElement from docx.oxml.ns import qn from docx.shared import Cm, Pt, RGBColor DARK_BLUE = "1F4E79" LIGHT_BLUE = "D6E4F0" WHITE = "FFFFFF" FAKULTAS = os.environ.get("FAKULTAS", "FAKULTAS TEKNIK") PRODI = os.environ.get("PRODI", "PROGRAM STUDI INFORMATIKA") def _shd(cell, hex_color): tc_pr = cell._tc.get_or_add_tcPr() el = OxmlElement("w:shd") el.set(qn("w:val"), "clear") el.set(qn("w:color"), "auto") el.set(qn("w:fill"), hex_color) tc_pr.append(el) def _ct(cell, text, bold=False, italic=False, size=10, color=None, align=WD_ALIGN_PARAGRAPH.LEFT): p = cell.paragraphs[0] p.alignment = align p.paragraph_format.space_before = Pt(2) p.paragraph_format.space_after = Pt(2) run = p.add_run() for i, line in enumerate(str(text or "").split("\n")): if i: run.add_break() run.add_text(line) run.bold = bold run.italic = italic run.font.name = "Times New Roman" run.font.size = Pt(size) if color: run.font.color.rgb = RGBColor.from_string(color) def _hdr(cell, text, size=10): _shd(cell, DARK_BLUE) _ct(cell, text, bold=True, color=WHITE, size=size, align=WD_ALIGN_PARAGRAPH.CENTER) def _borders(table): tbl_pr = table._tbl.tblPr el = OxmlElement("w:tblBorders") for sisi in ("top", "left", "bottom", "right", "insideH", "insideV"): b = OxmlElement(f"w:{sisi}") b.set(qn("w:val"), "single") b.set(qn("w:sz"), "4") b.set(qn("w:color"), "000000") el.append(b) tbl_pr.append(el) def build_rtm(d: dict) -> bytes: doc = Document() for sec in doc.sections: sec.top_margin = sec.bottom_margin = Cm(2.5) sec.left_margin = sec.right_margin = Cm(2.5) def para(text, size=12, bold=False, align=WD_ALIGN_PARAGRAPH.LEFT, before=6, after=3): p = doc.add_paragraph() p.alignment = align p.paragraph_format.space_before = Pt(before) p.paragraph_format.space_after = Pt(after) r = p.add_run(text) r.bold = bold r.font.name = "Times New Roman" r.font.size = Pt(size) return p para("RANCANGAN TUGAS MAHASISWA (RTM)", size=16, bold=True, align=WD_ALIGN_PARAGRAPH.CENTER) para(PRODI, size=12, bold=True, align=WD_ALIGN_PARAGRAPH.CENTER, before=0) para(f"{FAKULTAS} — UNIVERSITAS TANJUNGPURA", size=11, align=WD_ALIGN_PARAGRAPH.CENTER, before=0) para(f"Tahun Ajaran {d.get('tahun', '')}", size=11, align=WD_ALIGN_PARAGRAPH.CENTER, before=0, after=10) daftar_tugas = d.get("tugas", []) if not daftar_tugas: para("Belum ada Tugas yang diisi untuk RTM ini.", after=6) for idx, t in enumerate(daftar_tugas, 1): if idx > 1: doc.add_page_break() ti = doc.add_table(rows=3, cols=4) _borders(ti) baris = [ ("Mata Kuliah", d.get("nama", ""), "Kode MK", d.get("kode", "")), ("Tugas ke", str(idx), "SKS", str(d.get("sks", "") or "—")), ("Dosen Pengampu", d.get("pengampu", "") or "—", "Semester", str(d.get("semester", "") or "—")), ] for ri, (l1, v1, l2, v2) in enumerate(baris): _shd(ti.cell(ri, 0), LIGHT_BLUE) _ct(ti.cell(ri, 0), l1, bold=True) _ct(ti.cell(ri, 1), v1) _shd(ti.cell(ri, 2), LIGHT_BLUE) _ct(ti.cell(ri, 2), l2, bold=True) _ct(ti.cell(ri, 3), v2) doc.add_paragraph() subs_teks = ", ".join(t.get("subs") or []) or "—" jadwal = "; ".join(x for x in ( f"Alokasi waktu: {t['alokasi_waktu']}" if t.get("alokasi_waktu") else "", f"Tanggal: {t['tanggal']}" if t.get("tanggal") else "", ) if x) or "—" rows = [ ("BENTUK TUGAS", t.get("bentuk_tugas", "") or "—"), ("JUDUL TUGAS", t.get("judul", "") or "—"), ("SUB-CPMK YANG DICAPAI", subs_teks), ("DESKRIPSI TUGAS", t.get("deskripsi", "") or "—"), ("METODE PENGERJAAN TUGAS", t.get("metode_pengerjaan", "") or "—"), ("BENTUK DAN FORMAT LUARAN", t.get("luaran", "") or "—"), ("INDIKATOR, KRITERIA DAN BOBOT PENILAIAN", t.get("indikator_bobot", "") or "—"), ("JADWAL PELAKSANAAN", jadwal), ("LAIN-LAIN", t.get("lain_lain", "") or "—"), ("DAFTAR RUJUKAN", t.get("rujukan", "") or "—"), ] tt = doc.add_table(rows=1 + len(rows), cols=2) _borders(tt) _hdr(tt.cell(0, 0), "RANCANGAN TUGAS MAHASISWA") tt.cell(0, 0).merge(tt.cell(0, 1)) for ri, (label, isi) in enumerate(rows, 1): _shd(tt.cell(ri, 0), LIGHT_BLUE) _ct(tt.cell(ri, 0), label, bold=True, size=9) _ct(tt.cell(ri, 1), isi, size=10) tt.rows[ri].cells[0].width = Cm(5.5) tt.rows[ri].cells[1].width = Cm(11) buf = io.BytesIO() doc.save(buf) return buf.getvalue()