Penjelasan konsep kartu ujian digital yang bisa dibaca oleh manusia dan komputer (khususnya telepon genggam)

Overview

Konsep Kartu Ujian Digital

Ini adalah penjelasan atas konsep kartu ujian digital yang dapat memudahkan siswa tanpa perlu repot memasukkan NPSN (Nomor Pokok Sekolah Nasional), Username, dan Password. Nantinya jadwal dan token dapat langsung digunakan tanpa perlu mengetikkannya, tetapi tidak akan menggantikan input secara manual ketika token memiliki kesalahan.

Sebelumnya, saya berterima kasih kepada pihak sekolah yang sudah bergerak dengan menggunakan aplikasi sebagai sarana ulangan dan tidak menggunakan kertas sebagai upaya mengurangi dampak kerusakan lingkungan. Disini saya memberikan saran dalam sebuah konsep, berikan saya kritik dan saran serta maafkan jika ada kesalahan dalam bertutur kata.

Dalam kata lain, saya memberikan saran supaya kita lebih paperless dalam melaksanakan ujian.

TL;DR

Too Long; Didn't Read. Jika tidak ingin membaca terlalu panjang, ini adalah ringkasan penjelasan dengan menggunakan diagram.

Pertama-tama puppeteer mengunjungi halaman kartu.

Diagram puppeteer mengunjungi halaman kartu

Kemudian puppeteer melakukan web scraping, mengambil data yang diperlukan. Mekanisme yang lebih jelas sudah diterangkan di bagian Melakukan Ekstraksi Data.

Diagram puppeteer melakukan web scraping

Masih didalam fungsi puppeteer, scraping yang dilakukan oleh puppeteer akan menghasilkan file pdf asli, file pdf yang sudah disusupi data json, dan data mentahan. Teknik penyusupan data json ini menggunakan Buffer.concat bawaan dari NodeJS.

Diagram puppeteer melakukan web scraping

Secara teknis, aplikasi android bisa membaca file dan membaca buffer. Buffer yang sudah dibaca akan di ubah menjadi ascii dan dipisahkan dari EOF (End Of File) PDF dan mendapatkan string JSON. String JSON yang sudah dibaca akan digunakan oleh aplikasi android itu kedepannya.

Diagram puppeteer melakukan web scraping

Bagaimana Siswa Mendapatkan Kartu Saat Ini?


Website yang diakses saat ini jika ingin mendapatkan kartu ulangan

Sebelum masuk ke konsep utama, saya sebagai siswa mendapatkan kartu dengan memasukan U-PIN yang diberikan dengan cara verifikasi melalui Whatsapp. Pesan yang ada di Whatsapp akan mengarahkan siswa ke website https://kartuujian.sman12-bekasi.sch.id/ dan mengisikan U-PIN yang tertera.

Setelah tombol Login ditekan, akan diarahkan ke halaman kartu yang sebenarnya. Kira-kira tampilannya akan jadi seperti ini.

Setelah siswa mengisi U-PIN

Analisis

Ini adalah bagian analisis yang akan memberikan penjelasan bagaimana halaman web kartu bekerja agar memberikan gambaran bagaimana nantinya bisa membuat kartu digital.

Bagaimana Kartu Bisa di Download atau di Print

Saya melakukan inspect element ke tombol Cetak yang ada, dan ternyata yang dilakukan adalah menjalankan window.print() setelah tombol di tekan. Setelahnya akan dimunculkan popup atau dialogue yang menginformasikan ke pengguna untuk menyimpan atau langsung mengeprintnya.

Tombol cetak

Jika diperhatikan, tombol Cetak hilang jika sudah di print/didownload dikarenakan ada class .no-print yang cssnya tidak menampilkan pada saat media print yang artinya dalam mode printing.

Setelah siswa mengisi U-PIN

Bagaimana Kartu Bisa Terender di Web

Jika dilihat dari URL yang ada di address bar, website dibuat dengan menggunakan php. Hal ini menandakan terdapat sebuah backend dan sebuah database yang menyimpan nama dan identitas siswa beserta jadwal juga token.

