`;
const ACQ_ORDER = ['KMUTT_SUB_PRICED', 'KMUTT_SUB_NOPRICE', 'KMUTT_UNCONF', 'KMUTT_PERP', 'MHESI'];
const acqSeries = () => ACQ_ORDER.filter(k => passAcq(k) && D.use.some(r => r.acq === k && r.fy >= D.firstFY && passRes(r.rc) && r.use > 0)).map(k => ({ k, name: ACQLG[k], color: ACQCOL[k], hatch: k === 'KMUTT_UNCONF' }));
function fyCats(from) { return D.fys.filter(f => f >= from).map(f => ({ k: f, lab: String(FY(f)) })); }
const filterNote = () => (filtered() ? `${tx('ตามตัวกรอง', 'filtered')}` : '');
const mkLabel = mk => mkName(mk);
// the latest fiscal year is 'still running' only while it has fewer than 12 months (use: complete month · users: last OpenAthens month)
const fyOpen = y => y === D.curFY && D.compM < 12;
const uOpen = y => y === D.curFY && (D.usersMaxYTD[y] || 12) < 12;
const FYAXIS = () => tx('ปีงบประมาณ (พ.ศ.)', 'Fiscal year (Oct–Sep)');
const UNIT_USE = () => tx(' ครั้ง', ' uses'), UNIT_PPL = () => tx(' คน', ' people');
// ---------------------------------------------------------------- overview
function pgOverview() {
const f = fy(), e = endM(f);
const a = sumUse(f, e), b = sumUse(f - 1, e);
const kp = r => pass(r) && (D.res[r.rc] || {}).funding_source === 'KMUTT';
const ka = sumUse(f, e, kp), kb = sumUse(f - 1, e, kp);
const ks = r => kp(r) && r.rc !== 'SPRINGER_B';
const ksp = chg(sumUse(f, e, ks), sumUse(f - 1, e, ks));
const pp = r => pass(r) && r.acq === 'KMUTT_SUB_PRICED';
const pa = sumUse(f, e, pp), pb = sumUse(f - 1, e, pp);
const plat = userPlat();
const ua = plat && U(f, 'YTD', e, plat), ub = plat && U(f - 1, 'YTD', e, plat);
const q = Math.ceil(e / 3), qa = plat && U(f, 'Q', q, plat), qb = plat && U(f - 1, 'Q', q, plat);
const qp = qa && qb ? chg(qa.u, qb.u) : null;
const cfy = Math.max(...Object.values(D.cost).filter(c => c.done && c.fy <= f).map(c => c.fy));
const cc = D.cost[cfy], cpc = cpCode(), ca = cpAt(f, cpc, 'PA'), cb = cpAt(f - 1, cpc, 'PA');
const uVal = !plat ? '—' : ua && ua.u != null ? fmt(ua.u) : ua && ua.sup ? '<10' : '—';
const K = [
[tx('ใช้มากขึ้นหรือน้อยลง', 'Is use going up or down?'), tx('การใช้ทุกฐาน', 'Use, all resources'), fmt(a), tx('ครั้ง', 'uses'), vs(chg(a, b), b), filtered() ? tx('ตามตัวกรองที่เลือก', 'Using the selected filters') : `${periodLabel(f, e)}`],
[tx('ฐานที่หอสมุดจ่ายเอง', 'Resources the Library pays for'), tx('บอกรับ + ซื้อขาด' + (hasUnc() ? ' + รอยืนยัน' : ''), 'Subscribed + perpetual' + (hasUnc() ? ' + to be confirmed' : '')), fmt(ka), tx('ครั้ง', 'uses'), vs(chg(ka, kb), kb), ksp == null ? '' : `${tx('ไม่รวม Springer eBooks', 'Excluding Springer eBooks')} ${pctSpan(ksp)}`],
[tx('ตัวชี้วัด PA', 'PA indicator'), tx(`${cc ? cc.n : 6} ฐานบอกรับรายปีที่มีราคา`, `${cc ? cc.n : 6} priced annual subscriptions`), fmt(pa), tx('ครั้ง', 'uses'), vs(chg(pa, pb), pb), ''],
[tx('ใช้กี่คน', 'How many people?'), `${tx('ผู้ใช้ไม่ซ้ำ OpenAthens', 'Unique OpenAthens users')}${plat && plat !== 'ALL' ? ' · ' + esc(platName(plat)) : ''}`, uVal, tx('คน', 'people'),
!plat ? `${tx('เลือกฐานที่อยู่แพลตฟอร์มเดียวกันเพื่อดูผู้ใช้', 'Select resources on one platform to see users')}` : vs(ua && ub ? chg(ua.u, ub.u) : null, ub && ub.u),
qp == null ? (S.acq.size ? tx('ผู้ใช้ไม่แยกตามรูปแบบการได้มา', 'Users are not split by acquisition model') : '') : `${tx('ไตรมาสล่าสุด (วิธีนับเดียวกัน)', 'Latest quarter (same counting method)')} ${pctSpan(qp)}`],
[tx('คุ้มค่าไหม', 'Is it good value?'), tx(`ต้นทุนต่อการใช้ ${ca ? ca.n : ''} ฐาน${useInt() ? ' (ไม่รวม Scopus)' : ''}`, `Cost per use, ${ca ? ca.n : ''} resources${useInt() ? ' (excl. Scopus)' : ''}`), ca ? '฿' + fmt(ca.cpu, 1) : '—', tx('บาท/ครั้ง', 'THB/use'),
ca && cb ? `${CPER[ca.code][0]} ${fyName(f)} · ${pctSpan(chg(ca.cpu, cb.cpu), true)} ${tx(`จาก ${f - 1}`, `from FY${FY(f - 1)}`)}` : ca ? `${CPER[ca.code][0]} ${fyName(f)}` : '',
ca && ca.proj ? `${tx('คาดการณ์', 'projected')} ${tx('ไม่ขึ้นกับตัวกรอง', 'not affected by filters')}` : tx('ไม่ขึ้นกับตัวกรอง', 'Not affected by filters')],
];
const ys = D.fys.filter(y => y >= D.firstFY && y <= f);
const SP = [ys.map(y => sumUse(y, e)), ys.map(y => sumUse(y, e, kp)), ys.map(y => sumUse(y, e, pp)), ys.map(y => { const r = plat && U(y, 'YTD', e, plat); return r && r.u; }), cpYears().filter(y => y <= f).map(y => (cpAt(y, cpc, 'PA') || {}).cpu)];
const kpis = K.map((k, i) => card(`
${k[0]}
${k[1]} (${k[3]})
${k[2]}${k[3]}
${spark(SP[i])}
${k[4]}
${k[5] ? `
${k[5]}
` : ''}
`)).join('');
const lastY = D.usersMaxYTD[D.curFY];
const html = `
${kpis}
${card(head(`${tx('การใช้', 'Use,')} ${periodShort(D.compM)} ${tx('ของทุกปีงบ (ครั้ง)', 'of every fiscal year (uses)')}${filterNote()}`, tx('แยกรูปแบบการได้มา · ไม่นำการใช้ของกลุ่มอื่นมาเฉลี่ยต้นทุน · คลิกแท่งเพื่อเลือกปี', 'By acquisition model · other groups\' use is never averaged into cost · click a bar to pick the year'), go('trend', tx('ดูแนวโน้ม', 'See trends'))) + `` + acqLegend(acqSeries().map(s => s.k)))}
${card(head(tx('ต้นทุนต่อการใช้ (บาท/ครั้ง)', 'Cost per use (THB/use)'), `${ca ? ca.n : ''} ${tx('ฐาน', 'resources')}${useInt() ? tx(' ไม่รวม Scopus', ' excl. Scopus') : ''} · ${cpLab(cpc)} · ${tx('ทุกปีช่วงเดียวกัน', 'same period every year')}`, go('value', tx('ความคุ้มค่า', 'Value for money'))) + `
${tx('ต้นทุนต่อการใช้ = ค่าใช้จ่ายของช่วง ÷ การใช้ช่วงเดียวกัน (แบบรายงาน PA) · เลือกช่วงอื่นได้ที่หน้าความคุ้มค่า', 'Cost per use = cost of the period ÷ use in the same period (PA report method) · other periods on the Value for money page')}
${card(head(tx(`การใช้${full ? 'ทั้งปีงบ' : ' ' + periodShort(D.compM) + ' ของทุกปีงบ'} แยกรูปแบบการได้มา (ครั้ง)`, `Use${full ? ' per full fiscal year' : ', ' + periodShort(D.compM) + ' of every fiscal year,'} by acquisition model (uses)`) + filterNote(), full ? !fyOpen(D.curFY) ? tx('ทุกปีงบมีข้อมูลครบ 12 เดือน จึงเทียบกันได้ตรง', 'Every fiscal year has all 12 months, so years compare directly') : tx(`ปีงบ ${D.curFY} ยังไม่จบ (ถึง ${MTH[D.compM]}) — ใช้โหมด "ช่วงเดียวกัน" เมื่อต้องการเทียบกันอย่างยุติธรรม`, `FY${FY(D.curFY)} is not over yet (through ${MTH[D.compM]}) — use "Same period" for a fair comparison`) : tx('เทียบเฉพาะเดือนที่ปีล่าสุดมีข้อมูลครบ จึงเทียบข้ามปีได้ตรง', 'Only the months the latest year has complete data for, so years compare fairly'),
``) +
`` + acqLegend(acqSeries().map(s => s.k)))}
${card(head(`${tx('แยกตามรูปแบบ', 'By model')} · ${periodLabel(f, e)}`, tx('ไม่เอาการใช้ของกลุ่มอื่นมาเฉลี่ยต้นทุน · ± เทียบช่วงเดียวกันปีก่อน', 'Other groups\' use is never averaged into cost · ± vs the same period last year')) + tb)}
${card(head(tx('จุดที่วิธีนับเปลี่ยน / หมายเหตุ', 'Method changes / notes'), '') + `
${notes}
`)}
`;
return { html, after: () => {
const cats = fyCats(D.firstFY).map(c => ({ ...c, lab: full && fyOpen(c.k) ? FY(c.k) + '*' : c.lab, note: full ? (fyOpen(c.k) ? tx('ถึง ', 'through ') + MTH[D.compM] : tx('ทั้งปีงบ', 'full fiscal year')) : periodShort(D.compM) }));
mount($('#c_trend'), el => columns(el, { cats, series: acqSeries(), val: (c, k) => sumUse(c.k, full ? 12 : D.compM, r => pass(r) && r.acq === k), totals: true, xTitle: `${FYAXIS()}${full ? '' : ` · ${tx('ช่วง', 'period')} ` + periodShort(D.compM)}`, yTitle: tx('การใช้ (ครั้ง)', 'Use (uses)'), unit: UNIT_USE(), active: c => c.k === f, onClick: c => setFY(c.k), fade: c => full && fyOpen(c.k) }));
$$('[data-trend]').forEach(b => b.onclick = () => { S.trend = b.dataset.trend; render(); });
} };
}
// ---------------------------------------------------------------- lookup
function lookupRows(f, e) {
const per = {};
for (const r of D.use) if ((r.fy === f || r.fy === f - 1) && r.fm <= e && pass(r)) {
const o = per[r.rc] || (per[r.rc] = { a: 0, b: 0, sa: 0, sb: 0, hs: false });
if (r.fy === f) { o.a += r.use; if (r.sea != null) { o.sa += r.sea; if (r.sea > 0) o.hs = true; } } else { o.b += r.use; if (r.sea != null) { o.sb += r.sea; if (r.sea > 0) o.hs = true; } }
}
return Object.entries(per).filter(([rc, o]) => o.a > 0 || o.b > 0).map(([rc, o]) => {
const d = D.res[rc] || {}, p = platOf(rc), u = p ? U(f, 'YTD', e, p) : null;
const hs = o.hs && searchInfo(rc).show;
return { rc, name: d.resource_name || rc, ct: tx(d.content_type_th, d.content_type) || '', acq: resAcq(rc, f), use: o.a, up: chg(o.a, o.b), sea: hs ? o.sa : null, sp: hs && o.sa > 0 ? chg(o.sa, o.sb) : null, users: u ? u.u : null, usup: u && u.sup, shared: p && platShared(p), st: covStatus(rc, f, e) };
});
}
function pgLookup() {
const f = fy(), e = endM(f);
let rows = lookupRows(f, e);
const types = [...new Set(rows.map(r => r.ct))].filter(Boolean).sort();
if (S.ltype !== 'all') rows = rows.filter(r => r.ct === S.ltype);
if (S.lq) rows = rows.filter(r => r.name.toLowerCase().includes(S.lq.toLowerCase()));
const k = S.lsort.k, dsc = S.lsort.d;
rows.sort((x, y) => { const a = x[k], b = y[k]; if (a == null && b == null) return 0; if (a == null) return 1; if (b == null) return -1; return (typeof a === 'string' ? a.localeCompare(b, 'th') : a - b) * dsc; });
const cols = [['name', tx('ฐานข้อมูล', 'Resource'), ''], ['acq', tx('รูปแบบ', 'Model'), ''], ['use', tx('การใช้ (ครั้ง)', 'Use (uses)'), 'r'], ['up', tx('±ปีก่อน', '± prior yr'), 'r'], ['sea', tx('การค้น/เข้าดู (ครั้ง)', 'Searches/views'), 'r'], ['sp', tx('±ปีก่อน', '± prior yr'), 'r'], ['users', tx('ผู้ใช้ (คน)', 'Users (people)'), 'r'], ['st', tx('ข้อมูลในช่วง', 'Data in period'), '']];
const th = cols.map(c => `
`).join('');
const segs = [['all', tx('ทั้งหมด', 'All')], ...types.map(t => [t, t])].map(([k2, l]) => ``).join('');
const html = card(head(`${tx('ทุกฐาน', 'All resources')} · ${periodLabel(f, e)}${filterNote()}`, tx('คลิกชื่อฐาน = เจาะลึกรายฐาน · คลิกหัวคอลัมน์เพื่อเรียง · ± เทียบช่วงเดียวกันปีก่อน · * บอกรับรายปี ยังไม่มีราคาในระบบ · † ผู้ใช้นับรวมทั้งแพลตฟอร์ม (เช่น Scopus + ScienceDirect)', 'Click a name for resource detail · click a column header to sort · ± vs the same period last year · * annual subscription, no price in the system yet · † users counted for the whole platform (e.g. Scopus + ScienceDirect)'),
`
${segs}
`) +
`
${th}
${body || `
${tx('ไม่มีฐานที่ตรงกับตัวกรอง', 'No resources match the filters')}
`);
return { html, after: () => {
$$('[data-sort]').forEach(h => h.onclick = () => { const kk = h.dataset.sort; S.lsort = { k: kk, d: S.lsort.k === kk ? -S.lsort.d : (kk === 'name' ? 1 : -1) }; render(); });
$$('[data-lt]').forEach(b => b.onclick = () => { S.ltype = b.dataset.lt; render(); });
$$('tbody tr[data-rc]').forEach(tr => tr.onclick = () => { S.deep = tr.dataset.rc; setTab('deep'); });
const inp = $('#lq'); inp.oninput = () => { S.lq = inp.value; const pos = inp.selectionStart; render(); const i2 = $('#lq'); i2.focus(); i2.setSelectionRange(pos, pos); };
$('#lexp').onclick = () => {
const h = tx(['ฐานข้อมูล', 'รูปแบบ', 'การใช้ (ครั้ง)', 'เปลี่ยนแปลงจากปีก่อน (%)', 'การค้น/เข้าดู (ครั้ง)', 'เปลี่ยนแปลงจากปีก่อน (%)', 'ผู้ใช้ (คน)', 'ข้อมูลในช่วง'],
['Resource', 'Model', 'Use', 'Change vs prior year (%)', 'Searches/views', 'Change vs prior year (%)', 'Users', 'Data in period']);
const lines = [h.join(',')].concat(rows.map(r => [r.name, (ACQPILL[r.acq] || ['', ''])[1], r.use, r.up == null ? '' : (r.up * 100).toFixed(1), r.sea ?? '', r.sp == null ? '' : (r.sp * 100).toFixed(1), r.users ?? (r.usup ? '<10' : ''), r.st.t].map(csvCell).join(',')));
download(tx(`ตัวเลขรายฐาน_ปีงบ${f}_${MTH[e]}.csv`, `KMUTT_eresources_by_resource_FY${FY(f)}_${MTH[e]}.csv`), lines.join('\r\n'));
};
} };
}
// ---------------------------------------------------------------- deep dive
// document types reported by standards platforms (shown instead of subjects)
const DOCTYPE_TH = { 'ASTM-ACTIVE': 'มาตรฐานฉบับปัจจุบัน (Active)', 'ASTM-REDLINE': 'ฉบับเทียบการแก้ไข (Redline)', 'ASTM-HISTORICAL': 'มาตรฐานฉบับเก่า (Historical)', 'ASTM-WITHDRAWN': 'มาตรฐานที่ยกเลิกแล้ว (Withdrawn)', 'ASTM-JOURNALS': 'วารสารของ ASTM (Journals)' };
const DOCTYPE_EN = { 'ASTM-ACTIVE': 'Active standards', 'ASTM-REDLINE': 'Redline (tracked changes)', 'ASTM-HISTORICAL': 'Historical standards', 'ASTM-WITHDRAWN': 'Withdrawn standards', 'ASTM-JOURNALS': 'ASTM journals' };
function ensureDeep() {
const f = fy(), e = endM(f);
if (!S.deep || !D.res[S.deep]) {
if (S.res.size === 1) S.deep = [...S.res][0];
else { const rows = lookupRows(f, e).sort((a, b) => b.use - a.use); S.deep = rows.length ? rows[0].rc : Object.keys(D.res)[0]; }
}
}
// search/views are shown only when the provider reports them in a meaningful amount (not for e.g. standards platforms where people open documents directly)
function searchInfo(rc) {
const yrs = D.fys.filter(y => y >= D.firstFY);
const su = yrs.map(y => sumUse(y, 12, r => r.rc === rc, 'sea')), us = yrs.map(y => sumUse(y, 12, r => r.rc === rc));
const S1 = su.reduce((a, b) => a + (b || 0), 0), U1 = us.reduce((a, b) => a + (b || 0), 0);
if (!S1) return { show: false, why: tx('ผู้ให้บริการไม่รายงานการค้น/เข้าดูของฐานนี้ มีเฉพาะการใช้งานจริง จึงแสดงเฉพาะการใช้', 'The provider does not report searches/views for this resource, only actual use, so only use is shown') };
const ratio = S1 / (U1 || 1);
if (ratio < 0.05) return { show: false, why: tx(`การค้น/เข้าดูมีน้อยมาก (ราว ${(ratio * 100).toFixed(1)}% ของการใช้) เพราะผู้ใช้เปิดเอกสารได้โดยตรงโดยไม่ต้องค้น จึงไม่แสดงเป็นกราฟ`, `Searches/views are very few (about ${(ratio * 100).toFixed(1)}% of use) because people open documents directly without searching, so no chart is shown`) };
return { show: true };
}
function pgDeep() {
const f = fy(), e = endM(f);
ensureDeep();
const rc = S.deep, d = D.res[rc], isSc = rc === 'SCOPUS';
const si = searchInfo(rc);
const one = r => r.rc === rc;
const a = sumUse(f, e, one), b = sumUse(f - 1, e, one);
const sa = sumUse(f, e, one, 'sea'), sb = sumUse(f - 1, e, one, 'sea');
const p = platOf(rc), ua = p && U(f, 'YTD', e, p), ub = p && U(f - 1, 'YTD', e, p), sh = p && platShared(p);
const pn = p && D.plat[p] ? platName(p) : '';
const uTxt = ua && ua.u != null ? fmt(ua.u) : ua && ua.sup ? '<10' : '—';
const K = [
[isSc ? tx('การใช้หลัก: Informetrics (ครั้ง)', 'Main use: Informetrics (uses)') : tx('การใช้ (ครั้ง)', 'Use (uses)'), fmt(a), '', vs(chg(a, b), b)],
[isSc ? tx('การค้นหา (ครั้ง)', 'Searches') : tx('การค้น/เข้าดู (ครั้ง)', 'Searches/views'), si.show && sa ? fmt(sa) : '—', '', si.show && sa ? vs(chg(sa, sb), sb) : `${tx('ไม่นำเสนอสำหรับฐานนี้ (ดูหมายเหตุ)', 'Not shown for this resource (see note)')}`],
[tx('ผู้ใช้ไม่ซ้ำ (คน)', 'Unique users (people)'), uTxt + (sh ? '†' : ''), '', ua && ub && ua.u && ub.u ? vs(chg(ua.u, ub.u), ub.u) : sh ? `† ${tx('รวมทั้งแพลตฟอร์ม', 'whole platform:')} ${esc(pn)}` : `${tx('ไม่มีข้อมูลผ่าน OpenAthens', 'No OpenAthens data')}`],
[tx('เข้าใช้ต่อคน', 'Accesses per person'), ua && ua.u ? fmt(ua.a / ua.u, 1) : '—', tx('ครั้ง/คน', 'per person'), ua && ua.a ? tx(`เข้าผ่าน OpenAthens ${fmt(ua.a)} ครั้ง`, `${fmt(ua.a)} accesses via OpenAthens`) : ''],
];
if (CFG.internal && typeof deepCost === 'function') { const x = deepCost(rc, f); if (x) K.push(x); }
const kp = K.map(k => card(`
${k[0]}
${k[1]}${k[2] ? `${k[2]}` : ''}
${k[3]}
`)).join('');
const notes = D.notes.filter(n => n.resource_code === rc);
// left card
let left;
if (isSc) {
const grp = {};
for (const r of D.scopus) if (r.show === 'Y' && r.fm <= e && (r.fy === f || r.fy === f - 1)) { const k = r.code; const o = grp[k] || (grp[k] = { n: r.name, g: tx(r.gth, r.g), go: r.go, a: 0, b: 0 }); if (r.fy === f) o.a += r.v; else o.b += r.v; }
const items = Object.values(grp).filter(o => o.a > 0 || o.b > 0).sort((x, y) => x.go - y.go || y.a - x.a);
const mx = Math.max(...items.map(i => Math.max(i.a, i.b)), 1);
let cur = '', h = '';
items.slice(0, 40).forEach(i => {
if (i.g !== cur) { cur = i.g; h += `
${esc(cur)}
`; }
h += `
${esc(i.n)}${fmt(i.a)} ${pctSpan(chg(i.a, i.b))}
`;
});
left = card(head(`${tx('กิจกรรมบน Scopus', 'Activity on Scopus')} · ${periodShort(e)}`, tx(`หน่วย: ครั้ง · แท่งบน = ${f} แท่งล่าง = ${f - 1}`, `Unit: uses · top bar = ${FY(f)}, bottom bar = ${FY(f - 1)}`)) + `
${h || `
${tx('ไม่มีข้อมูล', 'No data')}
`}
`);
} else {
const sm = {}; let tot = 0;
for (const r of D.subj) if (r.rc === rc && r.fy === f && r.fm <= e) { tot += r.v; if (r.sc !== 'NO_DATA' && !/NO_DATA$/.test(r.sc)) { const l = tx(DOCTYPE_TH, DOCTYPE_EN)[r.sc] || subjName(r); sm[l] = (sm[l] || 0) + r.v; } }
const it = Object.entries(sm).sort((x, y) => y[1] - x[1]).slice(0, 12).map(([l, v]) => ({ l, v, sub: tot ? Math.round(v / tot * 100) + '%' : '' }));
const std = d.content_type === 'Standards', book = d.content_type === 'E-Book';
const ttl = std ? tx('ประเภทเอกสารที่ถูกใช้ (ครั้ง)', 'Document types used (uses)') : book ? tx('หมวดหนังสือที่ถูกใช้มากที่สุด (ครั้ง)', 'Most used book categories (uses)') : tx('สาขาของวารสารที่ถูกใช้มากที่สุด (ครั้ง)', 'Most used journal subjects (uses)');
const sub = std ? tx(`${periodShort(e)} ${yy(f)} · % ของการใช้ทั้งฐาน · ฐานมาตรฐานรายงานตามประเภทเอกสาร (ฉบับปัจจุบัน ฉบับเก่า ฯลฯ) ไม่ได้แยกตามสาขาวิชา`, `${periodShort(e)} ${yy(f)} · % of all use of this resource · standards platforms report by document type (active, historical, etc.), not by subject`)
: tx(`${periodShort(e)} ${yy(f)} · % ของการใช้ทั้งฐาน · ${book ? 'หมวดของหนังสือที่ถูกอ่าน' : 'สาขาของวารสารที่ถูกเปิด'} ไม่ใช่สาขาของผู้อ่าน`, `${periodShort(e)} ${yy(f)} · % of all use of this resource · ${book ? 'category of the book read' : 'subject of the journal opened'}, not the reader's field`);
left = card(head(ttl, sub) + (it.length ? hbars(it, { unit: UNIT_USE(), lw: std ? '58%' : '44%' }) : `
${tx(`ผู้ให้บริการไม่ได้ส่งข้อมูล${std ? 'ประเภทเอกสาร' : 'สาขา'}มา หรือยังไม่ได้จัด${std ? 'ประเภท' : 'สาขา'}ให้ฐานนี้`, `The provider has not sent ${std ? 'document type' : 'subject'} data or this resource has not been classified yet`)}
`, '', 'margin-top:12px');
}
const sc = I && cur && cur.sep[0], scp = I && prev && prev.sep[0];
const scCpu = x => x && (meth === 'ADHOC' ? x.cpuad : x.cpupa);
const html = `${card(ctl, '', 'padding:10px 14px;margin-bottom:12px')}
${card(head(tx(`ค่าใช้จ่าย (ล้านบาท) และต้นทุนต่อการใช้ (บาท/ครั้ง) · ${cpLab(code)}`, `Cost (million THB) and cost per use (THB/use) · ${cpLab(code)}`), `${scope} · ${tx('ทุกปีเทียบช่วงเดียวกัน · ไม่ขึ้นกับตัวกรอง', 'same period every year · not affected by filters')}`, `
${tx('ค่าใช้จ่ายของช่วง', 'Cost of the period')}${meth === 'ADHOC' ? 'เฉลี่ยรายฐาน' : tx('ต้นทุน/ครั้ง', 'Cost/use')}
`) +
(ys.length ? `` : `
${tx('ยังไม่มีข้อมูลครบช่วงนี้', 'No complete data for this period yet')}
`) : ''}
${card(head(tx('ฐานไหนถูกนำมาคิดต้นทุน', 'Which resources are in the cost'), tx('ตัวตั้งและตัวหารมาจากฐานชุดเดียวกันเสมอ', 'Cost and use always come from the same set of resources')) +
row(tx('นำมาคิด', 'Included'), 'up', cur ? cur.list : (cc ? cc.list : ''), tx('บอกรับรายปี มีราคาในระบบ', 'Annual subscription with a price in the system')) +
(I && cur && cur.sep.length ? row('แสดงแยก', 'wr', cur.sep.map(r => (D.res[r.rc] || {}).resource_name || r.rc).join(' · '), 'นับการใช้คนละประเภท (Informetrics) จึงไม่นำมาเฉลี่ยรวม — ตามรายงาน PA') : '') +
(unpriced.length ? row(tx('ยังไม่นำมาคิด', 'Not yet'), 'wr', unpriced.join(' · '), tx('บอกรับรายปี แต่ยังไม่มีราคาหรือยังไม่มีข้อมูลการใช้ในระบบ — เพิ่มเมื่อครบ', 'Annual subscription, but no price or no usage data in the system yet — added once both are in')) : '') +
(unc.length ? row(tx('ยังไม่นำมาคิด', 'Not yet'), 'wr', unc.join(' · '), tx('รอยืนยันว่าเป็นบอกรับรายปีหรือซื้อขาด', 'Waiting to confirm whether it is a subscription or a perpetual purchase')) : '') +
row(tx('ไม่นำมาคิด', 'Excluded'), 'dn', perp.join(' · '), tx('ซื้อขาด ไม่มีค่าบำรุงรายปี', 'Perpetual purchase, no annual fee')) +
row(tx('ไม่นำมาคิด', 'Excluded'), 'dn', tx(`ฐานจาก สป.อว. (${nM} ฐาน)`, `Resources from MHESI (${nM})`), tx('มจธ. ไม่ได้จ่าย', 'Not paid by KMUTT')))}
${CFG.internal ? `
ภายในราคาและต้นทุนรายฐานทั้งปี — ดูหน้า
` : `
${tx('ยังไม่เปิดเผย', 'Not published')}${tx('ต้นทุนรายฐาน — ไม่เผยแพร่ราคารายฐานเพื่อรักษาเงื่อนไขกับผู้ให้บริการและหน่วยงานอื่น', 'Cost per resource — prices of individual resources are not published, to respect agreements with providers and other institutions')}
`}
${tbl}`;
return { html, after: () => {
$$('[data-cper]').forEach(x => x.onclick = () => { S.cper = x.dataset.cper; render(); });
$$('[data-cmeth]').forEach(x => x.onclick = () => { S.cmeth = x.dataset.cmeth; render(); });
if (!ys.length) return;
mount($('#c_combo'), el => combo(el, { cats: ys.map(y => ({ k: y, lab: String(FY(y)), proj: !!cpAt(y, code, meth).proj })), bars: { vals: ys.map(y => cpAt(y, code, meth).cost / 1e6), color: '#E9DDB8' },
line: { vals: ys.map(y => cpAt(y, code, meth).cpu), color: '#1F2124' }, xTitle: FYAXIS(), y1: tx('ค่าใช้จ่ายของช่วง (ล้านบาท)', 'Cost of the period (million THB)'), y2: tx('ต้นทุนต่อการใช้ (บาท/ครั้ง)', 'Cost per use (THB/use)'), tip }));
} };
}
// ---------------------------------------------------------------- users
function pgUsers() {
const f = fy(), e = endM(f), plat = userPlat() || 'ALL';
const pn = platName(plat);
const tot = U(f, 'YTD', e, plat);
const uf = Object.keys(D.usersMaxYTD).map(Number).sort();
const deg = D.group.filter(g => g.breakdown === 'DEGREE').sort((a, b) => a.sort_order - b.sort_order).map(g => { const r = U(f, 'YTD', e, plat, 'DEGREE', g.group_code); return `${grpName(g)} ${r && r.u != null ? fmt(r.u) : r && r.sup ? '<10' : '—'}`; }).join(' · ');
const pl = Object.keys(D.plat).filter(k => k !== 'ALL').map(k => { const r = U(f, 'YTD', e, k); return { k, l: platName(k), v: r && r.u }; }).filter(x => x.v).sort((a, b) => b.v - a.v).slice(0, 12);
const html = `
${card(head(tx('ผู้ใช้ไม่ซ้ำรายปีงบ (คน)', 'Unique users per fiscal year (people)'), `${esc(pn)}${uOpen(D.curFY) ? ` · * ${FY(D.curFY)} ${tx('ถึง', 'through')} ${MTH[D.usersMaxYTD[D.curFY]]}` : ''} · ${tx('เส้นประ = เปลี่ยนวิธีนับ (เม.ย. 68)', 'dashed line = counting method changed (Apr 2025)')}`) + ``)}
${card(head(tx('ผู้ใช้รายไตรมาส (คน)', 'Users per quarter (people)'), `${esc(pn)} · ${FY(f)} ${tx('เทียบ', 'vs')} ${FY(f - 1)} · ${tx('นับไม่ซ้ำภายในไตรมาส', 'unique within each quarter')}`, `
${tx('ไม่มีข้อมูลสาขาในฐานที่เลือก', 'No subject data for the selected resources')}
`))}
${card(head(tx('3 มุมมอง แยกกันชัด', '3 separate views'), '') +
`
${tx('① การใช้เนื้อหา (ครั้ง) ตามสาขาของเนื้อหา', '① Content use (uses) by subject of the content')}
${tx('นับการเปิดเนื้อหาเต็ม แล้วจัดตามสาขาของวารสาร/หนังสือ — ไม่ใช่สาขาของผู้อ่าน', 'Full-content opens, grouped by subject of the journal/book — not the reader\'s field')}
${tx('สาธารณะ', 'Public')}
${tx('② ผู้ใช้ (คน) ตามคณะของผู้ใช้', '② Users (people) by the user\'s faculty')}
${tx('คนที่เข้าผ่าน OpenAthens จัดตามคณะจากรหัสนักศึกษา', 'People signing in via OpenAthens, grouped by faculty from the student ID')}
${tx('ภายใน · รอยืนยันชื่อคณะ', 'Internal · faculty names to be confirmed')}
${tx('③ การเข้าใช้ (ครั้ง) ตามคณะของผู้ใช้', '③ Accesses (count) by the user\'s faculty')}
${tx('จำนวนครั้งที่เข้าผ่าน OpenAthens (ไม่ใช่การดาวน์โหลด)', 'Number of sign-ins via OpenAthens (not downloads)')}
${tx('ภายใน · รอยืนยันชื่อคณะ', 'Internal · faculty names to be confirmed')}
`)}
${card(`
${fmt(cls)}
${tx('ครั้งที่จัดสาขาได้', 'uses with a subject')}
${fmt(Math.max(useAll - cls, 0))}
${tx('ครั้งที่ยังไม่มีข้อมูลสาขา', 'uses without subject data yet')}
${tx('ฐานใหญ่ที่ผู้ให้บริการยังไม่ส่งข้อมูลสาขา', 'Large resources whose providers send no subject data yet')}: ${esc(top.join(' · ') || '—')}
`;
const DEF = LANG === 'en' ? [['Use (uses)', 'Full-content opens (COUNTER R5 Total_Item_Requests) · Hibrary/2eLibrary = loans (+ reads) · Scopus = Informetrics'], ['Searches/views', 'COUNTER = item views (Total_Item_Investigations) · Scopus = searches · not available for some resources'],
['Users (people)', 'Accounts that signed in via OpenAthens at least once in the period · excludes on-campus IP access · counted per platform'], ['Subjects', 'Content use grouped by subject of the journal/book, not the reader\'s field'],
['Vs prior year', 'Same months only, months with complete data only'], ['Cost per use', 'Cost of the period (annual price × months ÷ 12 · per quarter = price ÷ 4) ÷ use in the same period, for the same set of priced annual subscriptions · default = latest complete quarter · full year (projected) = annual price ÷ use × 12 ÷ months'],
['Preliminary figures', 'Months not yet locked may be revised by providers · periods already reported for PA are locked'], ['Small groups', `Groups under ${D.minG} people are not shown`], ['Acquisition model', 'Annual subscription · perpetual · to be confirmed · provided by MHESI — always kept apart; other groups\' use is never averaged into cost']]
: [['การใช้ (ครั้ง)', 'เปิดเนื้อหาเต็ม (COUNTER R5 Total_Item_Requests) · Hibrary/2eLibrary = ยืม(+อ่าน) · Scopus = Informetrics'], ['การค้น/เข้าดู (ครั้ง)', 'COUNTER = เข้าดูรายการ (Total_Item_Investigations) · Scopus = การค้น · บางฐานไม่มี'],
['ผู้ใช้ (คน)', 'บัญชีที่เข้าผ่าน OpenAthens อย่างน้อย 1 ครั้งในช่วง · ไม่รวมการเข้าผ่าน IP ในมหาวิทยาลัย · นับแยกแต่ละแพลตฟอร์ม'], ['สาขาวิชา', 'การใช้เนื้อหาจัดตามสาขาของวารสาร/หนังสือ ไม่ใช่สาขาของผู้อ่าน'],
['เทียบปีก่อน', 'ช่วงเดือนเดียวกัน เฉพาะเดือนที่ข้อมูลครบ'], ['ต้นทุนต่อการใช้', 'ค่าใช้จ่ายของช่วง (ราคาทั้งปี × เดือน ÷ 12 · รายไตรมาส = ราคา ÷ 4) ÷ การใช้ช่วงเดียวกัน ของฐานที่บอกรับรายปีและมีราคาชุดเดียวกัน · ค่าเริ่มต้น = ไตรมาสล่าสุดที่ข้อมูลครบ · ทั้งปี (คาดการณ์) = ราคาทั้งปี ÷ การใช้ × 12 ÷ จำนวนเดือน'],
['ตัวเลขเบื้องต้น', 'เดือนที่ยังไม่ล็อก ผู้ให้บริการอาจแก้ย้อนหลัง · ช่วงที่รายงาน PA แล้วถูกล็อก'], ['กลุ่มเล็ก', `กลุ่มที่มีน้อยกว่า ${D.minG} คน ไม่แสดงตัวเลข`], ['รูปแบบการได้มา', 'บอกรับรายปี · ซื้อขาด · รอยืนยัน · สป.อว. จัดให้ — แยกกันเสมอ ไม่นำการใช้ของกลุ่มอื่นมาเฉลี่ยต้นทุน']];
const DL = [['pub_usage_month', 'การใช้รายเดือนรายฐาน', 'Monthly use by resource'], ['pub_users_period', 'ผู้ใช้ไม่ซ้ำรายช่วง', 'Unique users by period'], ['pub_subject_month', 'การใช้ตามสาขา', 'Use by subject'], ['pub_cost_fy', 'งบและต้นทุนต่อการใช้', 'Budget and cost per use'], ['pub_coverage', 'สถานะข้อมูลรายเดือน', 'Monthly data status'], ['pub_dim_resource', 'รายชื่อฐาน', 'Resource list']];
const rs = D.meta.data_note ? `
${tx(`ช่องจาง = เดือนหลัง ${MTH[e]} ยังอยู่ระหว่างรวบรวม ยังไม่นับในตัวเลข`, `Faded cells = months after ${MTH[e]}, still being collected and not yet counted`)}
` : '') + `
${hm}
`)}
${card(head(tx('นิยามตัวชี้วัด', 'Indicator definitions'), tx('ใช้คำเดียวกันทุกหน้า', 'The same terms are used on every page')) + `
${DEF.map(d => `
${d[0]}
${esc(d[1])}
`).join('')}
`)}
${card(head(tx('แหล่งที่มา', 'Sources'), '') + `
${tx('รายงาน COUNTER R5 จากผู้ให้บริการ · ThaiLIS/UniNet (ฐาน สป.อว.) · รายงานของ Hibrary, 2eLibrary, Scopus · OpenAthens (สรุปจำนวนคน ไม่มีข้อมูลรายบุคคล)', 'COUNTER R5 reports from providers · ThaiLIS/UniNet (MHESI resources) · Hibrary, 2eLibrary and Scopus reports · OpenAthens (people counts only, no personal data)')}
${tx('สถิติการใช้ฐานข้อมูลออนไลน์', 'Online Database Usage Statistics')}${tx('สำนักหอสมุด มหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรี', 'KMUTT Library, King Mongkut\'s University of Technology Thonburi')}