Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import streamlit.components.v1 as components | |
| # Set page config | |
| st.set_page_config(page_title="Precision Medicine Network", layout="wide") | |
| # Title | |
| st.title("Precision Medicine Network") | |
| # HTML content (previous D3.js visualization) | |
| html_content = """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Precision Medicine Network</title> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script> | |
| <style> | |
| #graph { width: 100%; height: 600px; border: 1px solid #ccc; position: relative; } | |
| .popup { | |
| position: absolute; | |
| background: white; | |
| border: 1px solid #999; | |
| border-radius: 4px; | |
| padding: 10px; | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="graph"></div> | |
| <div id="popup" class="popup"></div> | |
| <script> | |
| const width = 800; | |
| const height = 600; | |
| const svg = d3.select("#graph") | |
| .append("svg") | |
| .attr("width", width) | |
| .attr("height", height); | |
| const simulation = d3.forceSimulation() | |
| .force("link", d3.forceLink().id(d => d.id)) | |
| .force("charge", d3.forceManyBody().strength(-50)) | |
| .force("center", d3.forceCenter(width / 2, height / 2)); | |
| const nodes = []; | |
| const links = []; | |
| // Create nodes for healthcare professionals | |
| const doctors = 5; | |
| const nurses = 10; | |
| const clinicians = 8; | |
| for (let i = 0; i < doctors; i++) { | |
| nodes.push({ id: `Doctor${i + 1}`, group: "doctor" }); | |
| } | |
| for (let i = 0; i < nurses; i++) { | |
| nodes.push({ id: `Nurse${i + 1}`, group: "nurse" }); | |
| } | |
| for (let i = 0; i < clinicians; i++) { | |
| nodes.push({ id: `Clinician${i + 1}`, group: "clinician" }); | |
| } | |
| // Create nodes for patients | |
| for (let i = 0; i < 100; i++) { | |
| nodes.push({ id: `Patient${i + 1}`, group: "patient" }); | |
| } | |
| // Create links between patients and healthcare professionals | |
| nodes.filter(n => n.group === "patient").forEach(patient => { | |
| const doctor = nodes.find(n => n.group === "doctor" && Math.random() < 0.2); | |
| if (doctor) links.push({ source: patient.id, target: doctor.id }); | |
| const nurse = nodes.find(n => n.group === "nurse" && Math.random() < 0.3); | |
| if (nurse) links.push({ source: patient.id, target: nurse.id }); | |
| const clinician = nodes.find(n => n.group === "clinician" && Math.random() < 0.25); | |
| if (clinician) links.push({ source: patient.id, target: clinician.id }); | |
| }); | |
| const link = svg.append("g") | |
| .selectAll("line") | |
| .data(links) | |
| .enter().append("line") | |
| .attr("stroke", "#999") | |
| .attr("stroke-opacity", 0.6); | |
| const node = svg.append("g") | |
| .selectAll("circle") | |
| .data(nodes) | |
| .enter().append("circle") | |
| .attr("r", d => d.group === "patient" ? 3 : 5) | |
| .attr("fill", d => { | |
| switch (d.group) { | |
| case "doctor": return "#ff0000"; | |
| case "nurse": return "#00ff00"; | |
| case "clinician": return "#0000ff"; | |
| default: return "#cccccc"; | |
| } | |
| }) | |
| .call(d3.drag() | |
| .on("start", dragstarted) | |
| .on("drag", dragged) | |
| .on("end", dragended)) | |
| .on("click", showPopup); | |
| simulation | |
| .nodes(nodes) | |
| .on("tick", ticked); | |
| simulation.force("link") | |
| .links(links); | |
| function ticked() { | |
| link | |
| .attr("x1", d => d.source.x) | |
| .attr("y1", d => d.source.y) | |
| .attr("x2", d => d.target.x) | |
| .attr("y2", d => d.target.y); | |
| node | |
| .attr("cx", d => d.x) | |
| .attr("cy", d => d.y); | |
| } | |
| function dragstarted(event) { | |
| if (!event.active) simulation.alphaTarget(0.3).restart(); | |
| event.subject.fx = event.subject.x; | |
| event.subject.fy = event.subject.y; | |
| } | |
| function dragged(event) { | |
| event.subject.fx = event.x; | |
| event.subject.fy = event.y; | |
| } | |
| function dragended(event) { | |
| if (!event.active) simulation.alphaTarget(0); | |
| event.subject.fx = null; | |
| event.subject.fy = null; | |
| } | |
| function showPopup(event, d) { | |
| const popup = d3.select("#popup"); | |
| popup.style("display", "block") | |
| .style("left", (event.pageX + 10) + "px") | |
| .style("top", (event.pageY - 10) + "px") | |
| .html(`<strong>ID:</strong> ${d.id}<br><strong>Role:</strong> ${d.group}`); | |
| // Hide popup when clicking elsewhere | |
| d3.select("body").on("click", function() { | |
| if (event.target.tagName !== "circle") { | |
| popup.style("display", "none"); | |
| } | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| # Embed the HTML content | |
| components.html(html_content, height=650) | |
| # Add some explanation | |
| st.markdown(""" | |
| This graph represents a precision medicine network with the following components: | |
| - 5 Doctors (red nodes) | |
| - 10 Nurses (green nodes) | |
| - 8 Clinicians (blue nodes) | |
| - 100 Patients (gray nodes) | |
| The connections between patients and healthcare professionals are generated randomly with the following probabilities: | |
| - 20% chance for a patient to be connected to a doctor | |
| - 30% chance for a patient to be connected to a nurse | |
| - 25% chance for a patient to be connected to a clinician | |
| You can interact with the graph by: | |
| - Dragging nodes to rearrange the layout | |
| - Clicking on a node to see its ID and role | |
| """) |