/* Thư Đạo · cham-thi-tot-nghiep · nguồn: blogspot/pages/cham-thi-tot-nghiep.html */
(function(I,C,H,J){var d=document,w=window,r=d.getElementById(I);if(!r||r.getAttribute("data-on"))return;r.setAttribute("data-on","1");if(C){var st=d.createElement("style");st.textContent=C;d.head.appendChild(st);}r.insertAdjacentHTML("beforebegin",H);r.parentNode.removeChild(r);var late=d.readyState!=="loading";function fire(t,f,o){var e=new Event(t);setTimeout(function(){typeof f==="function"?f.call(o,e):f&&f.handleEvent&&f.handleEvent(e);});}if(late){d.addEventListener=function(t,f){if(t==="DOMContentLoaded")return fire(t,f,d);return EventTarget.prototype.addEventListener.apply(this,arguments);};w.addEventListener=function(t,f){if(t==="DOMContentLoaded"||(t==="load"&&d.readyState==="complete"))return fire(t,f,w);return EventTarget.prototype.addEventListener.apply(this,arguments);};}try{for(var i=0;i<J.length;i++){var s=d.createElement("script");s.textContent=J[i];d.body.appendChild(s);}}finally{if(late){delete d.addEventListener;delete w.addEventListener;}}if(w.__tdpFriendly)w.__tdpFriendly(I);})("tdp-cham-thi-tot-nghiep","#graduationGradingPage{--paper:#fffdf8;--paper2:#f7f1e7;--ink:#302923;--muted:#786f66;--line:#e3d7c7;--brown:#765344;--brown-dark:#51372d;--gold:#aa7a35;--green:#397153;--green-soft:#eaf5ee;--red:#963b3b;--red-soft:#faeaea;--orange:#a66d25;--orange-soft:#fff1d7;--blue:#406f8a;--blue-soft:#edf5f9;max-width:1180px;margin:24px auto 60px;padding:0 12px;font-family:Arial,Helvetica,sans-serif;color:var(--ink);line-height:1.6}#graduationGradingPage *{box-sizing:border-box}.gg-login-wrap{max-width:520px;margin:60px auto;padding:0 8px}.gg-login-card{padding:28px;border:1px solid #dec792;border-radius:20px;background:radial-gradient(circle at 10% 10%,rgba(181,135,69,.13),transparent 32%),linear-gradient(135deg,#fff8ea,#fffdf9);box-shadow:0 14px 40px #37281c1a}.gg-login-icon{display:flex;width:62px;height:62px;margin:0 auto 13px;align-items:center;justify-content:center;border-radius:50%;background:#f3e7d4;color:var(--brown-dark);font-size:26px;font-weight:900}.gg-login-title{margin:0;font-family:Georgia,serif;font-size:27px;color:var(--brown-dark);text-align:center}.gg-login-desc{max-width:390px;margin:8px auto 18px;color:var(--muted);font-size:13px;text-align:center}.gg-login-row{display:flex;gap:9px}.gg-login-input{width:100%;min-height:49px;padding:10px 14px;border:1px solid #d8c8b5;border-radius:11px;background:#fff;color:var(--ink);font-size:18px;font-weight:900;letter-spacing:.14em;text-align:center;outline:none}.gg-login-input:focus{border-color:#ad824d;box-shadow:0 0 0 3px #ad824d1f}.gg-login-message{display:none;margin-top:12px;padding:10px 12px;border-radius:9px;background:var(--red-soft);color:#873838;font-size:13px;font-weight:800;text-align:center}.gg-login-message.show{display:block}.gg-hero{position:relative;padding:36px 24px;border:1px solid #dec792;border-radius:22px;background:radial-gradient(circle at 10% 10%,rgba(181,135,69,.17),transparent 30%),radial-gradient(circle at 90% 90%,rgba(118,83,68,.1),transparent 34%),linear-gradient(135deg,#fff7e8,#fffdf8);text-align:center;box-shadow:0 12px 35px #37281c14}.gg-hero-badge{display:inline-flex;padding:6px 13px;margin-bottom:10px;border:1px solid #dec58f;border-radius:999px;background:#fff8e6;color:#7c592a;font-size:12px;font-weight:900}.gg-hero h1{margin:0;font-family:Georgia,serif;font-size:34px;color:var(--brown-dark)}.gg-hero p{max-width:760px;margin:10px auto 0;color:var(--muted)}.gg-logout{position:absolute;top:15px;right:15px}.gg-card{margin-top:18px;padding:22px;border:1px solid var(--line);border-radius:18px;background:#fff;box-shadow:0 8px 25px #37281c0e}.gg-title{margin:0 0 14px;font-family:Georgia,serif;font-size:21px;color:var(--brown-dark)}.gg-filter-grid{display:grid;grid-template-columns:minmax(0,2fr) minmax(180px,.8fr);gap:10px}.gg-filter-label{display:block;margin-bottom:5px;color:var(--brown-dark);font-size:11px;font-weight:900}.gg-search-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:11px}.gg-filter-help{margin-top:9px;color:var(--muted);font-size:12px}.gg-input,.gg-textarea{width:100%;padding:11px 13px;border:1px solid #d9cbb8;border-radius:11px;background:#fff;color:var(--ink);font-size:14px;outline:none}.gg-input{min-height:47px}.gg-textarea{min-height:92px;resize:vertical}.gg-input:focus,.gg-textarea:focus{border-color:#ad824d;box-shadow:0 0 0 3px #ad824d1f}.gg-btn{min-height:44px;padding:9px 16px;border:0;border-radius:10px;background:var(--brown);color:#fff;font-size:13px;font-weight:900;cursor:pointer!important;pointer-events:auto!important}.gg-btn.green{background:var(--green)}.gg-btn.gold{background:var(--gold)}.gg-btn.red{background:var(--red)}.gg-btn.blue{background:var(--blue)}.gg-btn.secondary{border:1px solid #d7c8b6;background:#fff;color:var(--brown-dark)}.gg-btn.small{min-height:36px;padding:7px 11px;font-size:11px}.gg-btn:disabled{opacity:.45;cursor:not-allowed!important}.gg-message{display:none;margin-top:12px;padding:11px 13px;border-radius:10px;font-size:13px;font-weight:700}.gg-message.show{display:block}.gg-message.loading{background:#f4eee5;color:#765d43}.gg-message.success{background:var(--green-soft);color:#326646}.gg-message.error{background:var(--red-soft);color:#873838}.gg-filter-summary{display:none;margin-top:12px;padding:10px 12px;border:1px solid #e1d4c3;border-radius:10px;background:#fffaf2;color:#705b48;font-size:12px}.gg-filter-summary.show{display:block}.gg-exam-list{display:grid;gap:10px;margin-top:14px}.gg-exam-item{display:grid;grid-template-columns:1fr auto;align-items:center;gap:12px;padding:13px;border:1px solid var(--line);border-radius:12px;background:var(--paper)}.gg-exam-name{font-weight:900;color:var(--brown-dark)}.gg-exam-code{margin-left:5px;color:#8b704f;font-size:11px;font-weight:800}.gg-exam-meta{margin-top:4px;color:var(--muted);font-size:12px}.gg-exam-date{display:inline-block;margin-top:5px;padding:3px 7px;border-radius:999px;background:#f2e9dc;color:#73583c;font-size:10px;font-weight:900}.gg-info-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.gg-info{padding:12px;border:1px solid var(--line);border-radius:12px;background:var(--paper)}.gg-info-label{color:var(--muted);font-size:10px;font-weight:900;text-transform:uppercase}.gg-info-value{margin-top:4px;color:var(--brown-dark);font-size:15px;font-weight:900;overflow-wrap:anywhere}.gg-score-summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-top:15px}.gg-score-box{padding:13px;border:1px solid var(--line);border-radius:12px;background:#fffaf2;text-align:center}.gg-score-value{color:var(--brown-dark);font-size:24px;font-weight:900}.gg-score-label{color:var(--muted);font-size:11px}.gg-practical-nav{margin-top:18px;padding:13px;border:1px solid #e2d5c5;border-radius:13px;background:#fffaf2}.gg-nav-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.gg-nav-title{color:var(--brown-dark);font-size:13px;font-weight:900}.gg-nav-status{color:var(--muted);font-size:12px;font-weight:800}.gg-qgrid{display:grid;grid-template-columns:repeat(5,1fr);gap:7px;margin-top:10px}.gg-qbtn{min-height:40px;border:1px solid #ddd1c2;border-radius:9px;background:#fff;color:#684e3c;font-weight:900;cursor:pointer}.gg-qbtn.graded{border-color:#afd0ba;background:#e8f3eb;color:#356647}.gg-qbtn.current{border-color:var(--brown);background:var(--brown);color:#fff}.gg-practical-wrap{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(300px,.85fr);gap:18px;margin-top:18px}.gg-question-number{color:#9c7137;font-size:12px;font-weight:900}.gg-question-title{margin:4px 0 13px;font-family:Georgia,serif;font-size:22px;color:var(--brown-dark)}.gg-image-wrap{overflow:hidden;width:100%;min-height:280px;border:1px solid var(--line);border-radius:14px;background:#f2ede7}.gg-image{display:block;width:100%;max-height:680px;object-fit:contain;background:#f2ede7}.gg-no-image{display:flex;min-height:280px;align-items:center;justify-content:center;padding:30px;color:var(--muted);text-align:center}.gg-submission-meta{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;margin-top:12px}.gg-meta{padding:10px;border-radius:10px;background:var(--paper2)}.gg-meta-label{color:var(--muted);font-size:10px;font-weight:900;text-transform:uppercase}.gg-meta-value{margin-top:3px;color:var(--brown-dark);font-size:13px;font-weight:800;overflow-wrap:anywhere}.gg-grading-panel{padding:16px;border:1px solid var(--line);border-radius:14px;background:#fffdf8}.gg-old-score{padding:13px;border-radius:11px;background:#f5eddf;color:#755724}.gg-old-score-number{font-size:26px;font-weight:900}.gg-field{margin-top:13px}.gg-field-label{display:block;margin-bottom:5px;color:var(--brown-dark);font-size:12px;font-weight:900}.gg-grade-input{width:100%;min-height:48px;padding:10px 13px;border:1px solid #d6c7b5;border-radius:11px;background:#fff;font-size:19px;font-weight:900;text-align:center;outline:none}.gg-grade-actions{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px}.gg-saved{margin-top:12px;padding:9px 11px;border-radius:9px;background:var(--green-soft);color:#326646;font-size:12px;font-weight:900}.gg-final{margin-top:18px;padding:17px;border:1px solid #d7c291;border-radius:14px;background:linear-gradient(135deg,#fff8e9,#fffdf9)}.gg-final-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.gg-final-box{padding:13px;border:1px solid #e2d4bb;border-radius:11px;background:#ffffffc7;text-align:center}.gg-final-number{color:var(--brown-dark);font-size:26px;font-weight:900}.gg-final-label{color:var(--muted);font-size:11px}.gg-final-submit{margin-top:16px;padding-top:16px;border-top:1px dashed var(--line)}.gg-locked-banner{margin-top:14px;padding:14px;border-radius:11px;background:#f3eee7;color:#625348;font-weight:800}.gg-remote{margin-top:14px;padding:14px;border-radius:11px;font-size:13px}.gg-remote.sent{background:var(--green-soft);color:#316446}.gg-remote.sending{background:var(--blue-soft);color:#345e75}.gg-remote.pending{background:var(--orange-soft);color:#76511c}.gg-remote.error{background:var(--red-soft);color:#873838}.gg-remote-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:11px}.gg-hidden{display:none!important}@media(max-width:800px){.gg-filter-grid{grid-template-columns:1fr}.gg-info-grid,.gg-score-summary{grid-template-columns:repeat(2,minmax(0,1fr))}.gg-practical-wrap,.gg-final-grid{grid-template-columns:1fr}.gg-logout{position:static;margin-top:14px}}@media(max-width:480px){#graduationGradingPage{padding-left:8px;padding-right:8px}.gg-login-wrap{margin-top:28px}.gg-login-card{padding:20px 15px}.gg-login-row{display:grid}.gg-hero{padding:26px 15px}.gg-hero h1{font-size:26px}.gg-card{padding:15px}.gg-info-grid{grid-template-columns:1fr}.gg-remote-actions,.gg-search-actions{display:grid}.gg-exam-item{grid-template-columns:1fr}}.tdp-page{-webkit-text-size-adjust:100%;text-size-adjust:100%}.tdp-page img,.tdp-page video,.tdp-page canvas{max-width:100%}.tdp-page button,.tdp-page [role=button],.tdp-page a,.tdp-page input,.tdp-page select,.tdp-page textarea,.tdp-page label{touch-action:manipulation;-webkit-tap-highlight-color:rgba(176,107,44,.18)}.tdp-page button:not([hidden]),.tdp-page [role=button],.tdp-page .nut,.tdp-page a[class*=btn],.tdp-page a[class*=button],.tdp-page a[class*=cta],.tdp-page a[class*=Cta]{min-height:44px}.tdp-page input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=range]):not([type=file]),.tdp-page select,.tdp-page textarea{min-height:44px;font-size:max(16px,1em)}.tdp-page input[type=checkbox],.tdp-page input[type=radio]{width:22px;height:22px;min-height:0}.tdp-page button:active,.tdp-page [role=button]:active,.tdp-page .nut:active{transform:scale(.97);filter:brightness(.92)}.tdp-page :is(button,a,input,select,textarea,[role=button],[tabindex]):focus-visible{outline:3px solid #b06b2c;outline-offset:2px}@media(prefers-reduced-motion:reduce){.tdp-page *{animation-duration:.01ms!important;transition-duration:.01ms!important}}","<div class=\"tdp-page\"><div id=\"graduationGradingPage\">\r\n<section\r\nclass=\"gg-login-wrap\"\r\nid=\"ggLoginPanel\"\r\n>\r\n<div class=\"gg-login-card\">\r\n<div class=\"gg-login-icon\">\r\n🔒\r\n</div>\r\n<h1 class=\"gg-login-title\">\r\nKhu vực chấm thi\r\n</h1>\r\n<div class=\"gg-login-desc\">\r\nĐây là khu vực dành cho giáo viên.\r\nVui lòng nhập mã truy cập để tiếp tục.\r\n</div>\r\n<div class=\"gg-login-row\">\r\n<input\r\nclass=\"gg-login-input\"\r\nid=\"ggPasscode\"\r\ntype=\"password\"\r\ninputmode=\"numeric\"\r\nautocomplete=\"off\"\r\nplaceholder=\"••••••\"\r\nmaxlength=\"6\"\r\nonkeydown=\"\r\nif(event.key==='Enter'){\r\nevent.preventDefault();\r\nreturn window.OCDGraduationGrading.login();\r\n}\r\n\"\r\n>\r\n<button\r\nclass=\"gg-btn gold\"\r\ntype=\"button\"\r\nonclick=\"return window.OCDGraduationGrading.login();\"\r\n>\r\nĐĂNG NHẬP\r\n</button>\r\n</div>\r\n<div\r\nclass=\"gg-login-message\"\r\nid=\"ggLoginMessage\"\r\n></div>\r\n</div>\r\n</section>\r\n<div\r\nclass=\"gg-hidden\"\r\nid=\"ggProtectedApp\"\r\n>\r\n<section class=\"gg-hero\">\r\n<div class=\"gg-hero-badge\">\r\nHỆ THỐNG GVCN\r\n</div>\r\n<h1>\r\nChấm thi tốt nghiệp\r\n</h1>\r\n<p>\r\nTra cứu, lọc bài thi, chấm phần thực hành\r\nvà gửi kết quả chính thức vào hệ thống tốt nghiệp.\r\n</p>\r\n<button\r\nclass=\"gg-btn secondary small gg-logout\"\r\ntype=\"button\"\r\nonclick=\"return window.OCDGraduationGrading.logout();\"\r\n>\r\nĐĂNG XUẤT\r\n</button>\r\n</section>\r\n<section class=\"gg-card\">\r\n<h2 class=\"gg-title\">\r\nTra cứu & lọc bài thi\r\n</h2>\r\n<div class=\"gg-filter-grid\">\r\n<div>\r\n<label\r\nclass=\"gg-filter-label\"\r\nfor=\"ggLookupInput\"\r\n>\r\nEXAM ID / MÃ HỌC VIÊN / HỌ TÊN\r\n</label>\r\n<input\r\nclass=\"gg-input\"\r\nid=\"ggLookupInput\"\r\ntype=\"text\"\r\nautocomplete=\"off\"\r\nplaceholder=\"Ví dụ: OCD-EXAM..., D401, Nguyễn Văn A\"\r\nonkeydown=\"\r\nif(event.key==='Enter'){\r\nevent.preventDefault();\r\nreturn window.OCDGraduationGrading.lookup();\r\n}\r\n\"\r\n>\r\n</div>\r\n<div>\r\n<label\r\nclass=\"gg-filter-label\"\r\nfor=\"ggDateFilter\"\r\n>\r\nNGÀY THI\r\n</label>\r\n<input\r\nclass=\"gg-input\"\r\nid=\"ggDateFilter\"\r\ntype=\"date\"\r\n>\r\n</div>\r\n</div>\r\n<div class=\"gg-search-actions\">\r\n<button\r\nclass=\"gg-btn\"\r\nid=\"ggLookupButton\"\r\ntype=\"button\"\r\nonclick=\"return window.OCDGraduationGrading.lookup();\"\r\n>\r\nTRA CỨU\r\n</button>\r\n<button\r\nclass=\"gg-btn secondary\"\r\ntype=\"button\"\r\nonclick=\"return window.OCDGraduationGrading.clearFilters();\"\r\n>\r\nXÓA BỘ LỌC\r\n</button>\r\n</div>\r\n<div class=\"gg-filter-help\">\r\nCó thể tìm riêng theo tên/mã/Exam ID, riêng theo ngày,\r\nhoặc kết hợp cả hai điều kiện.\r\n</div>\r\n<div\r\nclass=\"gg-filter-summary\"\r\nid=\"ggFilterSummary\"\r\n></div>\r\n<div\r\nclass=\"gg-message\"\r\nid=\"ggMessage\"\r\n></div>\r\n<div\r\nclass=\"gg-exam-list gg-hidden\"\r\nid=\"ggExamList\"\r\n></div>\r\n</section>\r\n<section\r\nclass=\"gg-card gg-hidden\"\r\nid=\"ggGradingPanel\"\r\n>\r\n<h2 class=\"gg-title\">\r\nHồ sơ bài thi\r\n</h2>\r\n<div\r\nclass=\"gg-info-grid\"\r\nid=\"ggStudentInfo\"\r\n></div>\r\n<div\r\nclass=\"gg-score-summary\"\r\nid=\"ggTheorySummary\"\r\n></div>\r\n<div class=\"gg-practical-nav\">\r\n<div class=\"gg-nav-head\">\r\n<div class=\"gg-nav-title\">\r\nCHỌN CÂU THỰC HÀNH\r\n</div>\r\n<div\r\nclass=\"gg-nav-status\"\r\nid=\"ggPracticalStatus\"\r\n></div>\r\n</div>\r\n<div\r\nclass=\"gg-qgrid\"\r\nid=\"ggQuestionNav\"\r\n></div>\r\n</div>\r\n<div id=\"ggPracticalContent\"></div>\r\n<div\r\nclass=\"gg-final\"\r\nid=\"ggFinalSummary\"\r\n></div>\r\n</section>\r\n</div>\r\n<iframe\r\nid=\"ggHiddenFormFrame\"\r\nname=\"ggHiddenFormFrame\"\r\nstyle=\"\r\ndisplay:none;\r\nwidth:0;\r\nheight:0;\r\nborder:0;\r\n\"\r\n></iframe>\r\n</div></div>",["(function(){var d=document,w=window;w.__tdpFriendly=function(I){var root=d.querySelector(\".tdp-page\");if(!root)return;var n=0,t=0;function floor(){n++;if(n>40)return;try{var els=root.querySelectorAll(\"*\"),i,el,fs;for(i=0;i<els.length;i++){el=els[i];if(el.__tdpF||!el.firstChild||el.firstChild.nodeType!==3||!el.firstChild.nodeValue.trim())continue;if(/^(SCRIPT|STYLE|SVG|CANVAS|OPTION)$/.test(el.tagName))continue;fs=parseFloat(getComputedStyle(el).fontSize);if(fs&&fs<15){el.style.fontSize=\"15px\"}el.__tdpF=1}}catch(e){}}floor();try{new MutationObserver(function(){clearTimeout(t);t=setTimeout(floor,600)}).observe(root,{childList:true,subtree:true})}catch(e){}};if(1)return;w.__tdpCsvCache=1;var F=w.fetch,TTL=9e4,P=\"tdpcsv:\";function key(u){return P+String(u).replace(/[?&](_|cb|t|ts|nocache)=\\d+/g,\"\")}w.fetch=function(u,o){var url=typeof u===\"string\"?u:u&&u.url||\"\";if(!(o&&o.method&&o.method!==\"GET\")&&/^https:\\/\\/docs\\.google\\.com\\/spreadsheets\\/[^?]*(pub\\?|export\\?|gviz\\/)/.test(url)){var k=key(url),hit=null;try{hit=JSON.parse(sessionStorage.getItem(k)||\"null\")}catch(e){}if(hit&&Date.now()-hit.at<TTL)return Promise.resolve(new Response(hit.body,{status:200,headers:{\"Content-Type\":\"text/csv; charset=utf-8\",\"X-Tdp-Cache\":\"hit\"}}));return F.call(w,u,o).then(function(r){if(r.ok){r.clone().text().then(function(b){if(b.length<4e5){try{sessionStorage.setItem(k,JSON.stringify({at:Date.now(),body:b}))}catch(e){}}})}return r})}return F.call(w,u,o)}})();","var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,\"name\",{value,configurable:!0});window.OCDGraduationGrading=window.OCDGraduationGrading||{},(function(APP){\"use strict\";var CONFIG={version:\"OCD_GRADUATION_GRADING_V1.4\",passcode:\"835847\",authSessionKey:\"OCD_GRADUATION_GRADING_AUTH_V14\",spreadsheetId:\"1GJoTRsbq0kZfZrDdh0uCC667PwS3Bgkje2fHQnwnCKs\",examSheet:\"BaiThiTotNghiep\",gradingSheet:\"ChamThiTotNghiep\",timeout:15e3,verifyDelay:1500,verifyAttempts:8,storagePrefix:\"OCD_GRADUATION_GRADING_V14_\",lastLookupKey:\"OCD_GRADUATION_GRADING_LAST_LOOKUP\",lastDateKey:\"OCD_GRADUATION_GRADING_LAST_DATE\",examinerKey:\"OCD_GRADUATION_GRADING_EXAMINER\",formAction:\"https://docs.google.com/forms/d/e/1FAIpQLSdQ0U3FE6YmBLl58zYIGE0bQETxquwlLFQZkMmrSKmrKLAV7w/formResponse\",formEntries:{examId:\"entry.300398796\",studentCode:\"entry.630965694\",studentName:\"entry.2078670620\",group:\"entry.485868457\",course:\"entry.6979931\",theoryScore:\"entry.769860916\",practicalScore:\"entry.1409581903\",practicalGraded:\"entry.1184379468\",gradingData:\"entry.847321243\",examiner:\"entry.1561539253\",gradedAt:\"entry.1341313335\",version:\"entry.1627789074\"}},STATE={authenticated:!1,allExams:[],exams:[],examMap:{},exam:null,practical:[],currentIndex:0,record:null,sending:!1,verifying:!1};function el(id){return document.getElementById(id)}__name(el,\"el\");function showLoginMessage(text){var box=el(\"ggLoginMessage\");box&&(box.textContent=text,box.classList.add(\"show\"))}__name(showLoginMessage,\"showLoginMessage\");function clearLoginMessage(){var box=el(\"ggLoginMessage\");box&&(box.textContent=\"\",box.classList.remove(\"show\"))}__name(clearLoginMessage,\"clearLoginMessage\");function openProtectedApp(){STATE.authenticated=!0,el(\"ggLoginPanel\").classList.add(\"gg-hidden\"),el(\"ggProtectedApp\").classList.remove(\"gg-hidden\")}__name(openProtectedApp,\"openProtectedApp\");function openLogin(){STATE.authenticated=!1,el(\"ggProtectedApp\").classList.add(\"gg-hidden\"),el(\"ggLoginPanel\").classList.remove(\"gg-hidden\");var input=el(\"ggPasscode\");input&&(input.value=\"\",setTimeout(function(){try{input.focus()}catch{}},80))}__name(openLogin,\"openLogin\"),APP.login=function(){var input=el(\"ggPasscode\"),code=String(input?input.value:\"\").trim();if(code!==CONFIG.passcode)return showLoginMessage(\"Mã truy cập không đúng.\"),input&&(input.value=\"\",input.focus()),!1;clearLoginMessage();try{sessionStorage.setItem(CONFIG.authSessionKey,\"1\")}catch{}return openProtectedApp(),restoreSearchFilters(),!1},APP.logout=function(){if(!window.confirm(\"Đăng xuất khỏi khu vực chấm thi?\"))return!1;try{sessionStorage.removeItem(CONFIG.authSessionKey)}catch{}return STATE.authenticated=!1,STATE.exam=null,STATE.exams=[],STATE.examMap={},STATE.practical=[],el(\"ggGradingPanel\").classList.add(\"gg-hidden\"),el(\"ggExamList\").classList.add(\"gg-hidden\"),openLogin(),!1};function checkAuthentication(){var authenticated=!1;try{authenticated=sessionStorage.getItem(CONFIG.authSessionKey)===\"1\"}catch{}return authenticated?(openProtectedApp(),!0):(openLogin(),!1)}__name(checkAuthentication,\"checkAuthentication\");function normalizeText(value){return String(value??\"\").trim().toLowerCase().normalize(\"NFD\").replace(/[\\u0300-\\u036f]/g,\"\").replace(/đ/g,\"d\").replace(/\\s+/g,\" \")}__name(normalizeText,\"normalizeText\");function normalizeCode(value){return String(value??\"\").trim().toUpperCase()}__name(normalizeCode,\"normalizeCode\");function escapeHtml(value){return String(value??\"\").replace(/&/g,\"&\").replace(/</g,\"<\").replace(/>/g,\">\").replace(/\"/g,\""\").replace(/'/g,\"'\")}__name(escapeHtml,\"escapeHtml\");function parseNumber(value){var str=String(value??\"\").trim().replace(\",\",\".\");if(str===\"\")return null;var number=Number(str);return Number.isFinite(number)?number:null}__name(parseNumber,\"parseNumber\");function round2(value){return Math.round(Number(value)*100)/100}__name(round2,\"round2\");function sleep(ms){return new Promise(function(resolve){setTimeout(resolve,ms)})}__name(sleep,\"sleep\");function formatDateTime(value){var date=value instanceof Date?value:new Date(value);isNaN(date.getTime())&&(date=new Date);function pad(n){return String(n).padStart(2,\"0\")}return __name(pad,\"pad\"),pad(date.getDate())+\"/\"+pad(date.getMonth()+1)+\"/\"+date.getFullYear()+\" \"+pad(date.getHours())+\":\"+pad(date.getMinutes())+\":\"+pad(date.getSeconds())}__name(formatDateTime,\"formatDateTime\");function dateKeyFromValue(value){var raw=String(value||\"\").trim();if(!raw)return\"\";var vn=raw.match(/(?:^|\\D)(\\d{1,2})[\\/\\-](\\d{1,2})[\\/\\-](\\d{4})(?:\\D|$)/);if(vn)return String(vn[3])+\"-\"+String(vn[2]).padStart(2,\"0\")+\"-\"+String(vn[1]).padStart(2,\"0\");var iso=raw.match(/(\\d{4})-(\\d{1,2})-(\\d{1,2})/);if(iso)return String(iso[1])+\"-\"+String(iso[2]).padStart(2,\"0\")+\"-\"+String(iso[3]).padStart(2,\"0\");var date=new Date(raw);return isNaN(date.getTime())?\"\":date.getFullYear()+\"-\"+String(date.getMonth()+1).padStart(2,\"0\")+\"-\"+String(date.getDate()).padStart(2,\"0\")}__name(dateKeyFromValue,\"dateKeyFromValue\");function examDateKey(exam){return dateKeyFromValue(exam.submittedAt)||dateKeyFromValue(exam.timestamp)||dateKeyFromValue(exam.startedAt)}__name(examDateKey,\"examDateKey\");function prettyDateKey(value){var match=String(value||\"\").match(/^(\\d{4})-(\\d{2})-(\\d{2})$/);return match?match[3]+\"/\"+match[2]+\"/\"+match[1]:value||\"\"}__name(prettyDateKey,\"prettyDateKey\");function parseSortTime(value){var raw=String(value||\"\").trim();if(!raw)return 0;var vn=raw.match(/(\\d{1,2})[\\/\\-](\\d{1,2})[\\/\\-](\\d{4})(?:\\s+(\\d{1,2}):(\\d{1,2})(?::(\\d{1,2}))?)?/);if(vn)return new Date(Number(vn[3]),Number(vn[2])-1,Number(vn[1]),Number(vn[4]||0),Number(vn[5]||0),Number(vn[6]||0)).getTime();var direct=new Date(raw);return isNaN(direct.getTime())?0:direct.getTime()}__name(parseSortTime,\"parseSortTime\");function examSortTime(exam){return parseSortTime(exam.submittedAt)||parseSortTime(exam.timestamp)||parseSortTime(exam.startedAt)||0}__name(examSortTime,\"examSortTime\");function parseCSV(text){var rows=[],row=[],field=\"\",quoted=!1;text=String(text||\"\").replace(/^\\uFEFF/,\"\");for(var i=0;i<text.length;i++){var c=text[i];c==='\"'?quoted&&text[i+1]==='\"'?(field+='\"',i++):quoted=!quoted:c===\",\"&&!quoted?(row.push(field),field=\"\"):(c===`\n`||c===\"\\r\")&&!quoted?(c===\"\\r\"&&text[i+1]===`\n`&&i++,row.push(field),row.some(function(v){return String(v).trim()!==\"\"})&&rows.push(row),row=[],field=\"\"):field+=c}return row.push(field),row.some(function(v){return String(v).trim()!==\"\"})&&rows.push(row),rows}__name(parseCSV,\"parseCSV\");var SHEET_GIDS={BaiThiTotNghiep:\"60957786\",ChamThiTotNghiep:\"1476302274\"};function sheetUrl(sheetName){var gid=SHEET_GIDS[sheetName];return\"https://docs.google.com/spreadsheets/d/\"+CONFIG.spreadsheetId+(gid?\"/export?format=csv&gid=\"+gid:\"/gviz/tq?tqx=out:csv&sheet=\"+encodeURIComponent(sheetName))+\"&_=\"+Date.now()}__name(sheetUrl,\"sheetUrl\");function fetchWithTimeout(url){var controller=typeof AbortController<\"u\"?new AbortController:null,timer=controller?setTimeout(function(){controller.abort()},CONFIG.timeout):null;return fetch(url,{cache:\"no-store\",signal:controller?controller.signal:void 0}).then(function(response){if(!response.ok)throw new Error(\"HTTP \"+response.status);return response.text()}).finally(function(){timer&&clearTimeout(timer)})}__name(fetchWithTimeout,\"fetchWithTimeout\");async function fetchRetry(url){for(var error=null,i=0;i<2;i++)try{return await fetchWithTimeout(url)}catch(e){error=e,i===0&&await sleep(400)}throw error}__name(fetchRetry,\"fetchRetry\");function findColumn(headers,aliases){for(var normalized=headers.map(normalizeText),i=0;i<aliases.length;i++){var wanted=normalizeText(aliases[i]),index=normalized.indexOf(wanted);if(index!==-1)return index}for(var a=0;a<aliases.length;a++)for(var partial=normalizeText(aliases[a]),h=0;h<normalized.length;h++)if(partial&&normalized[h].indexOf(partial)!==-1)return h;return-1}__name(findColumn,\"findColumn\");function valueAt(row,index){return index<0||!row||index>=row.length?\"\":String(row[index]||\"\").trim()}__name(valueAt,\"valueAt\");function driveIdFromUrl(value){for(var str=String(value||\"\").trim(),patterns=[/\\/file\\/d\\/([a-zA-Z0-9_-]+)/,/[?&]id=([a-zA-Z0-9_-]+)/,/\\/d\\/([a-zA-Z0-9_-]+)/],i=0;i<patterns.length;i++){var match=str.match(patterns[i]);if(match)return match[1]}return/^[a-zA-Z0-9_-]{20,}$/.test(str)?str:\"\"}__name(driveIdFromUrl,\"driveIdFromUrl\");function imageUrl(value){var RS=window.StudentRewardSystem;if(RS&&typeof RS.convertDriveImageUrl==\"function\")try{return RS.convertDriveImageUrl(value,900)}catch{}var id=driveIdFromUrl(value);return id?\"https://drive.google.com/thumbnail?id=\"+encodeURIComponent(id)+\"&sz=w900\":String(value||\"\")}__name(imageUrl,\"imageUrl\");function setMessage(text,type){var box=el(\"ggMessage\");box.className=\"gg-message show \"+(type||\"loading\"),box.textContent=text}__name(setMessage,\"setMessage\");function hideMessage(){var box=el(\"ggMessage\");box&&(box.className=\"gg-message\",box.textContent=\"\")}__name(hideMessage,\"hideMessage\");function renderFilterSummary(query,dateFilter,count){var box=el(\"ggFilterSummary\");if(box){var pieces=[];if(query&&pieces.push('Từ khóa: \"'+query+'\"'),dateFilter&&pieces.push(\"Ngày thi: \"+prettyDateKey(dateFilter)),!pieces.length){box.classList.remove(\"show\"),box.textContent=\"\";return}box.textContent=pieces.join(\" • \")+\" • Kết quả: \"+count+\" bài thi\",box.classList.add(\"show\")}}__name(renderFilterSummary,\"renderFilterSummary\");async function loadExamRows(){var text=await fetchRetry(sheetUrl(CONFIG.examSheet)),rows=parseCSV(text);if(!rows||rows.length<2)throw new Error(\"BaiThiTotNghiep chưa có dữ liệu.\");var headers=rows[0],cols={timestamp:findColumn(headers,[\"Dấu thời gian\"]),examId:findColumn(headers,[\"Exam ID\",\"ExamID\"]),code:findColumn(headers,[\"Mã học viên\"]),name:findColumn(headers,[\"Họ và tên\"]),group:findColumn(headers,[\"Tổ\"]),course:findColumn(headers,[\"Khoá\",\"Khóa\"]),startedAt:findColumn(headers,[\"Thời gian bắt đầu\"]),submittedAt:findColumn(headers,[\"Thời gian nộp\"]),submitReason:findColumn(headers,[\"Hình thức nộp\"]),theoryAnswered:findColumn(headers,[\"Số câu lý thuyết đã làm\"]),theoryCorrect:findColumn(headers,[\"Số câu lý thuyết đúng\"]),theoryWrong:findColumn(headers,[\"Số câu lý thuyết sai\"]),theoryScore:findColumn(headers,[\"Điểm lý thuyết\"]),practicalAnswered:findColumn(headers,[\"Số câu thực hành đã chọn\"]),practicalData:findColumn(headers,[\"PracticalData\"])};if(cols.examId<0||cols.code<0||cols.practicalData<0)throw new Error(\"Không nhận diện được cấu trúc BaiThiTotNghiep.\");for(var result=[],i=1;i<rows.length;i++){var row=rows[i],examId=valueAt(row,cols.examId);examId&&result.push({timestamp:valueAt(row,cols.timestamp),examId,studentCode:normalizeCode(valueAt(row,cols.code)),studentName:valueAt(row,cols.name),group:valueAt(row,cols.group),course:valueAt(row,cols.course),startedAt:valueAt(row,cols.startedAt),submittedAt:valueAt(row,cols.submittedAt),submitReason:valueAt(row,cols.submitReason),theoryAnswered:parseNumber(valueAt(row,cols.theoryAnswered))||0,theoryCorrect:parseNumber(valueAt(row,cols.theoryCorrect))||0,theoryWrong:parseNumber(valueAt(row,cols.theoryWrong))||0,theoryScore:parseNumber(valueAt(row,cols.theoryScore))||0,practicalAnswered:parseNumber(valueAt(row,cols.practicalAnswered))||0,practicalRaw:valueAt(row,cols.practicalData)})}return result}__name(loadExamRows,\"loadExamRows\");function matchesSearch(exam,query){if(!query)return!0;var wanted=normalizeText(query),haystack=normalizeText((exam.examId||\"\")+\" \"+(exam.studentCode||\"\")+\" \"+(exam.studentName||\"\"));return haystack.indexOf(wanted)!==-1}__name(matchesSearch,\"matchesSearch\");function matchesDate(exam,dateFilter){return dateFilter?examDateKey(exam)===dateFilter:!0}__name(matchesDate,\"matchesDate\"),APP.lookup=async function(){if(!STATE.authenticated)return openLogin(),!1;var input=el(\"ggLookupInput\"),dateInput=el(\"ggDateFilter\"),button=el(\"ggLookupButton\"),query=String(input?input.value:\"\").trim(),dateFilter=String(dateInput?dateInput.value:\"\").trim();if(!query&&!dateFilter)return setMessage(\"Vui lòng nhập Exam ID, mã học viên, họ tên hoặc chọn ngày thi.\",\"error\"),renderFilterSummary(\"\",\"\",0),!1;button.disabled=!0,button.textContent=\"ĐANG TRA CỨU...\",setMessage(\"Đang đọc và lọc dữ liệu bài thi...\",\"loading\"),el(\"ggGradingPanel\").classList.add(\"gg-hidden\"),el(\"ggExamList\").classList.add(\"gg-hidden\");try{localStorage.setItem(CONFIG.lastLookupKey,query),localStorage.setItem(CONFIG.lastDateKey,dateFilter)}catch{}try{var rows=await loadExamRows();if(STATE.allExams=rows,STATE.exams=rows.filter(function(exam){return matchesSearch(exam,query)&&matchesDate(exam,dateFilter)}).sort(function(a,b){return examSortTime(b)-examSortTime(a)}),renderFilterSummary(query,dateFilter,STATE.exams.length),!STATE.exams.length)throw new Error(dateFilter?\"Không tìm thấy bài thi phù hợp với bộ lọc đã chọn.\":\"Không tìm thấy bài thi.\");STATE.exams.length===1?(loadExam(STATE.exams[0]),setMessage(\"Đã tìm thấy và tải bài thi.\",\"success\")):(renderExamList(),setMessage(\"Tìm thấy \"+STATE.exams.length+\" bài thi phù hợp.\",\"success\"))}catch(error){console.error(error),setMessage(error&&error.message?error.message:\"Không thể tải bài thi.\",\"error\")}finally{button.disabled=!1,button.textContent=\"TRA CỨU\"}return!1},APP.clearFilters=function(){var input=el(\"ggLookupInput\"),date=el(\"ggDateFilter\");input&&(input.value=\"\"),date&&(date.value=\"\");try{localStorage.removeItem(CONFIG.lastLookupKey),localStorage.removeItem(CONFIG.lastDateKey)}catch{}return STATE.exams=[],STATE.examMap={},el(\"ggExamList\").classList.add(\"gg-hidden\"),el(\"ggGradingPanel\").classList.add(\"gg-hidden\"),renderFilterSummary(\"\",\"\",0),hideMessage(),!1};function renderExamList(){var box=el(\"ggExamList\");STATE.examMap={};var html=\"\";STATE.exams.forEach(function(exam,index){STATE.examMap[String(index)]=exam;var dateKey=examDateKey(exam);html+='<div class=\"gg-exam-item\"><div><div class=\"gg-exam-name\">'+escapeHtml(exam.studentName||exam.studentCode)+'<span class=\"gg-exam-code\">'+escapeHtml(exam.studentCode)+'</span></div><div class=\"gg-exam-meta\">'+escapeHtml(exam.examId)+\"<br>Nộp: \"+escapeHtml(exam.submittedAt||exam.timestamp||\"-\")+\"</div>\"+(dateKey?'<div class=\"gg-exam-date\">NGÀY THI '+escapeHtml(prettyDateKey(dateKey))+\"</div>\":\"\")+'</div><button class=\"gg-btn\" type=\"button\" onclick=\"return window.OCDGraduationGrading.selectExam('+index+');\">CHẤM BÀI</button></div>'}),box.innerHTML=html,box.classList.remove(\"gg-hidden\")}__name(renderExamList,\"renderExamList\"),APP.selectExam=function(index){var exam=STATE.examMap[String(index)];return exam&&(loadExam(exam),setMessage(\"Đã mở bài thi của \"+(exam.studentName||exam.studentCode)+\".\",\"success\")),!1};function parsePracticalData(raw){try{var data=JSON.parse(raw||\"[]\");return Array.isArray(data)?data.filter(function(item){return item&&typeof item==\"object\"}).sort(function(a,b){return Number(a.q||0)-Number(b.q||0)}):[]}catch(error){return console.error(\"PracticalData:\",error),[]}}__name(parsePracticalData,\"parsePracticalData\");function storageKey(){return STATE.exam?CONFIG.storagePrefix+STATE.exam.examId:\"\"}__name(storageKey,\"storageKey\");function createRecord(){var examiner=\"\";try{examiner=localStorage.getItem(CONFIG.examinerKey)||\"\"}catch{}return{version:CONFIG.version,examId:STATE.exam.examId,status:\"active\",examiner,grades:{},finalResult:null,remoteSave:{status:\"not_sent\",attempts:0,verifiedAt:null,remoteRow:null,error:\"\"},savedAt:Date.now()}}__name(createRecord,\"createRecord\");function loadRecord(){try{var raw=localStorage.getItem(storageKey());if(!raw)return createRecord();var record=JSON.parse(raw);return record.grades=record.grades||{},record.remoteSave=record.remoteSave||{status:\"not_sent\",attempts:0,verifiedAt:null,remoteRow:null,error:\"\"},record}catch{return createRecord()}}__name(loadRecord,\"loadRecord\");function saveRecord(){if(!(!STATE.record||!storageKey())){STATE.record.savedAt=Date.now();try{localStorage.setItem(storageKey(),JSON.stringify(STATE.record))}catch{}}}__name(saveRecord,\"saveRecord\");function isLocked(){return!!(STATE.record&&STATE.record.status===\"submitted\")}__name(isLocked,\"isLocked\");function loadExam(exam){STATE.exam=exam,STATE.practical=parsePracticalData(exam.practicalRaw),STATE.currentIndex=0,STATE.record=loadRecord(),el(\"ggExamList\").classList.add(\"gg-hidden\"),el(\"ggGradingPanel\").classList.remove(\"gg-hidden\"),renderAll(),isLocked()&&STATE.record.finalResult&&STATE.record.remoteSave.status!==\"sent\"&&setTimeout(function(){verifyRemote(!1)},500)}__name(loadExam,\"loadExam\");function infoBox(label,value){return'<div class=\"gg-info\"><div class=\"gg-info-label\">'+escapeHtml(label)+'</div><div class=\"gg-info-value\">'+escapeHtml(value||\"-\")+\"</div></div>\"}__name(infoBox,\"infoBox\");function renderInfo(){var exam=STATE.exam;el(\"ggStudentInfo\").innerHTML=infoBox(\"Học viên\",exam.studentName)+infoBox(\"Mã học viên\",exam.studentCode)+infoBox(\"Tổ\",exam.group)+infoBox(\"Khóa\",exam.course)+infoBox(\"Exam ID\",exam.examId)+infoBox(\"Ngày thi\",prettyDateKey(examDateKey(exam))||\"-\")+infoBox(\"Bắt đầu\",exam.startedAt)+infoBox(\"Nộp bài\",exam.submittedAt)}__name(renderInfo,\"renderInfo\");function scoreBox(label,value){return'<div class=\"gg-score-box\"><div class=\"gg-score-value\">'+escapeHtml(value)+'</div><div class=\"gg-score-label\">'+escapeHtml(label)+\"</div></div>\"}__name(scoreBox,\"scoreBox\");function renderTheory(){var exam=STATE.exam;el(\"ggTheorySummary\").innerHTML=scoreBox(\"Điểm lý thuyết\",exam.theoryScore+\"/10\")+scoreBox(\"Câu đúng\",exam.theoryCorrect)+scoreBox(\"Câu sai\",exam.theoryWrong)+scoreBox(\"Thực hành đã chọn\",exam.practicalAnswered)}__name(renderTheory,\"renderTheory\");function gradeFor(questionId){var key=String(questionId);return STATE.record.grades[key]||(STATE.record.grades[key]={examScore:null,comment:\"\",usedOldScore:!1,gradedAt:null}),STATE.record.grades[key]}__name(gradeFor,\"gradeFor\");function isGraded(questionId){var grade=STATE.record.grades[String(questionId)];return!!(grade&&grade.examScore!==null&&grade.examScore!==void 0&&Number.isFinite(Number(grade.examScore)))}__name(isGraded,\"isGraded\");function gradedCount(){return STATE.practical.filter(function(item){return isGraded(item.q)}).length}__name(gradedCount,\"gradedCount\");function renderNav(){var html=\"\";STATE.practical.forEach(function(item,index){html+='<button class=\"gg-qbtn '+(isGraded(item.q)?\"graded \":\"\")+(index===STATE.currentIndex?\"current\":\"\")+'\" type=\"button\" onclick=\"return window.OCDGraduationGrading.goQuestion('+index+');\">'+escapeHtml(item.q)+\"</button>\"}),el(\"ggQuestionNav\").innerHTML=html,el(\"ggPracticalStatus\").textContent=\"Đã chấm \"+gradedCount()+\"/\"+STATE.practical.length+\" câu\"}__name(renderNav,\"renderNav\"),APP.goQuestion=function(index){return index=Number(index),index<0||index>=STATE.practical.length||!isLocked()&&!saveCurrentInputs()||(STATE.currentIndex=index,renderAll()),!1};function renderPractical(){var box=el(\"ggPracticalContent\");if(!STATE.practical.length){box.innerHTML='<div class=\"gg-message show error\">Không đọc được PracticalData.</div>';return}var item=STATE.practical[STATE.currentIndex],grade=gradeFor(item.q),oldScore=parseNumber(item.originalScore),locked=isLocked(),img=imageUrl(item.file),html='<div class=\"gg-practical-wrap\"><div><div class=\"gg-question-number\">CÂU '+escapeHtml(item.q)+'</div><h3 class=\"gg-question-title\">'+escapeHtml(item.prompt||\"Bài thực hành\")+\"</h3>\";img?html+='<div class=\"gg-image-wrap\"><img class=\"gg-image\" loading=\"lazy\" src=\"'+escapeHtml(img)+'\" alt=\"Bài thực hành\"></div>':html+='<div class=\"gg-image-wrap\"><div class=\"gg-no-image\">Không có ảnh xem trước.</div></div>',html+='<div class=\"gg-submission-meta\"><div class=\"gg-meta\"><div class=\"gg-meta-label\">Mô tả bài tập</div><div class=\"gg-meta-value\">'+escapeHtml(item.note||\"-\")+'</div></div><div class=\"gg-meta\"><div class=\"gg-meta-label\">Ngày nộp gốc</div><div class=\"gg-meta-value\">'+escapeHtml(item.submittedAt||\"-\")+'</div></div><div class=\"gg-meta\"><div class=\"gg-meta-label\">Submission ID</div><div class=\"gg-meta-value\">'+escapeHtml(item.submissionId||\"-\")+'</div></div><div class=\"gg-meta\"><div class=\"gg-meta-label\">File gốc</div><div class=\"gg-meta-value\">'+(item.file?'<a href=\"'+escapeHtml(item.file)+'\" target=\"_blank\" rel=\"noopener\">MỞ FILE GỐC</a>':\"-\")+\"</div></div></div></div>\",html+='<div class=\"gg-grading-panel\"><h3 class=\"gg-title\">Chấm điểm câu '+escapeHtml(item.q)+'</h3><div class=\"gg-old-score\"><div style=\"font-size:11px;font-weight:900;\">ĐIỂM BÀI LUYỆN TẬP CŨ</div><div class=\"gg-old-score-number\">'+(oldScore===null?\"Chưa có\":escapeHtml(oldScore))+'</div><div style=\"font-size:11px;margin-top:3px;\">Chỉ dùng để tham khảo. Điểm luyện tập gốc không bị thay đổi.</div></div>',oldScore!==null&&!locked&&(html+='<button class=\"gg-btn gold\" style=\"width:100%;margin-top:12px;\" type=\"button\" onclick=\"return window.OCDGraduationGrading.useOldScore();\">DÙNG ĐIỂM CŨ: '+escapeHtml(oldScore)+\"</button>\"),html+='<div class=\"gg-field\"><label class=\"gg-field-label\">ĐIỂM THI TỐT NGHIỆP</label><input class=\"gg-grade-input\" id=\"ggExamScore\" type=\"number\" min=\"0\" max=\"10\" step=\"0.1\" '+(locked?\"disabled \":\"\")+'value=\"'+(grade.examScore===null||grade.examScore===void 0?\"\":escapeHtml(grade.examScore))+'\"></div><div class=\"gg-field\"><label class=\"gg-field-label\">NHẬN XÉT CỦA GVCN</label><textarea class=\"gg-textarea\" id=\"ggExamComment\" '+(locked?\"disabled \":\"\")+'placeholder=\"Nhập nhận xét...\">'+escapeHtml(grade.comment||\"\")+\"</textarea></div>\",locked||(html+='<div class=\"gg-grade-actions\"><button class=\"gg-btn green\" type=\"button\" onclick=\"return window.OCDGraduationGrading.saveCurrent();\">LƯU ĐIỂM</button><button class=\"gg-btn\" type=\"button\" onclick=\"return window.OCDGraduationGrading.saveAndNext();\">LƯU & CÂU TIẾP</button></div>'),isGraded(item.q)&&(html+='<div class=\"gg-saved\">ĐÃ CHẤM - Điểm: '+escapeHtml(grade.examScore)+\"</div>\"),html+=\"</div></div>\",box.innerHTML=html}__name(renderPractical,\"renderPractical\"),APP.useOldScore=function(){if(isLocked())return!1;var item=STATE.practical[STATE.currentIndex],oldScore=parseNumber(item.originalScore);return oldScore===null||(el(\"ggExamScore\").value=oldScore,gradeFor(item.q).usedOldScore=!0),!1};function saveCurrentInputs(){if(isLocked())return!0;var item=STATE.practical[STATE.currentIndex];if(!item)return!0;var input=el(\"ggExamScore\");if(!input)return!0;var raw=String(input.value||\"\").trim(),grade=gradeFor(item.q),comment=el(\"ggExamComment\");if(grade.comment=comment?String(comment.value||\"\").trim():\"\",raw===\"\")return grade.examScore=null,saveRecord(),!0;var score=Number(raw.replace(\",\",\".\"));if(!Number.isFinite(score)||score<0||score>10)return alert(\"Điểm phải nằm trong khoảng từ 0 đến 10.\"),!1;grade.examScore=round2(score),grade.gradedAt=Date.now();var oldScore=parseNumber(item.originalScore);return grade.usedOldScore=oldScore!==null&&Number(oldScore)===Number(grade.examScore),saveRecord(),!0}__name(saveCurrentInputs,\"saveCurrentInputs\"),APP.saveCurrent=function(){return saveCurrentInputs()&&renderAll(),!1},APP.saveAndNext=function(){return saveCurrentInputs()&&(STATE.currentIndex<STATE.practical.length-1&&STATE.currentIndex++,renderAll()),!1};function practicalAverage(){var values=[];if(STATE.practical.forEach(function(item){var grade=STATE.record.grades[String(item.q)];if(grade&&grade.examScore!==null&&grade.examScore!==void 0){var number=Number(grade.examScore);Number.isFinite(number)&&values.push(number)}}),!values.length)return null;var total=values.reduce(function(sum,value){return sum+value},0);return round2(total/values.length)}__name(practicalAverage,\"practicalAverage\");function buildGradingData(){return STATE.practical.map(function(item){var grade=gradeFor(item.q);return{questionId:item.q,prompt:item.prompt||\"\",submissionId:item.submissionId||\"\",note:item.note||\"\",file:item.file||\"\",submittedAt:item.submittedAt||\"\",originalScore:item.originalScore===void 0?null:item.originalScore,examScore:grade.examScore,examinerComment:grade.comment||\"\",usedOldScore:!!grade.usedOldScore,gradedAt:grade.gradedAt||null}})}__name(buildGradingData,\"buildGradingData\");function buildFinalResult(){var now=Date.now();return{examId:STATE.exam.examId,studentCode:STATE.exam.studentCode,studentName:STATE.exam.studentName,group:STATE.exam.group,course:STATE.exam.course,theoryScore:STATE.exam.theoryScore,practicalScore:practicalAverage(),practicalGraded:gradedCount(),examiner:STATE.record.examiner,gradedAt:now,gradedAtText:formatDateTime(now),gradingData:buildGradingData(),version:CONFIG.version}}__name(buildFinalResult,\"buildFinalResult\");function renderFinal(){var average=practicalAverage(),complete=STATE.practical.length>0&&gradedCount()===STATE.practical.length,locked=isLocked(),html='<div class=\"gg-final-grid\"><div class=\"gg-final-box\"><div class=\"gg-final-number\">'+escapeHtml(STATE.exam.theoryScore)+'</div><div class=\"gg-final-label\">Điểm lý thuyết /10</div></div><div class=\"gg-final-box\"><div class=\"gg-final-number\">'+(average===null?\"-\":escapeHtml(average))+'</div><div class=\"gg-final-label\">Điểm thực hành /10</div></div><div class=\"gg-final-box\"><div class=\"gg-final-number\">'+gradedCount()+\"/\"+STATE.practical.length+'</div><div class=\"gg-final-label\">Câu đã chấm</div></div></div>';if(locked){html+='<div class=\"gg-locked-banner\"><strong>BẢN CHẤM ĐÃ KHÓA</strong><br>Người chấm: '+escapeHtml(STATE.record.examiner||\"-\")+'</div><div id=\"ggRemoteStatus\"></div><div class=\"gg-final-submit\"><button class=\"gg-btn secondary\" type=\"button\" onclick=\"return window.OCDGraduationGrading.resetTestGrade();\">XÓA BẢN CHẤM TEST TRÊN THIẾT BỊ</button></div>',el(\"ggFinalSummary\").innerHTML=html,renderRemoteStatus();return}complete||(html+='<div class=\"gg-message show loading\" style=\"margin-top:14px;\">Còn '+(STATE.practical.length-gradedCount())+\" câu chưa chấm.</div>\"),html+='<div class=\"gg-final-submit\"><div class=\"gg-field\"><label class=\"gg-field-label\">NGƯỜI CHẤM</label><input class=\"gg-input\" id=\"ggExaminer\" type=\"text\" placeholder=\"Nhập tên giáo viên chấm\" value=\"'+escapeHtml(STATE.record.examiner||\"\")+'\"></div><button class=\"gg-btn red\" style=\"width:100%;margin-top:12px;\" type=\"button\" '+(complete?\"\":\"disabled \")+'onclick=\"return window.OCDGraduationGrading.finalizeGrading();\">HOÀN TẤT VÀ GỬI KẾT QUẢ CHẤM</button></div>',el(\"ggFinalSummary\").innerHTML=html}__name(renderFinal,\"renderFinal\"),APP.finalizeGrading=function(){if(isLocked()||!saveCurrentInputs())return!1;if(gradedCount()!==STATE.practical.length)return alert(\"Bạn chưa chấm đủ tất cả câu thực hành.\"),!1;var examinerInput=el(\"ggExaminer\"),examiner=examinerInput?String(examinerInput.value||\"\").trim():\"\";if(!examiner)return alert(\"Vui lòng nhập tên người chấm.\"),!1;if(!window.confirm(`Xác nhận hoàn tất chấm thi?\n\nBản chấm sẽ bị khóa và gửi vào ChamThiTotNghiep.`))return!1;STATE.record.examiner=examiner;try{localStorage.setItem(CONFIG.examinerKey,examiner)}catch{}return STATE.record.finalResult=buildFinalResult(),STATE.record.status=\"submitted\",STATE.record.remoteSave.status=\"pending\",STATE.record.remoteSave.error=\"\",saveRecord(),renderAll(),submitAndVerify(),!1};function formPayload(){var result=STATE.record&&STATE.record.finalResult;if(!result)return null;var payload={};return payload[CONFIG.formEntries.examId]=result.examId,payload[CONFIG.formEntries.studentCode]=result.studentCode,payload[CONFIG.formEntries.studentName]=result.studentName,payload[CONFIG.formEntries.group]=result.group||\"\",payload[CONFIG.formEntries.course]=result.course||\"\",payload[CONFIG.formEntries.theoryScore]=result.theoryScore,payload[CONFIG.formEntries.practicalScore]=result.practicalScore,payload[CONFIG.formEntries.practicalGraded]=result.practicalGraded,payload[CONFIG.formEntries.gradingData]=JSON.stringify(result.gradingData),payload[CONFIG.formEntries.examiner]=result.examiner,payload[CONFIG.formEntries.gradedAt]=result.gradedAtText,payload[CONFIG.formEntries.version]=result.version,payload}__name(formPayload,\"formPayload\");function postGoogleForm(){return new Promise(function(resolve,reject){var payload=formPayload();if(!payload){reject(new Error(\"Không có dữ liệu để gửi.\"));return}var form=document.createElement(\"form\");form.method=\"POST\",form.action=CONFIG.formAction,form.target=\"ggHiddenFormFrame\",form.acceptCharset=\"UTF-8\",form.style.display=\"none\";function addField(name,value){var input=document.createElement(\"input\");input.type=\"hidden\",input.name=name,input.value=String(value??\"\"),form.appendChild(input)}__name(addField,\"addField\"),Object.keys(payload).forEach(function(name){addField(name,payload[name])}),addField(\"fvv\",\"1\"),addField(\"pageHistory\",\"0\"),document.body.appendChild(form);try{HTMLFormElement.prototype.submit.call(form),setTimeout(function(){try{form.remove()}catch{}resolve()},900)}catch(formError){console.warn(\"Hidden form POST lỗi, thử fetch fallback:\",formError);try{form.remove()}catch{}var body=new URLSearchParams;Object.keys(payload).forEach(function(name){body.append(name,String(payload[name]===void 0||payload[name]===null?\"\":payload[name]))}),body.append(\"fvv\",\"1\"),body.append(\"pageHistory\",\"0\"),fetch(CONFIG.formAction,{method:\"POST\",mode:\"no-cors\",cache:\"no-store\",headers:{\"Content-Type\":\"application/x-www-form-urlencoded;charset=UTF-8\"},body:body.toString()}).then(function(){resolve()}).catch(function(fetchError){reject(fetchError)})}})}__name(postGoogleForm,\"postGoogleForm\");async function findRemoteResult(){var result=STATE.record&&STATE.record.finalResult;if(!result)return null;var text=await fetchRetry(sheetUrl(CONFIG.gradingSheet)),rows=parseCSV(text);if(!rows||rows.length<2)return null;var headers=rows[0],cols={examId:findColumn(headers,[\"Exam ID\",\"ExamID\"]),code:findColumn(headers,[\"Mã học viên\"]),practicalScore:findColumn(headers,[\"Điểm thực hành\"]),examiner:findColumn(headers,[\"Người chấm\"]),gradedAt:findColumn(headers,[\"Thời gian chấm\"]),version:findColumn(headers,[\"Phiên bản hệ thống\"])};if(cols.examId<0)throw new Error(\"Không tìm thấy cột Exam ID trong ChamThiTotNghiep.\");for(var i=rows.length-1;i>=1;i--){var row=rows[i];if(normalizeCode(valueAt(row,cols.examId))===normalizeCode(result.examId)){var code=valueAt(row,cols.code);if(!(cols.code>=0&&code&&normalizeCode(code)!==normalizeCode(result.studentCode))){var remoteScore=parseNumber(valueAt(row,cols.practicalScore));if(!(remoteScore!==null&&Math.abs(remoteScore-Number(result.practicalScore))>.001))return{rowNumber:i+1,examId:valueAt(row,cols.examId),studentCode:code,practicalScore:remoteScore,examiner:valueAt(row,cols.examiner),gradedAt:valueAt(row,cols.gradedAt),version:valueAt(row,cols.version)}}}}return null}__name(findRemoteResult,\"findRemoteResult\");async function verifyRemote(showAlert){if(STATE.verifying||!STATE.record||!STATE.record.finalResult)return!1;STATE.verifying=!0;try{var found=await findRemoteResult();return found?(STATE.record.remoteSave.status=\"sent\",STATE.record.remoteSave.verifiedAt=Date.now(),STATE.record.remoteSave.remoteRow=found.rowNumber,STATE.record.remoteSave.error=\"\",saveRecord(),renderRemoteStatus(),showAlert&&alert(\"Đã xác nhận dữ liệu thực sự tồn tại trong ChamThiTotNghiep.\"),!0):(STATE.record.remoteSave.status=\"pending\",STATE.record.remoteSave.error=\"Chưa tìm thấy Exam ID này trong ChamThiTotNghiep.\",saveRecord(),renderRemoteStatus(),showAlert&&alert(\"Chưa tìm thấy bản chấm trong ChamThiTotNghiep.\"),!1)}catch(error){return console.error(\"Verify:\",error),STATE.record.remoteSave.status=\"pending\",STATE.record.remoteSave.error=error.message||\"Không thể kiểm tra Sheet.\",saveRecord(),renderRemoteStatus(),!1}finally{STATE.verifying=!1}}__name(verifyRemote,\"verifyRemote\");async function submitAndVerify(){if(!(STATE.sending||!STATE.record||!STATE.record.finalResult)){STATE.sending=!0;try{var existing=await findRemoteResult();if(existing){STATE.record.remoteSave.status=\"sent\",STATE.record.remoteSave.verifiedAt=Date.now(),STATE.record.remoteSave.remoteRow=existing.rowNumber,STATE.record.remoteSave.error=\"\",saveRecord(),renderRemoteStatus();return}STATE.record.remoteSave.status=\"sending\",STATE.record.remoteSave.attempts=Number(STATE.record.remoteSave.attempts||0)+1,STATE.record.remoteSave.error=\"\",saveRecord(),renderRemoteStatus(),await postGoogleForm(),STATE.record.remoteSave.status=\"pending\",STATE.record.remoteSave.error=\"Đã gửi Form. Đang chờ Google ghi dữ liệu vào Sheet...\",saveRecord(),renderRemoteStatus();for(var i=0;i<CONFIG.verifyAttempts;i++){await sleep(CONFIG.verifyDelay);var found=await findRemoteResult();if(found){STATE.record.remoteSave.status=\"sent\",STATE.record.remoteSave.verifiedAt=Date.now(),STATE.record.remoteSave.remoteRow=found.rowNumber,STATE.record.remoteSave.error=\"\",saveRecord(),renderRemoteStatus();return}}STATE.record.remoteSave.status=\"pending\",STATE.record.remoteSave.error=\"Đã POST Google Form nhưng chưa tìm thấy dòng tương ứng trong ChamThiTotNghiep.\",saveRecord(),renderRemoteStatus()}catch(error){console.error(\"Submit:\",error),STATE.record.remoteSave.status=\"pending\",STATE.record.remoteSave.error=error&&error.message?error.message:\"Không thể gửi kết quả.\",saveRecord(),renderRemoteStatus()}finally{STATE.sending=!1}}}__name(submitAndVerify,\"submitAndVerify\");function renderRemoteStatus(){var box=el(\"ggRemoteStatus\");if(!(!box||!STATE.record)){var remote=STATE.record.remoteSave;if(remote.status===\"sent\"){box.innerHTML='<div class=\"gg-remote sent\"><strong>ĐÃ GHI THÀNH CÔNG VÀO CHAMTHITOTNGHIEP</strong><br>Exam ID: '+escapeHtml(STATE.exam.examId)+(remote.remoteRow?\"<br>Dòng trong Sheet: \"+escapeHtml(remote.remoteRow):\"\")+\"<br>Trạng thái này chỉ xuất hiện sau khi hệ thống đọc lại Sheet và tìm thấy bản chấm thực tế.</div>\";return}if(remote.status===\"sending\"){box.innerHTML='<div class=\"gg-remote sending\"><strong>ĐANG GỬI GOOGLE FORM...</strong><br>Hệ thống sẽ kiểm tra lại ChamThiTotNghiep ngay sau khi gửi.</div>';return}box.innerHTML='<div class=\"gg-remote pending\"><strong>CHƯA XÁC NHẬN GHI VÀO SHEET</strong><br>'+escapeHtml(remote.error||\"Đang chờ xác nhận.\")+\"<br>Số lần gửi: \"+escapeHtml(remote.attempts||0)+'<div class=\"gg-remote-actions\"><button class=\"gg-btn blue\" type=\"button\" onclick=\"return window.OCDGraduationGrading.checkRemote();\">KIỂM TRA LẠI SHEET</button><button class=\"gg-btn gold\" type=\"button\" onclick=\"return window.OCDGraduationGrading.retrySend();\">GỬI LẠI KẾT QUẢ</button></div></div>'}}__name(renderRemoteStatus,\"renderRemoteStatus\"),APP.checkRemote=function(){return verifyRemote(!0),!1},APP.retrySend=function(){return STATE.sending||submitAndVerify(),!1},APP.resetTestGrade=function(){if(!STATE.exam||!window.confirm(`Xóa bản chấm TEST đang lưu trên thiết bị?\n\nDòng đã gửi lên Google Sheet sẽ không bị xóa.`))return!1;try{localStorage.removeItem(storageKey())}catch{}return STATE.record=createRecord(),STATE.currentIndex=0,renderAll(),!1};function renderAll(){renderInfo(),renderTheory(),renderNav(),renderPractical(),renderFinal()}__name(renderAll,\"renderAll\"),window.addEventListener(\"online\",function(){isLocked()&&STATE.record&&STATE.record.remoteSave.status!==\"sent\"&&verifyRemote(!1)});function restoreSearchFilters(){try{var last=localStorage.getItem(CONFIG.lastLookupKey),lastDate=localStorage.getItem(CONFIG.lastDateKey);last&&el(\"ggLookupInput\")&&(el(\"ggLookupInput\").value=last),lastDate&&el(\"ggDateFilter\")&&(el(\"ggDateFilter\").value=lastDate)}catch{}}__name(restoreSearchFilters,\"restoreSearchFilters\");function init(){var authenticated=checkAuthentication();authenticated&&restoreSearchFilters(),console.log(CONFIG.version+\" READY\")}__name(init,\"init\"),document.readyState===\"loading\"?document.addEventListener(\"DOMContentLoaded\",init):init()})(window.OCDGraduationGrading);"]);
Nhận xét
Đăng nhận xét