WIP: Statistik mata

This commit is contained in:
Mahayoga 2026-05-19 22:36:49 +07:00
parent 9a895c57ba
commit 01258eb92d
2 changed files with 169 additions and 9 deletions

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Models\BookingModel;
use App\Models\DataSoalModel;
use App\Models\DetailTestModel;
use App\Models\TestSessionsModel;
@ -73,6 +74,49 @@ public function index(Request $request)
$data_session = TestSessionsModel::where('id_test_sessions', $id_session)->first();
if($data_session != null) {
$start = strtotime($data_session->created_at);
$end = strtotime($data_session->updated_at);
$difference = gmdate("i:s", $end - $start);
$data_booking = BookingModel::where('id_booking', $data_session->id_booking)->first();
$data_information = [
'nama' => $data_booking->nama_lengkap,
'email' => $data_booking->email,
'no_hp' => $data_booking->no_telp,
];
$screen_dim = [
'width' => $data_session->screen_w,
'height' => $data_session->screen_h
];
$answered = DetailTestModel::where('id_test_sessions', $id_session)->where('jawaban', '!=', null)->count();
$question_count = DataSoalModel::all()->count();
$soalTerjawab = $answered;
$soalTidakTerjawab = $question_count - $answered;
$totalScore = 0;
$detail_test = DetailTestModel::where('id_test_sessions', $request->id_test_sessions)->get();
foreach($detail_test as $detail) {
$data_soal = DataSoalModel::where('id_soal', $detail->id_soal)->first();
$data_jawaban = $detail->jawaban;
if($data_jawaban == 'a') {
$totalScore += 1;
} else if($data_jawaban == 'b') {
$totalScore += 2;
} else if($data_jawaban == 'c') {
$totalScore += 3;
} else if($data_jawaban == 'd') {
$totalScore += 4;
} else if($data_jawaban == 'e') {
$totalScore += 5;
}
}
$data_aswered = [
'answered' => $answered,
'unanswered' => $soalTidakTerjawab,
'totalScore' => $totalScore
];
if($data_session->status_session == '0') {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes dinonaktifkan. Silahkan cek status dengan kode untuk informasi lebih lanjut.');
} else if($data_session->status_session == '1') {
@ -80,9 +124,9 @@ public function index(Request $request)
'status_session' => '2',
'is_active' => '0'
]);
return view('user.pages.finish_ujian.index', compact('id_session'));
return view('user.pages.finish_ujian.index', compact('id_session', 'difference', 'data_information', 'screen_dim', 'data_aswered'));
} else if($data_session->status_session == '2') {
return view('user.pages.finish_ujian.index', compact('id_session'));
return view('user.pages.finish_ujian.index', compact('id_session', 'difference', 'data_information', 'screen_dim', 'data_aswered'));
}
} else {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes tidak ditemukan.');

View File

@ -1,6 +1,23 @@
@section('title', 'Hasil Tes')
@extends('user.layouts.index')
@section('content')
<style>
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.stat-card {
padding: 20px;
border-radius: 12px;
background: #f5f5f5;
}
.bg-custom {
background: #f5f5f5;
}
</style>
<!-- Page Title -->
<div class="page-title">
<div class="heading">
@ -27,6 +44,68 @@
<!-- About Section -->
<section id="about" class="about section">
<div class="container">
<div class="row">
<div class="col-md-12">
<h2 class="title text-center">Detail Tes/Ujian kamu</h2>
<div class="row">
<div class="col-md-4 col-6 rounded p-3">
<div class="container border border-primary rounded h-100 p-2 bg-custom">
<h3>Informasi Pribadi</h3>
<ul class="list-unstyled">
<li><strong>Nama:</strong> {{ $data_information['nama'] }}</li>
<li><strong>Email:</strong> {{ $data_information['email'] }}</li>
<li><strong>Nomor Handphone:</strong> {{ $data_information['no_hp'] }}</li>
</ul>
</div>
</div>
<div class="col-md-4 col-6 rounded p-3">
<div class="container border border-primary rounded h-100 p-2 bg-custom">
<h3>Informasi Ujian</h3>
<ul class="list-unstyled">
<li><strong>Durasi Ujian:</strong> {{ $difference }}</li>
<li><strong>Resolusi Layar (saat ujian):</strong> {{ $screen_dim['width'] }} x {{ $screen_dim['height'] }}</li>
<li><strong>Jumlah Pergerakan Mata:</strong> <span id="total_movement">0</span></li>
</ul>
</div>
</div>
<div class="col-md-4 col-6 primary p-3">
<div class="container border border-success rounded h-100 p-2 bg-custom">
<h3>Hasil Ujian (STAI-T)</h3>
<ul class="list-unstyled">
<li><strong>Total Skor:</strong> {{ $data_aswered['totalScore'] }}</li>
<li><strong>Soal terjawab:</strong> {{ $data_aswered['answered'] }}</li>
<li><strong>Soal tidak terjawab:</strong> {{ $data_aswered['unanswered'] }}</li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="stats-grid">
<div class="stat-card">
<h3>Avg Velocity</h3>
<p id="avgVelocity"></p>
</div>
<div class="stat-card">
<h3>Fixation Ratio</h3>
<p id="fixationRatio"></p>
</div>
<div class="stat-card">
<h3>Total Eye Distance</h3>
<p id="totalDistance"></p>
</div>
<div class="stat-card">
<h3>Most Viewed Area</h3>
<p id="mostViewedArea"></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container" data-aos="fade-up" data-aos-delay="100">
<table class="table" id="table-data-hasil-ujian">
<thead>
@ -34,6 +113,7 @@
<th>No</th>
<th>Pertanyaan</th>
<th>Jawaban</th>
<th>Skor</th>
</tr>
</thead>
<tbody></tbody>
@ -85,6 +165,9 @@
let scaleX = null;
let scaleY = null;
let heatmapInstance = null;
let score = null;
let totalScore = null;
let soalTerjawab = 0;
function renderVelocityChart(timeseries) {
const base = timeseries.timestamp[0];
@ -144,9 +227,25 @@ function renderVelocityChart(timeseries) {
data: mappedHeatmapData
});
heatmapContainer.style.removeProperty('position');
renderStats(result.statistics);
Swal.close();
}
}
function renderStats(stats) {
document.getElementById("avgVelocity")
.innerText = stats.avg_velocity.toFixed(4);
document.getElementById("fixationRatio")
.innerText = stats.fixation_ratio.toFixed(2) + "%";
document.getElementById("totalDistance")
.innerText = stats.total_distance.toFixed(2);
document.getElementById("mostViewedArea")
.innerText = stats.most_viewed_area;
}
$(document).ready(function() {
table = new DataTable("#table-data-hasil-ujian", {
searching: false,
@ -171,19 +270,30 @@ function renderVelocityChart(timeseries) {
let renderedJawaban = null;
if(row.jawaban_key == 'a') {
renderedJawaban = `<span class="badge text-bg-primary">${row.jawaban}</span>`;
score = 1;
} else if(row.jawaban_key == 'b') {
renderedJawaban = `<span class="badge" style="background-color: #fc7938; color: white;">${row.jawaban}</span>`;
score = 2;
} else if(row.jawaban_key == 'c') {
renderedJawaban = `<span class="badge text-bg-danger">${row.jawaban}</span>`;
score = 3;
} else if(row.jawaban_key == 'd') {
renderedJawaban = `<span class="badge text-bg-warning">${row.jawaban}</span>`;
score = 4;
} else if(row.jawaban_key == 'e') {
renderedJawaban = `<span class="badge text-bg-success">${row.jawaban}</span>`;
score = 5;
} else {
renderedJawaban = `<span class="badge text-bg-secondary">${row.jawaban}</span>`;
}
return renderedJawaban;
}
},
{
data: null,
render: function(data, type, row) {
return `<span>${score}</span>`;
}
}
]
});
@ -208,28 +318,34 @@ function renderVelocityChart(timeseries) {
const data = JSON.parse(event.data);
if(data.type === 'json_file_data') {
if(data.data != null) {
data_tobii = data.data;
data_tobii = null;
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
let data = JSON.parse(this.responseText);
if(data.status) {
let data_ = JSON.parse(this.responseText);
$('#total_movement').html(data.data.length + " (1 detik = 3 pergerakan)");
if(data_.status) {
heatmapContainer = document.getElementById('heatmapChart');
clientWidth = heatmapContainer.clientWidth;
clientHeight = heatmapContainer.clientHeight;
recordedScreenWidth = data.screen_w;
recordedScreenHeight = data.screen_h;
recordedScreenWidth = data_.screen_w;
recordedScreenHeight = data_.screen_h;
scaleX = clientWidth / recordedScreenWidth;
scaleY = clientHeight / recordedScreenHeight;
data_tobii = {
data: data.data,
screen_w: recordedScreenWidth,
screen_h: recordedScreenHeight
};
heatmapInstance = h337.create({
container: heatmapContainer,
radius: 40,
maxOpacity: 0.6,
blur: 0.85
});
Swal.close();
loadData(data_tobii);
} else {
Swal.fire({
@ -244,7 +360,7 @@ function renderVelocityChart(timeseries) {
}
};
xhttp.open('GET', '{{ route('test-sessions.getScreen', ['id_test_sessions', $id_session]) }}', true);
xhttp.open('GET', '{{ route('test-sessions.getScreen', ['id_test_sessions' => $id_session]) }}', true);
xhttp.send();
}
}