https://kartuujian.sman12-bekasi.sch.id/cetakskl.php?nisn=<U-PIN>

Data akan ditampilkan sesuai apa yang dimasukan sebagai U-PIN oleh pengguna. Artinya website ini memiliki template yang sama dan dapat digunakan untuk scraping dan dapat diambil datanya sebagai data yang bisa digunakan sebagai kartu digital.

Melakukan Ekstraksi Data

Cara yang paling mudah adalah menggunakan teknik scraping dari halaman website yang ada. Setelah menyelidiki dan melakukan percobaan, ini teknik yang bisa dilakukan untuk mengambil otomatis yang nantinya bisa di otomasi menggunakan puppeteer.

Mendapatkan Hari, Mata Pelajaran, Token, dan Waktu

Yang pertama kali saya lakukan adalah mengambil elemen yang merupakan nomor dari kumpulan hari yang ada. Hal ini diambil karena elemen ini memiliki karakteristik khusus yaitu mengisi 4 baris sekaligus (4 rowspan).

Elemen nomor table data (td)

Jadi, elemen ini bisa ditampung ke dalam variabel jadwalRef.

const jadwalRef = [...document.querySelectorAll('td[rowspan="4"]')];

Setelah menampung variabel yang ada, saya melakukan mapping untuk mendapatkan mata pelajaran, token, dan waktu yang sesungguhnya. Untuk mendapatkannya, hal yang paling mungkin adalah melakukan while loop yang dapat dihentikan jika sudah mencapai batas tertentu.

Hal yang dilakukan selanjutnya adalah mengecek parent parentElement dan mengecek elemen sesudahnya atau nextElementSibling apakah tidak ada elemen selanjutnya itu sebuah 'td[rowspan="4"]', jika pernyataan itu valid maka di hentikanlah while loop dan keluar melanjutkan proses eksekusi kode.

Jika pernyataan itu tidak valid maka kita bisa melakukan pengecekan apakah mata pelajaran bukan merupakan -. Dengan aman, kita bisa mengambil mata pelajaran, token dan waktu ulangan.

Untuk mendapatkan informasi tentang tanggal dan hari, kita cukup manfaatkan saja parent element yang terdapat didalam elemen td.

Kira-kira kode akan terlihat seperti ini. Penjelasan tambahan tertera pada kode dibawah.

const jadwalRef = [...document.querySelectorAll('td[rowspan="4"]')];

const jadwal = jadwalRef.map((j) => {
  let data = [];
  let currentElement;

  const parent = j.parentElement;

  while (true) {
    // Mengambil element selanjutnya
    const next =
      data.length < 1
        ? parent.nextElementSibling
        : currentElement.nextElementSibling;

    // Apakah elemen selanjutnya tidak ada
    // atau elemen selanjutnya merupakan nomor tabel
    if (!next || next.querySelector('td[rowspan="4"]')) break;

    // Apakah elemen selanjutnya tidak memiliki string "-"
    if (!next.querySelector("td").innerText.includes("-")) {
      const mapelElement = next.querySelector("td");
      const tokenElement = next.querySelector("td:nth-child(2)");
      const waktuElement = next.querySelector("td:nth-child(3)");

      // Mereplace string 1-9<spasi> dengan ''
      const pelajaran = mapelElement.innerText.replace(/[0-9].\s/, "");

      const tokenStr = tokenElement.innerText;

      // Terdapat token yang memiliki beragam opsi.
      // Supaya tetap konsisten, tipe data akan tetap
      // berupa array of string
      const token = tokenStr.includes("/") ? tokenStr.split(" / ") : [tokenStr];

      // Mereplace – (en dash) dengan - (hypen)
      // Karena menyebabkan bug ketika diubah
      // menjadi string json yang disisipkan
      // ke dalam file
      const waktu = waktuElement.innerText.replace("–", "-");

      data.push({
        pelajaran,
        token,
        waktu,
      });
    }

    currentElement = next;
  }

  const hariElement = parent.querySelector("th");

  // Memisahkan antara hari dan tanggal
  const [hari, tanggal] = hariElement.innerText.split(", ");

  return {
    hari,
    tanggal,
    mapel: data,
  };
});

