Download index.html from Rolaficus/Medsecure: direct link, hf CLI and curl.
- Browser
- Download file 19.7 kB
-
https://huggingface.co/spaces/Rolaficus/Medsecure/resolve/main/index.html
- Command line
-
hf download hf://spaces/Rolaficus/Medsecure/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Rolaficus/Medsecure/resolve/main/index.html
19.7 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>MedSecure: EMR Access Compliance Auditor</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 0; background-color: #f4f4f9; color: #333; } | |
| .header { background-color: #007bff; color: white; padding: 20px; text-align: center; } | |
| .container { max-width: 1200px; margin: 30px auto; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); } | |
| .screen { display: none; padding: 20px; } | |
| h1, h2, h3 { color: #007bff; border-bottom: 2px solid #eee; padding-bottom: 10px; margin-bottom: 20px; } | |
| .back-button { background-color: #6c757d; color: white; padding: 8px 15px; border: none; border-radius: 4px; cursor: pointer; margin-bottom: 20px; display: inline-block; } | |
| .role-button { background-color: #28a745; color: white; padding: 15px 30px; border: none; border-radius: 8px; cursor: pointer; font-size: 1.2em; margin: 10px; transition: background-color 0.3s; } | |
| .role-button:hover { background-color: #1e7e34; } | |
| /* Form Styling */ | |
| label { display: block; margin-top: 15px; font-weight: bold; } | |
| input[type="text"], select, button[type="submit"] { width: 100%; padding: 12px; margin-top: 5px; border-radius: 4px; border: 1px solid #ccc; box-sizing: border-box; } | |
| button[type="submit"] { background-color: #007bff; color: white; cursor: pointer; margin-top: 25px; } | |
| /* Dashboard & Log Styling */ | |
| .admin-dashboard { background-color: #ffe0e0; border: 2px solid #ff4d4d; padding: 20px; border-radius: 8px; margin-top: 20px; } | |
| .flag-result { font-weight: bold; padding: 15px; margin-top: 15px; border-radius: 4px; display: none; font-size: 1.1em; } | |
| .violation { background-color: #ffcccc; color: #cc0000; border: 2px solid #cc0000; display: block ; } | |
| .compliant { background-color: #ccffcc; color: #008000; border: 2px solid #008000; display: block ; } | |
| .suspicious { color: #cc0000; font-weight: bold; } | |
| .report-item { margin-bottom: 15px; padding: 10px; border-left: 5px solid #007bff; background-color: #f9f9f9; } | |
| .shap-list li { margin-bottom: 5px; list-style-type: disc; margin-left: 20px; } | |
| .benign-report { background-color: #f0fff0; border: 2px solid #00aa00; } | |
| .benign-report .suspicious { color: #008000; } | |
| /* New styling for the Suspicious Score */ | |
| #ml-score-display { | |
| font-size: 2em; | |
| font-weight: bold; | |
| color: #cc0000; | |
| display: inline-block; | |
| margin-top: 10px; | |
| } | |
| /* Log Table */ | |
| .log-table-container { max-height: 300px; overflow-y: auto; margin-top: 15px; } | |
| .access-log-table { width: 100%; border-collapse: collapse; font-size: 0.9em; } | |
| .access-log-table th, .access-log-table td { border: 1px solid #ddd; padding: 8px; text-align: left; } | |
| .access-log-table th { background-color: #f2f2f2; } | |
| .log-violation { background-color: #ffdddd; } | |
| /* Stats */ | |
| .stats-container { display: flex; justify-content: space-between; margin-top: 20px; } | |
| .stat-box { flex-basis: 30%; padding: 15px; border-radius: 5px; text-align: center; } | |
| .stat-box h4 { margin: 0 0 5px 0; color: #333; } | |
| .stat-box p { font-size: 1.8em; font-weight: bold; margin: 0; } | |
| .stat-flag { background-color: #ff4d4d; color: white; } | |
| .stat-total { background-color: #007bff; color: white; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <h1>MedSecure: EMR Access Compliance Auditor</h1> | |
| <p>AI-Based Detection, Verification, and Explanation System</p> | |
| </div> | |
| <div class="container"> | |
| <div id="landing-page" class="screen" style="display: block;"> | |
| <h2>Select Your Role to Access the Portal</h2> | |
| <div style="text-align: center;"> | |
| <button class="role-button" onclick="showScreen('staff-access-page', 'Doctor')">Doctor Access</button> | |
| <button class="role-button" onclick="showScreen('staff-access-page', 'Nurse')">Nurse Access</button> | |
| <button class="role-button" onclick="showScreen('admin-dashboard', 'Admin')">Admin Dashboard</button> | |
| </div> | |
| </div> | |
| <div id="staff-access-page" class="screen"> | |
| <button class="back-button" onclick="showScreen('landing-page')">← Back to Role Selection</button> | |
| <h2 id="staff-title">EMR Access Terminal (<span id="current-user-role"></span>)</h2> | |
| <p>Use this screen to simulate accessing a patient's Electronic Medical Record (EMR). <br>**Patient assignment is now dynamically determined by the system.**</p> | |
| <form id="accessForm"> | |
| <label for="patientID">Patient Record ID:</label> | |
| <input type="text" id="patientID" value="P456" placeholder="Try P456 (Assigned) or P104 (VIP/Unassigned)"> | |
| <p><strong>Assignment Status:</strong> <span id="assignment-status" style="font-weight: normal; color: #666;">Will be determined upon access attempt.</span></p> | |
| <label for="hasEmergency">Is Access for Emergency Reasons? (Bypasses night-time check)</label> | |
| <select id="hasEmergency"> | |
| <option value="false">No</option> | |
| <option value="true">Yes</option> | |
| </select> | |
| <button type="submit">Access Patient Record (Run CSP Verification)</button> | |
| </form> | |
| <div id="access-details" style="margin-top: 30px; padding: 15px; border: 1px dashed #007bff; background-color: #e6f0ff;"> | |
| <h3>Patient EMR Data / Access Result</h3> | |
| <div id="local-csp-result" class="flag-result">Awaiting access attempt...</div> | |
| <div id="patient-data"> | |
| <p>Access status: **ACCESS GRANTED/DENIED**</p> | |
| <p>Details will appear here upon compliant access.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="admin-dashboard" class="screen"> | |
| <button class="back-button" onclick="showScreen('landing-page')">← Back to Role Selection</button> | |
| <h2>Admin Compliance Audit Dashboard</h2> | |
| <h3>Access Statistics</h3> | |
| <div class="stats-container"> | |
| <div class="stat-box stat-total"> | |
| <h4>Total Access Attempts</h4> | |
| <p id="stat-total-access">0</p> | |
| </div> | |
| <div class="stat-box stat-flag"> | |
| <h4>CSP Policy Flags (Violations)</h4> | |
| <p id="stat-policy-flags">0</p> | |
| </div> | |
| <div class="stat-box" style="background-color: #ffc107; color: #333;"> | |
| <h4>Unassigned Patient Access</h4> | |
| <p id="stat-unassigned-access">0</p> | |
| </div> | |
| </div> | |
| <h3 style="margin-top: 30px;">Access Log History</h3> | |
| <div class="log-table-container"> | |
| <table class="access-log-table"> | |
| <thead> | |
| <tr> | |
| <th>Time</th> | |
| <th>Role</th> | |
| <th>Patient ID</th> | |
| <th>Assigned</th> | |
| <th>CSP Verdict</th> | |
| <th>Message</th> | |
| </tr> | |
| </thead> | |
| <tbody id="access-log-body"> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div id="ml-report-panel" class="admin-dashboard"> | |
| <h3 style="color: #ff4d4d;">AI-Driven Audit Report (Suspicious Session Context)</h3> | |
| <div class="report-item"> | |
| <strong>ML Session Classification:</strong> <span id="ml-prediction-text" class="suspicious">**Loading...**</span> | |
| </div> | |
| <div class="report-item"> | |
| <strong>ML Suspicious Score:</strong> | |
| <span id="ml-score-display">0.00%</span> | |
| <p style="font-size:0.9em; color:#666;">*This is the model's confidence probability that the session is suspicious.*</p> | |
| </div> | |
| <div class="report-item"> | |
| <strong>XAI (Explainable AI) - Feature Contribution Analysis:</strong> | |
| <ul id="shap-list" class="shap-list"> | |
| <li>Awaiting ML analysis from backend.</li> | |
| </ul> | |
| </div> | |
| <div class="report-item"> | |
| <strong>A* Suspicious Path Analysis:</strong> | |
| <p id="a-star-path">Path: **Loading...** | Total Suspicion Score: <strong>0.00</strong></p> | |
| <p style="font-size:0.9em; color:#666;">*Score of 16.50 indicates the maximum severity snooping path (e.g., path to VIP patient P104).* </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentUserRole = ''; | |
| // Static Patient Database (Used only for local display in the staff screen) | |
| const patientDB = { | |
| "P456": { Name: "Alice Johnson", Condition: "Cancer", Room: 328, Doctor: "Dr. Smith"}, | |
| "P104": { Name: "VIP Patient (John Doe)", Condition: "Elective Surgery", Room: 450, Doctor: "Dr. Wells"}, | |
| "P999": { Name: "Unassigned Patient", Condition: "Flu", Room: 101, Doctor: "Dr. Gray"}, | |
| }; | |
| function showScreen(screenId, role = null) { | |
| // Hide all screens | |
| document.querySelectorAll('.screen').forEach(screen => { | |
| screen.style.display = 'none'; | |
| }); | |
| // Show the requested screen | |
| document.getElementById(screenId).style.display = 'block'; | |
| if (role) { | |
| currentUserRole = role; | |
| const staffTitle = document.getElementById('staff-title'); | |
| if (staffTitle) { | |
| document.getElementById('current-user-role').textContent = role; | |
| } | |
| } | |
| // On Admin Dashboard load, refresh the stats and log by calling the backend | |
| if (screenId === 'admin-dashboard') { | |
| fetchDashboardData(); | |
| } | |
| // Clear EMR data when navigating back to role selection or to staff page | |
| if (screenId === 'landing-page' || screenId === 'staff-access-page') { | |
| const localCspResultDiv = document.getElementById('local-csp-result'); | |
| localCspResultDiv.className = 'flag-result'; | |
| localCspResultDiv.innerHTML = 'Awaiting access attempt...'; | |
| document.getElementById('patient-data').innerHTML = '<p>Access status: **ACCESS GRANTED/DENIED**</p><p>Details will appear here upon compliant access.</p>'; | |
| document.getElementById('assignment-status').innerHTML = 'Will be determined upon access attempt.'; | |
| } | |
| } | |
| function displayPatientData(patientID, isAssigned) { | |
| const patient = patientDB[patientID] || { Name: "Patient Data (Unknown ID)", Condition: "N/A", Room: "N/A", Doctor: "N/A" }; | |
| const statusText = isAssigned ? 'Assigned' : 'Unassigned'; | |
| const statusColor = isAssigned ? 'green' : 'red'; | |
| document.getElementById('patient-data').innerHTML = ` | |
| <h4>Patient ID: ${patientID}</h4> | |
| <p><strong>Name:</strong> ${patient.Name}</p> | |
| <p><strong>Condition:</strong> ${patient.Condition}</p> | |
| <p><strong>Room:</strong> ${patient.Room}</p> | |
| <p><strong>Primary Doctor:</strong> ${patient.Doctor}</p> | |
| <p><strong>Assignment Status:</strong> <span style="font-weight: bold; color: ${statusColor}">${statusText}</span></p> | |
| `; | |
| } | |
| async function fetchDashboardData() { | |
| try { | |
| // IMPORTANT: This fetches data from the running Python server | |
| const response = await fetch('/api/admin_dashboard'); | |
| const data = await response.json(); | |
| // Update Stats | |
| document.getElementById('stat-total-access').textContent = data.stats.total; | |
| document.getElementById('stat-policy-flags').textContent = data.stats.violations; | |
| document.getElementById('stat-unassigned-access').textContent = data.stats.unassigned; | |
| // Update Access Log Table | |
| const logBody = document.getElementById('access-log-body'); | |
| logBody.innerHTML = ''; | |
| data.logs.forEach(entry => { | |
| const row = logBody.insertRow(); | |
| if (entry.is_violation) row.classList.add('log-violation'); | |
| row.insertCell().textContent = entry.timestamp; | |
| row.insertCell().textContent = entry.role; | |
| row.insertCell().textContent = entry.patientID; | |
| row.insertCell().textContent = entry.is_assigned ? 'Yes' : 'No'; | |
| row.insertCell().textContent = entry.is_violation ? 'VIOLATION (FLAG)' : 'COMPLIANT'; | |
| row.insertCell().textContent = entry.message.split(': ').length > 1 ? entry.message.split(': ')[1] : entry.message; | |
| }); | |
| // Update ML Report | |
| const predictionSpan = document.getElementById('ml-prediction-text'); | |
| const scoreDisplay = document.getElementById('ml-score-display'); | |
| const mlReportPanel = document.getElementById('ml-report-panel'); | |
| // Set the Classification Text | |
| predictionSpan.textContent = `**CLASSIFIED: ${data.ml_report.prediction}**`; | |
| // Set the Suspicious Score | |
| scoreDisplay.textContent = `${(data.ml_report.score*100).toFixed(2)}%`; | |
| // Style the report panel based on prediction | |
| if (data.ml_report.prediction === 'SUSPICIOUS') { | |
| predictionSpan.classList.add('suspicious'); | |
| scoreDisplay.style.color = '#cc0000'; | |
| mlReportPanel.classList.remove('benign-report'); | |
| mlReportPanel.style.borderColor = '#ff4d4d'; | |
| } else { | |
| predictionSpan.classList.remove('suspicious'); | |
| scoreDisplay.style.color = '#008000'; | |
| mlReportPanel.classList.add('benign-report'); | |
| mlReportPanel.style.borderColor = '#00aa00'; | |
| } | |
| // Update XAI Explanation | |
| const shapList = document.getElementById('shap-list'); | |
| shapList.innerHTML = ''; | |
| if (data.ml_report.explanation.length > 0) { | |
| data.ml_report.explanation.forEach(line => { | |
| const li = document.createElement('li'); | |
| li.innerHTML = line; | |
| shapList.appendChild(li); | |
| }); | |
| } else { | |
| shapList.innerHTML = '<li>No significant feature contributions found, session is neutral.</li>'; | |
| } | |
| // Update A* Search | |
| document.getElementById('a-star-path').innerHTML = `Path: <strong>${data.a_star.path}</strong> | Total Suspicion Score: <strong>${data.a_star.score.toFixed(2)}</strong>`; | |
| } catch (error) { | |
| console.error("Error fetching dashboard data:", error); | |
| document.getElementById('ml-report-panel').innerHTML = '<p style="color:red;">Error fetching AI report. Check backend server.</p>'; | |
| } | |
| } | |
| document.getElementById('accessForm').addEventListener('submit', async function(event) { | |
| event.preventDefault(); | |
| const role = currentUserRole; | |
| const patientID = document.getElementById('patientID').value || 'PXXX'; | |
| const hasEmergency = document.getElementById('hasEmergency').value === 'true'; | |
| const localCspResultDiv = document.getElementById('local-csp-result'); | |
| const patientDataDiv = document.getElementById('patient-data'); | |
| const assignmentStatusSpan = document.getElementById('assignment-status'); | |
| localCspResultDiv.className = 'flag-result'; | |
| patientDataDiv.innerHTML = ''; | |
| try { | |
| // Send data to Flask backend for CSP check and logging | |
| const response = await fetch('/api/log_access', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| role: role, | |
| patientID: patientID, | |
| hasEmergency: hasEmergency | |
| }) | |
| }); | |
| const data = await response.json(); | |
| const isAssigned = data.isAssignedStatus; // Use the calculated status from the backend | |
| // Update the Dynamic Assignment Status Display | |
| assignmentStatusSpan.textContent = isAssigned ? 'YES - You are assigned to this patient.' : 'NO - Unassigned patient record.'; | |
| assignmentStatusSpan.style.color = isAssigned ? 'green' : 'red'; | |
| // Update the Local Log (Staff Screen) based on backend CSP verdict | |
| if (data.isViolation) { | |
| localCspResultDiv.classList.add('violation'); | |
| patientDataDiv.innerHTML = `<p style="color:red; font-weight:bold;">ACCESS DENIED: Policy Violation. Audit log initiated.</p>`; | |
| } else { | |
| localCspResultDiv.classList.add('compliant'); | |
| if (isAssigned) { | |
| displayPatientData(patientID, isAssigned); | |
| } else { | |
| // Doctor access to unassigned patient | |
| patientDataDiv.innerHTML = `<p style="color:orange; font-weight:bold;">ACCESS GRANTED (Logged): Viewing unassigned patient record. Check Admin Dashboard for risk assessment.</p>`; | |
| } | |
| } | |
| localCspResultDiv.innerHTML = `<strong>ACCESS LOG:</strong> ${data.message}`; | |
| } catch (error) { | |
| console.error("Error logging access:", error); | |
| localCspResultDiv.classList.add('violation'); | |
| localCspResultDiv.innerHTML = `<strong>ERROR:</strong> Could not reach compliance backend.`; | |
| } | |
| }); | |
| // Initialize view to the landing page | |
| document.addEventListener('DOMContentLoaded', () => showScreen('landing-page')); | |
| </script> | |
| </body> | |
| </html> |