Spaces:
Running
Running
{% extends 'base.html' %} | |
{% block head %} | |
<title>Indonesian Lyrics Classification By Age Group - Prediction</title> | |
{% endblock %} | |
{% block body %} | |
{% include 'navbar.html' %} | |
<main class="flex flex-row justify-center items-center min-h-screen"> | |
<div class="container max-w-xl w-full bg-white rounded-2xl text-center space-y-6"> | |
<div class="flex items-center justify-between p-4 bg-gray-100"> | |
<h5 class="text-md font-bold text-gray-900 md:text-md uppercase">Prediction History</h5> | |
</div> | |
<table id="export-table" class=""> | |
<thead> | |
<tr> | |
<th class="text-gray-900 text-center">Action</th> | |
<th> | |
<span class="flex items-center text-gray-900"> | |
Lyric | |
<svg class="w-4 h-4 ms-1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" | |
height="24" fill="none" viewBox="0 0 24 24"> | |
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" | |
stroke-width="2" d="m8 15 4 4 4-4m0-6-4-4-4 4" /> | |
</svg> | |
</span> | |
</th> | |
<th> | |
<span class="flex items-center text-gray-900"> | |
Predicted Age Group | |
<svg class="w-4 h-4 ms-1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" | |
height="24" fill="none" viewBox="0 0 24 24"> | |
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" | |
stroke-width="2" d="m8 15 4 4 4-4m0-6-4-4-4 4" /> | |
</svg> | |
</span> | |
</th> | |
<th> | |
<span class="flex items-center text-gray-900"> | |
Processing Time (s) | |
<svg class="w-4 h-4 ms-1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" | |
height="24" fill="none" viewBox="0 0 24 24"> | |
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" | |
stroke-width="2" d="m8 15 4 4 4-4m0-6-4-4-4 4" /> | |
</svg> | |
</span> | |
</th> | |
<th data-type="date" data-format="YYYY-MM-DD HH:mm:ss"> | |
<span class="flex items-center text-gray-900"> | |
Created Date | |
<svg class="w-4 h-4 ms-1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" | |
height="24" fill="none" viewBox="0 0 24 24"> | |
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" | |
stroke-width="2" d="m8 15 4 4 4-4m0-6-4-4-4 4" /> | |
</svg> | |
</span> | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
{% for item in data_history %} | |
<tr class="hover:bg-gray-50 cursor-pointer"> | |
<td class="text-center"> | |
<!-- Modal toggle --> | |
<button data-modal-target="select-modal-{{ item.id }}" data-modal-toggle="select-modal-{{ item.id }}" | |
class="text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-2 py-0.5 text-center" | |
type="button"> | |
Detail | |
</button> | |
<!-- Main modal --> | |
<div id="select-modal-{{ item.id }}" data-modal-backdrop="static" tabindex="-1" aria-hidden="true" | |
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full"> | |
<div class="relative w-full max-w-7xl max-h-full"> | |
<!-- Modal content --> | |
<div class="relative bg-white rounded-lg shadow-sm"> | |
<!-- Modal header --> | |
<div | |
class="flex items-center justify-between p-4 md:p-5 border-b rounded-t border-gray-200"> | |
<h3 class="text-lg font-semibold text-gray-900"> | |
{{ item.created_date.strftime('%A, %d %B %Y (%I:%M %p)') }} | |
</h3> | |
<button type="button" | |
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm h-8 w-8 ms-auto inline-flex justify-center items-center" | |
data-modal-toggle="select-modal-{{ item.id }}"> | |
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" | |
fill="none" viewBox="0 0 14 14"> | |
<path stroke="currentColor" stroke-linecap="round" | |
stroke-linejoin="round" stroke-width="2" | |
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" /> | |
</svg> | |
<span class="sr-only">Close modal</span> | |
</button> | |
</div> | |
<!-- Modal body --> | |
<div class="p-4 md:p-5"> | |
<!-- Lyrics --> | |
<div class="mb-3"> | |
<h1 class="text-lg font-semibold text-gray-800 sm:text-xl">🎵 Lyrics</h1> | |
<p class="text-center px-2 py-2 text-1xl sm:text-lg text-gray-800">{{ item.lyric }}</p> | |
</div> | |
<hr class="mb-3 border-t border-gray-200" /> | |
<!-- Predicted Age Group --> | |
<div class="mt-2 mb-3"> | |
<h2 class="text-lg font-semibold text-gray-800">Predicted Age Group</h2> | |
<span class="text-center text-lg font-extrabold text-slate-900 sm:text-2xl"> | |
{{ item.predicted_label.upper() }} | |
</span> | |
</div> | |
<hr class="border-t border-gray-200" /> | |
<!-- Class Probabilities --> | |
<div class="mt-2 mb-3 flex flex-col text-center items-center"> | |
<h3 class="text-lg font-semibold text-gray-800 mb-2">📊 Class Probabilities | |
</h3> | |
<div class="space-y-3"> | |
<div class="w-full"> | |
{% for label, prob in item.probabilities %} | |
<div | |
class="flex gap-20 justify-between items-center px-4 py-0.5 text-gray-800 {{ 'font-medium bg-green-500' if label == item.predicted_label else '' }}"> | |
<div | |
class="capitalize text-1xl sm:text-lg {{ 'font-medium' if label == item.predicted_label else '' }}"> | |
{{ label.capitalize() }} | |
</div> | |
<div | |
class="text-1xl sm:text-lg {{ 'font-medium' if label == item.predicted_label else '' }}"> | |
{{ prob }} | |
</div> | |
</div> | |
{% endfor %} | |
</div> | |
</div> | |
</div> | |
<hr class="border-t border-gray-200" /> | |
<!-- Processing Time --> | |
{% if item.processing_time %} | |
<div class="text-sm mt-2 mb-3 text-gray-500"> | |
⏱️ Total Processing Time: <strong>{{ "%.2f"|format(item.processing_time) }} | |
seconds</strong> | |
</div> | |
{% endif %} | |
</div> | |
<!-- Modal footer --> | |
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b"> | |
<button data-modal-hide="select-modal-{{ item.id }}" type="button" | |
class="inline-flex w-full justify-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center">Close</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</td> | |
<td class="font-medium text-gray-900 whitespace-nowrap">{{ item.lyric | truncate(60) }}</td> | |
<td class="text-gray-900">{{ item.predicted_label | capitalize }}</td> | |
<td class="text-right !pr-[5rem] text-gray-900">{{ "%.2f"|format(item.processing_time) }} seconds | |
</td> | |
<td class="text-gray-900">{{ item.created_date.strftime('%Y-%m-%d %H:%M:%S') }}</td> | |
</tr> | |
{% endfor %} | |
</tbody> | |
</table> | |
</div> | |
</main> | |
<footer class="flex flex-row justify-center items-center"> | |
<div class="mb-3 text-gray-500">By Michael Natanael</div> | |
</footer> | |
{% endblock %} | |
{% block script %} | |
<script> | |
if (document.getElementById("export-table") && typeof simpleDatatables.DataTable !== 'undefined') { | |
const exportCustomCSV = function (dataTable, userOptions = {}) { | |
// A modified CSV export that includes a row of minuses at the start and end. | |
const clonedUserOptions = { | |
...userOptions | |
} | |
clonedUserOptions.download = false | |
const csv = simpleDatatables.exportCSV(dataTable, clonedUserOptions) | |
// If CSV didn't work, exit. | |
if (!csv) { | |
return false | |
} | |
const defaults = { | |
download: true, | |
lineDelimiter: "\n", | |
columnDelimiter: ";" | |
} | |
const options = { | |
...defaults, | |
...clonedUserOptions | |
} | |
const separatorRow = Array(dataTable.data.headings.filter((_heading, index) => !dataTable.columns.settings[index]?.hidden).length) | |
.fill("+") | |
.join("+"); // Use "+" as the delimiter | |
const str = separatorRow + options.lineDelimiter + csv + options.lineDelimiter + separatorRow; | |
if (userOptions.download) { | |
// Create a link to trigger the download | |
const link = document.createElement("a"); | |
link.href = encodeURI("data:text/csv;charset=utf-8," + str); | |
link.download = (options.filename || "Prediction History") + ".txt"; | |
// Append the link | |
document.body.appendChild(link); | |
// Trigger the download | |
link.click(); | |
// Remove the link | |
document.body.removeChild(link); | |
} | |
return str | |
} | |
const table = new simpleDatatables.DataTable("#export-table", { | |
template: (options, dom) => "<div class='" + options.classes.top + "'>" + | |
"<div class='flex flex-col sm:flex-row sm:items-center space-y-4 sm:space-y-0 sm:space-x-3 rtl:space-x-reverse w-full sm:w-auto'>" + | |
(options.paging && options.perPageSelect ? | |
"<div class='" + options.classes.dropdown + "'>" + | |
"<label>" + | |
"<select class='" + options.classes.selector + "'></select> " + options.labels.perPage + | |
"</label>" + | |
"</div>" : "" | |
) + "<button id='exportDropdownButton' type='button' class='flex w-full items-center justify-center rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 sm:w-auto'>" + | |
"Export as" + | |
"<svg class='-me-0.5 ms-1.5 h-4 w-4' aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'>" + | |
"<path stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m19 9-7 7-7-7' />" + | |
"</svg>" + | |
"</button>" + | |
"<div id='exportDropdown' class='z-10 hidden w-52 divide-y divide-gray-100 rounded-lg bg-white shadow-sm data-popper-placement='bottom'>" + | |
"<ul class='p-2 text-left text-sm font-medium text-gray-500 aria-labelledby='exportDropdownButton'>" + | |
"<li>" + | |
"<button id='export-csv' class='group inline-flex w-full items-center rounded-md px-3 py-2 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-900'>" + | |
"<svg class='me-1.5 h-4 w-4 text-gray-400 group-hover:text-gray-900 aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' viewBox='0 0 24 24'>" + | |
"<path fill-rule='evenodd' d='M9 2.221V7H4.221a2 2 0 0 1 .365-.5L8.5 2.586A2 2 0 0 1 9 2.22ZM11 2v5a2 2 0 0 1-2 2H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2 2 2 0 0 0 2 2h12a2 2 0 0 0 2-2 2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2V4a2 2 0 0 0-2-2h-7Zm1.018 8.828a2.34 2.34 0 0 0-2.373 2.13v.008a2.32 2.32 0 0 0 2.06 2.497l.535.059a.993.993 0 0 0 .136.006.272.272 0 0 1 .263.367l-.008.02a.377.377 0 0 1-.018.044.49.49 0 0 1-.078.02 1.689 1.689 0 0 1-.297.021h-1.13a1 1 0 1 0 0 2h1.13c.417 0 .892-.05 1.324-.279.47-.248.78-.648.953-1.134a2.272 2.272 0 0 0-2.115-3.06l-.478-.052a.32.32 0 0 1-.285-.341.34.34 0 0 1 .344-.306l.94.02a1 1 0 1 0 .043-2l-.943-.02h-.003Zm7.933 1.482a1 1 0 1 0-1.902-.62l-.57 1.747-.522-1.726a1 1 0 0 0-1.914.578l1.443 4.773a1 1 0 0 0 1.908.021l1.557-4.773Zm-13.762.88a.647.647 0 0 1 .458-.19h1.018a1 1 0 1 0 0-2H6.647A2.647 2.647 0 0 0 4 13.647v1.706A2.647 2.647 0 0 0 6.647 18h1.018a1 1 0 1 0 0-2H6.647A.647.647 0 0 1 6 15.353v-1.706c0-.172.068-.336.19-.457Z' clip-rule='evenodd'/>" + | |
"</svg>" + | |
"<span>Export CSV</span>" + | |
"</button>" + | |
"</li>" + | |
"<li>" + | |
"<button id='export-json' class='group inline-flex w-full items-center rounded-md px-3 py-2 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-900'>" + | |
"<svg class='me-1.5 h-4 w-4 text-gray-400 group-hover:text-gray-900 aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' viewBox='0 0 24 24'>" + | |
"<path fill-rule='evenodd' d='M9 2.221V7H4.221a2 2 0 0 1 .365-.5L8.5 2.586A2 2 0 0 1 9 2.22ZM11 2v5a2 2 0 0 1-2 2H4v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-7Zm-.293 9.293a1 1 0 0 1 0 1.414L9.414 14l1.293 1.293a1 1 0 0 1-1.414 1.414l-2-2a1 1 0 0 1 0-1.414l2-2a1 1 0 0 1 1.414 0Zm2.586 1.414a1 1 0 0 1 1.414-1.414l2 2a1 1 0 0 1 0 1.414l-2 2a1 1 0 0 1-1.414-1.414L14.586 14l-1.293-1.293Z' clip-rule='evenodd'/>" + | |
"</svg>" + | |
"<span>Export JSON</span>" + | |
"</button>" + | |
"</li>" + | |
"<li>" + | |
"<button id='export-txt' class='group inline-flex w-full items-center rounded-md px-3 py-2 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-900'>" + | |
"<svg class='me-1.5 h-4 w-4 text-gray-400 group-hover:text-gray-900 aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' viewBox='0 0 24 24'>" + | |
"<path fill-rule='evenodd' d='M9 2.221V7H4.221a2 2 0 0 1 .365-.5L8.5 2.586A2 2 0 0 1 9 2.22ZM11 2v5a2 2 0 0 1-2 2H4v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-7ZM8 16a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2H9a1 1 0 0 1-1-1Zm1-5a1 1 0 1 0 0 2h6a1 1 0 1 0 0-2H9Z' clip-rule='evenodd'/>" + | |
"</svg>" + | |
"<span>Export TXT</span>" + | |
"</button>" + | |
"</li>" + | |
"<li>" + | |
"<button id='export-sql' class='group inline-flex w-full items-center rounded-md px-3 py-2 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-900'>" + | |
"<svg class='me-1.5 h-4 w-4 text-gray-400 group-hover:text-gray-900 aria-hidden='true' xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' viewBox='0 0 24 24'>" + | |
"<path d='M12 7.205c4.418 0 8-1.165 8-2.602C20 3.165 16.418 2 12 2S4 3.165 4 4.603c0 1.437 3.582 2.602 8 2.602ZM12 22c4.963 0 8-1.686 8-2.603v-4.404c-.052.032-.112.06-.165.09a7.75 7.75 0 0 1-.745.387c-.193.088-.394.173-.6.253-.063.024-.124.05-.189.073a18.934 18.934 0 0 1-6.3.998c-2.135.027-4.26-.31-6.3-.998-.065-.024-.126-.05-.189-.073a10.143 10.143 0 0 1-.852-.373 7.75 7.75 0 0 1-.493-.267c-.053-.03-.113-.058-.165-.09v4.404C4 20.315 7.037 22 12 22Zm7.09-13.928a9.91 9.91 0 0 1-.6.253c-.063.025-.124.05-.189.074a18.935 18.935 0 0 1-6.3.998c-2.135.027-4.26-.31-6.3-.998-.065-.024-.126-.05-.189-.074a10.163 10.163 0 0 1-.852-.372 7.816 7.816 0 0 1-.493-.268c-.055-.03-.115-.058-.167-.09V12c0 .917 3.037 2.603 8 2.603s8-1.686 8-2.603V7.596c-.052.031-.112.059-.165.09a7.816 7.816 0 0 1-.745.386Z'/>" + | |
"</svg>" + | |
"<span>Export SQL</span>" + | |
"</button>" + | |
"</li>" + | |
"</ul>" + | |
"</div>" + "</div>" + | |
(options.searchable ? | |
"<div class='" + options.classes.search + "'>" + | |
"<input class='" + options.classes.input + "' placeholder='" + options.labels.placeholder + "' type='search' title='" + options.labels.searchTitle + "'" + (dom.id ? " aria-controls='" + dom.id + "'" : "") + ">" + | |
"</div>" : "" | |
) + | |
"</div>" + | |
"<div class='" + options.classes.container + "'" + (options.scrollY.length ? " style='height: " + options.scrollY + "; overflow-Y: auto;'" : "") + "></div>" + | |
"<div class='" + options.classes.bottom + "'>" + | |
(options.paging ? | |
"<div class='" + options.classes.info + "'></div>" : "" | |
) + | |
"<nav class='" + options.classes.pagination + "'></nav>" + | |
"</div>" | |
}) | |
const $exportButton = document.getElementById("exportDropdownButton"); | |
const $exportDropdownEl = document.getElementById("exportDropdown"); | |
const dropdown = new Dropdown($exportDropdownEl, $exportButton); | |
console.log(dropdown) | |
document.getElementById("export-csv").addEventListener("click", () => { | |
simpleDatatables.exportCSV(table, { | |
download: true, | |
lineDelimiter: "\n", | |
columnDelimiter: ";" | |
}) | |
}) | |
document.getElementById("export-sql").addEventListener("click", () => { | |
simpleDatatables.exportSQL(table, { | |
download: true, | |
tableName: "export_table" | |
}) | |
}) | |
document.getElementById("export-txt").addEventListener("click", () => { | |
simpleDatatables.exportTXT(table, { | |
download: true | |
}) | |
}) | |
document.getElementById("export-json").addEventListener("click", () => { | |
simpleDatatables.exportJSON(table, { | |
download: true, | |
space: 3 | |
}) | |
}) | |
} | |
</script> | |
{% endblock %} |