Mendapatkan Identitas Siswa/i

Identitas siswa/i yang bisa diambil

Bagian ini merupakan tabel yang memiliki 800px dan tabel kedua yang ada di jenisnya ('table[width="800"]:nth-of-type(2)'). Semua text yang ada dicetak tebal menggunakan tag b, jadi bisa di simpulkan bahwa elemen bisa diambil menggunakan querySelectorAll dengan parameter 'table[width="800"]:nth-of-type(2) td b'.

Jika kita gabungkan dengan kode yang sebelumnya, hasilnya akan jadi seperti ini. Penjelasan lebih sudah tercantum dalam kode dibawah ini.

const jadwalRef = [...document.querySelectorAll('td[rowspan="4"]')];
const [nama, kelas, nomorPeserta, npsn, username, password] = [
  ...document.querySelectorAll('table[width="800"]:nth-of-type(2) td b'),
]
  // Di filter apakah merupakan 6 elemen yang dimaksud
  .filter((el) => el.parentElement.getAttribute("colspan") === "2")
  // Akan terdapat spasi, oleh karenya di trim
  // untuk menghilangkan spasi
  .map((el) => el.innerText.trim());

const jadwal = jadwalRef.map((j) => {
  let data = [];
  let currentElement;

  const parent = j.parentElement;

  while (true) {
    // Mengambil element selanjutnya
    const next =
      data.length < 1
        ? parent.nextElementSibling
        : currentElement.nextElementSibling;

    // Apakah elemen selanjutnya tidak ada
    // atau elemen selanjutnya merupakan nomor tabel
    if (!next || next.querySelector('td[rowspan="4"]')) break;

    // Apakah elemen selanjutnya tidak memiliki string "-"
    if (!next.querySelector("td").innerText.includes("-")) {
      const mapelElement = next.querySelector("td");
      const tokenElement = next.querySelector("td:nth-child(2)");
      const waktuElement = next.querySelector("td:nth-child(3)");

      // Mereplace string 1-9<spasi> dengan ''
      const pelajaran = mapelElement.innerText.replace(/[0-9].\s/, "");

      const tokenStr = tokenElement.innerText;

      // Terdapat token yang memiliki beragam opsi.
      // Supaya tetap konsisten, tipe data akan tetap
      // berupa array of string
      const token = tokenStr.includes("/") ? tokenStr.split(" / ") : [tokenStr];

      // Mereplace – (en dash) dengan - (hypen)
      // Karena menyebabkan bug ketika diubah
      // menjadi string json yang disisipkan
      // ke dalam file
      const waktu = waktuElement.innerText.replace("–", "-");

      data.push({
        pelajaran,
        token,
        waktu,
      });
    }

    currentElement = next;
  }

  const hariElement = parent.querySelector("th");

  // Memisahkan antara hari dan tanggal
  const [hari, tanggal] = hariElement.innerText.split(", ");

  return {
    hari,
    tanggal,
    mapel: data,
  };
});

// Ini adalah data yang dapat digunakan
const finalData = {
  user: {
    nama,
    kelas,
    nomor_peserta: nomorPeserta,
    npsn,
    username,
    password,
  },
  jadwal,
};

Typescript Declaration

Kode diatas sudah menunjukkan cara bagaimana dokumen bisa dibuat, tapi supaya dapat memberikan gambaran struktur data yang lengkap, berikut ini merupakan file typescript declaration hasil scraping diatas.

type UserType = {
  nama: string;
  kelas: string;
  nomor_peserta: string;
  npsn: string;
  username: string;
  password: string;
};

type MapelType = {
  pelajaran: string;
  token: string[];
  waktu: string;
};

type jadwalType = {
  hari: string;
  tanggal: string;
  mapel: MapelType[];
};

export type IDigitalCard = {
  user: UserType;
  jadwal: jadwalType;
};

Menjalankan Scraper

