Compare commits
9 Commits
1ddcfd2c80
...
700feee1d5
| Author | SHA1 | Date | |
|---|---|---|---|
| 700feee1d5 | |||
| c862d7b56f | |||
| 65a553787c | |||
| 0ca54f014d | |||
| 6449e96452 | |||
| fb0c3a03de | |||
| d260636903 | |||
| dad10d2467 | |||
| 04c0bfb154 |
721
index.html
721
index.html
File diff suppressed because one or more lines are too long
@ -1,5 +0,0 @@
|
||||
flask>=3.0
|
||||
flask-cors>=4.0
|
||||
python-docx>=1.0
|
||||
reportlab>=4.0
|
||||
requests>=2.31
|
||||
973
script.js
Normal file
973
script.js
Normal file
@ -0,0 +1,973 @@
|
||||
var ALL_EVENTS=[{"id":1,"sec":0,"b":6,"s":"warn","p":45,"due":"31.12.2026","done":"—","dname":"Сертификаты","r":"Куратор","t":"Продолжить обучение","ai":"Обучение ведётся","h":["15.01"]},
|
||||
{"id":2,"sec":0,"b":0,"s":"done","p":100,"due":"31.03.2026","done":"28.03.2026","dname":"Анализ","r":"Директор","t":"Провести анализ","ai":"Завершён","h":["10.01"]},
|
||||
{"id":3,"sec":0,"b":0,"s":"warn","p":50,"due":"31.12.2026","done":"—","dname":"Протоколы","r":"Директор","t":"Совещания","ai":"","h":[]}];
|
||||
var cu=null,evs=[],tab="events",curSub=null;
|
||||
var secs=["\u0420\u0430\u0437\u0434\u0435\u043B I. \u041E\u0431\u0443\u0447\u0435\u043D\u0438\u0435, \u043A\u043E\u043C\u043F\u0435\u0442\u0435\u043D\u0446\u0438\u0438 \u0438 \u043A\u0443\u043B\u044C\u0442\u0443\u0440\u0430 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438","\u0420\u0430\u0437\u0434\u0435\u043B II. \u0422\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u044C \u0438 \u043D\u0430\u0434\u0435\u0436\u043D\u043E\u0441\u0442\u044C","\u0420\u0430\u0437\u0434\u0435\u043B III. \u0413\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u044C \u043A \u0427\u0421","\u0420\u0430\u0437\u0434\u0435\u043B IV. \u041A\u043E\u043C\u043C\u0443\u043D\u0438\u043A\u0430\u0446\u0438\u0438 \u0438 \u0432\u043E\u0432\u043B\u0435\u0447\u0435\u043D\u043D\u043E\u0441\u0442\u044C","\u0420\u0430\u0437\u0434\u0435\u043B V. \u0426\u0438\u0444\u0440\u043E\u0432\u0438\u0437\u0430\u0446\u0438\u044F \u0438 \u0438\u043D\u043D\u043E\u0432\u0430\u0446\u0438\u0438"];
|
||||
var brs=["\u0414\u0438\u0440\u0435\u043A\u0446\u0438\u044F \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0439 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438","\u041E\u0431\u044A\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435 \u00AB\u0414\u0438\u0432\u0438\u0437\u0438\u043E\u043D \u00AB\u0421\u0435\u0442\u044C\u00BB","\u0414\u0438\u0432\u0438\u0437\u0438\u043E\u043D \u043F\u043E \u043A\u043E\u0440\u043F\u043E\u0440\u0430\u0442\u0438\u0432\u043D\u043E\u043C\u0443 \u0431\u0438\u0437\u043D\u0435\u0441\u0443","\u0414\u0438\u0432\u0438\u0437\u0438\u043E\u043D \u043F\u043E \u0440\u043E\u0437\u043D\u0438\u0447\u043D\u043E\u043C\u0443 \u0431\u0438\u0437\u043D\u0435\u0441\u0443","\u0421\u0435\u0440\u0432\u0438\u0441\u043D\u0430\u044F \u0444\u0430\u0431\u0440\u0438\u043A\u0430","\u0414\u0438\u0440\u0435\u043A\u0446\u0438\u044F \u00AB\u0422\u0435\u043B\u0435\u043A\u043E\u043C \u041A\u043E\u043C\u043F\u043B\u0435\u043A\u0442\u00BB","\u041A\u043E\u0440\u043F\u043E\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0443\u043D\u0438\u0432\u0435\u0440\u0441\u0438\u0442\u0435\u0442","\u0414\u0438\u0440\u0435\u043A\u0446\u0438\u044F \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0435\u043A\u0442\u0430\u043C\u0438","\u0414\u0438\u0432\u0438\u0437\u0438\u043E\u043D \u0446\u0438\u0444\u0440\u043E\u0432\u043E\u0433\u043E \u0431\u0438\u0437\u043D\u0435\u0441\u0430"];
|
||||
var stn={warn:"\u0412 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435",late:"\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E",done:"\u0418\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u043E"};
|
||||
var stc={warn:"a",late:"r",done:"g"};
|
||||
var USR={curator:{n:"\u041A\u0443\u0440\u0430\u0442\u043E\u0440 \u041F\u0411",bg:0},admin:{n:"\u0410\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440",bg:0},dpp:{n:"\u0414\u0438\u0440\u0435\u043A\u0442\u043E\u0440 \u0414\u041F\u0411",bg:0},ivanov:{n:"\u0418\u0432\u0430\u043D\u043E\u0432 \u0418\u0432\u0430\u043D",bg:1},petrov:{n:"\u041F\u0435\u0442\u0440\u043E\u0432 \u041F\u0435\u0442\u0440",bg:2},sidorov:{n:"\u0421\u0438\u0434\u043E\u0440\u043E\u0432 \u0421\u0438\u0434\u043E\u0440",bg:3},kozhin:{n:"\u041A\u043E\u0436\u0438\u043D \u0410.\u041C.",bg:4},ismailov:{n:"\u0418\u0441\u043C\u0430\u0438\u043B\u043E\u0432 \u0420.\u041A.",bg:1},nurpeisov:{n:"\u041D\u0443\u0440\u043F\u0435\u0438\u0441\u043E\u0432 \u0414.\u0410.",bg:5},suleimenov:{n:"\u0421\u0443\u043B\u0435\u0439\u043C\u0435\u043D\u043E\u0432 \u041A.\u0422.",bg:6},kassenov:{n:"\u041A\u0430\u0441\u0435\u043D\u043E\u0432 \u0411.\u0411.",bg:7},serikov:{n:"\u0421\u0435\u0440\u0438\u043A\u043E\u0432 \u0415.\u0421.",bg:8},zhunusov:{n:"\u0416\u0443\u043D\u0443\u0441\u043E\u0432 \u0410.\u0410.",bg:2},muratov:{n:"\u041C\u0443\u0440\u0430\u0442\u043E\u0432 \u0410.\u0422.",bg:3},bakirov:{n:"\u0411\u0430\u043A\u0438\u0440\u043E\u0432 \u0422.\u041D.",bg:4}};
|
||||
|
||||
function esc(s){
|
||||
return String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")
|
||||
}
|
||||
function nl2c(s){
|
||||
if(!s)return"";
|
||||
return s.split(String.fromCharCode(10)).join(", ")
|
||||
}
|
||||
function subResp(r,letter){
|
||||
if(!r)return"";
|
||||
var parts=r.split(String.fromCharCode(10));
|
||||
var cur="",found=false;
|
||||
for(var i=0;i<parts.length;i++){
|
||||
var p=parts[i].trim();
|
||||
if(!p)continue;
|
||||
var m=p.match(/^([a-z\u0430-\u044F]+)[\u0029\)]/);
|
||||
if(m){
|
||||
if(found)break;
|
||||
var ids=m[1].split(",");
|
||||
for(var j=0;j<ids.length;j++){
|
||||
if(ids[j].trim()===letter){found=true;cur=p.replace(/^[a-z\u0430-\u044F,\s]+[\u0029\)]\s*/,"");break}
|
||||
}
|
||||
}else{
|
||||
if(found)cur+=String.fromCharCode(10)+p
|
||||
}
|
||||
}
|
||||
return cur||""
|
||||
}
|
||||
|
||||
function init(){
|
||||
try{
|
||||
var su=localStorage.getItem("su");
|
||||
if(su)cu=JSON.parse(su);
|
||||
}catch(e){}
|
||||
loadEv();
|
||||
var fb=document.getElementById("fb");
|
||||
if(fb){
|
||||
for(var i=0;i<brs.length;i++){
|
||||
var o=document.createElement("option");
|
||||
o.value=i;o.textContent=brs[i];
|
||||
fb.appendChild(o)
|
||||
}
|
||||
}
|
||||
if(cu)showApp()
|
||||
}
|
||||
|
||||
function doLogin(){
|
||||
var e=document.getElementById("lem").value.trim().toLowerCase();
|
||||
var k=e.split("@")[0];
|
||||
var u=USR[k];
|
||||
if(u){
|
||||
cu={n:u.n,bg:u.bg};
|
||||
localStorage.setItem("su",JSON.stringify(cu));
|
||||
showApp()
|
||||
}else{
|
||||
document.getElementById("lerr").style.display="block"
|
||||
}
|
||||
}
|
||||
function doLogout(){
|
||||
localStorage.removeItem("su");
|
||||
cu=null;
|
||||
document.getElementById("login").style.display="flex";
|
||||
document.getElementById("app").style.display="none"
|
||||
}
|
||||
function showApp(){
|
||||
document.getElementById("login").style.display="none";
|
||||
document.getElementById("app").style.display="block";
|
||||
document.getElementById("su_name").textContent=cu?cu.n:"";
|
||||
var unav=document.getElementById("snav_users");
|
||||
if(unav)unav.style.display=cu&&cu.bg===0?"":"none";
|
||||
var hsenav=document.getElementById("snav_hse");
|
||||
if(hsenav)hsenav.style.display=cu&&cu.bg===0?"":"none";
|
||||
switchTab("events")
|
||||
}
|
||||
|
||||
function loadEv(){
|
||||
evs=[];
|
||||
if(typeof ALL_EVENTS!=="undefined"&&ALL_EVENTS&&ALL_EVENTS.length){
|
||||
var saved=localStorage.getItem("se5");
|
||||
var smap={};
|
||||
if(saved){
|
||||
try{
|
||||
var sd=JSON.parse(saved);
|
||||
for(var i=0;i<sd.length;i++){
|
||||
if(sd[i]&&sd[i].id)smap[sd[i].id]=sd[i]
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
for(var i=0;i<ALL_EVENTS.length;i++){
|
||||
var e=ALL_EVENTS[i];
|
||||
if(smap[e.id]){
|
||||
e.s=smap[e.id].s||e.s;
|
||||
if(smap[e.id].p!==undefined)e.p=smap[e.id].p;
|
||||
if(smap[e.id].done)e.done=smap[e.id].done;
|
||||
if(smap[e.id].h)e.h=smap[e.id].h;
|
||||
if(smap[e.id].q!==undefined)e.q=smap[e.id].q;
|
||||
if(smap[e.id].n!==undefined)e.n=smap[e.id].n
|
||||
}
|
||||
evs.push(e)
|
||||
}
|
||||
}
|
||||
storCheck()
|
||||
}
|
||||
function saveEv(){
|
||||
var out=[];
|
||||
for(var i=0;i<evs.length;i++){
|
||||
out.push({id:evs[i].id,s:evs[i].s,p:evs[i].p,done:evs[i].done,h:evs[i].h,q:evs[i].q,n:evs[i].n})
|
||||
}
|
||||
try{localStorage.setItem("se5",JSON.stringify(out))}catch(e){}
|
||||
storCheck()
|
||||
}
|
||||
|
||||
function switchTab(t){
|
||||
tab=t;
|
||||
var tabs=["events","analytics","reports","ai","users","hse"];
|
||||
var tn={events:"\u041C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u044F",analytics:"\u0410\u043D\u0430\u043B\u0438\u0442\u0438\u043A\u0430",reports:"\u041E\u0442\u0447\u0451\u0442\u043D\u043E\u0441\u0442\u044C",ai:"\u0414\u0436\u0430\u0440\u0432\u0438\u0441",users:"\u0423\u0447\u0451\u0442\u043D\u044B\u0435 \u0437\u0430\u043F\u0438\u0441\u0438",hse:"HSE.sk.kz"};
|
||||
for(var i=0;i<tabs.length;i++){
|
||||
var el=document.getElementById("tab_"+tabs[i]);
|
||||
if(el)el.style.display="none";
|
||||
var nav=document.getElementById("snav_"+tabs[i]);
|
||||
if(nav)nav.className=""
|
||||
}
|
||||
var sel=document.getElementById("tab_"+t);
|
||||
if(sel)sel.style.display="block";
|
||||
var nav=document.getElementById("snav_"+t);
|
||||
if(nav)nav.className="active";
|
||||
document.getElementById("page_title").textContent=tn[t];
|
||||
if(t==="events")renderEv();
|
||||
if(t==="analytics")renderAnalytics();
|
||||
if(t==="reports")renderReports();
|
||||
if(t==="ai")renderAI()
|
||||
if(t==="hse"){var hm=document.getElementById("hse_month");if(hm&&!hm.value)hm.value=new Date().toISOString().slice(0,7)}
|
||||
if(t==="users")renderUsers()
|
||||
}
|
||||
|
||||
function daysRem(due){
|
||||
if(!due||due==="\u2014")return 999;
|
||||
var p=due.split(".");
|
||||
if(p.length!==3)return 999;
|
||||
var d=new Date(parseInt(p[2],10),parseInt(p[1],10)-1,parseInt(p[0],10));
|
||||
var now=new Date();
|
||||
now.setHours(0,0,0,0);
|
||||
return Math.floor((d-now)/86400000)
|
||||
}
|
||||
|
||||
function renderEv(){
|
||||
var sea=document.getElementById("sea").value.toLowerCase().trim();
|
||||
var fs=document.getElementById("fs").value;
|
||||
var fb=document.getElementById("fb").value;
|
||||
var fl=evs;
|
||||
if(sea){
|
||||
fl=fl.filter(function(e){
|
||||
return e.t.toLowerCase().indexOf(sea)!==-1||
|
||||
e.r.toLowerCase().indexOf(sea)!==-1||
|
||||
e.dname.toLowerCase().indexOf(sea)!==-1||
|
||||
String(e.id).indexOf(sea)!==-1
|
||||
})
|
||||
}
|
||||
if(fs)fl=fl.filter(function(e){return e.s===fs});
|
||||
if(fb!=="")fl=fl.filter(function(e){return String(e.b)===fb});
|
||||
document.getElementById("sc").textContent="\u041F\u043E\u043A\u0430\u0437\u0430\u043D\u043E: "+fl.length+" \u0438\u0437 "+evs.length;
|
||||
var h="";
|
||||
var lastSec=-1;
|
||||
for(var i=0;i<fl.length;i++){
|
||||
var e=fl[i];
|
||||
if(e.sec!==lastSec){
|
||||
if(lastSec!==-1)h+="<tr><td colspan='7' style='padding:4px;border:none'></td></tr>";
|
||||
h+="<tr><td colspan='7' style='padding:0;border:none'><div class='sec-h'>"+esc(secs[e.sec])+"</div></td></tr>";
|
||||
lastSec=e.sec
|
||||
}
|
||||
var dr=daysRem(e.due);
|
||||
var rowCl=e.s==="done"?"tr-green":dr<=0&&e.s!=="done"?"tr-red":dr<=14?"tr-amber":"";
|
||||
var cl=stc[e.s]||"w";
|
||||
var drText=dr<=0&&e.s!=="done"?"\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E \u043D\u0430 "+Math.abs(dr)+" \u0434\u043D.":e.s==="done"?"\u0413\u043E\u0442\u043E\u0432\u043E":dr===999?"\u2014":dr+" \u0434\u043D.";
|
||||
var hasSub=e.sub&&e.sub.length>0;
|
||||
h+="<tr class='"+rowCl+"'>";
|
||||
h+="<td style='font-weight:700;font-size:12px'>"+e.id+"</td>";
|
||||
h+="<td><div style='font-size:12px;line-height:1.3'>"+esc(e.t)+"</div><div style='font-size:10px;color:var(--gray-500);margin-top:2px'>"+esc(e.dname)+"</div></td>";
|
||||
h+="<td style='font-size:11px'>"+esc(nl2c(e.r))+"</td>";
|
||||
h+="<td style='font-size:12px;white-space:nowrap'>"+e.due+" <span style='font-size:10px;color:var(--gray-500)'>("+drText+")</span></td>";
|
||||
if(hasSub){h+="<td></td><td></td>"}
|
||||
else{
|
||||
h+="<td><span class='badge "+cl+"'>"+stn[e.s]+"</span></td>";
|
||||
h+="<td><button class='btn btn-sm' style='padding:4px 12px;font-size:11px' onclick='openEv("+e.id+")'>\u041E\u0442\u043A\u0440\u044B\u0442\u044C</button></td>"
|
||||
}
|
||||
h+="</tr>";
|
||||
if(hasSub){
|
||||
for(var si=0;si<e.sub.length;si++){
|
||||
var sr=subResp(e.r,e.sub[si].l)||nl2c(e.r);
|
||||
var ss=e.s==="wait"?"warn":e.s;
|
||||
var scl=stc[ss]||"a";
|
||||
h+="<tr class='"+rowCl+" sub-item-row'><td style='padding-left:24px;font-size:11px;color:var(--gray-500)'>"+e.id+"."+esc(e.sub[si].l)+"</td>";
|
||||
h+="<td style='font-size:11px'>"+esc(e.sub[si].t)+"</td>";
|
||||
h+="<td style='font-size:11px'>"+esc(sr)+"</td>";
|
||||
h+="<td style='font-size:11px'>"+e.due+"</td>";
|
||||
h+="<td><span class='badge "+scl+"' style='font-size:10px'>"+stn[ss]+"</span></td>";
|
||||
h+="<td><button class='btn btn-sm' style='padding:4px 10px;font-size:11px' onclick='openEv("+e.id+","+si+")'>\u041E\u0442\u043A\u0440\u044B\u0442\u044C</button></td></tr>"
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!h)h="<p style='color:var(--gray-500);padding:20px;text-align:center'>\u041D\u0435\u0442 \u043C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0439</p>";
|
||||
document.getElementById("ev_content").innerHTML="<table><tr><th>N</th><th>\u041C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0435</th><th>\u041E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0435</th><th>\u0421\u0440\u043E\u043A</th><th>\u0421\u0442\u0430\u0442\u0443\u0441</th><th></th></tr>"+h+"</table>"
|
||||
}
|
||||
|
||||
function togSub(id){
|
||||
var el=document.getElementById("sub_"+id);
|
||||
if(!el)return;
|
||||
var arr=document.getElementById("arr_"+id);
|
||||
if(el.style.display==="none"){
|
||||
el.style.display="block";
|
||||
if(arr)arr.innerHTML="▼"
|
||||
}else{
|
||||
el.style.display="none";
|
||||
if(arr)arr.innerHTML="▸"
|
||||
}
|
||||
}
|
||||
function chkSub(id,idx,val){
|
||||
var key="ss_"+id;
|
||||
var ss=localStorage.getItem(key);
|
||||
var arr=[];
|
||||
if(ss){try{arr=JSON.parse(ss)}catch(e){}}
|
||||
arr[idx]=val?true:false;
|
||||
try{localStorage.setItem(key,JSON.stringify(arr))}catch(e){}
|
||||
renderEv()
|
||||
}
|
||||
|
||||
function openEv(id,subIdx){
|
||||
curSub=subIdx!==undefined?subIdx:null;
|
||||
var e=null;
|
||||
for(var i=0;i<evs.length;i++){if(evs[i].id===id){e=evs[i];break}}
|
||||
if(!e)return;
|
||||
var fk=subIdx!==undefined?"sf_"+id+"_s"+subIdx:"sf_"+id;
|
||||
var h="<div style='max-width:700px'>";
|
||||
var titlePre=subIdx!==undefined?"N"+id+"."+e.sub[subIdx].l+" ":"N"+id+". ";
|
||||
h+="<h3 style='margin-bottom:8px;padding-right:30px'>"+titlePre+esc(e.t)+"</h3>";
|
||||
h+="<div style='font-size:12px;color:var(--gray-500);margin-bottom:6px'><strong>\u041E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0439:</strong> "+esc(nl2c(e.r))+"</div>";
|
||||
h+="<div style='font-size:12px;color:var(--gray-500);margin-bottom:6px'><strong>\u0424\u0438\u043B\u0438\u0430\u043B:</strong> "+brs[e.b]+" | <strong>\u0421\u0440\u043E\u043A:</strong> "+e.due;
|
||||
if(e.done&&e.done!=="\u2014")h+=" | <strong>\u0418\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u043E:</strong> "+e.done;
|
||||
h+="</div>";
|
||||
h+="<div style='margin-bottom:12px'><strong>\u0421\u0442\u0430\u0442\u0443\u0441:</strong> <select id='evs_"+e.id+"' onchange='chgSt("+e.id+")'>";
|
||||
var sk=["warn","late","done"];
|
||||
for(var si=0;si<sk.length;si++){
|
||||
h+="<option value='"+sk[si]+"'";
|
||||
if(e.s===sk[si])h+=" selected";
|
||||
h+=">"+stn[sk[si]]+"</option>"
|
||||
}
|
||||
h+="</select></div>";
|
||||
if(e.h&&e.h.length){
|
||||
h+="<div style='margin-bottom:12px'><strong>\u0418\u0441\u0442\u043E\u0440\u0438\u044F:</strong><ul style='font-size:12px;margin:4px 0 0 16px'>";
|
||||
for(var hi=0;hi<e.h.length;hi++){h+="<li>"+esc(e.h[hi])+"</li>"}
|
||||
h+="</ul></div>"
|
||||
}
|
||||
h+="<div style='margin-bottom:12px;padding:10px;background:#F0F9FF;border-radius:8px'><strong>AI-\u0430\u043D\u0430\u043B\u0438\u0437:</strong> <span style='font-size:13px;color:var(--gray-500)'>"+esc(e.ai||"\u2014")+"</span></div>";
|
||||
h+="<div style='margin-bottom:12px'><strong>\u041E\u0442\u0447\u0451\u0442\u043D\u043E\u0441\u0442\u044C:</strong></div>";
|
||||
h+="<div style='margin-bottom:8px;display:flex;gap:8px;flex-wrap:wrap;align-items:center'>";
|
||||
h+="<span style='font-size:12px'>\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E:</span> <input type='number' id='evq_"+e.id+"' value='"+(e.q||"")+"' min='0' style='width:80px;padding:4px;border:1px solid var(--gray-100);border-radius:4px;font-size:12px'>";
|
||||
var now=new Date();
|
||||
var curMonth=now.getMonth();
|
||||
var savedMonth=localStorage.getItem("evmonth_"+e.id);
|
||||
var selMonth=savedMonth!==null?parseInt(savedMonth,10):curMonth;
|
||||
h+="<span style='font-size:12px;margin-left:8px'>\u041C\u0435\u0441\u044F\u0446:</span> <select id='evm_"+e.id+"' style='padding:4px;border:1px solid var(--gray-100);border-radius:4px;font-size:12px'>";
|
||||
var mnames=["\u042F\u043D\u0432","\u0424\u0435\u0432","\u041C\u0430\u0440","\u0410\u043F\u0440","\u041C\u0430\u0439","\u0418\u044E\u043D","\u0418\u044E\u043B","\u0410\u0432\u0433","\u0421\u0435\u043D","\u041E\u043A\u0442","\u041D\u043E\u044F","\u0414\u0435\u043A"];
|
||||
for(var mi=0;mi<12;mi++){
|
||||
h+="<option value='"+mi+"'";
|
||||
if(mi===selMonth)h+=" selected";
|
||||
h+=">"+mnames[mi]+"</option>"
|
||||
}
|
||||
h+="</select></div>";
|
||||
h+="<div style='margin-bottom:12px'><textarea id='evn_"+e.id+"' placeholder='\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435 / \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F...' style='width:100%;padding:8px;border:1px solid var(--gray-100);border-radius:6px;font-size:12px;resize:vertical;min-height:50px'>"+esc(subIdx!==undefined?((localStorage.getItem("sn_"+e.id+"_s"+subIdx)||"")):(e.n||""))+"</textarea></div>";
|
||||
h+="<div style='margin-bottom:12px'><strong>\u0424\u0430\u0439\u043B\u044B \u0437\u0430 \u043C\u0435\u0441\u044F\u0446:</strong>";
|
||||
if(cu&&cu.bg===0){
|
||||
for(var bi=0;bi<brs.length;bi++){
|
||||
var bk=fk+"_b"+bi;
|
||||
var fd=localStorage.getItem(bk);
|
||||
if(fd){
|
||||
try{
|
||||
var fa=JSON.parse(fd);
|
||||
if(fa.length)h+="<div style='font-size:11px;color:var(--gray-500);margin-top:4px'><strong>"+esc(brs[bi])+":</strong></div>";
|
||||
for(var fi=0;fi<fa.length;fi++){
|
||||
var f=fa[fi];
|
||||
h+="<div class='file-item'><span class='fn'>"+esc(f.n)+"</span><span class='fs'>("+(f.s?Math.round(f.s/1024)+"KB":"")+", "+esc(f.u||"")+" "+esc(f.d||"")+")</span><a onclick='dlFile("+e.id+","+fi+")'>\u0421\u043A\u0430\u0447\u0430\u0442\u044C</a><a style='color:#EF4444;margin-left:4px' onclick='delFile("+e.id+","+fi+")'>\u0423\u0434\u0430\u043B\u0438\u0442\u044C</a></div>"
|
||||
}
|
||||
}catch(ex){}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
var bk=fk+"_b"+(cu?cu.bg:0);
|
||||
var fd=localStorage.getItem(bk);
|
||||
if(fd){
|
||||
try{
|
||||
var fa=JSON.parse(fd);
|
||||
for(var fi=0;fi<fa.length;fi++){
|
||||
var f=fa[fi];
|
||||
h+="<div class='file-item'><span class='fn'>"+esc(f.n)+"</span><span class='fs'>("+(f.s?Math.round(f.s/1024)+"KB":"")+", "+esc(f.u||"")+" "+esc(f.d||"")+")</span><a onclick='dlFile("+e.id+","+fi+")'>\u0421\u043A\u0430\u0447\u0430\u0442\u044C</a><a style='color:#EF4444;margin-left:4px' onclick='delFile("+e.id+","+fi+")'>\u0423\u0434\u0430\u043B\u0438\u0442\u044C</a></div>"
|
||||
}
|
||||
}catch(ex){}
|
||||
}
|
||||
}
|
||||
h+="<div style='margin-top:6px'><input type='file' id='fu_"+e.id+"' style='font-size:12px' onchange='upFile("+e.id+(subIdx!==undefined?","+subIdx:"")+")'></div>";
|
||||
h+="</div>";
|
||||
if(subIdx===undefined&&e.sub&&e.sub.length>0){
|
||||
h+="<div style='margin-bottom:12px'><strong>\u041F\u043E\u0434\u043F\u0443\u043D\u043A\u0442\u044B:</strong>";
|
||||
var ss=localStorage.getItem("ss_"+e.id);
|
||||
for(var si=0;si<e.sub.length;si++){
|
||||
var checked="";
|
||||
if(ss){try{var sp=JSON.parse(ss);if(sp[si])checked="checked"}catch(ex){}}
|
||||
h+="<div style='font-size:12px;padding:4px 0'><input type='checkbox' "+checked+" onchange='chkSub("+e.id+","+si+",this.checked)'> "+esc(e.sub[si].l)+") "+esc(e.sub[si].t)+"</div>"
|
||||
}
|
||||
h+="</div>"
|
||||
}
|
||||
h+="<div style='margin-top:16px;text-align:right;border-top:1px solid var(--gray-100);padding-top:12px'><button class='btn btn-sm btn-g' onclick='saveEvModal("+e.id+")'>\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C</button>";
|
||||
h+="<button class='btn btn-sm' style='margin-left:8px;background:var(--gray-100);color:var(--ink)' onclick='closeModal()'>\u041E\u0442\u043C\u0435\u043D\u0430</button></div>";
|
||||
h+="</div>";
|
||||
showModal(h)
|
||||
}
|
||||
|
||||
function showModal(html){
|
||||
var mb=document.getElementById("modal_body");
|
||||
var m=document.getElementById("modal");
|
||||
if(!mb||!m)return;
|
||||
mb.innerHTML=html;
|
||||
m.style.display="flex"
|
||||
}
|
||||
function closeModal(){
|
||||
document.getElementById("modal").style.display="none"
|
||||
}
|
||||
|
||||
function saveEvModal(id){
|
||||
var sel=document.getElementById("evs_"+id);
|
||||
var inq=document.getElementById("evq_"+id);
|
||||
var inn=document.getElementById("evn_"+id);
|
||||
for(var i=0;i<evs.length;i++){
|
||||
if(evs[i].id===id){
|
||||
if(sel)evs[i].s=sel.value;
|
||||
if(inq)evs[i].q=parseInt(inq.value,10)||0;
|
||||
if(inn){if(curSub!==null){try{localStorage.setItem("sn_"+id+"_s"+curSub,inn.value.trim())}catch(e){}}else{evs[i].n=inn.value.trim()}}
|
||||
if(sel&&sel.value==="done"&&(evs[i].done==="\u2014"||!evs[i].done)){
|
||||
var d=new Date();
|
||||
evs[i].done=d.getDate()+"."+String(d.getMonth()+1).padStart(2,"0")+"."+d.getFullYear()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
var inm=document.getElementById("evm_"+id);
|
||||
if(inm)try{localStorage.setItem("evmonth_"+id,inm.value)}catch(e){}
|
||||
saveEv();
|
||||
closeModal();
|
||||
renderEv()
|
||||
}
|
||||
function chgSt(id){
|
||||
var sel=document.getElementById("evs_"+id);
|
||||
if(!sel)return;
|
||||
for(var i=0;i<evs.length;i++){
|
||||
if(evs[i].id===id){
|
||||
evs[i].s=sel.value;
|
||||
if(sel.value==="done"&&(evs[i].done==="\u2014"||!evs[i].done)){
|
||||
var d=new Date();
|
||||
evs[i].done=d.getDate()+"."+String(d.getMonth()+1).padStart(2,"0")+"."+d.getFullYear()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function upFile(id){
|
||||
var inp=document.getElementById("fu_"+id);
|
||||
if(!inp||!inp.files||!inp.files[0])return;
|
||||
var f=inp.files[0];
|
||||
if(f.size>3145728){alert("\u0424\u0430\u0439\u043B \u0431\u043E\u043B\u044C\u0448\u0435 3MB");return}
|
||||
var fr=new FileReader();
|
||||
var subKey=curSub!==null?"_s"+curSub:"";
|
||||
var inm=document.getElementById("evm_"+id);
|
||||
var monthKey=inm?"_m"+inm.value:"";
|
||||
var brKey="_b"+(cu?cu.bg:0);
|
||||
fr.onload=function(){
|
||||
var key="sf_"+id+monthKey+subKey+brKey;
|
||||
var arr=[];
|
||||
var ex=localStorage.getItem(key);
|
||||
if(ex){try{arr=JSON.parse(ex)}catch(e){}}
|
||||
var d=new Date();
|
||||
arr.push({n:f.name,s:f.size,d:d.getDate()+"."+String(d.getMonth()+1).padStart(2,"0")+"."+d.getFullYear(),u:cu?cu.n:"",data:fr.result});
|
||||
try{localStorage.setItem(key,JSON.stringify(arr))}catch(e){alert("\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u044F")}
|
||||
openEv(id,curSub!==null?curSub:undefined)
|
||||
};
|
||||
fr.readAsDataURL(f)
|
||||
}
|
||||
function dlFile(id,idx){
|
||||
var inm=document.getElementById("evm_"+id);
|
||||
var monthKey=inm?"_m"+inm.value:"";
|
||||
var key="sf_"+id+monthKey+(curSub!==null?"_s"+curSub:"")+"_b"+(cu?cu.bg:0);
|
||||
var ex=localStorage.getItem(key);
|
||||
if(!ex)return;
|
||||
try{
|
||||
var arr=JSON.parse(ex);
|
||||
var f=arr[idx];
|
||||
if(!f||!f.data)return;
|
||||
var a=document.createElement("a");
|
||||
a.href=f.data;
|
||||
a.download=f.n;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a)
|
||||
}catch(e){}
|
||||
}
|
||||
function delFile(id,idx){
|
||||
if(!confirm("\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0444\u0430\u0439\u043B?"))return;
|
||||
var inm=document.getElementById("evm_"+id);
|
||||
var monthKey=inm?"_m"+inm.value:"";
|
||||
var key="sf_"+id+monthKey+(curSub!==null?"_s"+curSub:"")+"_b"+(cu?cu.bg:0);
|
||||
var ex=localStorage.getItem(key);
|
||||
if(!ex)return;
|
||||
try{
|
||||
var arr=JSON.parse(ex);
|
||||
arr.splice(idx,1);
|
||||
if(arr.length){localStorage.setItem(key,JSON.stringify(arr))}
|
||||
else{localStorage.removeItem(key)}
|
||||
openEv(id,curSub!==null?curSub:undefined)
|
||||
}catch(e){}
|
||||
}
|
||||
|
||||
function saveBackup(){
|
||||
saveEv();
|
||||
var blob=new Blob([JSON.stringify(evs,null,2)],{type:"application/json"});
|
||||
var a=document.createElement("a");
|
||||
a.href=URL.createObjectURL(blob);
|
||||
a.download="backup_"+new Date().toISOString().slice(0,10)+".json";
|
||||
a.click()
|
||||
}
|
||||
function loadBackup(inp){
|
||||
if(!inp.files||!inp.files[0])return;
|
||||
var fr=new FileReader();
|
||||
fr.onload=function(){
|
||||
try{
|
||||
var d=JSON.parse(fr.result);
|
||||
if(!d||!d.length){alert("\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442");return}
|
||||
var out=[];
|
||||
for(var i=0;i<d.length;i++){
|
||||
out.push({id:d[i].id,s:d[i].s||"warn",p:d[i].p||0,done:d[i].done||"\u2014",h:d[i].h||[]})
|
||||
}
|
||||
localStorage.setItem("se5",JSON.stringify(out));
|
||||
loadEv();
|
||||
renderEv();
|
||||
alert("\u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E "+out.length+" \u0437\u0430\u043F\u0438\u0441\u0435\u0439")
|
||||
}catch(e){alert("\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438")}
|
||||
};
|
||||
fr.readAsText(inp.files[0])
|
||||
}
|
||||
|
||||
function storCheck(){
|
||||
var el=document.getElementById("stor_ind");
|
||||
if(!el)return;
|
||||
try{
|
||||
var used=0;
|
||||
for(var k in localStorage){
|
||||
if(localStorage.hasOwnProperty(k)){
|
||||
used+=((localStorage[k].length||0)*2)
|
||||
}
|
||||
}
|
||||
var max=5242880;
|
||||
var pct=Math.round(used/max*100);
|
||||
el.textContent="\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435: "+pct+"% ("+Math.round(used/1024)+"KB)"
|
||||
}catch(e){el.textContent=""}
|
||||
}
|
||||
|
||||
function renderAnalytics(){
|
||||
if(cu&&cu.bg===0)loadLTIF();
|
||||
var total=evs.length;
|
||||
var done=0,late=0,warn=0;
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var s=evs[i].s;
|
||||
if(s==="done")done++;
|
||||
else if(s==="late")late++;
|
||||
else if(s==="warn")warn++
|
||||
}
|
||||
var html="";
|
||||
html+="<div class='stat-card sb'><div class='lb'>\u0412\u0441\u0435\u0433\u043E \u043C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0439</div><div class='num'>"+total+"</div></div>";
|
||||
html+="<div class='stat-card sg'><div class='lb'>\u0418\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u043E</div><div class='num'>"+done+"</div><div class='lb'>"+Math.round(done/total*100)+"%</div></div>";
|
||||
html+="<div class='stat-card sr'><div class='lb'>\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E</div><div class='num'>"+late+"</div></div>";
|
||||
html+="<div class='stat-card'><div class='lb'>\u0412 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435</div><div class='num'>"+warn+"</div></div>";
|
||||
document.getElementById("an_stats").innerHTML=html;
|
||||
|
||||
var problem=[];
|
||||
for(var i=0;i<evs.length;i++){
|
||||
if(evs[i].s==="late"||evs[i].s==="warn"){
|
||||
var dr=daysRem(evs[i].due);
|
||||
problem.push({id:evs[i].id,t:evs[i].t,b:evs[i].b,s:evs[i].s,dr:dr,dn:evs[i].dname})
|
||||
}
|
||||
}
|
||||
problem.sort(function(a,b){return a.dr-b.dr});
|
||||
var pt=problem.slice(0,10);
|
||||
if(pt.length){
|
||||
var ph="<table><tr><th>N</th><th>\u041C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0435</th><th>\u0424\u0438\u043B\u0438\u0430\u043B</th><th>\u0421\u0442\u0430\u0442\u0443\u0441</th><th>\u0414\u043D\u0435\u0439</th></tr>";
|
||||
for(var i=0;i<pt.length;i++){
|
||||
var pc=pt[i].s==="late"?"r":"w";
|
||||
ph+="<tr><td>"+pt[i].id+"</td><td style='font-size:12px'>"+esc(pt[i].t)+"</td><td>"+brs[pt[i].b]+"</td><td><span class='badge "+pc+"'>"+stn[pt[i].s]+"</span></td><td>"+(pt[i].dr<=0?"\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E":pt[i].dr+" \u0434\u043D.")+"</td></tr>"
|
||||
}
|
||||
ph+="</table>";
|
||||
document.getElementById("an_top").innerHTML=ph
|
||||
}else{
|
||||
document.getElementById("an_top").innerHTML="<p style='color:var(--gray-500)'>\u041F\u0440\u043E\u0431\u043B\u0435\u043C \u043D\u0435\u0442</p>"
|
||||
}
|
||||
}
|
||||
|
||||
function getFilteredEvs(){
|
||||
var statusF=document.getElementById("rp_status").value;
|
||||
var r=[];
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var e=evs[i];
|
||||
if(statusF&&e.s!==statusF)continue;
|
||||
r.push(e)
|
||||
}
|
||||
return r
|
||||
}
|
||||
function getReportPeriod(){
|
||||
var period=document.getElementById("rp_period").value;
|
||||
var year=document.getElementById("rp_year").value;
|
||||
var month=parseInt(document.getElementById("rp_month").value,10)||0;
|
||||
var mnames=["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"];
|
||||
if(period==="month")return mnames[month]+" "+year;
|
||||
if(period==="q1")return "I квартал "+year;
|
||||
if(period==="q2")return "II квартал "+year;
|
||||
if(period==="q3")return "III квартал "+year;
|
||||
if(period==="q4")return "IV квартал "+year;
|
||||
if(period==="h1")return "1-е полугодие "+year;
|
||||
if(period==="h2")return "2-е полугодие "+year;
|
||||
return year+" год"
|
||||
}
|
||||
function rpPeriodChange(){
|
||||
var v=document.getElementById("rp_period").value;
|
||||
var mSel=document.getElementById("rp_month");
|
||||
if(v==="month"){mSel.style.display="inline-block"}else{mSel.style.display="none"}
|
||||
renderReports()
|
||||
}
|
||||
function renderReports(){
|
||||
var fl=getFilteredEvs();
|
||||
var cnt=document.getElementById("rp_count");
|
||||
if(cnt)cnt.textContent="\u0412\u044B\u0431\u0440\u0430\u043D\u043E \u043C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0439: "+fl.length;
|
||||
document.getElementById("rp_preview").innerHTML=""
|
||||
}
|
||||
function dlCSV(){
|
||||
var fl=getFilteredEvs();
|
||||
var csv="\uFEFFN;\u041C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0435;\u0424\u0438\u043B\u0438\u0430\u043B;\u0421\u0440\u043E\u043A;\u0421\u0442\u0430\u0442\u0443\u0441;\u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441;\u041A\u043E\u043B-\u0432\u043E;\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435;\u041E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0439\n";
|
||||
for(var i=0;i<fl.length;i++){
|
||||
var e=fl[i];
|
||||
csv+=e.id+";\""+esc(e.t)+"\";\""+brs[e.b]+"\";"+e.due+";"+stn[e.s]+";"+(e.p||0)+"%;"+(e.q||"")+";\""+esc(e.n||"")+"\";\""+esc(nl2c(e.r))+"\"\n"
|
||||
}
|
||||
var blob=new Blob([csv],{type:"text/csv;charset=utf-8"});
|
||||
var a=document.createElement("a");
|
||||
a.href=URL.createObjectURL(blob);
|
||||
a.download="report_pb_"+document.getElementById("rp_year").value+"_"+getReportPeriod().replace(/\s/g,"_")+".csv";
|
||||
a.click()
|
||||
}
|
||||
function dlHTML(){
|
||||
var fl=getFilteredEvs();
|
||||
var month=parseInt(document.getElementById("rp_month").value,10)+1;
|
||||
var year=document.getElementById("rp_year").value;
|
||||
var periodLabel=getReportPeriod(); var hh="<!DOCTYPE html><html><head><meta charset='utf-8'><title>\u041E\u0442\u0447\u0451\u0442 \u041F\u043B\u0430\u043D \u041F\u0411 \u0437\u0430 "+periodLabel+"</title><style>body{font:14px Arial;padding:20px}table{border-collapse:collapse;width:100%}th,td{border:1px solid #ccc;padding:6px 10px;font-size:12px;text-align:left;vertical-align:top}th{background:#0B1A2E;color:#fff}.files{margin-top:4px;font-size:11px}.files a{color:#00B8D4;display:block}</style></head><body><h2>\u041F\u043B\u0430\u043D \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0439 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438</h2><p>QAZAQtelecom HSE \u0437\u0430 "+periodLabel+"</p><br><table><tr><th>N</th><th>\u041C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0435</th><th>\u0421\u0440\u043E\u043A</th><th>\u0421\u0442\u0430\u0442\u0443\u0441</th><th>\u041A\u043E\u043B-\u0432\u043E</th><th>\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435 / \u0424\u0430\u0439\u043B\u044B</th></tr>";
|
||||
for(var i=0;i<fl.length;i++){
|
||||
var e=fl[i];
|
||||
var notes=esc(e.n||"");
|
||||
var fhtml="";
|
||||
var keysToTry=[];
|
||||
for(var si=-1;si<(e.sub?e.sub.length:0);si++){
|
||||
var sk=si>=0?"_s"+si:"";
|
||||
for(var bk=0;bk<brs.length;bk++){
|
||||
keysToTry.push("sf_"+e.id+sk+"_b"+bk)
|
||||
}
|
||||
}
|
||||
keysToTry.push("sf_"+e.id);
|
||||
for(var ki=0;ki<keysToTry.length;ki++){
|
||||
var key=keysToTry[ki];
|
||||
var fd=localStorage.getItem(key);
|
||||
if(fd){
|
||||
try{var arr=JSON.parse(fd);
|
||||
for(var fi=0;fi<arr.length;fi++){
|
||||
var f=arr[fi];
|
||||
if(!f.n)continue;
|
||||
fhtml+="<a href='"+f.data+"' download='"+esc(f.n)+"'>"+esc(f.n)+" ("+Math.round((f.s||0)/1024)+" KB"+", "+esc(f.u||"")+")</a>"
|
||||
}}catch(ex){}
|
||||
}
|
||||
}
|
||||
if(fhtml)fhtml="<div class='files'>"+fhtml+"</div>";
|
||||
hh+="<tr><td>"+e.id+"</td><td>"+esc(e.t)+"</td><td>"+e.due+"</td><td>"+stn[e.s]+"</td><td>"+(e.q||"")+"</td><td>"+notes+fhtml+"</td></tr>"
|
||||
}
|
||||
hh+="</table><p><br><em>\u041E\u0442\u0447\u0451\u0442 \u0441\u0444\u043E\u0440\u043C\u0438\u0440\u043E\u0432\u0430\u043D: "+new Date().toLocaleDateString("ru-RU")+"</em></p></body></html>";
|
||||
var blob=new Blob([hh],{type:"text/html"});
|
||||
var a=document.createElement("a");
|
||||
a.href=URL.createObjectURL(blob);
|
||||
a.download="report_pb_"+year+"_"+month+".html";
|
||||
a.click()
|
||||
}
|
||||
function dlWord(){
|
||||
var fl=getFilteredEvs();
|
||||
var month=parseInt(document.getElementById("rp_month").value,10)+1;
|
||||
var year=document.getElementById("rp_year").value;
|
||||
var hh="<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'><title>\u041E\u0442\u0447\u0451\u0442 \u041F\u043B\u0430\u043D \u041F\u0411 "+month+"."+year+"</title><style>@page{size:A4;margin:20mm}body{font:12pt 'Times New Roman'}h2{font-size:16pt;text-align:center}table{border-collapse:collapse;width:100%}th,td{border:1px solid #000;padding:4px 8px;font-size:11pt}th{background:#ddd}</style></head><body><h2>\u041F\u043B\u0430\u043D \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0439 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438</h2><p style='text-align:center'>AO \u00AB\u041A\u0430\u0437\u0430\u0445\u0442\u0435\u043B\u0435\u043A\u043E\u043C\u00BB \u0437\u0430 "+month+"."+year+"</p><br><table><tr><th>N</th><th>\u041C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0435</th><th>\u0424\u0438\u043B\u0438\u0430\u043B</th><th>\u0421\u0440\u043E\u043A</th><th>\u0421\u0442\u0430\u0442\u0443\u0441</th><th>\u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441</th><th>\u041A\u043E\u043B-\u0432\u043E</th><th>\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435</th></tr>";
|
||||
for(var i=0;i<fl.length;i++){
|
||||
var e=fl[i];
|
||||
hh+="<tr><td>"+e.id+"</td><td>"+esc(e.t)+"</td><td>"+brs[e.b]+"</td><td>"+e.due+"</td><td>"+stn[e.s]+"</td><td>"+(e.p||0)+"%</td><td>"+(e.q||"")+"</td><td>"+esc(e.n||"")+"</td></tr>"
|
||||
}
|
||||
hh+="</table><p><br><em>\u041E\u0442\u0447\u0451\u0442 \u0441\u0444\u043E\u0440\u043C\u0438\u0440\u043E\u0432\u0430\u043D: "+new Date().toLocaleDateString("ru-RU")+"</em></p></body></html>";
|
||||
var blob=new Blob([hh],{type:"application/msword"});
|
||||
var a=document.createElement("a");
|
||||
a.href=URL.createObjectURL(blob);
|
||||
a.download="report_pb_"+year+"_"+month+".doc";
|
||||
a.click()
|
||||
}
|
||||
function dlPdf(){
|
||||
var fl=getFilteredEvs();
|
||||
var month=parseInt(document.getElementById("rp_month").value,10)+1;
|
||||
var year=document.getElementById("rp_year").value;
|
||||
var periodLabel=getReportPeriod(); var hh="<!DOCTYPE html><html><head><meta charset='utf-8'><title>\u041E\u0442\u0447\u0451\u0442 \u041F\u043B\u0430\u043D \u041F\u0411 \u0437\u0430 "+periodLabel+"</title><style>body{font:14px Arial;padding:20px}table{border-collapse:collapse;width:100%}th,td{border:1px solid #ccc;padding:6px 10px;font-size:12px;text-align:left}th{background:#0B1A2E;color:#fff}@media print{body{padding:10mm}table{page-break-inside:auto}tr{page-break-inside:avoid}}</style></head><body><h2>\u041F\u043B\u0430\u043D \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0439 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438</h2><p>AO \u00AB\u041A\u0430\u0437\u0430\u0445\u0442\u0435\u043B\u0435\u043A\u043E\u043C\u00BB \u0437\u0430 "+month+"."+year+"</p><br><table><tr><th>N</th><th>\u041C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0435</th><th>\u0424\u0438\u043B\u0438\u0430\u043B</th><th>\u0421\u0440\u043E\u043A</th><th>\u0421\u0442\u0430\u0442\u0443\u0441</th><th>\u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441</th><th>\u041A\u043E\u043B-\u0432\u043E</th><th>\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435</th></tr>";
|
||||
for(var i=0;i<fl.length;i++){
|
||||
var e=fl[i];
|
||||
hh+="<tr><td>"+e.id+"</td><td>"+esc(e.t)+"</td><td>"+brs[e.b]+"</td><td>"+e.due+"</td><td>"+stn[e.s]+"</td><td>"+(e.p||0)+"%</td><td>"+(e.q||"")+"</td><td>"+esc(e.n||"")+"</td></tr>"
|
||||
}
|
||||
hh+="</table><p><br><em>\u041E\u0442\u0447\u0451\u0442 \u0441\u0444\u043E\u0440\u043C\u0438\u0440\u043E\u0432\u0430\u043D: "+new Date().toLocaleDateString("ru-RU")+"</em></p><script>window.onload=function(){window.print()}<\/script></body></html>";
|
||||
var w=window.open("","_blank","width=900,height=700");
|
||||
w.document.write(hh);
|
||||
w.document.close()
|
||||
}
|
||||
function hseSend(){
|
||||
var btn=document.getElementById("hse_btn");
|
||||
var result=document.getElementById("hse_result");
|
||||
if(!btn||!result)return;
|
||||
result.innerHTML="<span style='color:var(--gray-500)'>\u042D\u0442\u043E \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F. \u0414\u043B\u044F \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438 \u0432 HSE.sk.kz \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u043A\u043E\u0440\u043F\u043E\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u043F\u043E\u0440\u0442\u0430\u043B.</span>";
|
||||
}
|
||||
|
||||
var aiGreeted=false;
|
||||
function renderAI(){
|
||||
if(!aiGreeted){
|
||||
aiGreeted=true;
|
||||
var box=document.getElementById("ai_chat");
|
||||
if(box){
|
||||
box.innerHTML="";
|
||||
addMsg("b","\u0414\u0436\u0430\u0440\u0432\u0438\u0441 \u043A \u0432\u0430\u0448\u0438\u043C \u0443\u0441\u043B\u0443\u0433\u0430\u043C. \u042F \u0430\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u0443\u044E \u043C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u044F \u041F\u0411 \u043F\u043E 9 \u0444\u0438\u043B\u0438\u0430\u043B\u0430\u043C. \u0421\u043F\u0440\u043E\u0441\u0438\u0442\u0435: \u0441\u0432\u043E\u0434\u043A\u0430, \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043D\u044B\u0435, \u0440\u0438\u0441\u043A\u0438, \u0440\u0435\u0439\u0442\u0438\u043D\u0433, \u0430\u0443\u0434\u0438\u0442, \u043F\u0440\u043E\u0433\u043D\u043E\u0437, \u0441\u043E\u0432\u0435\u0442\u043D\u0438\u043A.","\u0414\u0436\u0430\u0440\u0432\u0438\u0441")
|
||||
}
|
||||
}
|
||||
}
|
||||
function addMsg(role,text,name){
|
||||
var box=document.getElementById("ai_chat");
|
||||
if(!box)return;
|
||||
var nm=role==="u"?"\u0412\u044B":(name||"\u0411\u043E\u0442");
|
||||
box.innerHTML+="<div class='msg "+role+"'><div class='nm'>"+esc(nm)+"</div><div>"+esc(text)+"</div></div>";
|
||||
box.scrollTop=box.scrollHeight
|
||||
}
|
||||
function aiAsk(q){
|
||||
addMsg("u",q,"\u0412\u044B");
|
||||
setTimeout(function(){aiResp(q)},300)
|
||||
}
|
||||
function aiSend(){
|
||||
var inp=document.getElementById("ai_inp");
|
||||
if(!inp||!inp.value.trim())return;
|
||||
var q=inp.value.trim();
|
||||
inp.value="";
|
||||
aiAsk(q)
|
||||
}
|
||||
function aiResp(q){
|
||||
var total=evs.length;
|
||||
var done=0,late=0,warn=0;
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var s=evs[i].s;
|
||||
if(s==="done")done++;
|
||||
else if(s==="late")late++;
|
||||
else if(s==="warn")warn++
|
||||
}
|
||||
var ql=q.toLowerCase();
|
||||
var ans="";
|
||||
|
||||
if(ql.indexOf("\u0441\u0432\u043E\u0434")!==-1||ql.indexOf("\u043E\u0431\u0449")!==-1||ql.indexOf("\u0441\u0442\u0430\u0442\u0443\u0441")!==-1||ql.indexOf("\u0432\u0441\u0435")!==-1){
|
||||
ans="\u041E\u0431\u0449\u0430\u044F \u0441\u0432\u043E\u0434\u043A\u0430 \u043F\u043E \u043F\u043B\u0430\u043D\u0443 \u041F\u0411:";
|
||||
ans+="\n- \u0412\u0441\u0435\u0433\u043E: "+total+" \u043C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u0439";
|
||||
ans+="\n- \u0418\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u043E: "+done+" ("+Math.round(done/total*100)+"%)";
|
||||
ans+="\n- \u0412 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435: "+warn;
|
||||
ans+="\n- \u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E: "+late
|
||||
|
||||
}else if(ql.indexOf("\u043F\u0440\u043E\u0441\u0440\u043E\u0447")!==-1||ql.indexOf("\u0441\u0440\u043E\u0447\u043D")!==-1||ql.indexOf("\u043A\u0440\u0438\u0442\u0438\u0447")!==-1){
|
||||
var lateList=[];
|
||||
for(var i=0;i<evs.length;i++){if(evs[i].s==="late"){lateList.push(evs[i])}}
|
||||
if(lateList.length){
|
||||
ans="\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043D\u044B\u0435 \u043C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u044F ("+lateList.length+"):";
|
||||
for(var i=0;i<lateList.length;i++){
|
||||
ans+="\nN"+lateList[i].id+" - "+lateList[i].t.slice(0,80)+"... ("+lateList[i].due+", "+brs[lateList[i].b]+")"
|
||||
}
|
||||
}else{ans="\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043D\u044B\u0445 \u043D\u0435\u0442"}
|
||||
|
||||
}else if(ql.indexOf("\u0440\u0438\u043A")!==-1||ql.indexOf("risk")!==-1||ql.indexOf("\u043F\u0440\u043E\u0431\u043B")!==-1||ql.indexOf("\u0441\u0440\u044B\u0432")!==-1){
|
||||
var risk=[];
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var dr=daysRem(evs[i].due);
|
||||
if(evs[i].s!=="done"&&dr<=30&&dr>0){risk.push(evs[i])}
|
||||
}
|
||||
if(risk.length){
|
||||
ans="\u041C\u0435\u043D\u0435\u0435 30 \u0434\u043D\u0435\u0439 \u0434\u043E \u0441\u0440\u043E\u043A\u0430 ("+risk.length+"):";
|
||||
for(var i=0;i<risk.length;i++){
|
||||
ans+="\nN"+risk[i].id+" - "+risk[i].t.slice(0,60)+"... ("+daysRem(risk[i].due)+" \u0434\u043D.)"
|
||||
}
|
||||
}else{ans="\u0420\u0438\u0441\u043A\u043E\u0432 \u043D\u0435\u0442"}
|
||||
|
||||
}else if(ql.indexOf("\u0440\u0435\u0439\u0442")!==-1||ql.indexOf("\u0444\u0438\u043B\u0438\u0430\u043B")!==-1||ql.indexOf("\u043B\u0443\u0447\u0448")!==-1||ql.indexOf("\u0445\u0443\u0434\u0448")!==-1){
|
||||
var brd=[];
|
||||
for(var i=0;i<brs.length;i++){brd.push({n:brs[i],t:0,d:0})}
|
||||
for(var i=0;i<evs.length;i++){var e=evs[i];brd[e.b].t++;if(e.s==="done")brd[e.b].d++}
|
||||
brd.sort(function(a,b){return(b.d/b.t||0)-(a.d/a.t||0)});
|
||||
ans="\u0420\u0435\u0439\u0442\u0438\u043D\u0433 \u0444\u0438\u043B\u0438\u0430\u043B\u043E\u0432:";
|
||||
for(var i=0;i<brd.length;i++){
|
||||
var pct=brd[i].t?Math.round(brd[i].d/brd[i].t*100):0;
|
||||
ans+="\n"+(i+1)+". "+brd[i].n+": "+brd[i].d+"/"+brd[i].t+" ("+pct+"%)"
|
||||
}
|
||||
|
||||
}else if(ql.indexOf("\u043F\u0440\u043E\u0433\u043D\u043E\u0437")!==-1||ql.indexOf("\u043F\u0440\u043E\u0433\u043D")!==-1){
|
||||
var atRisk=0,crit=0,onTrack=0;
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var dr=daysRem(evs[i].due);
|
||||
if(evs[i].s==="done")onTrack++;
|
||||
else if(dr<=0)crit++;
|
||||
else if(dr<=30)atRisk++;
|
||||
else onTrack++
|
||||
}
|
||||
ans="\u041F\u0440\u043E\u0433\u043D\u043E\u0437 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u043F\u043B\u0430\u043D\u0430 \u041F\u0411:\n- \u0412\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u043E: "+onTrack+"\n- \u0412 \u0437\u043E\u043D\u0435 \u0440\u0438\u0441\u043A\u0430 (<30 \u0434\u043D): "+atRisk+"\n- \u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 (\u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E): "+crit;
|
||||
ans+="\n\n\u041F\u0440\u043E\u0433\u043D\u043E\u0437\u0438\u0440\u0443\u0435\u043C\u044B\u0439 % \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u043A \u043A\u043E\u043D\u0446\u0443 \u0433\u043E\u0434\u0430: "+Math.round((onTrack+atRisk*0.5)/evs.length*100)+"%";
|
||||
if(crit>3)ans+="\n\n\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F: \u0441\u0440\u043E\u0447\u043D\u044B\u0439 \u0448\u0442\u0430\u0431 \u043F\u043E "+crit+" \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043D\u044B\u043C \u043F\u0443\u043D\u043A\u0442\u0430\u043C."
|
||||
|
||||
}else if(ql.indexOf("\u0441\u0442\u0430\u0442\u0443\u0441")!==-1||ql.indexOf("\u0441\u043E\u0441\u0442\u043E\u044F\u043D")!==-1||ql.indexOf("\u043E\u0431\u0441\u0442\u0430\u043D")!==-1){
|
||||
var bySec=[];
|
||||
for(var si=0;si<secs.length;si++){bySec.push({n:secs[si].split(".")[0],t:0,d:0,l:0})}
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var e=evs[i];bySec[e.sec].t++;
|
||||
if(e.s==="done")bySec[e.sec].d++;
|
||||
else if(e.s==="late")bySec[e.sec].l++
|
||||
}
|
||||
ans="\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043F\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430\u043C:";
|
||||
for(var i=0;i<bySec.length;i++){
|
||||
ans+="\n"+bySec[i].n+": "+bySec[i].d+"/"+bySec[i].t+" ("+Math.round(bySec[i].d/bySec[i].t*100)+"%)"+(bySec[i].l?" \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E:"+bySec[i].l:"")
|
||||
}
|
||||
|
||||
}else if(ql.indexOf("\u043F\u043B\u0430\u043D")!==-1||ql.indexOf("\u0434\u0435\u0439\u0441\u0442\u0432")!==-1||ql.indexOf("\u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434")!==-1||ql.indexOf("\u0441\u043E\u0432\u0435\u0442")!==-1){
|
||||
var pct=Math.round(done/total*100);
|
||||
if(pct<30)ans="\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F: \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u043E \u043C\u0435\u043D\u0435\u0435 30%. \u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u0443\u0441\u0438\u043B\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C \u0437\u0430 \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043D\u044B\u043C\u0438 \u0438 \u043F\u0440\u043E\u0432\u0435\u0441\u0442\u0438 \u0448\u0442\u0430\u0431 \u0441 \u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u043C\u0438 \u043B\u0438\u0446\u0430\u043C\u0438";
|
||||
else if(pct<60)ans="\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F: \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u043E "+pct+"%. \u041E\u0431\u0440\u0430\u0442\u0438\u0442\u044C \u0432\u043D\u0438\u043C\u0430\u043D\u0438\u0435 \u043D\u0430 \u043F\u0440\u043E\u0446\u0435\u043D\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u0432 \u0444\u0438\u043B\u0438\u0430\u043B\u0430\u0445 \u0441 \u043D\u0438\u0437\u043A\u0438\u043C \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u0435\u043C";
|
||||
else ans="\u0425\u043E\u0440\u043E\u0448\u0438\u0439 \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441: "+pct+"%. \u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u0440\u0430\u0431\u043E\u0442\u0443 \u0432 \u0442\u043E\u043C \u0436\u0435 \u0442\u0435\u043C\u043F\u0435"
|
||||
|
||||
}else if(ql.indexOf("\u0430\u0443\u0434\u0438\u0442")!==-1||ql.indexOf("\u043F\u0440\u043E\u0432\u0435\u0440")!==-1||ql.indexOf("\u043A\u043E\u043D\u0442\u0440\u043E\u043B")!==-1){
|
||||
ans="\u0410\u0443\u0434\u0438\u0442 \u043F\u043B\u0430\u043D\u0430 \u041F\u0411:";
|
||||
ans+="\n- \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u043E: "+done+"/"+total+" ("+Math.round(done/total*100)+"%)";
|
||||
ans+="\n- \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E: "+late;
|
||||
var riskCount=0;
|
||||
for(var i=0;i<evs.length;i++){var dr=daysRem(evs[i].due);if(evs[i].s!=="done"&&dr<=30&&dr>0)riskCount++}
|
||||
ans+="\n- \u0432 \u0440\u0438\u0441\u043A\u0435 (<30 \u0434\u043D\u0435\u0439): "+riskCount;
|
||||
if(done/total>0.7)ans+="\n\u041E\u0431\u0449\u0430\u044F \u043E\u0446\u0435\u043D\u043A\u0430: \u0445\u043E\u0440\u043E\u0448\u043E";
|
||||
else if(done/total>0.4)ans+="\n\u041E\u0431\u0449\u0430\u044F \u043E\u0446\u0435\u043D\u043A\u0430: \u0443\u0434\u043E\u0432\u043B\u0435\u0442\u0432\u043E\u0440\u0438\u0442\u0435\u043B\u044C\u043D\u043E";
|
||||
else ans+="\n\u041E\u0431\u0449\u0430\u044F \u043E\u0446\u0435\u043D\u043A\u0430: \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0432\u043D\u0438\u043C\u0430\u043D\u0438\u044F"
|
||||
|
||||
}else if(ql.indexOf("\u043F\u0443\u043D\u043A\u0442")!==-1||ql.indexOf("\u043D\u043E\u043C\u0435\u0440")!==-1){
|
||||
var match=ql.match(/\d+/);
|
||||
if(match){
|
||||
var num=parseInt(match[0],10);
|
||||
var found=null;
|
||||
for(var i=0;i<evs.length;i++){if(evs[i].id===num){found=evs[i];break}}
|
||||
if(found){
|
||||
ans="N"+found.id+" "+found.t.slice(0,80)+"...";
|
||||
ans+="\n\u0421\u0442\u0430\u0442\u0443\u0441: "+stn[found.s];
|
||||
ans+="\n\u0424\u0438\u043B\u0438\u0430\u043B: "+brs[found.b];
|
||||
ans+="\n\u0421\u0440\u043E\u043A: "+found.due;
|
||||
ans+="\n\u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441: "+(found.p||0)+"%"
|
||||
}else{ans="\u041F\u0443\u043D\u043A\u0442 N"+num+" \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D"}
|
||||
}else{ans="\u041D\u0430\u043F\u0438\u0448\u0438 \u043D\u043E\u043C\u0435\u0440 \u043F\u0443\u043D\u043A\u0442\u0430, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u043F\u0443\u043D\u043A\u0442 5"}
|
||||
|
||||
}else{
|
||||
ans="\u042F \u2014 \u0414\u0436\u0430\u0440\u0432\u0438\u0441, \u0432\u0430\u0448 \u0430\u043D\u0430\u043B\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043D\u0442. \u041C\u043E\u0433\u0443 \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C:\n\n\u2022 \u0441\u0432\u043E\u0434\u043A\u0430 \u2014 \u043E\u0431\u0449\u0430\u044F \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0430\n\u2022 \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043D\u044B\u0435 \u2014 \u0441\u043F\u0438\u0441\u043E\u043A \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043A\n\u2022 \u0440\u0438\u0441\u043A\u0438 \u2014 \u0437\u043E\u043D\u0430 \u0440\u0438\u0441\u043A\u0430 (<30 \u0434\u043D\u0435\u0439)\n\u2022 \u0440\u0435\u0439\u0442\u0438\u043D\u0433 \u2014 \u0440\u0435\u0439\u0442\u0438\u043D\u0433 \u0444\u0438\u043B\u0438\u0430\u043B\u043E\u0432\n\u2022 \u0430\u0443\u0434\u0438\u0442 \u2014 \u043F\u043E\u043B\u043D\u044B\u0439 \u0430\u0443\u0434\u0438\u0442\n\u2022 \u043F\u0440\u043E\u0433\u043D\u043E\u0437 \u2014 \u043F\u0440\u043E\u0433\u043D\u043E\u0437 \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F\n\u2022 \u0441\u0442\u0430\u0442\u0443\u0441 \u2014 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043F\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430\u043C\n\u2022 \u043F\u043B\u0430\u043D \u2014 \u043F\u043B\u0430\u043D \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0438 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u0438\n\u2022 \u043F\u0443\u043D\u043A\u0442 N \u2014 \u0434\u0435\u0442\u0430\u043B\u0438 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0433\u043E \u043C\u0435\u0440\u043E\u043F\u0440\u0438\u044F\u0442\u0438\u044F"
|
||||
}
|
||||
|
||||
addMsg("b",ans,"\u0414\u0436\u0430\u0440\u0432\u0438\u0441")
|
||||
}
|
||||
function renderUsers(){
|
||||
if(!cu||cu.bg!==0){document.getElementById("tab_users").innerHTML="<div class='card'><p style='color:#EF4444'>\u0414\u043E\u0441\u0442\u0443\u043F \u0437\u0430\u043F\u0440\u0435\u0449\u0451\u043D</p></div>";return}
|
||||
var ex=localStorage.getItem("ext_users");
|
||||
if(ex){try{var eu=JSON.parse(ex);for(var k in eu){if(eu.hasOwnProperty(k)&&!USR[k])USR[k]=eu[k]}}catch(e){}}
|
||||
var h="<table><tr><th>\u041B\u043E\u0433\u0438\u043D</th><th>\u0424\u0418\u041E</th><th>\u0422\u0435\u043B\u0435\u0444\u043E\u043D</th><th>\u0424\u0438\u043B\u0438\u0430\u043B</th><th></th></tr>";
|
||||
for(var k in USR){
|
||||
if(!USR.hasOwnProperty(k))continue;
|
||||
var u=USR[k];
|
||||
h+="<tr><td>"+esc(k)+"@telecom.kz</td><td>"+esc(u.n)+"</td><td>"+esc(u.ph||"")+"</td><td>"+esc(brs[u.bg]||"")+"</td>";
|
||||
h+="<td><button class='btn btn-sm btn-o' style='padding:3px 8px;margin-right:4px' onclick=\"resetPw('"+esc(k)+"')\">\u0421\u0431\u0440\u043E\u0441</button><button class='btn btn-sm btn-r' style='padding:3px 10px' onclick=\"delUser('"+esc(k)+"')\">\u0423\u0434\u0430\u043B\u0438\u0442\u044C</button></td></tr>"
|
||||
}
|
||||
h+="</table>";
|
||||
document.getElementById("users_list").innerHTML=h
|
||||
}
|
||||
function resetPw(k){
|
||||
var np=prompt("\u041D\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u044F "+k+":","0000");
|
||||
if(np&&USR[k]){USR[k].pw=np;saveUsers();renderUsers()}
|
||||
}
|
||||
function addUser(){
|
||||
var em=document.getElementById("reg_email").value.trim().toLowerCase();
|
||||
var nm=document.getElementById("reg_name").value.trim();
|
||||
var ph=document.getElementById("reg_phone").value.trim();
|
||||
var bg=parseInt(document.getElementById("reg_branch").value,10);
|
||||
var pw=document.getElementById("reg_pass").value.trim();
|
||||
if(!em||!nm){alert("\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043B\u043E\u0433\u0438\u043D \u0438 \u0424\u0418\u041E");return}
|
||||
USR[em]={n:nm,bg:bg,ph:ph};
|
||||
if(pw)USR[em].pw=pw;
|
||||
saveUsers();
|
||||
closeRegModal();
|
||||
renderUsers()
|
||||
}
|
||||
function showRegModal(){
|
||||
var rb=document.getElementById("reg_branch");
|
||||
if(rb&&!rb.options.length){
|
||||
for(var i=0;i<brs.length;i++){
|
||||
var o=document.createElement("option");
|
||||
o.value=i;o.textContent=brs[i];
|
||||
rb.appendChild(o)
|
||||
}
|
||||
}
|
||||
document.getElementById("regModal").style.display="flex"
|
||||
}
|
||||
function closeRegModal(){
|
||||
document.getElementById("regModal").style.display="none";
|
||||
document.getElementById("reg_email").value="";
|
||||
document.getElementById("reg_name").value="";
|
||||
document.getElementById("reg_phone").value="";
|
||||
document.getElementById("reg_pass").value=""
|
||||
}
|
||||
function delUser(k){
|
||||
if(!confirm("\u0423\u0434\u0430\u043B\u0438\u0442\u044C "+k+"?"))return;
|
||||
delete USR[k];
|
||||
saveUsers();
|
||||
renderUsers()
|
||||
}
|
||||
function saveUsers(){
|
||||
var ex={};
|
||||
for(var k in USR){
|
||||
if(!USR.hasOwnProperty(k))continue;
|
||||
if(k!=="curator"&&k!=="admin"&&k!=="dpp")ex[k]=USR[k]
|
||||
}
|
||||
try{localStorage.setItem("ext_users",JSON.stringify(ex))}catch(e){}
|
||||
}
|
||||
function saveLTIF(){
|
||||
var m=parseInt(document.getElementById("ltif_month").value,10);
|
||||
var h=parseFloat(document.getElementById("ltif_hours").value)||0;
|
||||
var a=parseInt(document.getElementById("ltif_inj").value)||0;
|
||||
if(!h){alert("\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u043E-\u0447\u0430\u0441\u044B");return}
|
||||
var data=localStorage.getItem("ltif_data");
|
||||
var ltif=[];
|
||||
if(data){try{ltif=JSON.parse(data)}catch(e){}}
|
||||
if(ltif[m]&<if[m].h){
|
||||
var co=localStorage.getItem("ltif_corrections");
|
||||
var corr=[];
|
||||
if(co){try{corr=JSON.parse(co)}catch(e){}}
|
||||
var d=new Date();
|
||||
corr.push({month:m,oldH:ltif[m].h,oldA:ltif[m].a,newH:h,newA:a,comment:"\u041A\u043E\u0440\u0440\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u043A\u0430 \u0431\u0435\u0437 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u044F",date:d.getDate()+"."+String(d.getMonth()+1).padStart(2,"0")+"."+d.getFullYear(),user:cu?cu.n:""});
|
||||
try{localStorage.setItem("ltif_corrections",JSON.stringify(corr))}catch(e){}
|
||||
}
|
||||
ltif[m]={h:h,a:a};
|
||||
try{localStorage.setItem("ltif_data",JSON.stringify(ltif))}catch(e){}
|
||||
loadLTIF()
|
||||
}
|
||||
function saveLtifCorrection(m){
|
||||
var nh=parseFloat(document.getElementById("ltifc_h_"+m).value)||0;
|
||||
var na=parseInt(document.getElementById("ltifc_a_"+m).value)||0;
|
||||
var nc=document.getElementById("ltifc_c_"+m).value.trim()||"\u041A\u043E\u0440\u0440\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u043A\u0430";
|
||||
if(!nh){alert("\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u043E-\u0447\u0430\u0441\u044B");return}
|
||||
var data=localStorage.getItem("ltif_data");
|
||||
var ltif=[];
|
||||
if(data){try{ltif=JSON.parse(data)}catch(e){}}
|
||||
var oh=ltif[m]?ltif[m].h:0;
|
||||
var oa=ltif[m]?ltif[m].a:0;
|
||||
var co=localStorage.getItem("ltif_corrections");
|
||||
var corr=[];
|
||||
if(co){try{corr=JSON.parse(co)}catch(e){}}
|
||||
var d=new Date();
|
||||
corr.push({month:m,oldH:oh,oldA:oa,newH:nh,newA:na,comment:nc,date:d.getDate()+"."+String(d.getMonth()+1).padStart(2,"0")+"."+d.getFullYear(),user:cu?cu.n:""});
|
||||
try{localStorage.setItem("ltif_corrections",JSON.stringify(corr))}catch(e){}
|
||||
ltif[m]={h:nh,a:na};
|
||||
try{localStorage.setItem("ltif_data",JSON.stringify(ltif))}catch(e){}
|
||||
loadLTIF()
|
||||
}
|
||||
function loadLTIF(){
|
||||
var data=localStorage.getItem("ltif_data");
|
||||
var ltif=[];
|
||||
if(data){try{ltif=JSON.parse(data)}catch(e){}}
|
||||
var mnames=["\u042F\u043D\u0432","\u0424\u0435\u0432","\u041C\u0430\u0440","\u0410\u043F\u0440","\u041C\u0430\u0439","\u0418\u044E\u043D","\u0418\u044E\u043B","\u0410\u0432\u0433","\u0421\u0435\u043D","\u041E\u043A\u0442","\u041D\u043E\u044F","\u0414\u0435\u043A"];
|
||||
var m=parseInt(document.getElementById("ltif_month").value,10);
|
||||
var cur=ltif[m];
|
||||
if(cur){document.getElementById("ltif_hours").value=cur.h;document.getElementById("ltif_inj").value=cur.a}
|
||||
else{document.getElementById("ltif_hours").value="";document.getElementById("ltif_inj").value=""}
|
||||
var res=document.getElementById("ltif_result");
|
||||
var totalH=0,totalA=0;
|
||||
var tbl="<table><tr><th>\u041C\u0435\u0441\u044F\u0446</th><th>\u0427\u0435\u043B-\u0447\u0430\u0441\u044B</th><th>\u041F\u043E\u0441\u0442\u0440\u0430\u0434\u0430\u0432\u0448\u0438\u0435</th><th>LTIF</th><th>\u041A\u043E\u0440\u0440\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u043A\u0430</th></tr>";
|
||||
for(var i=0;i<12;i++){
|
||||
if(ltif[i]&<if[i].h){
|
||||
totalH+=ltif[i].h;totalA+=ltif[i].a;
|
||||
var lt=ltif[i].a*1000000/ltif[i].h;
|
||||
tbl+="<tr><td>"+mnames[i]+"</td><td>"+ltif[i].h.toLocaleString()+"</td><td>"+ltif[i].a+"</td><td>"+lt.toFixed(2)+"</td>";
|
||||
tbl+="<td><button class='btn btn-sm btn-o' style='padding:2px 6px;font-size:10px' onclick="showLtifCorr("+i+")">\u041A\u043E\u0440\u0440.</button></td></tr>"
|
||||
}
|
||||
}
|
||||
tbl+="</table>";
|
||||
if(totalH>0){
|
||||
var totalLT=totalA*1000000/totalH;
|
||||
res.innerHTML="<strong>\u0413\u043E\u0434\u043E\u0432\u043E\u0439 LTIF: "+totalLT.toFixed(2)+"</strong> (\u043F\u043E\u0441\u0442\u0440\u0430\u0434\u0430\u0432\u0448\u0438\u0445: "+totalA+", \u0447\u0435\u043B-\u0447\u0430\u0441\u043E\u0432: "+totalH.toLocaleString()+")"
|
||||
}else{res.innerHTML=""}
|
||||
document.getElementById("ltif_table").innerHTML=tbl;
|
||||
document.getElementById("ltif_corr_div").innerHTML="";
|
||||
document.getElementById("ltif_card").style.display=cu&&cu.bg===0?"":"none";
|
||||
renderLtifCorrections()
|
||||
}
|
||||
function showLtifCorr(m){
|
||||
var data=localStorage.getItem("ltif_data");
|
||||
var ltif=[];
|
||||
if(data){try{ltif=JSON.parse(data)}catch(e){}}
|
||||
var cur=ltif[m];
|
||||
var h="<div style='margin-top:8px;padding:8px;background:#FFF3E0;border-radius:6px;font-size:12px'>";
|
||||
h+="<strong>\u041A\u043E\u0440\u0440\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u043A\u0430 \u0437\u0430 "+esc(mnames[m])+":</strong><br>";
|
||||
h+="\u0427\u0435\u043B-\u0447\u0430\u0441\u044B: <input id='ltifc_h_"+m+"' type='number' value='"+(cur?cur.h:"")+"' style='width:120px;padding:4px;border:1px solid var(--gray-100);border-radius:4px;font-size:11px'> ";
|
||||
h+="\u041F\u043E\u0441\u0442\u0440\u0430\u0434\u0430\u0432\u0448\u0438\u0445: <input id='ltifc_a_"+m+"' type='number' value='"+(cur?cur.a:"0")+"' style='width:60px;padding:4px;border:1px solid var(--gray-100);border-radius:4px;font-size:11px'><br>";
|
||||
h+="\u041F\u0440\u0438\u0447\u0438\u043D\u0430: <input id='ltifc_c_"+m+"' placeholder='\u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u0432\u043D\u0435\u0441\u0435\u043D\u0438\u044F \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u043A\u0438' style='width:100%;padding:4px;border:1px solid var(--gray-100);border-radius:4px;font-size:11px'><br>";
|
||||
h+="<button class='btn btn-sm btn-g' style='margin-top:4px;padding:3px 10px;font-size:11px' onclick='saveLtifCorrection("+m+")'>\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C</button>";
|
||||
h+="<button class='btn btn-sm' style='margin-left:4px;padding:3px 10px;font-size:11px;background:var(--gray-100);color:var(--ink)' onclick='document.getElementById("ltif_corr_div").innerHTML=""'>\u041E\u0442\u043C\u0435\u043D\u0430</button></div>";
|
||||
document.getElementById("ltif_corr_div").innerHTML=h
|
||||
}
|
||||
function renderLtifCorrections(){
|
||||
var co=localStorage.getItem("ltif_corrections");
|
||||
if(!co)return;
|
||||
try{
|
||||
var corr=JSON.parse(co);
|
||||
if(!corr||!corr.length)return;
|
||||
var mnames=["\u042F\u043D\u0432","\u0424\u0435\u0432","\u041C\u0430\u0440","\u0410\u043F\u0440","\u041C\u0430\u0439","\u0418\u044E\u043D","\u0418\u044E\u043B","\u0410\u0432\u0433","\u0421\u0435\u043D","\u041E\u043A\u0442","\u041D\u043E\u044F","\u0414\u0435\u043A"];
|
||||
corr.sort(function(a,b){return b.month-a.month});
|
||||
var h="<div style='margin-top:12px'><strong>\u0418\u0441\u0442\u043E\u0440\u0438\u044F \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u043E\u043A:</strong><table style='font-size:11px'><tr><th>\u041C\u0435\u0441\u044F\u0446</th><th>\u0411\u044B\u043B\u043E \u0447-\u0447/\u043F</th><th>\u0421\u0442\u0430\u043B\u043E \u0447-\u0447/\u043F</th><th>\u041F\u0440\u0438\u0447\u0438\u043D\u0430</th><th>\u0414\u0430\u0442\u0430</th><th>\u041A\u0442\u043E</th></tr>";
|
||||
for(var i=0;i<corr.length;i++){
|
||||
var c=corr[i];
|
||||
h+="<tr><td>"+mnames[c.month]+"</td><td>"+(c.oldH||0)+"/"+(c.oldA||0)+"</td><td>"+c.newH+"/"+c.newA+"</td><td style='font-size:10px'>"+esc(c.comment||"")+"</td><td>"+esc(c.date||"")+"</td><td>"+esc(c.user||"")+"</td></tr>"
|
||||
}
|
||||
h+="</table></div>";
|
||||
document.getElementById("ltif_corr_div").innerHTML+=h
|
||||
}catch(e){}
|
||||
}
|
||||
function dlAnalyticsPPT(){
|
||||
var total=evs.length;
|
||||
var done=0,late=0,warn=0;
|
||||
for(var i=0;i<evs.length;i++){
|
||||
if(evs[i].s==="done")done++;else if(evs[i].s==="late")late++;else warn++
|
||||
}
|
||||
var h="<!DOCTYPE html><html><head><meta charset='utf-8'><title>\u0410\u043D\u0430\u043B\u0438\u0442\u0438\u043A\u0430 HSE</title><style>body{font:18px Arial;padding:40px}@page{size:landscape}.slide{page-break-after:always;min-height:400px;padding:20px}.num{font-size:48px;font-weight:800;color:var(--cyan)}.bar{height:24px;background:var(--cyan);border-radius:4px;margin:4px 0}</style></head><body><div class='slide'><h1>QAZAQtelecom HSE \u2014 \u0414\u0430\u0448\u0431\u043E\u0440\u0434</h1><p>\u0414\u0430\u0442\u0430: "+new Date().toLocaleDateString("ru-RU")+"</p><br><table><tr><td><div class='num'>"+total+"</div>\u0412\u0441\u0435\u0433\u043E</td><td><div class='num'>"+done+"</div>\u0418\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u043E</td><td><div class='num'>"+warn+"</div>\u0412 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435</td><td><div class='num'>"+late+"</div>\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E</td></tr></table><br><p>\u0412\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435: <strong>"+Math.round(done/total*100)+"%</strong></p><div class='bar' style='width:"+Math.round(done/total*300)+"px'></div></div></body></html>";
|
||||
var blob=new Blob([h],{type:"application/vnd.ms-powerpoint"});
|
||||
var a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download="dashboard.pptx";a.click()
|
||||
}
|
||||
function dlAnalyticsPDF(){
|
||||
var h=document.getElementById("tab_analytics").innerHTML;
|
||||
var w=window.open("","_blank","width=900,height=700");
|
||||
w.document.write("<!DOCTYPE html><html><head><meta charset='utf-8'><title>\u0410\u043D\u0430\u043B\u0438\u0442\u0438\u043A\u0430</title><style>body{font:14px Arial;padding:20px}@media print{body{padding:10mm}}.card{background:var(--white);border:1px solid var(--gray-100);border-radius:12px;padding:16px;margin-bottom:12px}.num{font-size:28px;font-weight:800}</style></head><body><h2>QAZAQtelecom HSE \u2014 \u0410\u043D\u0430\u043B\u0438\u0442\u0438\u043A\u0430</h2><p>"+new Date().toLocaleDateString("ru-RU")+"</p><br>"+h+"<script>window.onload=function(){window.print()}<\/script></body></html>");
|
||||
w.document.close()
|
||||
}
|
||||
function dlAnalyticsWord(){
|
||||
var total=evs.length;
|
||||
var done=0,late=0,warn=0;
|
||||
for(var i=0;i<evs.length;i++){
|
||||
if(evs[i].s==="done")done++;else if(evs[i].s==="late")late++;else warn++
|
||||
}
|
||||
var h="<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word'><head><meta charset='utf-8'><title>\u0410\u043D\u0430\u043B\u0438\u0442\u0438\u043A\u0430 HSE</title><style>@page{size:A4;margin:20mm}body{font:14pt 'Times New Roman'}h1{color:var(--cyan)}table{border-collapse:collapse}td{border:1px solid #000;padding:12px 20px;font-size:20pt;font-weight:700}</style></head><body><h1>QAZAQtelecom HSE \u2014 \u0410\u043D\u0430\u043B\u0438\u0442\u0438\u043A\u0430</h1><p>\u0414\u0430\u0442\u0430: "+new Date().toLocaleDateString("ru-RU")+"</p><br><table><tr><td>\u0412\u0441\u0435\u0433\u043E<br><span style='font-size:28pt'>"+total+"</span></td><td>\u0418\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u043E<br><span style='font-size:28pt'>"+done+" ("+Math.round(done/total*100)+"%)</span></td><td>\u0412 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435<br><span style='font-size:28pt'>"+warn+"</span></td><td>\u041F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D\u043E<br><span style='font-size:28pt;color:red'>"+late+"</span></td></tr></table></body></html>";
|
||||
var blob=new Blob([h],{type:"application/msword"});
|
||||
var a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download="analytics.doc";a.click()
|
||||
}
|
||||
133
server.py
133
server.py
@ -1,133 +0,0 @@
|
||||
import json
|
||||
import io
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
import requests as http_requests
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
HSE_API_URL = "https://hse.sk.kz/api/v1"
|
||||
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
|
||||
|
||||
def make_docx(report):
|
||||
from docx import Document
|
||||
from docx.shared import Pt
|
||||
doc = Document()
|
||||
doc.styles["Normal"].font.size = Pt(11)
|
||||
doc.add_heading("План ПБ — Казахтелеком", level=1)
|
||||
s = report.get("summary", {})
|
||||
doc.add_paragraph(
|
||||
f"Дата: {datetime.now().strftime('%d.%m.%Y')} | "
|
||||
f"Всего: {s.get('total', 0)} | "
|
||||
f"Выполнено: {s.get('done', 0)} ({s.get('pct', 0)}%)"
|
||||
)
|
||||
events = report.get("events", [])
|
||||
table = doc.add_table(rows=1, cols=6)
|
||||
table.style = "Light Grid Accent 1"
|
||||
for i, h in enumerate(["N", "Мероприятие", "Филиал", "Срок", "Статус", "%"]):
|
||||
table.rows[0].cells[i].text = h
|
||||
for e in events:
|
||||
row = table.add_row().cells
|
||||
row[0].text = str(e.get("id", ""))
|
||||
row[1].text = str(e.get("title", ""))[:100]
|
||||
row[2].text = str(e.get("branch", ""))
|
||||
row[3].text = str(e.get("deadline", ""))
|
||||
row[4].text = str(e.get("status", ""))
|
||||
row[5].text = str(e.get("progress", 0)) + "%"
|
||||
buf = io.BytesIO()
|
||||
doc.save(buf)
|
||||
buf.seek(0)
|
||||
return buf
|
||||
|
||||
|
||||
def make_pdf(report):
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.lib.colors import HexColor
|
||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
|
||||
|
||||
buf = io.BytesIO()
|
||||
doc = SimpleDocTemplate(buf, pagesize=A4, rightMargin=20 * mm, leftMargin=20 * mm,
|
||||
topMargin=20 * mm, bottomMargin=20 * mm)
|
||||
styles = getSampleStyleSheet()
|
||||
story = [Paragraph("План ПБ — Казахтелеком", styles["Title"]), Spacer(1, 10)]
|
||||
s = report.get("summary", {})
|
||||
story.append(Paragraph(
|
||||
f"Всего: {s.get('total', 0)} | Выполнено: {s.get('done', 0)} ({s.get('pct', 0)}%)",
|
||||
styles["Normal"]
|
||||
))
|
||||
story.append(Spacer(1, 10))
|
||||
data = [["N", "Мероприятие", "Филиал", "Срок", "Статус", "%"]]
|
||||
for e in report.get("events", []):
|
||||
data.append([
|
||||
str(e.get("id", "")), str(e.get("title", ""))[:80],
|
||||
str(e.get("branch", ""))[:25], str(e.get("deadline", "")),
|
||||
str(e.get("status", "")), str(e.get("progress", 0)) + "%",
|
||||
])
|
||||
table = Table(data, colWidths=[20, 220, 80, 50, 60, 40])
|
||||
table.setStyle(TableStyle([
|
||||
("FONTSIZE", (0, 0), (-1, 0), 9), ("FONTSIZE", (0, 1), (-1, -1), 8),
|
||||
("BACKGROUND", (0, 0), (-1, 0), HexColor("#003366")),
|
||||
("TEXTCOLOR", (0, 0), (-1, 0), HexColor("#FFFFFF")),
|
||||
("GRID", (0, 0), (-1, -1), 0.5, HexColor("#CCCCCC")),
|
||||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||
]))
|
||||
story.append(table)
|
||||
doc.build(story)
|
||||
buf.seek(0)
|
||||
return buf
|
||||
|
||||
|
||||
@app.route("/api/hse/send", methods=["POST"])
|
||||
def hse_send():
|
||||
data = request.get_json()
|
||||
month = data.get("month", "")
|
||||
api_key = data.get("api_key", "")
|
||||
fmt = data.get("format", "word")
|
||||
report = data.get("report", {})
|
||||
endpoint = data.get("endpoint", f"{HSE_API_URL}/documents/upload")
|
||||
|
||||
if not api_key:
|
||||
return jsonify({"ok": False, "error": "API key required"}), 400
|
||||
|
||||
if fmt == "pdf":
|
||||
buf = make_pdf(report)
|
||||
mime = "application/pdf"
|
||||
ext = "pdf"
|
||||
else:
|
||||
buf = make_docx(report)
|
||||
mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
ext = "docx"
|
||||
|
||||
try:
|
||||
files = {"file": (f"hse_report_{month}.{ext}", buf.getvalue(), mime)}
|
||||
headers = {"Authorization": f"Bearer {api_key}"}
|
||||
payload = {
|
||||
"title": f"Сводный отчет по ПБ за {month}",
|
||||
"description": "Автоматический отчет платформы мониторинга ПБ",
|
||||
"type": "safety_report",
|
||||
"period": month,
|
||||
}
|
||||
r = http_requests.post(endpoint, files=files, data=payload, headers=headers, timeout=30)
|
||||
if r.ok:
|
||||
return jsonify({"ok": True, "hse_response": r.json() if r.text else {"status": r.status_code}})
|
||||
return jsonify({"ok": False, "error": f"HSE API error: {r.status_code}", "detail": r.text[:500]}), 502
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 502
|
||||
|
||||
|
||||
@app.route("/api/health", methods=["GET"])
|
||||
def health():
|
||||
return jsonify({"ok": True, "time": datetime.now().isoformat()})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("HSE Integration Server — http://0.0.0.0:5000")
|
||||
app.run(host="0.0.0.0", port=5000, debug=False)
|
||||
8
start.sh
8
start.sh
@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
cd "$(dirname "$0")"
|
||||
echo "=== HSE Integration Server ==="
|
||||
echo "Installing..."
|
||||
pip3 install -r requirements.txt --break-system-packages -q 2>/dev/null
|
||||
echo "Starting on http://0.0.0.0:5000"
|
||||
python3 server.py
|
||||
328
style.css
Normal file
328
style.css
Normal file
@ -0,0 +1,328 @@
|
||||
:root {
|
||||
--ink: #0F1218;
|
||||
--cyan: #00E5FF;
|
||||
--cyan-dark: #00B8D4;
|
||||
--white: #FFFFFF;
|
||||
--gray-100: #F2F4F7;
|
||||
--gray-500: #5B6573;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font: 14px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
||||
background: var(--gray-100);
|
||||
color: var(--ink);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
input, select, textarea, button { font: inherit; outline: none; }
|
||||
|
||||
.btn {
|
||||
background: var(--cyan);
|
||||
color: var(--ink);
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.btn:hover {
|
||||
background: var(--cyan-dark);
|
||||
box-shadow: 0 2px 8px rgba(0, 229, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-sm { padding: 7px 16px; font-size: 12px; }
|
||||
.btn-r { background: #E53935; color: var(--white); }
|
||||
.btn-r:hover { background: #C62828; }
|
||||
.btn-g { background: #2E7D32; color: var(--white); }
|
||||
.btn-g:hover { background: #1B5E20; }
|
||||
.btn-o { background: #F57C00; color: var(--white); }
|
||||
.btn-o:hover { background: #E65100; }
|
||||
|
||||
#login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, var(--ink), #006478, var(--cyan-dark));
|
||||
}
|
||||
#login > div {
|
||||
background: var(--white);
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
width: 400px;
|
||||
max-width: 90vw;
|
||||
text-align: center;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
#login h1 { font-size: 22px; font-weight: 800; margin-bottom: 4px; }
|
||||
#login h1 span { color: var(--cyan-dark); }
|
||||
#login > div > p { color: var(--gray-500); font-size: 13px; margin-bottom: 24px; }
|
||||
#login input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 2px solid var(--gray-100);
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
transition: border 0.2s;
|
||||
}
|
||||
#login input:focus { border-color: var(--cyan-dark); }
|
||||
|
||||
#app { display: none; min-height: 100vh; }
|
||||
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 220px;
|
||||
background: var(--ink);
|
||||
color: var(--white);
|
||||
z-index: 100;
|
||||
overflow-y: auto;
|
||||
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
#sidebar .logo {
|
||||
padding: 20px 16px 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
#sidebar .logo span { color: var(--cyan); }
|
||||
#sidebar .user { font-size: 11px; color: #94A3B8; padding: 10px 16px; border-bottom: 1px solid rgba(255, 255, 255, .05); }
|
||||
#sidebar a {
|
||||
display: block;
|
||||
padding: 12px 16px;
|
||||
color: #CBD5E1;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
border-left: 3px solid transparent;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
#sidebar a:hover { background: rgba(0, 229, 255, .1); color: var(--white); }
|
||||
#sidebar a.active {
|
||||
background: rgba(0, 229, 255, .2);
|
||||
color: var(--cyan);
|
||||
border-left-color: var(--cyan);
|
||||
font-weight: 600;
|
||||
}
|
||||
#sidebar .logout { position: absolute; bottom: 16px; left: 16px; right: 16px; }
|
||||
|
||||
#main { margin-left: 220px; padding: 24px; min-height: 100vh; }
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 0 16px;
|
||||
border-bottom: 2px solid var(--gray-100);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.top h2 { font-size: 20px; font-weight: 700; }
|
||||
|
||||
.card {
|
||||
background: var(--white);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #E8ECF1;
|
||||
overflow-x: auto;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.card h3 { font-size: 16px; font-weight: 700; margin-bottom: 12px; }
|
||||
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th, td {
|
||||
padding: 8px 10px;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--gray-100);
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
background: var(--gray-100);
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
color: var(--gray-500);
|
||||
white-space: nowrap;
|
||||
}
|
||||
tr:hover { background: #FAFBFC; }
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 100px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.badge.g { background: #E8F5E9; color: #2E7D32; }
|
||||
.badge.a { background: #FFF3E0; color: #E65100; }
|
||||
.badge.r { background: #FFEBEE; color: #C62828; }
|
||||
.badge.b { background: #E3F2FD; color: #1565C0; }
|
||||
.badge.w { background: #F5F5F5; color: #757575; }
|
||||
|
||||
.fr { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; align-items: center; }
|
||||
.fr input, .fr select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--gray-100);
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
background: var(--white);
|
||||
}
|
||||
.fr input { min-width: 200px; }
|
||||
|
||||
.tr-red { background: #FFF5F5; }
|
||||
.tr-red td { border-bottom-color: #FECACA; color: #991B1B; }
|
||||
.tr-amber { background: #FFFBEB; }
|
||||
.tr-amber td { border-bottom-color: #FDE68A; color: #92400E; }
|
||||
.tr-green { background: #F0FDF4; }
|
||||
.tr-green td { border-bottom-color: #BBF7D0; color: #065F46; }
|
||||
|
||||
.sec-h {
|
||||
background: var(--cyan);
|
||||
color: var(--ink);
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
display: inline-block;
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.stat-card {
|
||||
background: var(--white);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--gray-100);
|
||||
text-align: center;
|
||||
}
|
||||
.stat-card .num { font-size: 28px; font-weight: 800; margin: 4px 0; }
|
||||
.stat-card .lb { font-size: 12px; color: var(--gray-500); }
|
||||
.stat-card.sg .num { color: #10B981; }
|
||||
.stat-card.sr .num { color: #EF4444; }
|
||||
.stat-card.sb .num { color: var(--cyan); }
|
||||
.stat-card.sa .num { color: #F59E0B; }
|
||||
|
||||
.chat-box {
|
||||
height: 280px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--gray-100);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
background: #FAFBFC;
|
||||
}
|
||||
.msg {
|
||||
padding: 8px 12px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 8px;
|
||||
max-width: 85%;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.msg.u { margin-left: auto; background: var(--cyan); color: var(--ink); }
|
||||
.msg.b { background: var(--gray-100); color: var(--ink); }
|
||||
.msg .nm { font-size: 10px; color: var(--gray-500); margin-bottom: 2px; }
|
||||
|
||||
.chat-inp { display: flex; gap: 8px; }
|
||||
.chat-inp input {
|
||||
flex: 1;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--gray-100);
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.chat-q { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
|
||||
.chat-q button {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--gray-100);
|
||||
border-radius: 100px;
|
||||
font-size: 11px;
|
||||
background: var(--white);
|
||||
cursor: pointer;
|
||||
color: var(--ink);
|
||||
}
|
||||
.chat-q button:hover {
|
||||
background: var(--cyan);
|
||||
color: var(--ink);
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
|
||||
.rank-bar {
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--gray-100);
|
||||
overflow: hidden;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.rank-bar > div { height: 100%; border-radius: 4px; transition: width .3s; }
|
||||
|
||||
.sub-row { font-size: 12px; color: var(--gray-500); cursor: pointer; user-select: none; }
|
||||
.sub-row:hover { color: var(--cyan-dark); }
|
||||
.sub-row .arr { display: inline-block; width: 16px; text-align: center; }
|
||||
.sub-items { padding-left: 24px; }
|
||||
.sub-item { font-size: 12px; padding: 4px 0; border-bottom: 1px solid var(--gray-100); }
|
||||
.sub-item:last-child { border: none; }
|
||||
|
||||
.mod-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0, 0, 0, .5);
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.mod-box {
|
||||
background: var(--white);
|
||||
border-radius: 16px;
|
||||
padding: 28px;
|
||||
max-width: 750px;
|
||||
width: 90vw;
|
||||
max-height: 85vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
background: #F8FAFC;
|
||||
border-radius: 6px;
|
||||
margin: 4px 0;
|
||||
border: 1px solid var(--gray-100);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.file-item .fn { font-size: 12px; flex: 1; min-width: 80px; }
|
||||
.file-item .fs { font-size: 10px; color: var(--gray-500); }
|
||||
.file-item a { font-size: 11px; color: var(--cyan-dark); cursor: pointer; text-decoration: underline; }
|
||||
|
||||
.stor-bar { font-size: 11px; color: var(--gray-500); padding: 4px 0; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#sidebar { width: 56px; }
|
||||
#sidebar .logo span, #sidebar a span, #sidebar .user { display: none; }
|
||||
#main { margin-left: 56px; }
|
||||
.stat-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user