{"id":144,"date":"2025-08-24T13:53:19","date_gmt":"2025-08-24T13:53:19","guid":{"rendered":"https:\/\/rehmahprojects.com\/attendance\/?page_id=144"},"modified":"2026-06-26T06:08:11","modified_gmt":"2026-06-26T06:08:11","slug":"actual-home","status":"publish","type":"page","link":"https:\/\/rehmahprojects.com\/attendance\/","title":{"rendered":"Attendance"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"144\" class=\"elementor elementor-144\">\n\t\t\t\t<div class=\"elementor-element elementor-element-063d15a e-flex e-con-boxed e-con e-parent\" data-id=\"063d15a\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a28ca96 elementor-widget elementor-widget-html\" data-id=\"a28ca96\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n  <meta charset=\"UTF-8\" \/>\r\n  <title>Attendance Analyzer<\/title>\r\n\r\n  <!-- PDF.js -->\r\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/2.14.305\/pdf.min.js\"><\/script>\r\n  <script>\r\n    pdfjsLib.GlobalWorkerOptions.workerSrc =\r\n      \"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/2.14.305\/pdf.worker.min.js\";\r\n  <\/script>\r\n\r\n  <!-- jsPDF + autotable -->\r\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\r\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf-autotable\/3.5.25\/jspdf.plugin.autotable.min.js\"><\/script>\r\n\r\n  <style>\r\n    body { font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 20px; background:white; }\r\n    h2 { text-align: center; margin-bottom: 20px; color:#white; }\r\n\r\n    .uploader {\r\n      max-width: 400px;\r\n      margin: 0 auto 0px;\r\n      padding: 20px;\r\n      background: white;\r\n      text-align: center;\r\n    }\r\n    .file-label {\r\n      display: block;\r\n      padding: 12px;\r\n      border: 2px dashed #007bff;\r\n      border-radius: 8px;\r\n      background: #f9fbff;\r\n      cursor: pointer;\r\n      color: #007bff;\r\n      font-weight: bold;\r\n      margin-bottom: 10px;\r\n      transition: all 0.3s ease;\r\n    }\r\n    .file-label:hover { background:#e8f0ff; }\r\n    input[type=\"file\"] { display: none; }\r\n    .progress-bar {\r\n      width: 100%;\r\n      height: 6px;\r\n      background: #e0e0e0;\r\n      border-radius: 3px;\r\n      overflow: hidden;\r\n      margin-top: 10px;\r\n      display: none;\r\n    }\r\n    .progress { height: 100%; width: 0%; background: #007bff; transition: width 0.3s ease; }\r\n    #summary { margin-top: 12px; font-size: 12px; color:#444; }\r\n\r\n    \/* Desktop table *\/\r\n    table { border-collapse: collapse; margin-top: 20px; width: 100%; background:#fff; border-radius: 8px; overflow:hidden; }\r\n    th, td { border: 1px solid #ddd; padding: 8px; text-align: center; font-size: 14px; }\r\n    th { background: #f1f5fb; font-weight: 600; }\r\n    td:first-child { text-align: left; }\r\n    .ok { background-color: #d4edda; color: #155724; font-weight: bold; }\r\n    .bad { background-color: #f8d7da; color: #721c24; font-weight: bold; }\r\n    tr.overall-row td { background: #eef2ff; font-weight: bold; border-top: 2px solid #007bff; }\r\n\r\n    \/* Mobile stacked cards *\/\r\n    .mobile-card {\r\n      background: white;\r\n      border-radius: 10px;\r\n      padding: 15px;\r\n      margin-bottom: 15px;\r\n      box-shadow: 0 2px 6px rgba(0,0,0,0.08);\r\n    }\r\n    .mobile-card h3 { margin: 0 0 10px; font-size: 16px; color:#007bff; }\r\n    .mobile-card p { margin: 4px 0; font-size: 14px; }\r\n    .mobile-card .ok { background:#d4edda; padding:2px 4px; border-radius:3px; }\r\n    .mobile-card .bad { background:#f8d7da; padding:2px 4px; border-radius:3px; }\r\n    .mobile-card.overall-card { background: #eef2ff; border: 2px solid #007bff; }\r\n    .mobile-card.overall-card h3 { color: #0044cc; }\r\n\r\n    #downloadBtn {\r\n      margin: 20px auto;\r\n      display: none;\r\n      padding: 10px 18px;\r\n      background:#007bff;\r\n      color:#fff;\r\n      border:none;\r\n      border-radius: 6px;\r\n      cursor:pointer;\r\n      font-size: 15px;\r\n      transition: background 0.3s ease;\r\n    }\r\n    #downloadBtn:hover { background:#0056b3; }\r\n\r\n    \/* Responsive switch *\/\r\n    @media(max-width: 768px) {\r\n      table { display: none; }\r\n      .mobile-card { display: block; }\r\n    }\r\n    @media(min-width: 769px) {\r\n      .mobile-card { display: none; }\r\n    }\r\n  <\/style>\r\n<\/head>\r\n<div style=\"background-color:white; padding-top:20px; padding-right:20px; padding-left:20px; padding-bottom:20px; border-radius:40px\">\r\n  <h2>Upload Your SVKM Attendance PDF<\/h2>\r\n\r\n  <div class=\"uploader\">\r\n    <label for=\"pdfUpload\" class=\"file-label\">\ud83d\udcc2 Choose Attendance PDF<\/label>\r\n    <input type=\"file\" id=\"pdfUpload\" accept=\"application\/pdf\" \/>\r\n    <div class=\"progress-bar\"><div class=\"progress\"><\/div><\/div>\r\n    <div id=\"summary\">Select \"Detailed Report\" PDF from <a href=\"https:\/\/sdc-sppap1.svkm.ac.in:50001\/irj\/portal\" target=\"_blank\">SVKM portal<\/a><\/div>\r\n  <\/div>\r\n\r\n  <div id=\"output\"><\/div>\r\n  <button id=\"downloadBtn\">\u2b07\ufe0f Download PDF<\/button>\r\n\r\n<script>\r\nlet latestTableData = [];\r\n\r\ndocument.getElementById(\"pdfUpload\").addEventListener(\"change\", async (e) => {\r\n  const file = e.target.files?.[0];\r\n  if (!file) return;\r\n\r\n  const bar = document.querySelector(\".progress-bar\");\r\n  const progress = document.querySelector(\".progress\");\r\n  bar.style.display = \"block\";\r\n  progress.style.width = \"20%\";\r\n\r\n  document.getElementById(\"summary\").textContent = \"Reading PDF\u2026\";\r\n\r\n  const text = await readPdfText(file, progress);\r\n  progress.style.width = \"80%\";\r\n\r\n  \/\/ Ignore details before \"Sr No\"\r\n  const idx = text.indexOf(\"Sr No\");\r\n  const cleanText = idx > -1 ? text.slice(idx) : text;\r\n\r\n  const { counts, matched } = parseAttendance(cleanText);\r\n  latestTableData = buildTableData(counts);\r\n  renderData(latestTableData);\r\n\r\n  progress.style.width = \"100%\";\r\n  setTimeout(() => { bar.style.display = \"none\"; }, 700);\r\n\r\n  document.getElementById(\"summary\").textContent =\r\n    `Parsed ${matched} lecture rows.`;\r\n  document.getElementById(\"downloadBtn\").style.display = \"block\";\r\n});\r\n\r\nasync function readPdfText(file, progress) {\r\n  const buf = await file.arrayBuffer();\r\n  const pdf = await pdfjsLib.getDocument({ data: buf }).promise;\r\n  let text = \"\";\r\n  for (let i = 1; i <= pdf.numPages; i++) {\r\n    const page = await pdf.getPage(i);\r\n    const content = await page.getTextContent();\r\n    text += content.items.map(it => it.str).join(\" \") + \" \\n\";\r\n    progress.style.width = (20 + (i \/ pdf.numPages) * 60) + \"%\";\r\n  }\r\n  return text;\r\n}\r\n\r\n\/\/ Known junk phrases that appear in page headers \/ footers of the PDF\r\nconst JUNK_PATTERNS = [\r\n  \/^of\\s+\\d+\\s+Sr\\s+No\/i,\r\n  \/^Sr\\s+No\\.?\\s+Course\\s+Name\/i,\r\n  \/^Page\\s+\\d+\/i,\r\n  \/^Attendance\\s+Report\/i,\r\n  \/^Student\\s+(Name|Number)\/i,\r\n  \/^Roll\\s+No\/i,\r\n  \/^Academic\\s+Year\/i,\r\n  \/^Program\\s+Name\/i,\r\n  \/^P\\s*-\\s*Present\/i,\r\n  \/^AG:\/i,\r\n  \/^Above\\s+data\/i,\r\n  \/^PS:\/i,\r\n];\r\n\r\nfunction isJunkCourse(name) {\r\n  return JUNK_PATTERNS.some(p => p.test(name.trim()));\r\n}\r\n\r\nfunction parseAttendance(text) {\r\n  \/\/ Match: <number> <course name> <Month Day, Year> ... <P|A|NU>\r\n  const pattern = \/(?:^|\\s)(\\d+)\\s+(.+?)\\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s+\\d{1,2},\\s+\\d{4}[\\s\\S]*?\\b(P|A|NU)\\b\/g;\r\n  const counts = {};\r\n  let matched = 0, m;\r\n\r\n  while ((m = pattern.exec(text)) !== null) {\r\n    let course = m[2].trim().replace(\/\\s{2,}\/g, \" \");\r\n\r\n    \/\/ Skip header\/footer lines mistakenly captured as course names\r\n    if (isJunkCourse(course)) continue;\r\n\r\n    const status = m[4];\r\n    if (!counts[course]) counts[course] = { P:0, A:0, NU:0, Total:0 };\r\n    counts[course][status]++; counts[course].Total++; matched++;\r\n  }\r\n  return { counts, matched };\r\n}\r\n\r\nfunction buildTableData(counts) {\r\n  const courses = Object.keys(counts).sort();\r\n  const rows = [];\r\n\r\n  let totalP = 0, totalA = 0, totalNU = 0, totalAll = 0;\r\n\r\n  for (const c of courses) {\r\n    const r = counts[c];\r\n    const total = r.Total || 1;\r\n\r\n    const effWithNU = r.P + r.NU;\r\n    const pctWithNU = (effWithNU \/ total * 100);\r\n\r\n    const effWithoutNU = r.P;\r\n    const pctWithoutNU = (effWithoutNU \/ total * 100);\r\n\r\n    const canBunkWithNU = calcCanBunk(total, r.A);\r\n    const needAttendWithNU = calcNeedAttend(total, r.A);\r\n\r\n    const canBunkWithoutNU = calcCanBunk(total, r.A + r.NU);\r\n    const needAttendWithoutNU = calcNeedAttend(total, r.A + r.NU);\r\n\r\n    rows.push({\r\n      course: c,\r\n      P: r.P, A: r.A, NU: r.NU, Total: r.Total,\r\n      pctWithNU: pctWithNU.toFixed(2)+\"%\",\r\n      canBunkWithNU, needAttendWithNU,\r\n      pctWithoutNU: pctWithoutNU.toFixed(2)+\"%\",\r\n      canBunkWithoutNU, needAttendWithoutNU,\r\n      isOverall: false\r\n    });\r\n\r\n    totalP += r.P;\r\n    totalA += r.A;\r\n    totalNU += r.NU;\r\n    totalAll += r.Total;\r\n  }\r\n\r\n  \/\/ Overall row\r\n  if (totalAll > 0) {\r\n    const ovPctWithNU  = ((totalP + totalNU) \/ totalAll * 100).toFixed(2) + \"%\";\r\n    const ovPctWithoutNU = (totalP \/ totalAll * 100).toFixed(2) + \"%\";\r\n    rows.push({\r\n      course: \"\ud83d\udcca Overall\",\r\n      P: totalP, A: totalA, NU: totalNU, Total: totalAll,\r\n      pctWithNU: ovPctWithNU,\r\n      canBunkWithNU: \"\",\r\n      needAttendWithNU: \"\",\r\n      pctWithoutNU: ovPctWithoutNU,\r\n      canBunkWithoutNU: \"\",\r\n      needAttendWithoutNU: \"\",\r\n      isOverall: true\r\n    });\r\n  }\r\n\r\n  return rows;\r\n}\r\n\r\nfunction calcCanBunk(T, A) {\r\n  const maxBunks = Math.floor(T \/ 4);\r\n  return Math.max(0, maxBunks - A);\r\n}\r\n\r\nfunction calcNeedAttend(T, A) {\r\n  const newn = A * 4;\r\n  const maxBunks = Math.floor(T \/ 4);\r\n  return A > maxBunks ? newn - T : 0;\r\n}\r\n\r\nfunction renderData(rows) {\r\n  \/\/ --- Desktop Table ---\r\n  let html = `\r\n  <table>\r\n    <tr>\r\n      <th rowspan=\"2\">Course<\/th>\r\n      <th rowspan=\"2\">P<\/th>\r\n      <th rowspan=\"2\">A<\/th>\r\n      <th rowspan=\"2\">NU<\/th>\r\n      <th rowspan=\"2\">Total<\/th>\r\n      <th colspan=\"3\">Considering NU as P<\/th>\r\n      <th colspan=\"3\">Considering NU as A<\/th>\r\n    <\/tr>\r\n    <tr>\r\n      <th>%<\/th><th>Can Bunk?<\/th><th>Need to Attend<\/th>\r\n      <th>%<\/th><th>Can Bunk?<\/th><th>Need to Attend<\/th>\r\n    <\/tr>`;\r\n\r\n  for (const row of rows) {\r\n    const rowClass = row.isOverall ? ' class=\"overall-row\"' : '';\r\n    html += `<tr${rowClass}>\r\n      <td>${escapeHtml(row.course)}<\/td>\r\n      <td>${row.P}<\/td>\r\n      <td>${row.A}<\/td>\r\n      <td>${row.NU}<\/td>\r\n      <td>${row.Total}<\/td>\r\n      <td class=\"${parseFloat(row.pctWithNU)>=75?'ok':'bad'}\">${row.pctWithNU}<\/td>\r\n      <td>${row.canBunkWithNU}<\/td>\r\n      <td>${row.needAttendWithNU}<\/td>\r\n      <td class=\"${parseFloat(row.pctWithoutNU)>=75?'ok':'bad'}\">${row.pctWithoutNU}<\/td>\r\n      <td>${row.canBunkWithoutNU}<\/td>\r\n      <td>${row.needAttendWithoutNU}<\/td>\r\n    <\/tr>`;\r\n  }\r\n  html += \"<\/table>\";\r\n\r\n  \/\/ --- Mobile Cards ---\r\n  let mobileHtml = \"\";\r\n  for (const row of rows) {\r\n    const cardClass = row.isOverall ? ' overall-card' : '';\r\n    mobileHtml += `\r\n    <div class=\"mobile-card${cardClass}\">\r\n      <h3>${escapeHtml(row.course)}<\/h3>\r\n      <p><b>Present:<\/b> ${row.P}<\/p>\r\n      <p><b>Absent:<\/b> ${row.A}<\/p>\r\n      <p><b>NU:<\/b> ${row.NU}<\/p>\r\n      <p><b>Total:<\/b> ${row.Total}<\/p>\r\n      <p><u>Considering NU as P<\/u><\/p>\r\n      <p><b>Percentage:<\/b> <span class=\"${parseFloat(row.pctWithNU)>=75?'ok':'bad'}\">${row.pctWithNU}<\/span><\/p>\r\n      ${row.isOverall ? '' : `<p><b>Can Bunk?:<\/b> ${row.canBunkWithNU}<\/p>\r\n      <p><b>Need to Attend:<\/b> ${row.needAttendWithNU}<\/p>`}\r\n      <p><u>Considering NU as A<\/u><\/p>\r\n      <p><b>Percentage:<\/b> <span class=\"${parseFloat(row.pctWithoutNU)>=75?'ok':'bad'}\">${row.pctWithoutNU}<\/span><\/p>\r\n      ${row.isOverall ? '' : `<p><b>Can Bunk?:<\/b> ${row.canBunkWithoutNU}<\/p>\r\n      <p><b>Need to Attend:<\/b> ${row.needAttendWithoutNU}<\/p>`}\r\n    <\/div>`;\r\n  }\r\n\r\n  document.getElementById(\"output\").innerHTML = html + mobileHtml;\r\n}\r\n\r\nfunction escapeHtml(s) {\r\n  return s.replace(\/[&<>\"']\/g, c => ({\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\",\"'\":\"&#39;\"}[c]));\r\n}\r\n\r\n\/\/ PDF Download\r\ndocument.getElementById(\"downloadBtn\").addEventListener(\"click\", async () => {\r\n  const { jsPDF } = window.jspdf;\r\n  const doc = new jsPDF();\r\n\r\n  doc.setFontSize(14);\r\n  doc.text(\"Attendance Summary\", 14, 18);\r\n\r\n  const head = [[\r\n    \"Course\",\"P\",\"A\",\"NU\",\"Total\",\r\n    \"% (NU as P)\",\"Can Bunk?\",\"Need to Attend\",\r\n    \"% (NU as A)\",\"Can Bunk?\",\"Need to Attend\"\r\n  ]];\r\n  const body = latestTableData.map(r => [\r\n    r.course, r.P, r.A, r.NU, r.Total,\r\n    r.pctWithNU, r.canBunkWithNU, r.needAttendWithNU,\r\n    r.pctWithoutNU, r.canBunkWithoutNU, r.needAttendWithoutNU\r\n  ]);\r\n\r\n  doc.autoTable({\r\n    head,\r\n    body,\r\n    startY: 25,\r\n    styles: { fontSize: 8 },\r\n    didParseCell: function(data) {\r\n      \/\/ Highlight overall row\r\n      if (data.row.index === body.length - 1) {\r\n        data.cell.styles.fillColor = [238, 242, 255];\r\n        data.cell.styles.fontStyle = 'bold';\r\n      }\r\n    }\r\n  });\r\n\r\n  const footerY = doc.internal.pageSize.getHeight() - 10;\r\n  doc.setFontSize(10);\r\n  doc.text(\"SVKM Attendance Tracker Powered by\", 14, footerY);\r\n\r\n  const linkText = \"rehmahprojects.com\";\r\n  doc.textWithLink(linkText, 120, footerY, { url: \"https:\/\/rehmahprojects.com\" });\r\n  const textWidth = doc.getTextWidth(linkText);\r\n  doc.line(120, footerY + 1, 120 + textWidth, footerY + 1);\r\n\r\n  \/\/ Try to load logo, but don't block download if it fails\r\n  try {\r\n    const logoUrl = \"https:\/\/rehmahprojects.com\/attendance\/wp-content\/uploads\/2025\/09\/RlogoT.png\";\r\n    const logoData = await fetch(logoUrl)\r\n      .then(res => { if (!res.ok) throw new Error(\"logo fetch failed\"); return res.blob(); })\r\n      .then(blob => new Promise((res, rej) => {\r\n        const r = new FileReader();\r\n        r.onload = () => res(r.result);\r\n        r.onerror = () => rej();\r\n        r.readAsDataURL(blob);\r\n      }));\r\n    doc.addImage(logoData, \"PNG\", 120 + textWidth + 3, footerY - 6, 8, 8);\r\n  } catch (_) {\r\n    \/\/ Logo unavailable \u2014 skip silently\r\n  }\r\n\r\n  doc.save(\"attendance_summary.pdf\");\r\n});\r\n<\/script>\r\n<\/div>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Attendance Analyzer Upload Your SVKM Attendance PDF \ud83d\udcc2 Choose Attendance PDF Select &#8220;Detailed Report&#8221; PDF from SVKM portal \u2b07\ufe0f Download PDF<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"_kad_post_transparent":"","_kad_post_title":"hide","_kad_post_layout":"fullwidth","_kad_post_sidebar_id":"","_kad_post_content_style":"unboxed","_kad_post_vertical_padding":"hide","_kad_post_feature":"hide","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-144","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/pages\/144","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/comments?post=144"}],"version-history":[{"count":188,"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/pages\/144\/revisions"}],"predecessor-version":[{"id":618,"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/pages\/144\/revisions\/618"}],"wp:attachment":[{"href":"https:\/\/rehmahprojects.com\/attendance\/wp-json\/wp\/v2\/media?parent=144"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}