למה אין נתונים לקווים? 🤔
-
@אA סכום הדקות תואם לך את מה שמוצג באתר הרשמי?
לי הפעם זה דווקא כתב פחות מהמוצג באתר -
@בוס יכול להיות שבאתר הוא לא מחשבן את של היום או את של השעות האחרונות
-
@אa נראה לי שהוא פשוט לא מבין מה הכוונה דקה,
והוא סופר אותם שונה ממה שאנחנו מכירים ולכן הוא מביא דברים כאלה -
@אa אני מפעיל ניתוח כל פעם לטווח תאריכים שונה, ורק הקבצים שהושמעו במלואם והגרף משתנים/מתעדכנים (מי יודע אם לנתונים נכונים)
-
@יוסף-חיים-5 אז היה אמור להיות הפוך שבאתר יש פחות ובניתוח נתונים יותר.
מה שאין כן במה שכתבתי בלמה אין נתונים לקווים?
:הפעם זה דווקא כתב פחות מהמוצג באתר
-
@בוס צודק. טעות שלי.
אצלי כל מה שבדקתי זה יותר,
אולי גם מההפרשים שימות כנראה מחשבנים שניות ואולי הוא רק דקות. -
טוב חברה.
נראה לי אני עושה פירוק לקובץ ועובר לבנות אותו מחדש ברעיון הזה רק ששליפת הנתונים והניתוח יהיה שונה.
אני מעלה מה עד עכשיו מוכנס בקובץ ואשמח שתעלו דברים נוספים.
זה מה שהקובץ מכיל כעת.- מסך מרכזי ובו מספר המאזינים שנכנסו למערכת, מספר הדקות הכולל, וכמות הקבצים שלהם האזינו בקו. - הכל כמובן בטווח הזמן המוגדר.
בנוסף גרף שמראה את נתוני השיחות שנכנסו למערכת לפי הכמות ובהמשך בעזה לפי השעות. - לשונית שלוחות- בו הקובץ מראה את כמות הנכנסים לשלוחות השונות ואת זמן השהייה בהן.
- השמעות - שם הקובץ מראה לכל השמעה את כמות המאזינים שהאזינו לה, האחוז הממוצע שנשמע מתוך הקובץ, אחוזי הטנטישה.
- מאזינים - מראה את המאזיני לפי כמות הדקות שלהם במערכת, כשבלחיצה עליהם מראה את פירוט השיחו בשלוחות ובהשמעות לכל מאזין. - כולל שמות המאזינים
- לוג הפעולות.
- מסך מרכזי ובו מספר המאזינים שנכנסו למערכת, מספר הדקות הכולל, וכמות הקבצים שלהם האזינו בקו. - הכל כמובן בטווח הזמן המוגדר.
-
@אA כתב בלמה אין נתונים לקווים?
:השמעות - שם הקובץ מראה לכל השמעה את כמות המאזינים שהאזינו לה
כמובן שיראה באיזה שלוחה הקובץ
-
@בוס כמובן
-
@בוס
ברור.
כבר בזה הוא מראה אבל אני רוצה משהו יותר מסודר.
או שיראה את השלוחה ורק בלחיצה עליה יפתח הפירוט לפי הקבצים שבה, או רק עמודה שבה יהיה כתוב מאיזו השלוחה הקוץ -
@אA כתב בלמה אין נתונים לקווים?
:או שיראה את השלוחה ורק בלחיצה עליה יפתח הפירוט לפי הקבצים שבה
ואז לא תוכל לעשות סינון לפי כמות השמעות/דקות
@אA כתב בלמה אין נתונים לקווים?
:רק עמודה שבה יהיה כתוב מאיזו השלוחה הקוץ
לא הצלחתי להבין כוונתך.
לדעתי, מה שהיה בעדכונים האחרונים היה טוב.
אגב עכשיו שמתי לב שהוא נותן כל קובץ רק פעם אחת, כלומר אם יש יותר משלוחה אחת שבה יש קובץ 000, הוא מציג רק את אחד מהם -
חברה, אני מקווה שחל שינוי.
אשמח מאוד מי שיכול לבדוק את הקוד החדש.
שימו לב! בדאשבורד המרכזי יש, כמות המאזינים, דקות השיחה, וכמות ההשמעות (שימו לב שאם שמעו קובץ אחד כמה פעמים כל פעם נספרת מחדש).
בנוסף, בלשונית שלוחות בלחיצה על השלוחה תראו את המאזינים שנכנסו ואת נתוני השהייה וכדו'.
בעז"ה זה יהיה גם בהשמעות.הקוד
<!DOCTYPE html> <html lang="he" dir="rtl"> <head> <meta charset="UTF-8"> <title>ניתוח נתוני מערכות | גרסה יציבה ומדויקת</title> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <style> :root { --bg: #0f172a; --card: #1e293b; --primary: #38bdf8; --accent: #22c55e; --text: #f1f5f9; --border: #334155; --danger: #ef4444; --warning: #f59e0b; } body { font-family: 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); margin: 0; display: flex; flex-direction: column; height: 100vh; } header { background: #1e293b; padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); } .logo-box { display: flex; align-items: center; gap: 12px; } .logo-icon { background: linear-gradient(135deg, var(--primary), #0ea5e9); color: #0f172a; width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; } .logo-text { font-size: 1.4rem; font-weight: 800; color: var(--text); } .config-box { display: flex; gap: 12px; align-items: center; } input, select { background: #0f172a; border: 1px solid var(--border); color: white; padding: 10px; border-radius: 8px; outline: none; } input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1); cursor: pointer; } .btn-run { background: var(--primary); color: #0f172a; border: none; padding: 10px 25px; border-radius: 8px; font-weight: bold; cursor: pointer; transition: 0.3s; } .btn-run:hover { opacity: 0.8; } .nav-tabs { background: #1e293b; display: flex; padding: 0 20px; border-bottom: 1px solid var(--border); } .tab { padding: 15px 25px; cursor: pointer; color: #94a3b8; border-bottom: 3px solid transparent; transition: 0.3s; } .tab.active { color: var(--primary); border-bottom-color: var(--primary); background: rgba(56, 189, 248, 0.07); } .main-content { flex: 1; padding: 25px; overflow-y: auto; background: #0f172a; } .card { background: var(--card); padding: 25px; border-radius: 16px; border: 1px solid var(--border); margin-bottom: 25px; } .stat-num { font-size: 2.5rem; font-weight: 800; color: var(--primary); } .control-bar { display: flex; gap: 20px; align-items: center; background: #334155; padding: 15px 25px; border-radius: 12px 12px 0 0; } .search-input { background: #0f172a; border: 1px solid var(--border); color: white; padding: 8px 15px; border-radius: 8px; width: 250px; } table { width: 100%; border-collapse: collapse; background: var(--card); border-radius: 0 0 12px 12px; overflow: hidden; } th { text-align: right; padding: 15px; background: #475569; color: white; font-size: 0.85rem; } td { padding: 15px; border-bottom: 1px solid var(--border); font-size: 0.95rem; } tr.clickable { cursor: pointer; } tr.clickable:hover { background: rgba(56, 189, 248, 0.1); } .bar-container { width: 100px; background: #0f172a; height: 10px; border-radius: 10px; display: inline-block; overflow: hidden; vertical-align: middle; margin-left: 8px; } .bar-fill { height: 100%; background: var(--accent); } .modal-overlay { position: fixed; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.85); display: none; justify-content: center; align-items: center; z-index: 1000; } .modal-content { background: var(--card); width: 85%; max-height: 85%; border-radius: 20px; border: 1px solid var(--primary); overflow-y: auto; padding: 30px; position: relative; } .close-modal { position: absolute; top: 20px; left: 20px; color: white; font-size: 2rem; cursor: pointer; } .progress-container { width: 100%; background: #1e293b; height: 20px; display: none; position: relative; text-align: center; } #progress-fill { height: 100%; background: var(--primary); width: 0%; transition: 0.4s; position: absolute; top:0; right:0; } #progress-text { position: relative; z-index: 2; font-size: 0.8rem; font-weight: bold; color: white; line-height: 20px; } .hidden { display: none; } .live-status { display: flex; align-items: center; gap: 6px; font-size: 0.75rem; padding: 4px 10px; border-radius: 20px; background: #334155; } .dot { width: 8px; height: 8px; border-radius: 50%; background: #666; } .dot.active { background: var(--accent); animation: pulse 2s infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } } </style> </head> <body> <header> <div class="logo-box"> <div class="logo-icon"><i class="fas fa-chart-pie"></i></div> <div class="logo-text">ניתוח נתוני מערכות</div> <div class="live-status"><div id="live-dot" class="dot"></div> <span id="live-text">OFFLINE</span></div> </div> <div class="config-box"> <input type="password" id="apiToken" placeholder="טוקן API"> <input type="date" id="startDate"> <input type="date" id="endDate"> <button class="btn-run" onclick="manualStart()">הפעל ניתוח</button> </div> </header> <div class="progress-container" id="progBar"> <div id="progress-fill"></div> <span id="progress-text">0%</span> </div> <div class="nav-tabs"> <div class="tab active" onclick="switchTab(event, 'dash-tab')">דאשבורד</div> <div class="tab" onclick="switchTab(event, 'ext-tab')">שלוחות</div> <div class="tab" onclick="switchTab(event, 'play-tab')">השמעות</div> <div class="tab" onclick="switchTab(event, 'users-tab')">מאזינים</div> <div class="tab" style="color: var(--warning)" onclick="switchTab(event, 'debug-tab')">לוג המערכת</div> </div> <div class="main-content"> <div id="dash-tab" class="tab-content"> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 25px; margin-bottom: 25px;"> <div class="card"><span style="color: #94a3b8">מאזינים ייחודיים בטבלה</span><br><span id="stat-users" class="stat-num">0</span></div> <div class="card"><span style="color: #94a3b8">סה"כ דקות שיחה (מערכת)</span><br><span id="stat-min" class="stat-num">0</span></div> <div class="card"><span style="color: #94a3b8">סה"כ השמעות שהושלמו עד הסוף</span><br><span id="stat-comp" class="stat-num">0</span></div> </div> <div class="card"><h3>גרף פעילות יומי</h3><canvas id="dailyChart" height="100"></canvas></div> </div> <div id="ext-tab" class="tab-content hidden"> <div class="control-bar"> <select id="extSort" onchange="renderExts()"><option value="count">כמות כניסות</option><option value="sec">זמן שהייה</option></select> <input type="text" id="extSearch" class="search-input" placeholder="חפש שלוחה..." onkeyup="renderExts()"> </div> <table id="extTable"><thead><tr><th>שם השלוחה</th><th>תיאור שלוחה</th><th>כניסות</th><th>דקות שהייה</th><th>ממוצע</th></tr></thead><tbody></tbody></table> </div> <div id="play-tab" class="tab-content hidden"> <div class="control-bar"> <select id="playSort" onchange="renderPlays()"><option value="count">פופולריות</option><option value="avg">אחוז השלמה</option></select> <input type="text" id="playSearch" class="search-input" placeholder="חפש קובץ..." onkeyup="renderPlays()"> </div> <table id="playTable"><thead><tr><th>שם קובץ</th><th>שלוחה</th><th>מספר השמעות</th><th>סה"כ דקות</th><th>אחוז השלמה</th><th>נטישה</th></tr></thead><tbody></tbody></table> </div> <div id="users-tab" class="tab-content hidden"> <div class="control-bar"> <select id="userSort" onchange="renderUsers()"><option value="sec">זמן האזנה כולל</option><option value="calls">מספר שיחות</option></select> <input type="text" id="userSearch" class="search-input" placeholder="חפש טלפון או שם..." onkeyup="renderUsers()"> </div> <table id="usersTable"><thead><tr><th>מספר טלפון</th><th>שם מאזין</th><th>דקות האזנה</th><th>מספר שיחות</th><th>סטטוס</th></tr></thead><tbody></tbody></table> </div> <div id="debug-tab" class="tab-content hidden"> <div class="card"><div id="debug-console" style="background:#000; color:#22c55e; padding:20px; height:400px; overflow-y:auto; font-family:monospace; border-radius: 8px;"></div></div> </div> </div> <!-- מודאל פירוט מאזין --> <div id="userModal" class="modal-overlay" onclick="closeModal()"> <div class="modal-content" onclick="event.stopPropagation()"> <span class="close-modal" onclick="closeModal()">×</span> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;"> <h2 id="modalTitle" style="color: var(--primary); margin:0;"></h2> <select id="modalFilter" onchange="filterModalContent()" style="background: var(--bg); color:white; border: 1px solid var(--primary); padding: 5px 15px; border-radius: 5px;"> <option value="all">הכל</option> <option value="שלוחה">שלוחות בלבד</option> <option value="השמעה">השמעות בלבד</option> </select> </div> <table id="modalTable"> <thead> <tr><th>סוג פעילות</th><th>שלוחה/קובץ</th><th>זמן שהייה</th><th>תאריך (עברי ולועזי)</th></tr> </thead> <tbody></tbody> </table> </div> </div> <!-- מודאל פירוט שלוחה --> <div id="extModal" class="modal-overlay" onclick="closeExtModal()"> <div class="modal-content" onclick="event.stopPropagation()"> <span class="close-modal" onclick="closeExtModal()">×</span> <h2 id="extModalTitle" style="color: var(--primary); margin-bottom: 20px;"></h2> <table id="extModalTable"> <thead> <tr> <th>שם מאזין</th> <th>מספר טלפון</th> <th>כמות כניסות (לשלוחה זו)</th> <th>זמן שהייה (בשלוחה זו)</th> </tr> </thead> <tbody></tbody> </table> </div> </div> <script> const API = "https://www.call2all.co.il/ym/api/"; let dataStore = { exts: {}, plays: {}, users: {}, daily: {}, rawActivity: [], totalSec: 0, completed: 0, names: {} }; let currentModalPhone = ""; let dailyChart = null; let autoRefreshTimer = null; function addLog(msg, color = '#22c55e') { const console = document.getElementById('debug-console'); if(!console) return; const div = document.createElement('div'); div.style.color = color; div.innerText = `[${new Date().toLocaleTimeString()}] ${msg}`; console.appendChild(div); console.scrollTop = console.scrollHeight; } function normalizePhone(p) { if (!p) return "חסוי"; let s = p.toString().replace(/\D/g, ''); if (s.length > 0 && !s.startsWith('0') && s.length >= 8) s = '0' + s; return s; } function formatExtName(ext) { if (!ext) return "ראשית"; let str = ext.toString().trim(); if (str === "" || str === "/" || str.toLowerCase() === "main" || str.toLowerCase() === "root") { return "ראשית"; } return str; } function parseYemotDateToIso(dStr) { if (!dStr || !dStr.includes('/')) return null; const parts = dStr.split('/'); if (parts.length === 3) { const d = parts[0].padStart(2, '0'); const m = parts[1].padStart(2, '0'); const y = parts[2]; return `${y}-${m}-${d}`; } return null; } function timeStrToSeconds(tStr) { if (!tStr) return 0; if (!tStr.includes(':')) return parseInt(tStr) || 0; const p = tStr.split(':').map(Number); if (p.length === 3) return (p[0] * 3600) + (p[1] * 60) + p[2]; if (p.length === 2) return (p[0] * 60) + p[1]; return parseInt(tStr) || 0; } function ensureUser(phone) { if (!dataStore.users[phone]) { dataStore.users[phone] = { sec: 0, calls: 0 }; } } async function fetchNames(token) { try { const res = await fetch(`${API}DownloadFile?token=${token}&path=ivr2:/EnterIDValName.ini`); const text = await res.text(); if(text && !text.includes("NOT_FOUND")) { text.split(/\r?\n/).forEach(line => { if (line.includes('=')) { const [phone, name] = line.split('='); const cleanPhone = normalizePhone(phone); if (cleanPhone && name) dataStore.names[cleanPhone] = name.trim(); } }); addLog(`נטענו שמות מקובץ ההגדרות הראשי`); } } catch(e) { } } async function manualStart() { if(autoRefreshTimer) clearInterval(autoRefreshTimer); await startFullAnalysis(); document.getElementById('live-dot').classList.add('active'); document.getElementById('live-text').innerText = "LIVE (פעיל)"; } async function startFullAnalysis() { const token = document.getElementById('apiToken').value; const startStr = document.getElementById('startDate').value; const endStr = document.getElementById('endDate').value; if (!token) return alert("נא להזין טוקן API"); addLog(`מתחיל עיבוד נתונים מתאריך ${startStr} עד ${endStr}...`); document.getElementById('progBar').style.display = 'block'; updateProgress(5, 100); dataStore = { exts: {}, plays: {}, users: {}, daily: {}, rawActivity: [], totalSec: 0, completed: 0, names: {} }; await fetchNames(token); updateProgress(10, 100); // חישוב חודשים בטוח (מניעת תקלות של אזור זמן) let currM = new Date(startStr); currM.setDate(1); // עקיפת בעיית 31 לחודש let endM = new Date(endStr); let monthsToFetch = []; while (currM <= endM || (currM.getFullYear() === endM.getFullYear() && currM.getMonth() === endM.getMonth())) { let y = currM.getFullYear(); let m = String(currM.getMonth() + 1).padStart(2, '0'); monthsToFetch.push(`${y}-${m}`); currM.setMonth(currM.getMonth() + 1); } // אובייקט לאיתור שיחות כפולות (למניעת קפיצת דקות) let callMap = {}; for (let month of monthsToFetch) { try { addLog(`מייבא לוג כניסות עבור חודש ${month}...`); const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogFolderEnterExit-${month}.ymgr&convertType=json`); const json = await res.json(); if (json.data && Array.isArray(json.data)) { json.data.forEach(row => { const gregDate = row["תאריך"]; const hebDate = row["תאריך עברי"] || row["עברי"] || ""; const isoDate = parseYemotDateToIso(gregDate); if (!isoDate) return; // סינון מוחלט של ימים לפי טקסט (למשל "2026-03-31") - מונע כפילויות! if (isoDate >= startStr && isoDate <= endStr) { const extName = formatExtName(row["שלוחה"]); const phone = normalizePhone(row["טלפון"]); const sec = parseInt(row["סה\"כ שניות"]) || 0; const callId = row["מזהה שיחה"]; const nameFromLog = row["שם מזהה"]; if (nameFromLog && !dataStore.names[phone]) dataStore.names[phone] = nameFromLog; // -- חישוב נתונים לשלוחות -- if (!dataStore.exts[extName]) { dataStore.exts[extName] = { count: 0, sec: 0, title: row["כותרת שלוחה"] || "", extUsers: {} }; } dataStore.exts[extName].count++; dataStore.exts[extName].sec += sec; if (!dataStore.exts[extName].extUsers[phone]) { dataStore.exts[extName].extUsers[phone] = { count: 0, sec: 0 }; } dataStore.exts[extName].extUsers[phone].count++; dataStore.exts[extName].extUsers[phone].sec += sec; // -- איסוף זמן למאזין וכללי במערכת (עם מניעת כפילויות של מזהה שיחה) -- if (callId) { if (!callMap[callId]) callMap[callId] = { phone: phone, maxSec: 0 }; if (sec > callMap[callId].maxSec) callMap[callId].maxSec = sec; } else { // במקרה נדיר שאין מזהה שיחה ensureUser(phone); dataStore.users[phone].sec += sec; dataStore.users[phone].calls++; dataStore.totalSec += sec; } if (!dataStore.daily[isoDate]) dataStore.daily[isoDate] = 0; dataStore.daily[isoDate]++; const displayDate = hebDate ? `${hebDate} (${gregDate})` : gregDate; dataStore.rawActivity.push({ phone, type: 'שלוחה', name: extName, sec: sec + " ש'", date: `${displayDate} ${row["התחלה שעה"]||''}` }); } }); } } catch(e) { addLog(`לא נמצא לוג כניסות בחודש ${month}`, "orange"); } } // עדכון סך הדקות והשיחות במערכת (מסונן ללא כפילויות!) Object.values(callMap).forEach(call => { dataStore.totalSec += call.maxSec; ensureUser(call.phone); dataStore.users[call.phone].sec += call.maxSec; dataStore.users[call.phone].calls += 1; }); updateProgress(50, 100); // חישוב ימים להשמעות בטוח let daysToFetch = []; let currDay = new Date(startStr); let endDay = new Date(endStr); while(currDay <= endDay) { let y = currDay.getFullYear(); let m = String(currDay.getMonth() + 1).padStart(2, '0'); let d = String(currDay.getDate()).padStart(2, '0'); daysToFetch.push(`${y}-${m}-${d}`); currDay.setDate(currDay.getDate() + 1); } for (let i=0; i<daysToFetch.length; i++) { const day = daysToFetch[i]; try { const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogPlaybackPlayStop/LogPlaybackPlayStop.${day}.ymgr&convertType=json`); const json = await res.json(); if (json.data && Array.isArray(json.data)) { json.data.forEach(row => { const gregDate = row["תאריך"] || day; const isoDate = parseYemotDateToIso(gregDate); // וידוא שההשמעה בטווח התאריכים המדויק if (isoDate && (isoDate < startStr || isoDate > endStr)) return; const file = row["השמעה"]; if (!file) return; const folder = formatExtName(row["שלוחה"]); const phone = normalizePhone(row["טלפון"]); const sec = parseInt(row["סה\"כ שניות"]) || 0; const fileLenSec = timeStrToSeconds(row["אורך הקובץ"]); const exitPoint = row["נקודת יציאה"]; const nameFromLog = row["שם"]; const hebDate = row["עברי"] || row["תאריך עברי"] || ""; const displayDate = hebDate ? `${hebDate} (${gregDate})` : gregDate; if (nameFromLog && !dataStore.names[phone]) dataStore.names[phone] = nameFromLog; ensureUser(phone); // מוודא שמי שהאזין לקובץ יופיע בטבלת מאזינים גם אם לא נכנס לשלוחה רשמית const isEnd = (exitPoint === "סוף") || (fileLenSec > 0 && sec >= fileLenSec - 2); if (isEnd) dataStore.completed++; if (!dataStore.plays[file]) dataStore.plays[file] = { count: 0, sec: 0, drops: 0, pcts: [], folder: folder }; dataStore.plays[file].count++; dataStore.plays[file].sec += sec; const pct = fileLenSec > 0 ? Math.min(100, Math.round((sec / fileLenSec) * 100)) : (isEnd ? 100 : 0); dataStore.plays[file].pcts.push(pct); if (!isEnd) dataStore.plays[file].drops++; dataStore.rawActivity.push({ phone, type: 'השמעה', name: file, sec: sec + " ש'", date: `${displayDate} ${row["התחלה שעה"]||''}` }); }); } } catch(e) {} updateProgress(50 + Math.round(((i + 1) / daysToFetch.length) * 50), 100); } addLog("עיבוד הנתונים הסתיים בהצלחה."); renderUI(); } function updateProgress(c, t) { const pct = Math.round((c/t)*100); document.getElementById('progress-fill').style.width = pct + '%'; document.getElementById('progress-text').innerText = pct + '%'; } function renderUI() { document.getElementById('progBar').style.display = 'none'; // כאן הסנכרון המושלם - הדאשבורד מציג בדיוק את מספר המאזינים שיש בטבלה document.getElementById('stat-users').innerText = Object.keys(dataStore.users).length.toLocaleString(); document.getElementById('stat-min').innerText = Math.floor(dataStore.totalSec / 60).toLocaleString(); document.getElementById('stat-comp').innerText = dataStore.completed.toLocaleString(); renderExts(); renderPlays(); renderUsers(); updateChart(); } function renderExts() { const sort = document.getElementById('extSort').value; const q = document.getElementById('extSearch').value.toLowerCase(); let items = Object.entries(dataStore.exts).map(([name, d]) => ({name, ...d})).filter(i => i.name.toLowerCase().includes(q)); items.sort((a,b) => b[sort] - a[sort]); document.querySelector('#extTable tbody').innerHTML = items.map(i => ` <tr class="clickable" onclick="openExtDetail('${i.name}')"> <td style="color:var(--primary); font-weight:600">${i.name}</td> <td>${i.title || '-'}</td> <td>${i.count}</td> <td>${(i.sec/60).toFixed(1)}</td> <td>${i.count ? Math.round(i.sec/i.count) : 0} ש'</td> </tr> `).join(''); } function renderPlays() { const sort = document.getElementById('playSort').value; const q = document.getElementById('playSearch').value.toLowerCase(); let items = Object.entries(dataStore.plays).map(([name, d]) => { const avg = d.pcts && d.pcts.length ? Math.round(d.pcts.reduce((a,b)=>a+b,0)/d.pcts.length) : 0; const drop = d.count ? Math.round((d.drops/d.count)*100) : 0; return {name, avg, drop, ...d}; }).filter(i => i.name.toLowerCase().includes(q)); items.sort((a,b) => sort === 'count' ? b.count - a.count : b.avg - a.avg); document.querySelector('#playTable tbody').innerHTML = items.map(i => ` <tr><td>${i.name}</td><td>${i.folder || '-'}</td><td>${i.count}</td><td>${(i.sec/60).toFixed(1)}</td> <td><div class="bar-container"><div class="bar-fill" style="width:${i.avg}%"></div></div> ${i.avg}%</td> <td style="color:${i.drop > 50 ? 'var(--danger)' : 'var(--accent)'}">${i.drop}%</td></tr> `).join(''); } function renderUsers() { const sortKey = document.getElementById('userSort').value; const q = document.getElementById('userSearch').value; let items = Object.entries(dataStore.users).map(([phone, d]) => ({ phone, name: dataStore.names[phone] || "לא ידוע", sec: d.sec || 0, calls: d.calls || 0 })).filter(i => i.phone.includes(q) || i.name.includes(q)); items.sort((a,b) => b[sortKey] - a[sortKey]); document.querySelector('#usersTable tbody').innerHTML = items.map((i, idx) => ` <tr class="clickable" onclick="openUserDetail('${i.phone}')"> <td style="font-weight:bold">${i.phone}</td><td style="color:var(--warning)">${i.name}</td> <td style="color:var(--primary); font-weight:bold">${(i.sec/60).toFixed(1)}</td><td>${i.calls}</td> <td>${idx < 3 ? '🏆 מוביל' : 'מאזין'}</td> </tr> `).join(''); } function openUserDetail(phone) { currentModalPhone = phone; document.getElementById('modalTitle').innerText = `פירוט מאזין: ${phone} (${dataStore.names[phone] || "לא ידוע"})`; filterModalContent(); document.getElementById('userModal').style.display = 'flex'; } function filterModalContent() { const filter = document.getElementById('modalFilter').value; const activity = dataStore.rawActivity.filter(a => a.phone === currentModalPhone); const filtered = filter === 'all' ? activity : activity.filter(a => a.type === filter); document.querySelector('#modalTable tbody').innerHTML = filtered.map(a => ` <tr><td style="color:${a.type==='השמעה'?'var(--primary)':'var(--accent)'}; font-weight:bold">${a.type}</td> <td>${a.name}</td><td>${a.sec}</td><td>${a.date}</td></tr> `).join(''); } function closeModal() { document.getElementById('userModal').style.display = 'none'; } function openExtDetail(extName) { document.getElementById('extModalTitle').innerText = `פירוט מאזינים בשלוחה: ${extName}`; const extData = dataStore.exts[extName]; if(!extData) return; let usersArr = Object.entries(extData.extUsers).map(([phone, d]) => { return { phone, name: dataStore.names[phone] || "לא ידוע", count: d.count, sec: d.sec }; }); usersArr.sort((a,b) => b.sec - a.sec); document.querySelector('#extModalTable tbody').innerHTML = usersArr.map(u => ` <tr> <td style="color:var(--warning)">${u.name}</td> <td style="font-weight:bold">${u.phone}</td> <td>${u.count}</td> <td style="color:var(--primary); font-weight:bold">${(u.sec/60).toFixed(1)} דק'</td> </tr> `).join(''); document.getElementById('extModal').style.display = 'flex'; } function closeExtModal() { document.getElementById('extModal').style.display = 'none'; } function switchTab(e, id) { document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden')); document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.getElementById(id).classList.remove('hidden'); e.currentTarget.classList.add('active'); } function updateChart() { if (dailyChart) dailyChart.destroy(); const ctx = document.getElementById('dailyChart').getContext('2d'); const sortedDates = Object.keys(dataStore.daily).sort(); const sortedData = sortedDates.map(d => dataStore.daily[d]); dailyChart = new Chart(ctx, { type: 'line', data: { labels: sortedDates, datasets: [{ label: 'פעולות במערכת', data: sortedData, borderColor: '#38bdf8', tension: 0.4, fill: true, backgroundColor: 'rgba(56, 189, 248, 0.1)' }] }, options: { plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } } }); } let dEnd = new Date(); let dStart = new Date(); dStart.setDate(dEnd.getDate() - 7); document.getElementById('endDate').value = dEnd.toISOString().split('T')[0]; document.getElementById('startDate').value = dStart.toISOString().split('T')[0]; </script> </body> </html> -
@אA איך זה יכול להיות? שניהם מתחילת החודש?


-
@קו-המוסיקה
יש לך מושג לפי מה נקרא אצלם שיחות נכנסות?
יש כאן תמיד איזה שהוא פער ואני מנסה למצוא אותו? -
@אA התכתבתי עם gemini על זה מקודם,
אני לא יודע אם זה נכון מה שהוא אומר,
אבל הוא טען לי את זההבעיה המרכזית בלוגים של "ימות המשיח" היא שלוג הכניסות (LogFolderEnterExit) נשמר בקובץ חודשי, בעוד שלוג ההשמעות נשמר בקבצים יומיים.
-
@קו-המוסיקה
יפה מאוד!!!
חבל שרק אצלך הוא קלט, כי אצלי אני זה שהבהרתי לו את העניין.
עכשיו זה כבר לא כך, שא"כ הנתונים היו אמורים להיות דרסטיים. -
@אA נכון אז אמרתי לו לתקן את זה,
ואז הוא הראה לי נתונים של משהו כמו 3,000 דקות היום,
ובדקתי והיו לי 15,000 דקות -
@קו-המוסיקה
עזוב את גימיני, אני על סטודיו. -
@אA אהה..
-
חברה, אני צריך עיצה.
איך אני יכול לבדוק האם הנתונים שאני מקבל בקובץ הם נכונים או לא.
אני שואל מכיון שאני לא בטוח שכל החריגות אינן נכונות, אבל אני לא בטוח בצורת הבדיקה.