Terdapat sebuah contoh scraper yang menjadi kode dasar yang mungkin kedepannya dapat digunakan.

Cloning Dari Github

Jalankan perintah ini Command Line.

# HTTPS
git clone https://github.com/reacto11mecha/konsep-kartu-ujian-digital.git

# SSH
git clone [email protected]:reacto11mecha/konsep-kartu-ujian-digital.git

Menginstall package

Anda ke root directory project dan menginstall package yang diperlukan.

npm install

# atau menggunakan pnpm
pnpm install

Scraping Web dengan U-PIN

Scraper dapat dijalankan dengan perintah berikut.

npm run scrape <U-PIN>

# atau

pnpm scrape <U-PIN>

Jika berhasil diambil, akan terbentuk folder result didalam folder result yang ada di 001-ekstraksi-data. Akan ada 3 file yang terbentuk, file pdf asli, yang sudah terdapat data kartu, dan data mentahan kartu.

Membaca Informasi Yang Tedapat Dalam Kartu

Singkatnya dengan snippet dibawah ini, kita dapat membaca mengetahui informasi yang tersembunyi dalam file.

const fs = require("fs");

const pdfBuffer = fs.readFileSync("./to_pdf_file_path.pdf");

const asciiString = fileBuffer.toString("ascii");
const str = asciiString.substring(asciiString.indexOf("%%EOF") + 5);

// Isinya ada disini
const message = str.trim();

Jalankan perintah dibawah ini untuk membaca file yang sudah ada di folder result folder ekstraksi data. Di folder 02-membaca-kartu juga terdapat contoh pembacaan informasi rahasia dengan menggunakan bahasa JavaScript.

npm run read

# atau

pnpm read

Menambahkan Fungsi Signature dan Verifikasi GnuPG (Opsional)

Jika ingin benar-benar memastikan kartu itu dibuat oleh pihak sekolah, kartu harus memiliki tanda tangan elektronik menggunakan GnuPG (Gnu Privacy Guard) sebagai software penjamin dan bisa diverifikasi dalam aplikasi.

Untuk menandatangani file

gpg --output <file_asli+extensi>.sig --detach-sig <file_asli+extensi>

Untuk memverifikasi keaslian file

gpg --verify <file_signaturenya> <file_aslinya>

Cara diatas merupakan cara manual, terdapat banyak wraper yang sudah ada untuk menangani gpg di banyak bahasa pemrograman. Seperti Bounty Castle dalam bahasa pemrograman Java dan OpenPGP.js di bahasa pemrograman JavaScript.

You might also like...

A string of four operations of the library, can solve the js digital calculation accuracy of scientific notation and formatting problems, support for thousands of decimal point formatting output operations

A string of four operations of the library, can solve the js digital calculation accuracy of scientific notation and formatting problems, support for thousands of decimal point formatting output operations

Apr 6, 2022

I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certificate. I use node-signpdf and pdf-lib library.

pdf-digital-signature-with-node-signpdf-ejs I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certif

Dec 25, 2022

Free Obisidian Publish alternative, for publishing your digital garden.

Free Obisidian Publish alternative, for publishing your digital garden.

What is MindStone? MindStone is a free open-source alternative solution to Obsidian Publish Here how it look like once published, checkout demo versio

Dec 30, 2022

A second course in programming aesthetically-inclined projects for Digital Media undergraduates; Building Interactive Systems

EECS_1720 commits made while instructing EECS 1720 - Building Interactive Systems (winter 2022) (course @York University, Canada) what's new Last Clas

Mar 25, 2022

Ossama Mehmood (ossamamehmood.me) 🚀 Digital Creator / 🎗️ Graphic Designer / 🔮 User Interface (UI) & Experience (UX)

Ossama Mehmood 샘 (ossamamehmood.me) Greetings..! I'm Ossama Mehmood DIGITAL CREATOR OSSAMAMEHMOOD Home About me Skills Resume Testimonials Portfolio C

Jun 17, 2022

Projeto de um relógio digital utilizando HTML, CSS e Javascript.

