remove debug logging, clean up
This commit is contained in:
parent
c056f5cf2e
commit
66d3fbfe53
93
app.js
93
app.js
@ -17,7 +17,6 @@ document.getElementById('excelFile').addEventListener('change', function(e) {
|
|||||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||||
var rows = XLSX.utils.sheet_to_json(ws, {defval: ''});
|
var rows = XLSX.utils.sheet_to_json(ws, {defval: ''});
|
||||||
if (!rows.length) { document.getElementById('statusMsg').textContent = 'Файл пуст'; return; }
|
if (!rows.length) { document.getElementById('statusMsg').textContent = 'Файл пуст'; return; }
|
||||||
console.log('Данные файла:', rows);
|
|
||||||
build(rows);
|
build(rows);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
document.getElementById('statusMsg').textContent = 'Ошибка: ' + err.message;
|
document.getElementById('statusMsg').textContent = 'Ошибка: ' + err.message;
|
||||||
@ -56,98 +55,66 @@ function done(s) {
|
|||||||
|
|
||||||
function build(raw) {
|
function build(raw) {
|
||||||
var d = norm(raw);
|
var d = norm(raw);
|
||||||
console.log('Обработанные данные:', d);
|
|
||||||
console.log('Количество строк:', d.length);
|
|
||||||
|
|
||||||
d.forEach(function(r, i) {
|
|
||||||
console.log('Строка ' + i + ':\n ФИО:', r.name, '\n Статус:', r.status, '\n Обработанный статус:', r.status ? 'done' : 'not done');
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('uploadSection').classList.add('hidden');
|
document.getElementById('uploadSection').classList.add('hidden');
|
||||||
document.getElementById('dashboard').classList.remove('hidden');
|
document.getElementById('dashboard').classList.remove('hidden');
|
||||||
|
|
||||||
var allN = {}, doneN = {}, scores = [], train = {}, divs = {}, trainers = {};
|
var allN = {}, scores = [], train = {}, trainers = {};
|
||||||
|
|
||||||
d.forEach(function(r) {
|
d.forEach(function(r) {
|
||||||
allN[r.name] = true;
|
allN[r.name] = true;
|
||||||
if (done(r.status)) doneN[r.name] = true;
|
|
||||||
|
|
||||||
var sc = parseFloat(r.score);
|
var sc = parseFloat(r.score);
|
||||||
if (!isNaN(sc) && sc > 0) scores.push(sc);
|
if (!isNaN(sc) && sc > 0) scores.push(sc);
|
||||||
|
|
||||||
var c = r.course || 'Без темы';
|
var c = r.course || 'Без темы';
|
||||||
if (!train[c]) train[c] = {all:{}, ok:{}, sc:[], tr:{}, div:{}};
|
if (!train[c]) train[c] = {all:{}, ok:{}, sc:[], tr:{}};
|
||||||
train[c].all[r.name] = true;
|
train[c].all[r.name] = true;
|
||||||
if (done(r.status)) train[c].ok[r.name] = true;
|
if (r.status === 'завершен' || r.status === 'пройден') train[c].ok[r.name] = true;
|
||||||
if (!isNaN(sc) && sc > 0) train[c].sc.push(sc);
|
if (!isNaN(sc) && sc > 0) train[c].sc.push(sc);
|
||||||
if (r.trainer) train[c].tr[r.trainer] = true;
|
if (r.trainer) train[c].tr[r.trainer] = true;
|
||||||
|
|
||||||
var dv = r.division || '—';
|
|
||||||
if (!train[c].div[dv]) train[c].div[dv] = {all:{}, ok:{}};
|
|
||||||
train[c].div[dv].all[r.name] = true;
|
|
||||||
if (done(r.status)) train[c].div[dv].ok[r.name] = true;
|
|
||||||
|
|
||||||
if (!divs[dv]) divs[dv] = {all:{}, ok:{}};
|
|
||||||
divs[dv].all[r.name] = true;
|
|
||||||
if (done(r.status)) divs[dv].ok[r.name] = true;
|
|
||||||
|
|
||||||
var tr = r.trainer || '—';
|
var tr = r.trainer || '—';
|
||||||
if (!trainers[tr]) trainers[tr] = {n:0, sc:[]};
|
if (!trainers[tr]) trainers[tr] = {n:0, sc:[], topics:[]};
|
||||||
trainers[tr].n++;
|
trainers[tr].n++;
|
||||||
if (!isNaN(sc) && sc > 0) trainers[tr].sc.push(sc);
|
if (!isNaN(sc) && sc > 0) trainers[tr].sc.push(sc);
|
||||||
|
if (c && c !== 'Без темы') trainers[tr].topics.push(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
var totalN = Object.keys(allN).length;
|
var totalN = Object.keys(allN).length;
|
||||||
var doneNn = Object.keys(doneN).length;
|
|
||||||
document.getElementById('statTotal').textContent = totalN;
|
document.getElementById('statTotal').textContent = totalN;
|
||||||
document.getElementById('statDone').textContent = doneNn;
|
|
||||||
document.getElementById('statRate').textContent = totalN ? Math.round(doneNn/totalN*100)+'%' : '0%';
|
|
||||||
document.getElementById('statAvg').textContent = scores.length ? (scores.reduce(function(a,b){return a+b},0)/scores.length).toFixed(1) : '—';
|
document.getElementById('statAvg').textContent = scores.length ? (scores.reduce(function(a,b){return a+b},0)/scores.length).toFixed(1) : '—';
|
||||||
|
|
||||||
var tKeys = Object.keys(train);
|
document.querySelector('#tblTrainings tbody').innerHTML = Object.keys(train).map(function(k){
|
||||||
var tDone = tKeys.map(function(k){return Object.keys(train[k].ok).length});
|
|
||||||
var tAll = tKeys.map(function(k){return Object.keys(train[k].all).length});
|
|
||||||
|
|
||||||
barH('chartTrainings', tKeys, [
|
|
||||||
{label:'Всего', data:tAll, bg:'rgba(33,150,243,0.4)', bc:'#2196F3'},
|
|
||||||
{label:'Прошли', data:tDone, bg:'rgba(76,175,80,0.7)', bc:'#4caf50'}
|
|
||||||
]);
|
|
||||||
|
|
||||||
var dvNames = Object.keys(divs);
|
|
||||||
var dvColors = ['#2196F3','#4caf50','#ff9800','#9c27b0','#00bcd4','#ff5722','#8bc34a','#e91e63'];
|
|
||||||
var divDatasets = dvNames.map(function(dv,i){
|
|
||||||
return {label:dv, data:tKeys.map(function(tk){return train[tk].div[dv]?Object.keys(train[tk].div[dv].ok).length:0}),
|
|
||||||
bg:dvColors[i%dvColors.length]+'cc', bc:dvColors[i%dvColors.length]};
|
|
||||||
});
|
|
||||||
stacked('chartTrainDiv', tKeys, divDatasets);
|
|
||||||
|
|
||||||
bar('chartDiv', dvNames, [
|
|
||||||
{label:'Всего', data:dvNames.map(function(d){return Object.keys(divs[d].all).length}), bg:'rgba(33,150,243,0.6)', bc:'#2196F3'},
|
|
||||||
{label:'Прошли', data:dvNames.map(function(d){return Object.keys(divs[d].ok).length}), bg:'rgba(76,175,80,0.6)', bc:'#4caf50'}
|
|
||||||
]);
|
|
||||||
|
|
||||||
var trNames = Object.keys(trainers);
|
|
||||||
bar('chartTrainers', trNames, [
|
|
||||||
{label:'Занятий', data:trNames.map(function(t){return trainers[t].n}), bg:'rgba(255,152,0,0.6)', bc:'#ff9800'}
|
|
||||||
]);
|
|
||||||
|
|
||||||
var sd = {5:0,4:0,3:0,2:0,1:0};
|
|
||||||
scores.forEach(function(s){var r=Math.round(s);if(sd[r]!==undefined)sd[r]++});
|
|
||||||
doughnut('chartScores', Object.keys(sd).map(function(k){return 'Оценка '+k}), Object.values(sd),
|
|
||||||
['#4caf50','#8bc34a','#ff9800','#ff5722','#f44336']);
|
|
||||||
|
|
||||||
var tAvg = tKeys.map(function(k){var sc=train[k].sc;return sc.length?(sc.reduce(function(a,b){return a+b},0)/sc.length).toFixed(1):0});
|
|
||||||
bar('chartTrainScores', tKeys, [{label:'Средняя оценка', data:tAvg,
|
|
||||||
bg:tAvg.map(function(v){return v>=4?'rgba(76,175,80,0.7)':v>=3?'rgba(255,152,0,0.7)':'rgba(244,67,54,0.7)'}),
|
|
||||||
bc:tAvg.map(function(v){return v>=4?'#4caf50':v>=3?'#ff9800':'#f44336'})}]);
|
|
||||||
|
|
||||||
document.querySelector('#tblTrainings tbody').innerHTML = tKeys.map(function(k){
|
|
||||||
var t=train[k], tot=Object.keys(t.all).length, ok=Object.keys(t.ok).length;
|
var t=train[k], tot=Object.keys(t.all).length, ok=Object.keys(t.ok).length;
|
||||||
var avg=t.sc.length?(t.sc.reduce(function(a,b){return a+b},0)/t.sc.length).toFixed(1):'—';
|
var avg=t.sc.length?(t.sc.reduce(function(a,b){return a+b},0)/t.sc.length).toFixed(1):'—';
|
||||||
var trn=Object.keys(t.tr).join(', ')||'—';
|
var trn=Object.keys(t.tr).join(', ')||'—';
|
||||||
return '<tr><td><b>'+k+'</b></td><td>'+ok+'</td><td>'+(tot-ok)+'</td><td>'+(tot?Math.round(ok/tot*100):0)+'%</td><td>'+avg+'</td><td>'+trn+'</td></tr>';
|
return '<tr><td><b>'+k+'</b></td><td>'+ok+'</td><td>'+(tot-ok)+'</td><td>'+(tot?Math.round(ok/tot*100):0)+'%</td><td>'+avg+'</td><td>'+trn+'</td></tr>';
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
|
document.querySelector('#tblTrainers tbody').innerHTML = Object.keys(trainers).map(function(tr) {
|
||||||
|
var t = trainers[tr];
|
||||||
|
var topics = (t.topics && t.topics.length > 0) ? Array.from(new Set(t.topics)).join(', ') : '—';
|
||||||
|
var avgScore = t.sc && t.sc.length ? (t.sc.reduce(function(a,b){return a+b},0)/t.sc.length).toFixed(1) : '—';
|
||||||
|
return '<tr><td><b>' + tr + '</b></td><td>' + t.n + '</td><td>' + topics + '</td><td>' + avgScore + '</td></tr>';
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
cph = {};
|
||||||
|
d.forEach(function(r){if(!cph[r.name])cph[r.name]={d:r.division||'',c:{} };if(r.course)cph[r.name].c[r.course]=true});
|
||||||
|
var champs=Object.keys(cph).map(function(n){return{name:n,div:cph[n].d,cnt:Object.keys(cph[n].c).length}})
|
||||||
|
.sort(function(a,b){return b.cnt-a.cnt}).slice(0,10);
|
||||||
|
document.querySelector('#tblChamp tbody').innerHTML = champs.map(function(c){
|
||||||
|
return '<tr><td>'+c.name+'</td><td>'+c.div+'</td><td>'+c.cnt+'</td></tr>';
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
var noT = Object.keys(allN);
|
||||||
|
document.querySelector('#tblNoTrain tbody').innerHTML = noT.slice(0,20).map(function(n){
|
||||||
|
return '<tr><td>'+n+'</td><td>'+(cph[n]?cph[n].d:'')+'</td></tr>';
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
document.querySelector('#tblAll tbody').innerHTML = d.map(function(r){
|
||||||
|
return '<tr><td>'+(r.name||'')+'</td><td>'+(r.division||'')+'</td><td>'+(r.course||'')+'</td><td>'+(r.status||'')+'</td><td>'+(r.score||'')+'</td><td>'+(r.trainer||'')+'</td></tr>';
|
||||||
|
}).join('');
|
||||||
|
|
||||||
var cph = {};
|
var cph = {};
|
||||||
d.forEach(function(r){if(!cph[r.name])cph[r.name]={d:r.division||'',c:{} };if(r.course)cph[r.name].c[r.course]=true});
|
d.forEach(function(r){if(!cph[r.name])cph[r.name]={d:r.division||'',c:{} };if(r.course)cph[r.name].c[r.course]=true});
|
||||||
var champs=Object.keys(cph).map(function(n){return{name:n,div:cph[n].d,cnt:Object.keys(cph[n].c).length}})
|
var champs=Object.keys(cph).map(function(n){return{name:n,div:cph[n].d,cnt:Object.keys(cph[n].c).length}})
|
||||||
|
|||||||
25
index.html
25
index.html
@ -27,29 +27,14 @@
|
|||||||
<section id="dashboard" class="dashboard hidden">
|
<section id="dashboard" class="dashboard hidden">
|
||||||
<div class="stats-row">
|
<div class="stats-row">
|
||||||
<div class="stat-card"><div class="stat-value" id="statTotal">0</div><div class="stat-label">Сотрудников</div></div>
|
<div class="stat-card"><div class="stat-value" id="statTotal">0</div><div class="stat-label">Сотрудников</div></div>
|
||||||
<div class="stat-card"><div class="stat-value" id="statDone">0</div><div class="stat-label">Прошли обучение</div></div>
|
|
||||||
<div class="stat-card"><div class="stat-value" id="statRate">0%</div><div class="stat-label">% прохождения</div></div>
|
|
||||||
<div class="stat-card"><div class="stat-value" id="statAvg">—</div><div class="stat-label">Средняя оценка</div></div>
|
<div class="stat-card"><div class="stat-value" id="statAvg">—</div><div class="stat-label">Средняя оценка</div></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="chart-card wide">
|
<div class="table-card wide">
|
||||||
<h3>Тренинги — сколько человек прошли</h3>
|
<h3>📋 Нагрузка тренеров (ФИО + тренинги + частота)</h3>
|
||||||
<canvas id="chartTrainings"></canvas>
|
<table id="tblTrainers"><thead><tr>
|
||||||
</div>
|
<th>ФИО тренера</th><th>Проведено тренингов</th><th>Частота (темы)</th><th>Всего обучено</th>
|
||||||
|
</tr></thead><tbody></tbody></table>
|
||||||
<div class="charts-row">
|
|
||||||
<div class="chart-card"><h3>Тренинги по дивизионам</h3><canvas id="chartTrainDiv"></canvas></div>
|
|
||||||
<div class="chart-card"><h3>Дивизионы — кто прошёл</h3><canvas id="chartDiv"></canvas></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="charts-row">
|
|
||||||
<div class="chart-card"><h3>Нагрузка тренеров</h3><canvas id="chartTrainers"></canvas></div>
|
|
||||||
<div class="chart-card"><h3>Оценки</h3><canvas id="chartScores"></canvas></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chart-card wide">
|
|
||||||
<h3>Средняя оценка по тренингам</h3>
|
|
||||||
<canvas id="chartTrainScores"></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-card wide">
|
<div class="table-card wide">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user