|
80 | 80 | </div> |
81 | 81 | </div> |
82 | 82 |
|
83 | | - <figure class="image-caption"> |
84 | | - <img class="figure-img img-fluid" src="/assets/images/250114 - Org Chart-2.png" alt="US CMS S&C Org Chart" /> |
85 | | - <figcaption></figcaption> |
86 | | -</figure> |
| 83 | + <html lang="en"> |
| 84 | +<head> |
| 85 | + <meta charset="UTF-8" /> |
| 86 | + <title>USCMS S&C Org Chart (Auto-Generated)</title> |
| 87 | + <style> |
| 88 | + body { background:#fff; font-family:sans-serif; } |
| 89 | + .chart-container { position:relative; width:1200px; height:800px; margin:auto;} |
| 90 | + |
| 91 | + .node { position:absolute; text-align:center; } |
| 92 | + .node.root { width:300px; } |
| 93 | + .node.leaf { width:200px; } |
| 94 | + |
| 95 | + .node-box { |
| 96 | + border:2px solid #000; padding:10px; background:#fff; |
| 97 | + font-size:14px; font-weight:normal; line-height:1.4; text-align:left; |
| 98 | + box-shadow:2px 2px 5px rgba(0,0,0,0.1); |
| 99 | + } |
| 100 | + .node-box strong { display:block; text-align:center; font-size:15px; margin-bottom:6px; } |
| 101 | + .person-line { font-size:12px; } |
| 102 | + .title-line { font-size:11px; font-style:italic; color:#555; margin-left:2px; } |
| 103 | + |
| 104 | + .photo-left,.photo-right,.photo-below{ |
| 105 | + position:absolute; width:75px; height:75px; object-fit:cover; |
| 106 | + } |
| 107 | + .photo-left{ left:-85px; top:50%; transform:translateY(-50%); } |
| 108 | + .photo-right{ right:-85px; top:50%; transform:translateY(-50%); } |
| 109 | + .photo-below{ top:100%; margin-top:8px; } |
| 110 | + canvas { position:absolute; top:0; left:0; z-index:0; } |
| 111 | + </style> |
| 112 | +</head> |
| 113 | +<body> |
| 114 | +<div id="chart" class="chart-container"> |
| 115 | + <canvas id="connectorCanvas" width="1200" height="800"></canvas> |
| 116 | +</div> |
| 117 | + |
| 118 | + |
| 119 | + |
| 120 | + |
| 121 | + |
| 122 | + |
| 123 | + |
| 124 | +<script> |
| 125 | +/* Parse Liquid-generated JSON */ |
| 126 | +const layoutMap = JSON.parse("{ \"management\": [ { \"name\": \"Tulika Bose\", \"shortname\": \"tulika176\", \"title\": \"S&C Operations Program Manager\", \"institution\": \"University of Wisconsin-Madison\", \"photo\": \"/assets/images/team/Tulika_Bose.jpg\" }, { \"name\": \"Dirk Hufnagel\", \"shortname\": \"d_hufnagel\", \"title\": \"Deputy S&C Operations Program Manager\\n\", \"institution\": \"Fermilab\", \"photo\": \"/assets/images/team/Dirk-Hufnagel.jpg\" }, { \"name\": \"Robert Tuck\", \"shortname\": \"rct225\", \"title\": \"Associate Project Manager\", \"institution\": \"Princeton University\", \"photo\": \"/assets/images/team/Robert-Tuck.jpg\" } ] , \"facilities\": [ { \"name\": \"Andrew Melo\", \"shortname\": \"PerilousApricot\", \"title\": \"Facilities Co-Leader\", \"institution\": \"Vanderbilt University\", \"photo\": \"/assets/images/team/Andrew_Melo.jpg\" }, { \"name\": \"David Mason\", \"shortname\": \"d_mason\", \"title\": \"Facilities Co-Leader\", \"institution\": \"Fermilab\", \"photo\": \"/assets/images/team/David_Mason.jpg\" } ] , \"software\": [ { \"name\": \"Saba Sehrish\", \"shortname\": \"ssehrish\", \"title\": \"Software Co-leader\", \"institution\": \"Fermilab\", \"photo\": \"/assets/images/team/Saba_Sehrish.jpg\" }, { \"name\": \"Kevin Lannon\", \"shortname\": \"klannon\", \"title\": \"Software Co-leader\", \"institution\": \"University of Notre Dame\", \"photo\": \"/assets/images/team/Kevin_Lannon.jpeg\" } ] , \"operations\": [ { \"name\": \"Duong Nguyen\", \"shortname\": \"nhduongvn\", \"title\": \"Operations Co-Leader\", \"institution\": \"University at Buffalo\", \"photo\": \"/assets/images/team/Duong_Nguyen.jpg\" }, { \"name\": \"Scarlet Norberg\", \"shortname\": \"s_norberg\", \"title\": \"Operations Co-Leader\\n\", \"institution\": \"Fermilab\", \"photo\": \"/assets/images/team/Scarlet-Norberg.png\" } ] , \"hllhc\": [ { \"name\": \"Lindsey Gray\", \"shortname\": \"l_gray\", \"title\": \"HL-LHC R&D Co-Leader\", \"institution\": \"Fermilab\", \"photo\": \"/assets/images/team/Lindsey-Gray.png\" } , { \"name\": \"David Sperka\", \"shortname\": \"dsperka\", \"title\": \"HL-LHC R&D Co-Leader\", \"institution\": \"Boston University\", \"photo\": \"/assets/images/team/David_Sperka.jpg\" } ], \"analysis\": [ { \"name\": \"Matteo Cremonesi\", \"shortname\": \"mcremone\", \"title\": \"HL-LHC R&D Analysis Systems Coordinator\", \"institution\": \"Carnegie Mellon University\", \"photo\": \"/assets/images/team/Matteo_Cremonesi.jpg\" } , { \"name\": \"Oksana Shadura\", \"shortname\": \"oshadura\", \"title\": \"HL-LHC R&D Analysis Systems Coordinator\", \"institution\": \"University of Nebraska - Lincoln\", \"photo\": \"/assets/images/team/Oksana-Shadura_.jpg\" } ], \"infra\": [ { \"name\": \"Nick Smith\", \"shortname\": \"nsmith-\", \"title\": \"U.S. CMS Fermilab Facility Architect\", \"institution\": \"Fermilab\", \"photo\": \"/assets/images/team/Nick_Smith.jpg\" } , { \"name\": \"James Letts\", \"shortname\": \"j_letts\", \"title\": \"U.S. CMS University Facility Architect\", \"institution\": \"UC San Diego\", \"photo\": \"/assets/images/team/James_Letts.jpg\" } ], \"algo\": [ { \"name\": \"Philip Chang\", \"shortname\": \"pchang\", \"title\": \"HL-LHC R&D Algorithms Coordinator\", \"institution\": \"University of Florida\", \"photo\": \"/assets/images/team/Philip_Chang.png\" } ], \"blueprint\": [ { \"name\": \"David Lange\", \"shortname\": \"dlange\", \"title\": \"Computational Physicist\", \"institution\": \"Princeton University\", \"photo\": \"/assets/images/team/David-Lange.png\" } ]}"); |
| 127 | +const titles = JSON.parse("{ \"management\": \"Software & Computing\", \"facilities\": \"Facilities\", \"software\": \"Software\", \"operations\": \"Operations\", \"hllhc\": \"HL-LHC R&D\", \"analysis\": \"Analysis\", \"infra\": \"Infrastructure\", \"algo\": \"Algorithms\", \"blueprint\": \"Blueprint\"}"); |
| 128 | +const instShortByPerson = JSON.parse("{ }"); |
| 129 | + |
| 130 | +/* Positions: Blueprint intentionally excluded */ |
| 131 | +const positions = { |
| 132 | + management: {left:450, top:40, title:"Software & Computing", type:"root"}, |
| 133 | + facilities: {left:300, top:220, title:"Facilities", type:"leaf"}, |
| 134 | + software: {left:300, top:360, title:"Software", type:"leaf"}, |
| 135 | + operations: {left:600, top:220, title:"Operations", type:"leaf"}, |
| 136 | + hllhc: {left:600, top:360, title:"HL-LHC R&D", type:"leaf"}, |
| 137 | + infra: {left:360, top:500, title:"Infrastructure", type:"leaf"}, |
| 138 | + algo: {left:470, top:500, title:"Algorithms", type:"leaf"}, |
| 139 | + analysis: {left:580, top:500, title:"Analysis", type:"leaf"} |
| 140 | + |
| 141 | + /* |
| 142 | + Example: If you ever want to show Blueprint (currently invisible), |
| 143 | + just uncomment and adjust coordinates like so: |
| 144 | +
|
| 145 | + blueprint: {left:900, top:500, title:"Blueprint", type:"leaf"} |
| 146 | + */ |
| 147 | +}; |
| 148 | + |
| 149 | +/* Overlay YAML titles */ |
| 150 | +Object.keys(positions).forEach(k => { |
| 151 | + if (titles[k]) positions[k].title = titles[k]; |
| 152 | +}); |
| 153 | + |
| 154 | +/* Titles to always display */ |
| 155 | +const showTitleForShort = new Set(["tulika176","d_hufnagel","rct225","j_letts","nsmith-"]); |
| 156 | + |
| 157 | +const chart = document.getElementById('chart'); |
| 158 | + |
| 159 | +/* Photo helpers */ |
| 160 | +function addPhotos(key, div, names){ |
| 161 | + if (["facilities","software"].includes(key)) { |
| 162 | + names.forEach((p,i)=>{ |
| 163 | + const img = document.createElement('img'); |
| 164 | + img.className = 'photo-left'; |
| 165 | + if (i === 1) img.style.left = '-170px'; |
| 166 | + if (p.photo) img.src = p.photo; |
| 167 | + img.alt = p.name; |
| 168 | + div.appendChild(img); |
| 169 | + }); |
| 170 | + } else if (["operations","hllhc"].includes(key)) { |
| 171 | + names.forEach((p,i)=>{ |
| 172 | + const img = document.createElement('img'); |
| 173 | + img.className = 'photo-right'; |
| 174 | + if (i === 1) img.style.right = '-170px'; |
| 175 | + if (p.photo) img.src = p.photo; |
| 176 | + img.alt = p.name; |
| 177 | + div.appendChild(img); |
| 178 | + }); |
| 179 | + } else if (key === "management") { |
| 180 | + names.forEach((p,i)=>{ |
| 181 | + const img = document.createElement('img'); |
| 182 | + img.className = i === 0 ? 'photo-left' : 'photo-right'; |
| 183 | + if (i === 1) img.style.right = '-85px'; |
| 184 | + if (i === 2) img.style.right = '-170px'; |
| 185 | + if (p.photo) img.src = p.photo; |
| 186 | + img.alt = p.name; |
| 187 | + div.appendChild(img); |
| 188 | + }); |
| 189 | + } else if (["infra","algo","analysis"].includes(key)) { |
| 190 | + const imgSize = 75; |
| 191 | + const gap = 8; |
| 192 | + const count = names.length; |
| 193 | + const groupWidth = count * imgSize + Math.max(0, count - 1) * gap; |
| 194 | + const nodeW = div.offsetWidth; |
| 195 | + const startX = Math.max(0, Math.round((nodeW - groupWidth) / 2)); |
| 196 | + names.forEach((p,i)=>{ |
| 197 | + const img = document.createElement('img'); |
| 198 | + img.className = 'photo-below'; |
| 199 | + img.style.left = `${startX + i * (imgSize + gap)}px`; |
| 200 | + if (p.photo) img.src = p.photo; |
| 201 | + img.alt = p.name; |
| 202 | + div.appendChild(img); |
| 203 | + }); |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +/* Render a node */ |
| 208 | +function mkNode(key){ |
| 209 | + const {left, top, title, type} = positions[key]; |
| 210 | + const div = document.createElement('div'); |
| 211 | + div.className = `node ${type}`; |
| 212 | + div.style.left = `${left}px`; |
| 213 | + div.style.top = `${top}px`; |
| 214 | + |
| 215 | + const box = document.createElement('div'); |
| 216 | + box.className = 'node-box'; |
| 217 | + |
| 218 | + const strong = document.createElement('strong'); |
| 219 | + strong.textContent = title; |
| 220 | + box.appendChild(strong); |
| 221 | + |
| 222 | + const names = (layoutMap[key]||[]).filter(Boolean); |
| 223 | + |
| 224 | + names.forEach(p => { |
| 225 | + const instShort = instShortByPerson[p.shortname] || p.institution || ""; |
| 226 | + const nameLine = document.createElement('div'); |
| 227 | + nameLine.className = 'person-line'; |
| 228 | + nameLine.textContent = instShort ? `${p.name} (${instShort})` : p.name; |
| 229 | + box.appendChild(nameLine); |
| 230 | + |
| 231 | + if (showTitleForShort.has(p.shortname) && p.title) { |
| 232 | + const t = document.createElement('div'); |
| 233 | + t.className = 'title-line'; |
| 234 | + t.textContent = p.title; |
| 235 | + box.appendChild(t); |
| 236 | + } |
| 237 | + }); |
| 238 | + |
| 239 | + div.appendChild(box); |
| 240 | + chart.appendChild(div); |
| 241 | + addPhotos(key, div, names); |
| 242 | + |
| 243 | + return div; |
| 244 | +} |
| 245 | + |
| 246 | +/* Build nodes */ |
| 247 | +const nodes = {}; |
| 248 | +Object.keys(positions).forEach(k => { nodes[k]=mkNode(k); }); |
| 249 | + |
| 250 | +/* Connectors */ |
| 251 | +const canvas = document.getElementById('connectorCanvas'); |
| 252 | +const ctx = canvas.getContext('2d'); |
| 253 | +ctx.lineWidth = 2; ctx.strokeStyle = '#000'; |
| 254 | + |
| 255 | +function rect(el){ const r = el.getBoundingClientRect(), p = canvas.getBoundingClientRect(); return { |
| 256 | + left:r.left-p.left, right:r.right-p.left, top:r.top-p.top, bottom:r.bottom-p.top, width:r.width, height:r.height |
| 257 | +};} |
| 258 | +function center(el){ const r=rect(el); return {x:r.left+r.width/2,y:r.top+r.height/2};} |
| 259 | +function topCenter(el){ const r=rect(el); return {x:r.left+r.width/2,y:r.top};} |
| 260 | +function bottomCenter(el){ const r=rect(el); return {x:r.left+r.width/2,y:r.bottom};} |
| 261 | +function line(x1,y1,x2,y2){ ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke(); } |
| 262 | +function setLeftByCenter(nodeEl, targetCenterX){ |
| 263 | + const r = rect(nodeEl); |
| 264 | + nodeEl.style.left = `${Math.round(targetCenterX - r.width/2)}px`; |
| 265 | +} |
| 266 | + |
| 267 | +window.onload = () => { |
| 268 | + const {management, facilities, software, operations, hllhc, infra, algo, analysis} = nodes; |
| 269 | + |
| 270 | + const trunkX = (center(facilities).x + center(operations).x) / 2; |
| 271 | + setLeftByCenter(management, trunkX); |
| 272 | + |
| 273 | + const hCenter = center(hllhc).x; |
| 274 | + const smallGap = 220; |
| 275 | + const centers = [hCenter - smallGap, hCenter, hCenter + smallGap]; |
| 276 | + [infra, algo, analysis].forEach((el, i) => setLeftByCenter(el, centers[i])); |
| 277 | + |
| 278 | + ctx.clearRect(0,0,canvas.width,canvas.height); |
| 279 | + |
| 280 | + const yConnector = center(software).y; |
| 281 | + line(bottomCenter(management).x, bottomCenter(management).y, bottomCenter(management).x, yConnector); |
| 282 | + |
| 283 | + line(center(facilities).x, center(facilities).y, center(operations).x, center(operations).y); |
| 284 | + line(center(software).x, center(software).y, center(hllhc).x, center(hllhc).y); |
| 285 | + |
| 286 | + const barY = topCenter(infra).y - 20; |
| 287 | + line(center(hllhc).x, bottomCenter(hllhc).y, center(hllhc).x, barY); |
| 288 | + |
| 289 | + [infra, algo, analysis].forEach(node=>{ |
| 290 | + line(center(hllhc).x, barY, center(node).x, barY); |
| 291 | + line(center(node).x, barY, topCenter(node).x, topCenter(node).y); |
| 292 | + }); |
| 293 | +}; |
| 294 | +</script> |
| 295 | +</body> |
| 296 | +</html> |
87 | 297 |
|
88 | 298 | <h1>Full Team</h1> |
89 | 299 | <p><br /></p> |
@@ -124,6 +334,21 @@ <h1>Full Team</h1> |
124 | 334 | </div> |
125 | 335 |
|
126 | 336 |
|
| 337 | + <div class="card" style="width: 12rem;"> |
| 338 | + <img class="card-img-top" src="/assets/images/team/Robert-Tuck.jpg" alt="Card image cap" /> |
| 339 | + <div class="card-body d-flex flex-column"> |
| 340 | + <div class="card-text"> |
| 341 | + |
| 342 | + <b>Robert Tuck</b><br /> |
| 343 | + |
| 344 | + <small>Princeton University</small><br /><br /> |
| 345 | + </div> |
| 346 | + <div class="card-text mt-auto"><i><p>Associate Project Manager</p> |
| 347 | +</i><br /></div> |
| 348 | + </div> |
| 349 | +</div> |
| 350 | + |
| 351 | + |
127 | 352 |
|
128 | 353 |
|
129 | 354 |
|
@@ -351,25 +576,6 @@ <h1>Full Team</h1> |
351 | 576 |
|
352 | 577 |
|
353 | 578 |
|
354 | | - |
355 | | - |
356 | | - |
357 | | - <div class="card" style="width: 12rem;"> |
358 | | - <img class="card-img-top" src="/assets/images/team/Robert-Tuck.jpg" alt="Card image cap" /> |
359 | | - <div class="card-body d-flex flex-column"> |
360 | | - <div class="card-text"> |
361 | | - |
362 | | - <b>Robert Tuck</b><br /> |
363 | | - |
364 | | - <small>Princeton University</small><br /><br /> |
365 | | - </div> |
366 | | - <div class="card-text mt-auto"><i><p>Associate Project Manager</p> |
367 | | -</i><br /></div> |
368 | | - </div> |
369 | | -</div> |
370 | | - |
371 | | - |
372 | | - |
373 | 579 | </div> |
374 | 580 | </div> |
375 | 581 |
|
|
0 commit comments