Link da aplicação online: https://filipelimavaz-relogiodigital.netlify.app/ Projeto de um relógio digital utilizando HTML, CSS e Javascript Obrigado a

Aug 16, 2022

More than a Password Protection and Management tool, it secures all your valuable digital assets in your own vault

ZeroPass Client ZeroPass is more than a Password Protection and Management tool, it secures all your valuable digital assets in your own vault, includ

Aug 22, 2022

This is a web app to add your books in a digital shelf

This is a web app to add your books in a digital shelf

This is a web app to add your books in a digital shelf. This app is built with Javascript. A local storage option is added with this project, so that a user can store data in this is web app.

Sep 3, 2022

The Digital Library is a webapp that allows the user to: Display a list of books

The Digital Library is a webapp that allows the user to: Display a list of books. Add a new book. Remove a selected book.It has been developed using React Redux and tested using Jest.

Sep 5, 2022
Owner
Ezra Khairan Permana
Ezra Khairan Permana
Digital Identifier is a secure, decentralized, anonymous and tampered proof way of maintaining and verifying all essential identity-based documents to create a unique digital identity of a person.

Digital Identifier ?? To design and develop a secure, decentralized, anonymous and tampered proof way of maintaining and verifying all essential ident

Mukul Kolpe 4 Dec 17, 2022
Rest API untuk mengumpulkan jokes receh dan meme dari Indonesia

Candaan API ?? Candaan API merupakan Rest API yang dibuat untuk mendapatkan beberapa jokes jokes receh dari Indonesia, semua data data yang ada di Can

Ardhi Putra P 57 Jan 3, 2023
Sumber kode dan kurikulum terbuka ngoding.org. Belajar pemrograman secara gratis.

Kode sumber dan kurikulum terbuka ngoding.org ngoding.org merupakan sebuah komunitas sumber terbuka yang memiliki misi untuk menyediakan sumber belaja

ngoding.org 1 Nov 13, 2022
A JavaScript component that is a date & time range picker, no need to build, no dependencies except Moment.js, that is based on Dan Grossman's bootstrap-daterangepicker.

vanilla-datetimerange-picker Overview. A JavaScript component that is a date & time range picker, no need to build, no dependencies except Moment.js,

null 22 Dec 6, 2022
Silahkan di fork dan jangan lupa stars.nya ya bro...

Chat OWNER Chat BOT (Aktif) BERKAHESPORT.ID OFFICIAL Terimakasih Untuk ALLAH S.W.T. Serta junjungan kami nabi Muhammad S.A.W WhatsappBOT ini menggunak

null 9 May 9, 2023
Mungkin sebuah bahasa Pemrogramman berbasis pada Javascript yang menggunakan Bahasa Jawa sebagai sintax di dalamnya...

sajen Mungkin sebuah bahasa Pemrogramman berbasis pada Javascript yang menggunakan Bahasa Jawa sebagai sintax di dalamnya... sajen mempunyai extensi .

Macaksara 4 Mar 11, 2022
Jangan lupa Kasih credits && Star Bang Jika Ada yang eror Silahkan Chat Owner

oscar-md-v3 Project created by JAROT OFFC to make it public | © | Reserved | Heroku Buildpack BuildPack LINK FFMPEG here IMAGEMAGICK here HOW TO CONNE

JAROT OFC 9 Dec 30, 2022
Custom touch bar or digital macropad app for GNU/Linux 🐧 using an Android device 📱

?? Boar ??️ Custom touch bar or digital macropad app for GNU/Linux ?? using an Android device ?? Those apps allow creating a keyboard with custom keys

Fabián Velosa 0 Oct 29, 2022
My-portfolio - Built with Namecheap, Digital Ocean, Nginx, PM2, SSL, NextJs, Tailwind 3, Graphql, NexusJS, Prisma, Postgres, Passion and Love

Current Implementation technologies Nextjs with Typescript. Static pages/ Server side rendering. Easy peasy state management (Might not need it with i

Samrood Ali 1 Jan 10, 2022
Portfolio for Intro to Photography/Digital Media Production

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Benjamin Smith 4 May 26, 2022