mirror of
https://github.com/asimonson1125/asimonson1125.github.io.git
synced 2026-09-22 11:35:33 -05:00
giving fable a shot
This commit is contained in:
14
src/app.py
14
src/app.py
@@ -1,6 +1,7 @@
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
import flask
|
||||
from flask_minify import Minify
|
||||
@@ -71,13 +72,26 @@ projects = load_json("./static/json/projects.json")
|
||||
books = load_json("./static/json/books.json")
|
||||
skills = load_json("./static/json/skills.json")
|
||||
pages = load_json("./static/json/pages.json")
|
||||
features = load_json("./static/json/features.json")
|
||||
|
||||
pages['about']['skillList'] = skills
|
||||
pages['projects']['projects'] = projects
|
||||
pages['projects']['features'] = features
|
||||
pages['home']['features'] = features
|
||||
pages['home']['books'] = books
|
||||
pages['home']['projects'] = projects
|
||||
pages['books']['books'] = books
|
||||
pages['status']['services'] = SERVICES
|
||||
|
||||
for _name, _page in pages.items():
|
||||
_page['id'] = _name
|
||||
|
||||
|
||||
@app.template_filter('slug')
|
||||
def slug(value):
|
||||
value = re.sub(r'[^a-z0-9]+', '-', str(value).lower())
|
||||
return value.strip('-')
|
||||
|
||||
|
||||
# ── Error rendering ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,8 @@ class Ball {
|
||||
function setup() {
|
||||
frameRate(15);
|
||||
const pixels = screenHeight * screenWidth;
|
||||
createCanvas(screenWidth, screenHeight);
|
||||
const canvas = createCanvas(screenWidth, screenHeight);
|
||||
canvas.parent('bg');
|
||||
for (let i = 0; i < pixels * density; i++) {
|
||||
balls.push(new Ball(
|
||||
random(screenWidth),
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
function toggleMenu(collapse) {
|
||||
if (window.innerWidth < 1400) {
|
||||
const menu = document.querySelector(".navControl");
|
||||
const bar = document.querySelector(".header");
|
||||
const header = document.getElementById("siteHeader");
|
||||
const btn = document.getElementById("menu");
|
||||
const isCollapsed = !menu.style.maxHeight || menu.style.maxHeight === "0px";
|
||||
|
||||
if (isCollapsed && !collapse) {
|
||||
menu.style.maxHeight = `${menu.scrollHeight + 10}px`;
|
||||
bar.style.borderBottomWidth = "0px";
|
||||
if (btn) btn.setAttribute("aria-expanded", "true");
|
||||
} else {
|
||||
menu.style.maxHeight = "0px";
|
||||
bar.style.borderBottomWidth = "3px";
|
||||
if (btn) btn.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
}
|
||||
if (!header) return;
|
||||
const open = collapse ? false : !header.classList.contains("open");
|
||||
header.classList.toggle("open", open);
|
||||
if (btn) btn.setAttribute("aria-expanded", open ? "true" : "false");
|
||||
}
|
||||
|
||||
async function goto(location, { push = true } = {}) {
|
||||
function markCurrentPage(location) {
|
||||
document.querySelectorAll(".site-nav a[data-page]").forEach(function (link) {
|
||||
if (link.dataset.page === location) {
|
||||
link.setAttribute("aria-current", "page");
|
||||
} else {
|
||||
link.removeAttribute("aria-current");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function goto(location, { push = true, hash = "" } = {}) {
|
||||
const loadingBar = document.getElementById('loading-bar');
|
||||
|
||||
if (loadingBar) {
|
||||
@@ -61,18 +61,20 @@ async function goto(location, { push = true } = {}) {
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
});
|
||||
|
||||
if (window.location.href.includes("#")) {
|
||||
const id = decodeURIComponent(window.location.hash.substring(1));
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.scrollIntoView();
|
||||
const target = hash || (push ? "" : decodeURIComponent(window.location.hash.substring(1)));
|
||||
const targetEl = target ? document.getElementById(target) : null;
|
||||
if (targetEl) {
|
||||
revealTarget(targetEl);
|
||||
targetEl.scrollIntoView({ behavior: "instant", block: "start" });
|
||||
} else {
|
||||
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
||||
}
|
||||
|
||||
toggleMenu(true);
|
||||
markCurrentPage(location);
|
||||
document.querySelector("title").textContent = metadata["title"];
|
||||
if (push) {
|
||||
history.pushState(null, null, metadata["canonical"]);
|
||||
history.pushState(null, null, metadata["canonical"] + (hash ? "#" + hash : ""));
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
@@ -93,15 +95,64 @@ async function goto(location, { push = true } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function backButton() {
|
||||
const path = window.location.pathname;
|
||||
goto(path.substring(1), { push: false });
|
||||
// Featured project panels: collapsed to a short peek until expanded.
|
||||
function setFeatureExpanded(feature, expanded) {
|
||||
const clips = feature.querySelectorAll(".feature-clip");
|
||||
const btn = feature.querySelector(".feature-expand");
|
||||
if (expanded) {
|
||||
feature.classList.add("expanded");
|
||||
clips.forEach(function (el) {
|
||||
el.style.maxHeight = el.scrollHeight + "px";
|
||||
});
|
||||
// Once open, let the regions size themselves so late-loading content is never clipped.
|
||||
setTimeout(function () {
|
||||
if (!feature.classList.contains("expanded")) return;
|
||||
clips.forEach(function (el) {
|
||||
el.style.maxHeight = "none";
|
||||
});
|
||||
}, 400);
|
||||
} else {
|
||||
// Pin the current heights, force a reflow, then let CSS animate down to the peek.
|
||||
clips.forEach(function (el) {
|
||||
el.style.maxHeight = el.scrollHeight + "px";
|
||||
});
|
||||
void feature.offsetHeight;
|
||||
feature.classList.remove("expanded");
|
||||
clips.forEach(function (el) {
|
||||
el.style.maxHeight = "";
|
||||
});
|
||||
}
|
||||
if (btn) btn.setAttribute("aria-expanded", expanded ? "true" : "false");
|
||||
}
|
||||
|
||||
function toggleFeatureProject(header) {
|
||||
const panel = header.closest(".feature-project");
|
||||
const expanded = panel.classList.toggle("expanded");
|
||||
header.setAttribute("aria-expanded", expanded ? "true" : "false");
|
||||
function toggleFeature(btn) {
|
||||
const feature = btn.closest(".feature");
|
||||
if (feature) setFeatureExpanded(feature, !feature.classList.contains("expanded"));
|
||||
}
|
||||
|
||||
// Expand a collapsed panel (or the one containing the target) before jumping to it.
|
||||
function revealTarget(el) {
|
||||
const feature = el.closest(".feature");
|
||||
if (feature && !feature.classList.contains("expanded")) {
|
||||
setFeatureExpanded(feature, true);
|
||||
feature.querySelectorAll(".feature-clip").forEach(function (el) {
|
||||
el.style.maxHeight = "none";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
if (!window.location.hash) return;
|
||||
const el = document.getElementById(decodeURIComponent(window.location.hash.substring(1)));
|
||||
if (el) {
|
||||
revealTarget(el);
|
||||
el.scrollIntoView({ behavior: "instant", block: "start" });
|
||||
}
|
||||
});
|
||||
|
||||
function backButton() {
|
||||
const path = window.location.pathname;
|
||||
goto(path.substring(1) || "home", { push: false });
|
||||
}
|
||||
|
||||
function activeSkill(obj) {
|
||||
|
||||
@@ -22,14 +22,14 @@ function updateStatusDisplay(data) {
|
||||
if (data.last_check) {
|
||||
const lastCheck = new Date(data.last_check);
|
||||
const lastUpdateEl = document.getElementById('lastUpdate');
|
||||
if (lastUpdateEl) lastUpdateEl.textContent = `Last checked: ${lastCheck.toLocaleString()}`;
|
||||
if (lastUpdateEl) lastUpdateEl.textContent = lastCheck.toLocaleString();
|
||||
}
|
||||
|
||||
if (data.next_check) {
|
||||
const nextCheckEl = document.getElementById('nextUpdate');
|
||||
if (nextCheckEl) {
|
||||
const nextCheck = new Date(data.next_check);
|
||||
nextCheckEl.textContent = `Next check: ${nextCheck.toLocaleString()}`;
|
||||
nextCheckEl.textContent = nextCheck.toLocaleString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ function updateStatusDisplay(data) {
|
||||
const refreshBtn = document.getElementById('refreshBtn');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.disabled = false;
|
||||
refreshBtn.textContent = 'Refresh Now';
|
||||
refreshBtn.textContent = 'Refresh now';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ function updateServiceCard(service) {
|
||||
formatUptime(service.uptime['7d'], '7d'),
|
||||
formatUptime(service.uptime['30d'], '30d'),
|
||||
formatUptime(service.uptime.all_time, 'All'),
|
||||
].join(' | ');
|
||||
].join(' · ');
|
||||
}
|
||||
|
||||
if (checksDisplay && service.total_checks !== undefined) {
|
||||
@@ -188,9 +188,9 @@ function showError(message) {
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'status-error';
|
||||
errorDiv.textContent = message;
|
||||
errorDiv.style.cssText = 'background: rgba(244, 67, 54, 0.2); color: #f44336; padding: 1em; margin: 1em 0; border-radius: 0.5em; text-align: center;';
|
||||
|
||||
const container = document.querySelector('.foregroundContent');
|
||||
|
||||
const container = document.querySelector('.page');
|
||||
if (container) {
|
||||
container.insertBefore(errorDiv, container.firstChild);
|
||||
setTimeout(function() { errorDiv.remove(); }, 5000);
|
||||
@@ -201,7 +201,7 @@ function refreshStatus() {
|
||||
const refreshBtn = document.getElementById('refreshBtn');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.disabled = true;
|
||||
refreshBtn.textContent = 'Checking...';
|
||||
refreshBtn.textContent = 'Checking';
|
||||
}
|
||||
fetchStatus();
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
{
|
||||
"selection": [
|
||||
"The Rational Optimist",
|
||||
"The End of the World is Just the Beginning",
|
||||
"When to Rob a Bank",
|
||||
"Project Hail Mary",
|
||||
"Freakonomics",
|
||||
"The Accidental Superpower",
|
||||
"Verbal Judo",
|
||||
"Zero To One"
|
||||
"The Martian"
|
||||
],
|
||||
"books": {
|
||||
"Fooled By Randomness": {
|
||||
"filename": "fooledbyrandomness.jpg",
|
||||
"link": "https://www.amazon.com/Fooled-Randomness-Hidden-Chance-Markets-dp-B006Q7VYC4/dp/B006Q7VYC4/ref=dp_ob_title_bk",
|
||||
"review": "A lengthy compendium on probabilistic reasoning that helped kick off a curiosity of indefinite computation. There's more ancient philosophy than a book like this really needs but the occasional brazen punchline from the contemporary anecdotes make it bearable."
|
||||
"Project Hail Mary": {
|
||||
"filename": "hailmary.jpg",
|
||||
"link": "https://www.amazon.com/Project-Hail-Mary-Andy-Weir/dp/0593135202",
|
||||
"review": "This must be the first hard-sci-fi book I have read and I absolutely loved it. When I watched the movie I concluded that both executed on an inspiring story with an endearing tone but only the book held the creative practicality that really made it a favorite."
|
||||
},
|
||||
"The Martian": {
|
||||
"filename": "themartian.jpg",
|
||||
"link": "https://www.amazon.com/Martian-Andy-Weir/dp/0553418025",
|
||||
"review": "I picked this up after Project Hail Mary looking for more creative uses of hard scifi. On that account The Martian surpassed expectations - partially because the plot didn't have to contend with fantasy elements. I aspire to one day use a radioactive space heater as a hot tub power source, just as my hero, Mark Watney, once did."
|
||||
},
|
||||
"The Rational Optimist": {
|
||||
"filename": "ratOpt.jpg",
|
||||
@@ -39,6 +42,11 @@
|
||||
"link": "https://freakonomics.com/books/",
|
||||
"review": "More like the other Freakonomics books than I expected (cracked storytelling), which is still excellent, but I wished there was greater insights into seeing past conventional wisdom, which is what thinking like a freak means. Still a great book."
|
||||
},
|
||||
"Fooled By Randomness": {
|
||||
"filename": "fooledbyrandomness.jpg",
|
||||
"link": "https://www.amazon.com/Fooled-Randomness-Hidden-Chance-Markets-dp-B006Q7VYC4/dp/B006Q7VYC4/ref=dp_ob_title_bk",
|
||||
"review": "A lengthy compendium on probabilistic reasoning that helped kick off a curiosity of indefinite computation. There's more ancient philosophy than a book like this really needs but the occasional brazen punchline from the contemporary anecdotes make it bearable."
|
||||
},
|
||||
"The Tyranny of Metrics": {
|
||||
"filename": "TyrannyOfMetrics.jpg",
|
||||
"link": "https://www.amazon.com/Tyranny-Metrics-Jerry-Z-Muller/dp/0691174954",
|
||||
@@ -144,11 +152,6 @@
|
||||
"link": "https://www.goodreads.com/book/show/8034188-where-good-ideas-come-from",
|
||||
"review": "I got this book at a recycling center. I didn't want to read it or like it. Unfortnuately, it's pretty good. 200 pages of considerate review of how innovation comes to be + suggestions to expand the utility of your ideas (I've adopted several!)"
|
||||
},
|
||||
"Make Your Bed": {
|
||||
"filename": "makeYourBed.jpg",
|
||||
"link": "https://www.amazon.com/Make-Your-Bed-Little-Things/dp/1455570249",
|
||||
"review": "Something small to read on a rainy day or flight. Valuable advice condensed into personal stories that stretch beyond anecdotes."
|
||||
},
|
||||
"12 Rules for Life": {
|
||||
"filename": "12RulesForLife.jpg",
|
||||
"link": "https://www.amazon.com/12-Rules-Life-Antidote-Chaos/dp/0345816021/",
|
||||
@@ -163,6 +166,11 @@
|
||||
"filename": "HitchhikersGuideToTheGalaxy.jpeg",
|
||||
"link": "https://www.amazon.com/Hitchhikers-Guide-Galaxy-Douglas-Adams/dp/0345418913",
|
||||
"review": "It's alright. It felt like an aimless journey without defined boundaries that reveled in that fact for irony and wit points."
|
||||
},
|
||||
"The Adventures of Sherlock Holmes": {
|
||||
"filename": "sherlockholmes.jpg",
|
||||
"link": "https://www.amazon.com/Adventures-Sherlock-Holmes-Arthur-Conan/dp/0141034332",
|
||||
"review": "Had there not been half a dozen TV shows and movies about Sherlock Holmes all pulling from the same source material, I might have been intrigued. Instead, I felt like I was proofreading an old English fan rewrite of a book that I had already read. By today's standards, the outcomes were very telegraphed. Still enjoyed thinking through the deductions, though."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/static/json/features.json
Normal file
16
src/static/json/features.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"homelab": {
|
||||
"title": "AI Homelab and Self Hosted Web Tooling",
|
||||
"tagline": "A self-hosted cluster serving Mixture-of-Experts models beyond their rated VRAM and independent web services",
|
||||
"body": "<p>I refined a method to pool GPUs from different vendors and generations into a single inference backend of roughly 20 GB over Vulkan. FreeToken, a bleeding-edge MoE inference server, fronts the cluster and handles the elastic inference, running Mixture-of-Experts models with far more total parameters than the cluster's VRAM would normally allow. My own scripts handle KV caching and system-prompt compaction, which is what makes the setup harness-capable.</p><p>The same rig trains as well as it serves, having refined Qwen SLMs and diffusion image models. All this is hosted alongside my personal web assets, like this website. I voluntarily subject myself to the maintenance of production-grade systems on every level of the tech stack to ensure that I survive the Big Tech apocalypse.</p>",
|
||||
"stack": ["FreeToken", "ComfyUI", "PEFT / TRL", "Docker", "Cloudflare", "Reverse Proxy"],
|
||||
"image": "photos/deepdives/homelab-dashboard.png",
|
||||
"caption": "Cluster dashboard during a long inference run."
|
||||
},
|
||||
"capstone": {
|
||||
"title": "Subatomic Particle Tracing and Anomaly Control",
|
||||
"tagline": "My Master's Capstone: Building a Diffusion Cloud Chamber and using stereo reconstruction to classify Muons and Alpha/Beta particles.",
|
||||
"body": "<p>My ongoing Data Science Capstone Project combines existing utilities for cheap quantum demonstrations and real world measuring techniques to create effective sensing of subatomic phenomena without breaking the bank or into the IAEA</p>",
|
||||
"stack": ["Ego"]
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"status": {
|
||||
"template": "status.html",
|
||||
"title":"Andrew Simonson - Status Page",
|
||||
"title": "Andrew Simonson - Status Page",
|
||||
"description": "Status page for my services",
|
||||
"canonical": "/status"
|
||||
},
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"duck": {
|
||||
"template": "duck.html",
|
||||
"title":"You've been ducked!",
|
||||
"title": "You've been ducked!",
|
||||
"description": "Face it, you've been ducked",
|
||||
"canonical": "/duck"
|
||||
},
|
||||
@@ -38,31 +38,31 @@
|
||||
"ecolab": {
|
||||
"title": "Data Scientist / Data Engineer — Ecolab",
|
||||
"date": "January 2024 – Present",
|
||||
"content": "<strong>Data Scientist / Data Engineer</strong><br/>Ecolab · Saint Paul, MN<br/><br/>• Primary model developer of RushReady from inception through multimillion-dollar product commercialization<br/>• Engineered near-real-time data platform with Delta Live Tables and automated MLOps lifecycle across QSR brands<br/>• Built explainable anomaly detection with XGBoost/SHAP attribution and brand-configurable recommendations, increasing speed of service by 20%<br/>• Represented Ecolab as liaison in Microsoft's 100-member AI accelerator program",
|
||||
"content": "<p class=\"timeitem-org\">Ecolab · Saint Paul, MN</p><ul><li>Primary model developer of RushReady from inception through multimillion-dollar product commercialization</li><li>Engineered near-real-time data platform with Delta Live Tables and automated MLOps lifecycle across QSR brands</li><li>Built explainable anomaly detection with XGBoost/SHAP attribution and brand-configurable recommendations, increasing speed of service by 20%</li><li>Represented Ecolab as liaison in Microsoft's 100-member AI accelerator program</li></ul>",
|
||||
"classes": "experience"
|
||||
},
|
||||
"rit_ms": {
|
||||
"title": "M.S. Data Science — RIT",
|
||||
"date": "Dec 2026 (expected)",
|
||||
"content": "<strong>Master of Science in Data Science</strong><br/>Rochester Institute of Technology · Rochester, NY<br/><br/>• Focus on probability theory, statistical learning, and Bayesian methods<br/>• Capstone: Fall 2026",
|
||||
"content": "<p class=\"timeitem-org\">Rochester Institute of Technology · Rochester, NY</p><ul><li>Focus on probability theory, statistical learning, and Bayesian methods</li><li>Capstone: Fall 2026</li></ul>",
|
||||
"classes": "education"
|
||||
},
|
||||
"dow_chemical": {
|
||||
"title": "Data Engineer — Dow Chemical",
|
||||
"date": "January 2023 – May 2023",
|
||||
"content": "<strong>Data Engineer</strong><br/>Dow Chemical · Freeport, TX<br/><br/>• Independently built reactive chemistry analysis Flask app encoding adiabatic correction and exotherm-detection logic across 9 test types<br/>• Architected object-model abstraction, decoupling test-type parsers from report generation and replacing 4 legacy VBA tools<br/>• Product became the interdepartmental data standard, saving >1,020 hours (~0.65 FTE) annually",
|
||||
"content": "<p class=\"timeitem-org\">Dow Chemical · Freeport, TX</p><ul><li>Independently built reactive chemistry analysis Flask app encoding adiabatic correction and exotherm-detection logic across 9 test types</li><li>Architected object-model abstraction, decoupling test-type parsers from report generation and replacing 4 legacy VBA tools</li><li>Product became the interdepartmental data standard, saving >1,020 hours (~0.65 FTE) annually</li></ul>",
|
||||
"classes": "experience"
|
||||
},
|
||||
"rit_bs": {
|
||||
"title": "B.S. Computer Science & Data Science — RIT",
|
||||
"date": "Dec 2024",
|
||||
"content": "<strong>B.S. in Computer Science · B.S. in Data Science (Dual Cluster)</strong><br/>Rochester Institute of Technology · Rochester, NY<br/><br/>• GPA 3.63 · Dean's List<br/>• Minor in International Relations<br/>• MicroMasters in Data Science – UC San Diego (edX)",
|
||||
"content": "<p class=\"timeitem-org\">Rochester Institute of Technology · Rochester, NY</p><ul><li>GPA 3.63 · Dean's List</li><li>Minor in International Relations</li><li>MicroMasters in Data Science – UC San Diego (edX)</li></ul>",
|
||||
"classes": "education"
|
||||
},
|
||||
"csh": {
|
||||
"title": "E-Board Member & Presenter — CSH",
|
||||
"date": "Aug 2021 – Present",
|
||||
"content": "<strong>E-Board Member & Presenter</strong><br/>Computer Science House · Rochester, NY<br/><br/>• Presented seminars on web scraping, analytics, and GIS<br/>• Built and maintained web services for 80+ members",
|
||||
"content": "<p class=\"timeitem-org\">Computer Science House · Rochester, NY</p><ul><li>Presented seminars on web scraping, analytics, and GIS</li><li>Built and maintained web services for 80+ members</li></ul>",
|
||||
"classes": "experience technical"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,28 +25,28 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
"PsyCom - Physical Combinatorics": {
|
||||
"PhysCom - Physical Combinatorics": {
|
||||
"status": "WIP",
|
||||
"classes": "programming",
|
||||
"content": "Experimental innovation engine operating on physical attributes and limitations of proven existing technologies with further AI review."
|
||||
},
|
||||
"Antietam-Conococheague Watershed Monitoring": {
|
||||
"status": "complete",
|
||||
"classes": "geospacial",
|
||||
"classes": "geospatial",
|
||||
"bgi": "watershedTemps.png",
|
||||
"content": "Live geospacial analysis of Maryland's Antietam and Conococheague sub-watersheds, monitoring water quality and temperatures through the summer months for governmental environment health review boards."
|
||||
"content": "Live geospatial analysis of Maryland's Antietam and Conococheague sub-watersheds, monitoring water quality and temperatures through the summer months for governmental environment health review boards."
|
||||
},
|
||||
"Automotive Brand Valuation Analysis": {
|
||||
"status": "complete",
|
||||
"classes": "programming",
|
||||
"bgi": "automotiveBrandAnalysis.png",
|
||||
"content": "Brand valuation analysis of the used car market, measuring value decay by mileage to extrapolate qualities such as percieved reliability and persistent value of luxury features."
|
||||
"content": "Brand valuation analysis of the used car market, measuring value decay by mileage to extrapolate qualities such as perceived reliability and persistent value of luxury features."
|
||||
},
|
||||
"RIT Hotspots": {
|
||||
"status": "incomplete",
|
||||
"classes": "pinned geospacial programming",
|
||||
"classes": "pinned geospatial programming",
|
||||
"bgi": "hotspotsrit.png",
|
||||
"content": "Live crowd migration map using RIT occupancy data. It seems RIT didn't like me exposing their surveilance state but since they didn't want to talk to me about it they instead changed the service response schema a few times. When that didn't stop me they just shut down the whole service. Nerds.",
|
||||
"content": "Live crowd migration map using RIT occupancy data. It seems RIT didn't like me exposing their surveillance state but since they didn't want to talk to me about it they instead changed the service response schema a few times. When that didn't stop me they just shut down the whole service. Nerds.",
|
||||
"links": [
|
||||
["github", "https://github.com/asimonson1125/hotspotsrit", "git repo"]
|
||||
]
|
||||
@@ -55,14 +55,14 @@
|
||||
"status": "complete",
|
||||
"classes": "pinned programming",
|
||||
"bgi": "calorimeterAnalysis.png",
|
||||
"content": "An analytical toolkit designed for reactive chemistry analysis, especially calorimetry. Works include automatic analysis, alerting unusual and dangerous results derived from a wide range of testing envrionments and equipment",
|
||||
"content": "An analytical toolkit designed for reactive chemistry analysis, especially calorimetry. Works include automatic analysis, alerting unusual and dangerous results derived from a wide range of testing environments and equipment",
|
||||
"links": []
|
||||
},
|
||||
"Geography of Alternative Energy": {
|
||||
"status": "complete",
|
||||
"classes": "pinned geospacial",
|
||||
"classes": "pinned geospatial",
|
||||
"bgi": "energyGeography.png",
|
||||
"content": "An ArcGIS geospacial analysis comparing the difference in effectiveness of wind, solar, and geothermal energy across the continental 48 United States.",
|
||||
"content": "An ArcGIS geospatial analysis comparing the difference in effectiveness of wind, solar, and geothermal energy across the continental 48 United States.",
|
||||
"links": [
|
||||
[
|
||||
"globe",
|
||||
|
||||
BIN
src/static/photos/books/hailmary.jpg
Normal file
BIN
src/static/photos/books/hailmary.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
BIN
src/static/photos/books/sherlockholmes.jpg
Normal file
BIN
src/static/photos/books/sherlockholmes.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
src/static/photos/books/themartian.jpg
Normal file
BIN
src/static/photos/books/themartian.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
src/static/photos/extradimensional.avif
Normal file
BIN
src/static/photos/extradimensional.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 259 KiB |
BIN
src/static/photos/omnidimensional.avif
Normal file
BIN
src/static/photos/omnidimensional.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 572 KiB |
BIN
src/static/photos/test.png
Normal file
BIN
src/static/photos/test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 649 KiB |
@@ -1,211 +1,132 @@
|
||||
{% block content %}
|
||||
<div class="foreground"></div>
|
||||
<div class="relative growBottom">
|
||||
<div id="nametag" class="flex" data-aos="fade-up">
|
||||
<div>
|
||||
<h1 class="textGrad">About</h1>
|
||||
<h2>Bio, timeline & credentials</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="homeSubContent">
|
||||
<div class="foregroundContent">
|
||||
<div class="page">
|
||||
<header class="page-head">
|
||||
<h1 class="display">About</h1>
|
||||
<p class="lede">
|
||||
I'm Andrew Simonson, a data scientist at Ecolab and a graduate data
|
||||
science student at Rochester Institute of Technology, where I recently
|
||||
finished a B.S. in Computer Science with a minor in international
|
||||
relations and a focus on probability theory.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- Bio -->
|
||||
<div style="max-width: 52em; margin-bottom: 3em;">
|
||||
<div class="prose about-bio">
|
||||
<p>
|
||||
I'm Andrew Simonson<!--, CEO of the anti-thermodynamics syndicate.-->,
|
||||
a <strong>Data Scientist at Ecolab</strong> and a graduate Data
|
||||
Science student at
|
||||
<strong>Rochester Institute of Technology</strong>, having
|
||||
recently completed the <b>Computer Science BS</b> program
|
||||
(international relations minor) with a focus on probability
|
||||
theory.
|
||||
<br/>
|
||||
<br/>
|
||||
I get bored and throw random stuff on this website.<br/>
|
||||
This is what unprofessional development looks like.
|
||||
At work I build the models and the pipelines behind them. At home I run
|
||||
the servers. In between, I get bored and put things on this website,
|
||||
self-built and self-hosted as a sandbox for
|
||||
webdev experiments. This is what unprofessional development looks like.
|
||||
</p>
|
||||
<br/>
|
||||
<p style="color: var(--text-secondary); font-size: 0.9rem;">
|
||||
I also have a
|
||||
<a href="Resume_Simonson_Andrew.pdf" target="_blank">resume</a>
|
||||
for unexplained reasons.
|
||||
<p>
|
||||
There is also a <a href="/resume">résumé</a> for paper-minded people.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="concentratedHead">
|
||||
<h3>Career & Education Timeline</h3>
|
||||
<div class="two-col">
|
||||
<section class="section" aria-labelledby="skills-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="skills-heading">Skills</h2>
|
||||
<p class="section-note">Click a group to expand it</p>
|
||||
</div>
|
||||
{% from 'partials/skills.html' import skills %}
|
||||
{{ skills(var['skillList']) }}
|
||||
</section>
|
||||
|
||||
<section class="section" aria-labelledby="certs-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="certs-heading">Certifications</h2>
|
||||
<p class="section-note">
|
||||
Full list on <a href="https://www.linkedin.com/in/simonsonandrew/details/certifications/" rel="noopener noreferrer">LinkedIn</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="timeline-filters" role="toolbar" aria-label="Filter timeline entries">
|
||||
<button class="filter-btn active" data-filter="all" aria-pressed="true">All</button>
|
||||
<button class="filter-btn" data-filter="experience" aria-pressed="false">Experience</button>
|
||||
<button class="filter-btn" data-filter="education" aria-pressed="false">Education</button>
|
||||
<button class="filter-btn" data-filter="technical" aria-pressed="false">Technical</button>
|
||||
<div class="cert-group">
|
||||
<h3>Data Science MicroMasters</h3>
|
||||
<p class="cert-provider">UC San Diego on edX ·
|
||||
<a href="http://credentials.edx.org/credentials/4b7e78dca8154c0d88ca9abc5aedb4ac" rel="noopener noreferrer">program certificate</a>
|
||||
</p>
|
||||
<ul class="cert-list">
|
||||
<li><a href="https://courses.edx.org/certificates/b6deccc56e5344ae84cb55f9ad81fd79" rel="noopener noreferrer">Python for Data Science</a> <span class="cert-code">DSE200x</span></li>
|
||||
<li><a href="https://courses.edx.org/certificates/f29d0e65fc024c6e95121619e329a286" rel="noopener noreferrer">Probability and Statistics in Data Science using Python</a> <span class="cert-code">DSE210x</span></li>
|
||||
<li><a href="https://courses.edx.org/certificates/cccc2bd2ed61470e8492d6da1be530c5" rel="noopener noreferrer">Machine Learning Fundamentals</a> <span class="cert-code">DSE220x</span></li>
|
||||
<li><a href="https://courses.edx.org/certificates/4dfd6563a1f84caaa8922a02a5125f29" rel="noopener noreferrer">Big Data Analytics Using Spark</a> <span class="cert-code">DSE230x</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="timeline" id="timeline">
|
||||
<div class="cert-group">
|
||||
<h3>Independent</h3>
|
||||
<ul class="cert-list">
|
||||
<li><a href="https://files.asimonson.com/u/2398_3_1303226_1776777828_Databricks%20-%20Generic.pdf" rel="noopener noreferrer">Machine Learning Model Deployment</a> <span class="cert-code">Databricks</span></li>
|
||||
<li><a href="https://files.asimonson.com/u/2662_3_1303226_1772561098_Databricks%20-%20Generic.pdf" rel="noopener noreferrer">Building Retrieval Agents on Databricks</a> <span class="cert-code">Databricks</span></li>
|
||||
<li><a href="https://files.asimonson.com/u/2403_3_1303226_1765822061_Databricks%20-%20Generic.pdf" rel="noopener noreferrer">Machine Learning Operations</a> <span class="cert-code">Databricks</span></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/2cb69378c606fec5a6f3a107b99a896862db392b7a3692f71a6b53af5d5545c5" rel="noopener noreferrer">Career Essentials in Data Analysis</a> <span class="cert-code">Microsoft</span></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/7facc28a13405134b3b7fa785303e9b1cf697f32d67f759e89960fbdc8a044d9" rel="noopener noreferrer">Career Essentials in GitHub</a> <span class="cert-code">GitHub</span></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/7b952323152e258ca468c33ddc9ebcf3c55036f58a5cfb3fb9c1410da655aaa5" rel="noopener noreferrer">Docker Foundations</a> <span class="cert-code">Docker</span></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/7017147ac73af5bc26fdab9b3c43671fb8105a0de59d4689d5f0f71c549c150f" rel="noopener noreferrer">Data Science Foundations: Fundamentals</a> <span class="cert-code">LinkedIn</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="cert-group">
|
||||
<h3>Entrepreneurship</h3>
|
||||
<p class="cert-provider">Rochester Institute of Technology</p>
|
||||
<ul class="cert-list">
|
||||
<li><a href="https://files.asimonson.com/u/designThinkingCert.pdf" rel="noopener noreferrer">Design Thinking</a></li>
|
||||
<li><a href="https://files.asimonson.com/u/ideationCert.pdf" rel="noopener noreferrer">Ideation</a></li>
|
||||
<li><a href="https://files.asimonson.com/u/toolsForInnovatorsCert.pdf" rel="noopener noreferrer">Tools for Innovators</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="section" aria-labelledby="timeline-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="timeline-heading">Timeline</h2>
|
||||
<p class="section-note filters" role="group" aria-label="Filter timeline entries">
|
||||
<button class="filter-btn" data-filter="all" aria-pressed="true" type="button">All</button>
|
||||
<button class="filter-btn" data-filter="experience" aria-pressed="false" type="button">Experience</button>
|
||||
<button class="filter-btn" data-filter="education" aria-pressed="false" type="button">Education</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ol class="timeline" id="timeline">
|
||||
{% for name, item in var['timeline'].items() %}
|
||||
<div class="timeitem" data-categories="{{ item.classes }}">
|
||||
<div class="timeitem-inner boxed">
|
||||
<div class="timeitem-header">
|
||||
<span class="datetext">{{ item.date }}</span>
|
||||
<strong>{{ item.title }}</strong>
|
||||
<li class="timeitem" data-categories="{{ item.classes }}">
|
||||
<p class="timeitem-date">{{ item.date }}</p>
|
||||
<div class="timeitem-body">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<div class="timeitem-content">{{ item.content | safe }}</div>
|
||||
</div>
|
||||
<div class="timeitem-content">
|
||||
{{ item.content | safe }}
|
||||
</div>
|
||||
{% if item.classes %}
|
||||
<div class="timeitem-tags">
|
||||
{% for cls in item.classes.split() %}
|
||||
<span class="tag tag-{{ cls }}">{{ cls | replace('_', ' ') | title }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="timeline-expand-wrap">
|
||||
<button id="timelineExpandBtn" class="timeline-expand-btn" aria-expanded="false">Show more</button>
|
||||
</div>
|
||||
|
||||
<div class="concentratedHead">
|
||||
<h3>Skills & Certifications</h3>
|
||||
</div>
|
||||
|
||||
<div class="skills-certs-grid">
|
||||
|
||||
<div class="certs-col">
|
||||
<p class="page-subtitle">
|
||||
Comprehensive list verifiable on
|
||||
<a href="https://www.linkedin.com/in/simonsonandrew/details/certifications/">LinkedIn</a>
|
||||
</p>
|
||||
|
||||
<div class="boxed cert-group">
|
||||
<p class="cert-group-provider">UCSanDiegoX · edX</p>
|
||||
<h4 class="concentratedHead">Data Science MicroMasters Program</h4>
|
||||
<a href="http://credentials.edx.org/credentials/4b7e78dca8154c0d88ca9abc5aedb4ac" class="cert-program-badge">
|
||||
View Program Certificate ›
|
||||
</a>
|
||||
<ul class="cert-list">
|
||||
<li><a href="https://courses.edx.org/certificates/b6deccc56e5344ae84cb55f9ad81fd79" class="cert-item">DSE200x — Python for Data Science</a></li>
|
||||
<li><a href="https://courses.edx.org/certificates/f29d0e65fc024c6e95121619e329a286" class="cert-item">DSE210x — Probability and Statistics in Data Science using Python</a></li>
|
||||
<li><a href="https://courses.edx.org/certificates/cccc2bd2ed61470e8492d6da1be530c5" class="cert-item">DSE220x — Machine Learning Fundamentals</a></li>
|
||||
<li><a href="https://courses.edx.org/certificates/4dfd6563a1f84caaa8922a02a5125f29" class="cert-item">DSE230x — Big Data Analytics Using Spark</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="boxed cert-group">
|
||||
<h4 class="concentratedHead">One-Off Courses</h4>
|
||||
<ul class="cert-list">
|
||||
<li><a href="https://files.asimonson.com/u/2398_3_1303226_1776777828_Databricks%20-%20Generic.pdf" class="cert-item">Machine Learning Model Deployment by Databricks</a></li>
|
||||
<li><a href="https://files.asimonson.com/u/2662_3_1303226_1772561098_Databricks%20-%20Generic.pdf" class="cert-item">Building Retrieval Agents On Databricks</a></li>
|
||||
<li><a href="https://files.asimonson.com/u/2403_3_1303226_1765822061_Databricks%20-%20Generic.pdf" class="cert-item">Machine Learning Operations by Databricks</a></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/2cb69378c606fec5a6f3a107b99a896862db392b7a3692f71a6b53af5d5545c5" class="cert-item">Career Essentials in Data Analysis by Microsoft</a></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/7facc28a13405134b3b7fa785303e9b1cf697f32d67f759e89960fbdc8a044d9" class="cert-item">Career Essentials in GitHub Professional Certificate</a></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/7b952323152e258ca468c33ddc9ebcf3c55036f58a5cfb3fb9c1410da655aaa5" class="cert-item">Docker Foundations Professional Certificate</a></li>
|
||||
<li><a href="https://www.linkedin.com/learning/certificates/7017147ac73af5bc26fdab9b3c43671fb8105a0de59d4689d5f0f71c549c150f" class="cert-item">Data Science Foundations: Fundamentals</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="boxed cert-group">
|
||||
<p class="cert-group-provider">Rochester Institute of Technology</p>
|
||||
<h4 class="concentratedHead">Entrepreneurial Certifications</h4>
|
||||
<ul class="cert-list">
|
||||
<li><a href="https://files.asimonson.com/u/designThinkingCert.pdf" class="cert-item">Design Thinking Certification</a></li>
|
||||
<li><a href="https://files.asimonson.com/u/ideationCert.pdf" class="cert-item">Ideation Certification</a></li>
|
||||
<li><a href="https://files.asimonson.com/u/toolsForInnovatorsCert.pdf" class="cert-item">Tools for Innovators Certification</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="skills-col">
|
||||
<h4>Technologies</h4>
|
||||
{% from 'partials/skills.html' import skills %} {{
|
||||
skills(var['skillList']) }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ol>
|
||||
<p class="timeline-empty" id="timelineEmpty" hidden>Nothing in this category yet.</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const buttons = document.querySelectorAll('.filter-btn[data-filter]');
|
||||
const items = document.querySelectorAll('.timeitem');
|
||||
const expandBtn = document.getElementById('timelineExpandBtn');
|
||||
const expandWrap = expandBtn ? expandBtn.closest('.timeline-expand-wrap') : null;
|
||||
const timeline = document.getElementById('timeline');
|
||||
const COLLAPSE_LIMIT = 3;
|
||||
const empty = document.getElementById('timelineEmpty');
|
||||
|
||||
var currentFilter = 'all';
|
||||
var expanded = false;
|
||||
|
||||
function applyVisibility() {
|
||||
var shown = 0;
|
||||
var matched = 0;
|
||||
function applyFilter(filter) {
|
||||
let shown = 0;
|
||||
items.forEach(function(item) {
|
||||
var cats = item.getAttribute('data-categories') || '';
|
||||
var matches = currentFilter === 'all' || cats.indexOf(currentFilter) !== -1;
|
||||
if (!matches) {
|
||||
item.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
matched++;
|
||||
if (expanded || shown < COLLAPSE_LIMIT) {
|
||||
item.classList.remove('hidden');
|
||||
shown++;
|
||||
} else {
|
||||
item.classList.add('hidden');
|
||||
}
|
||||
const cats = item.getAttribute('data-categories') || '';
|
||||
const matches = filter === 'all' || cats.split(' ').indexOf(filter) !== -1;
|
||||
item.hidden = !matches;
|
||||
if (matches) shown++;
|
||||
});
|
||||
|
||||
var hasMore = matched > COLLAPSE_LIMIT;
|
||||
|
||||
if (expandBtn) {
|
||||
var remaining = matched - shown;
|
||||
if (!hasMore) {
|
||||
expandBtn.classList.add('hidden');
|
||||
} else {
|
||||
expandBtn.classList.remove('hidden');
|
||||
expandBtn.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
||||
expandBtn.textContent = expanded ? 'Show fewer' : 'Show ' + remaining + ' more ↓';
|
||||
}
|
||||
}
|
||||
|
||||
var overlapping = hasMore && !expanded;
|
||||
|
||||
if (timeline) {
|
||||
timeline.classList.toggle('collapsed', overlapping);
|
||||
}
|
||||
|
||||
if (expandWrap) {
|
||||
expandWrap.classList.toggle('overlap', overlapping);
|
||||
}
|
||||
if (empty) empty.hidden = shown > 0;
|
||||
}
|
||||
|
||||
buttons.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
buttons.forEach(function(b) { b.classList.remove('active'); b.setAttribute('aria-pressed', 'false'); });
|
||||
this.classList.add('active');
|
||||
buttons.forEach(function(b) { b.setAttribute('aria-pressed', 'false'); });
|
||||
this.setAttribute('aria-pressed', 'true');
|
||||
currentFilter = this.getAttribute('data-filter');
|
||||
expanded = false;
|
||||
applyVisibility();
|
||||
applyFilter(this.getAttribute('data-filter'));
|
||||
});
|
||||
});
|
||||
|
||||
if (expandBtn) {
|
||||
expandBtn.addEventListener('click', function() {
|
||||
expanded = !expanded;
|
||||
applyVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
applyVisibility();
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,48 +1,58 @@
|
||||
{% block content %}
|
||||
<div class="foreground"></div>
|
||||
<div class="foregroundContent">
|
||||
<h2>"You can't judge a book by its cover but you can judge a person by their bookshelf" - Me, today.</h2>
|
||||
<h3>My Favorites</h3>
|
||||
<div class='flex wrap boxed' role="list" aria-label="Favorite books">
|
||||
<div class="page">
|
||||
<header class="page-head">
|
||||
<h1 class="display">Bookshelf</h1>
|
||||
<p class="lede">
|
||||
You can't judge a book by its cover, but you can judge a person by their
|
||||
bookshelf. Short reviews of what I've read, favorites first.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section class="section" aria-labelledby="fav-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="fav-heading">Favorites</h2>
|
||||
</div>
|
||||
<ul class="cover-row" aria-label="Favorite books">
|
||||
{% for i in var.books.selection %}
|
||||
<div role="listitem">
|
||||
<a href="#'{{i}}'" aria-label="View review of {{ i }}">
|
||||
<li>
|
||||
<a href="#'{{ i }}'" title="{{ i }}">
|
||||
<img
|
||||
class="bookcover"
|
||||
alt="{{ i }} cover"
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename))}}"
|
||||
alt="{{ i }}"
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename)) }}"
|
||||
loading="lazy"
|
||||
width="80"
|
||||
height="120"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="section" aria-labelledby="reviews-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="reviews-heading">All reviews</h2>
|
||||
<p class="section-note">{{ var.books.books | length }} books</p>
|
||||
</div>
|
||||
<h3>All Reviews</h3>
|
||||
<p>Hover to reveal review<br />Read to reveal KNOWLEDGE</p>
|
||||
<div class="booklist flex wrap" role="list" aria-label="Book reviews">
|
||||
<ul class="review-list">
|
||||
{% for i in var.books.books %}
|
||||
<div id="'{{i}}'" class="bookReview" role="listitem" aria-label="Review of {{ i }}">
|
||||
<div class='fullHeight flex'>
|
||||
<div>
|
||||
<div class='heightBox'>
|
||||
<li id="'{{ i }}'" class="review">
|
||||
<img
|
||||
class="bookcover"
|
||||
alt="{{ i }} cover"
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename))}}"
|
||||
class="review-cover"
|
||||
alt=""
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename)) }}"
|
||||
loading="lazy"
|
||||
width="80"
|
||||
height="120"
|
||||
/>
|
||||
<div class="review-body">
|
||||
<h3>{{ i }}</h3>
|
||||
<p>{{ var.books.books[i].review }}</p>
|
||||
<p class="review-link"><a href="{{ var.books.books[i].link }}" rel="noopener noreferrer">Where to find it</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class='vFlex emPad spaceBetween'>
|
||||
<div>
|
||||
<h4 class='nomargin'>{{ i }}</h4>
|
||||
<p class='nomargin'>{{ var.books.books[i].review }}</p>
|
||||
</div>
|
||||
<a href="{{ var.books.books[i].link }}" rel="noopener noreferrer" aria-label="Buy or learn more about {{ i }}">Book Source</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{% block content %}
|
||||
<div class="foreground"></div>
|
||||
<div class="foregroundContent">
|
||||
<h1>What the duck?</h1>
|
||||
<div class="page">
|
||||
<header class="page-head">
|
||||
<h1 class="display">What the duck?</h1>
|
||||
</header>
|
||||
<img
|
||||
class="duck"
|
||||
alt="mega spinney duck"
|
||||
src="{{ url_for('static', filename='photos/gifs/duck-spinning.gif') }}"
|
||||
style="max-width: calc(100% - 2em);"
|
||||
/>
|
||||
</div>
|
||||
<!-- BONUS DUCKS! -->
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
{% block content %}
|
||||
<div class="foreground homeground"></div>
|
||||
<div class="fPage">
|
||||
<div class="heightBox">
|
||||
<div class="neonBox">
|
||||
<h1 class="neon">ERROR {{error}}</h1>
|
||||
<br /><br />
|
||||
<h3 class="neon">{{message}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
href="{{ url_for('static', filename='icons/withBackground.svg') }}"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="theme-color" content="#141414" />
|
||||
|
||||
<!-- Resource hints for performance -->
|
||||
<link rel="preconnect" href="https://www.googletagmanager.com" />
|
||||
@@ -29,7 +29,7 @@
|
||||
property="twitter:image"
|
||||
content="{{ url_for('static', filename='icons/rasterLogoCircle.png') }}"
|
||||
/>
|
||||
<meta name="twitter:image:alt" content="some example picture idk" />
|
||||
<meta name="twitter:image:alt" content="Andrew Simonson's logo" />
|
||||
<meta name="twitter:site" content="@asimonson1125" />
|
||||
|
||||
<!-- Structured data for SEO -->
|
||||
@@ -75,7 +75,6 @@
|
||||
/>
|
||||
<link rel="canonical" href="{{ request.url_root | trim('/') }}{{ var['canonical'] }}" />
|
||||
<script defer src="{{ url_for('static', filename='js/responsive.js') }}"></script>
|
||||
{# <script src="{{ url_for('static', filename='js/chessbed.js') }}"></script> #}
|
||||
<script defer src="{{ url_for('static', filename='js/idler.js') }}"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.min.js"></script>
|
||||
<title>{{ var['title'] }}</title>
|
||||
@@ -83,50 +82,47 @@
|
||||
{% block header %}
|
||||
<body onpopstate="backButton()">
|
||||
<a class="skip-link" href="#root">Skip to content</a>
|
||||
<div id="loading-bar"></div>
|
||||
<div id="loading-bar" aria-hidden="true"></div>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<main id='map' aria-label="Main content"></main>
|
||||
<div id="contentStuffer">
|
||||
<header class="header">
|
||||
<div id="name-container" onclick="goto('home')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('home')" role="button" tabindex="0" aria-label="Go to home">
|
||||
<div class="line name">
|
||||
<span class="textGrad">{# Andrew Simonoson #}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img
|
||||
src="{{ url_for('static', filename='icons/menu.svg')}}"
|
||||
alt="menu"
|
||||
<div id="bg" aria-hidden="true"></div>
|
||||
<div class="site">
|
||||
<header class="site-header" id="siteHeader">
|
||||
<a class="brand" href="/" data-page="home" onclick="goto('home'); return false;" aria-label="Andrew Simonson, home">
|
||||
<img class="brand-mark" src="{{ url_for('static', filename='icons/neonfinal3.svg') }}" alt="" width="30" height="30" />
|
||||
<span class="brand-name">Andrew Simonson</span>
|
||||
</a>
|
||||
<button
|
||||
id="menu"
|
||||
role="button"
|
||||
aria-label="Toggle navigation menu"
|
||||
class="menu-toggle"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-controls="navBar"
|
||||
onClick="toggleMenu()"
|
||||
onKeyDown="if(event.key==='Enter'||event.key===' ')toggleMenu()"
|
||||
/>
|
||||
<div class="navControl">
|
||||
<nav id="navBar" class="navBar" role="menubar" aria-label="Main navigation">
|
||||
<div onClick="goto('home')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('home')" role="menuitem" tabindex="0" class="navElement">
|
||||
<p>Home</p>
|
||||
</div>
|
||||
<div onClick="goto('about')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('about')" role="menuitem" tabindex="0" class="navElement">
|
||||
<p>About</p>
|
||||
</div>
|
||||
<div onClick="goto('projects')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('projects')" role="menuitem" tabindex="0" class="navElement">
|
||||
<p>Work</p>
|
||||
</div>
|
||||
<div onclick="goto('status')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('status')" role="menuitem" tabindex="0" class="navElement">
|
||||
<p>Status</p>
|
||||
</div>
|
||||
onclick="toggleMenu()"
|
||||
>Menu</button>
|
||||
<nav id="navBar" class="site-nav" aria-label="Main navigation">
|
||||
<a href="/" data-page="home" onclick="goto('home'); return false;" {% if var['id'] == 'home' %}aria-current="page"{% endif %}>Home</a>
|
||||
<a href="/projects" data-page="projects" onclick="goto('projects'); return false;" {% if var['id'] == 'projects' %}aria-current="page"{% endif %}>Work</a>
|
||||
<a href="/about" data-page="about" onclick="goto('about'); return false;" {% if var['id'] == 'about' %}aria-current="page"{% endif %}>About</a>
|
||||
<a href="/status" data-page="status" onclick="goto('status'); return false;" {% if var['id'] == 'status' %}aria-current="page"{% endif %}>Status</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
{% endblock %} {% block content%}
|
||||
<div id="root">{% include var['template'] %}</div>
|
||||
<main id="root">{% include var['template'] %}</main>
|
||||
{% endblock %} {% block footer %}
|
||||
<div id="footerSpacer"></div>
|
||||
<footer class="footer" role="contentinfo">{% include 'partials/socials.html' %}</footer>
|
||||
<footer class="site-footer">
|
||||
<div class="footer-inner">
|
||||
<p class="footer-name">Andrew Simonson</p>
|
||||
<ul class="footer-links">
|
||||
<li><a href="https://github.com/asimonson1125" rel="noopener noreferrer">GitHub</a></li>
|
||||
<li><a href="https://www.linkedin.com/in/simonsonandrew/" rel="noopener noreferrer">LinkedIn</a></li>
|
||||
<li><a href="mailto:asimonson1125@gmail.com">Email</a></li>
|
||||
<li><a href="/resume">Résumé</a></li>
|
||||
<li><a href="/books" onclick="goto('books'); return false;">Bookshelf</a></li>
|
||||
<li><a href="https://github.com/asimonson1125/asimonson1125.github.io" rel="noopener noreferrer">Source</a></li>
|
||||
</ul>
|
||||
<p class="footer-note">Self-hosted. Uptime on the <a href="/status" onclick="goto('status'); return false;">status page</a>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -1,62 +1,123 @@
|
||||
{% block content %} {% macro nameplate() %}
|
||||
<div>
|
||||
<h1 id="homeName" class="textGrad">oh no.</h1>
|
||||
<h2>I did not plan for visitors.</h2>
|
||||
|
||||
<br /> <hr> <br />
|
||||
|
||||
<div class="flex vertOnMobile">
|
||||
<div>
|
||||
<img
|
||||
src="{{ url_for('static', filename='photos/extradimensionalSq.avif') }}"
|
||||
id="homeIcon"
|
||||
fetchpriority=high
|
||||
alt="Biblically Accurate Seraphim"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
<div class="foreground homeground"></div>
|
||||
<div class="relative growBottom">
|
||||
<div id="nametag" class="flex" data-aos="fade-up">{{ nameplate() }}</div>
|
||||
<!--<INSERT SMALL BANNER HERE FOR PROJECT IMAGECARD CAROUSEL>-->
|
||||
<div id="desktopSpacer"></div>
|
||||
<div class="homeSubContent">
|
||||
<img class='blinkies' alt='Pepsi Addict' src="{{ url_for('static', filename='photos/blinkies/pepsiaddict.gif') }}" loading="lazy" />
|
||||
<img class='blinkies' alt='Secret Message' src="{{ url_for('static', filename='photos/blinkies/tooclose.gif') }}" loading="lazy" />
|
||||
<img class='blinkies' alt="They took my blood but it wasn't DNA, it was USA" src="{{ url_for('static', filename='photos/blinkies/usa.gif') }}" loading="lazy" />
|
||||
<img class='blinkies' alt='Bob the Builder gif' src="{{ url_for('static', filename='photos/blinkies/bobthebuilder.gif') }}" loading="lazy" />
|
||||
<div>
|
||||
<br />
|
||||
<strong> You've reached the website for Andrew Simonson's digital shenanigans.</strong>
|
||||
<h3>Now What?</h3>
|
||||
<p>
|
||||
Go back and find the link that I originally shared. Or poke around. Be your own person.</br>
|
||||
I'll grant myself some titles while I'm at it:
|
||||
{% block content %}
|
||||
<section class="hero" aria-labelledby="homeName">
|
||||
<div class="hero-inner">
|
||||
<h1 id="homeName" class="display">Andrew Simonson</h1>
|
||||
<p class="lede">
|
||||
Data scientist at Ecolab and a graduate student at RIT.
|
||||
I build end-to-end decision machines: from data pipelines that process scale, to epistemic models which understand, to user interfaces that convey.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Wicked Wizard of the West</li>
|
||||
<li>Enemy of Node.js, Hater of Bloat</li>
|
||||
<li>Load-Bearing Coconut</li>
|
||||
<li>Creator and Harnesser of Energy</li>
|
||||
<p class="hero-links">
|
||||
<a href="/projects" onclick="goto('projects'); return false;">Work</a>
|
||||
<a href="/about" onclick="goto('about'); return false;">About</a>
|
||||
<a href="/resume">Résumé</a>
|
||||
<a href="https://github.com/asimonson1125" rel="noopener noreferrer">GitHub</a>
|
||||
<a href="https://www.linkedin.com/in/simonsonandrew/" rel="noopener noreferrer">LinkedIn</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="page">
|
||||
|
||||
<section class="section" aria-labelledby="now-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="now-heading">Now</h2>
|
||||
<p class="section-note">September 2026</p>
|
||||
</div>
|
||||
<dl class="now-list">
|
||||
<div class="now-item">
|
||||
<dt>At work</dt>
|
||||
<dd>
|
||||
Primary model developer on RushReady at Ecolab: explainable anomaly
|
||||
detection with XGBoost and SHAP, plus brand-configurable recommendations
|
||||
for quick-service restaurants, all running on a near-real-time Delta
|
||||
Live Tables platform.
|
||||
</dd>
|
||||
</div>
|
||||
<div class="now-item">
|
||||
<dt>At school</dt>
|
||||
<dd>
|
||||
M.S. in Data Science at Rochester Institute of Technology, focused on
|
||||
probability theory, statistical learning, and Bayesian methods.
|
||||
Capstone in fall 2026.
|
||||
</dd>
|
||||
</div>
|
||||
<div class="now-item">
|
||||
<dt>At home</dt>
|
||||
<dd>
|
||||
Running a <a href="/projects#feature-homelab" onclick="goto('projects', {hash: 'feature-homelab'}); return false;">two-GPU homelab</a>
|
||||
that serves Mixture-of-Experts models to coding agents over local
|
||||
endpoints, and using the same rig to fine-tune small models.
|
||||
</dd>
|
||||
</div>
|
||||
<div class="now-item">
|
||||
<dt>On the shelf</dt>
|
||||
<dd>
|
||||
<ul class="shelf-row" aria-label="A few favorite books">
|
||||
{% for title in var.books.selection[:5] %}
|
||||
{% set book = var.books.books[title] %}
|
||||
<li>
|
||||
<a href="/books#'{{ title }}'" onclick="goto('books', {hash: "'{{ title }}'"}); return false;" title="{{ title }}">
|
||||
<img
|
||||
src="{{ url_for('static', filename='photos/books/' + book.filename) }}"
|
||||
alt="{{ title }}"
|
||||
loading="lazy"
|
||||
width="60"
|
||||
height="90"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<a class="shelf-more" href="/books" onclick="goto('books'); return false;">All reviews</a>
|
||||
</dd>
|
||||
</div>
|
||||
<br />
|
||||
{#
|
||||
<div id="aboutCards" class="flex">
|
||||
<div class="chess">
|
||||
{% from 'partials/chess.html' import chess %} {{
|
||||
chess('asimonson1125') }}
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="section" aria-labelledby="selected-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="selected-heading">Selected work</h2>
|
||||
<p class="section-note"><a href="/projects" onclick="goto('projects'); return false;">Everything</a></p>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<img height='150px' alt='stabby' src="{{ url_for('static', filename='photos/electricityStabby.png') }}" loading="lazy" />
|
||||
<ol class="selected-list">
|
||||
{% for id, f in var.features.items() %}
|
||||
<li>
|
||||
<a href="/projects#feature-{{ id }}" onclick="goto('projects', {hash: 'feature-{{ id }}'}); return false;">{{ f.title }}</a>
|
||||
<span>{{ f.summary or f.tagline }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="section" aria-labelledby="site-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="site-heading">What now?</h2>
|
||||
</div>
|
||||
<br />
|
||||
<div class="colophon">
|
||||
<div class="colophon-text">
|
||||
<p>
|
||||
I wasn't really expecting anyone to read this far.
|
||||
If you're looking for shared resources you've taken a wrong turn.
|
||||
<p>
|
||||
From here, we'll rekindle the future.
|
||||
</p>
|
||||
<p class="blinkies" aria-label="Retro web badges">
|
||||
<img alt="Pepsi Addict" src="{{ url_for('static', filename='photos/blinkies/pepsiaddict.gif') }}" loading="lazy" width="150" height="20" />
|
||||
<img alt="If you can read this you are too close to the monitor" src="{{ url_for('static', filename='photos/blinkies/tooclose.gif') }}" loading="lazy" width="150" height="20" />
|
||||
<img alt="They took my blood but it wasn't DNA, it was USA" src="{{ url_for('static', filename='photos/blinkies/usa.gif') }}" loading="lazy" width="150" height="20" />
|
||||
<img alt="Bob the Builder: can we build it?" src="{{ url_for('static', filename='photos/blinkies/bobthebuilder.gif') }}" loading="lazy" width="150" height="20" />
|
||||
</p>
|
||||
</div>
|
||||
#}
|
||||
<figure class="colophon-figure flex">
|
||||
<img
|
||||
src="{{ url_for('static', filename='photos/extradimensional.avif') }}"
|
||||
alt="Seraphim over a globe"
|
||||
width="180"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
<figcaption></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
{% macro feature_project(id, title, tagline, body, stack, image=none) %}
|
||||
<div class="feature-project boxed{{ ' has-media' if image else '' }}" id="feature-{{ id }}">
|
||||
<div class="feature-project-content">
|
||||
<div class="feature-project-header" onclick="toggleFeatureProject(this)" onKeyDown="if(event.key==='Enter'||event.key===' ')toggleFeatureProject(this)" role="button" tabindex="0" aria-expanded="false" aria-controls="feature-{{ id }}-body">
|
||||
{% if image %}
|
||||
<div class="feature-project-media" style="background-image: url('{{ url_for('static', filename=image) }}')"></div>
|
||||
{% endif %}
|
||||
<div class="feature-project-heading">
|
||||
{% macro feature_project(id, title, tagline, body, stack, image=none, caption='') %}
|
||||
<article class="feature{{ ' has-figure' if image else '' }}" id="feature-{{ id }}">
|
||||
<div class="feature-head">
|
||||
<h3>{{ title }}</h3>
|
||||
<p class="feature-project-tagline">{{ tagline }}</p>
|
||||
<p class="feature-tagline">{{ tagline }}</p>
|
||||
</div>
|
||||
<span class="feature-project-toggle" aria-hidden="true">+</span>
|
||||
</div>
|
||||
<div class="feature-project-body" id="feature-{{ id }}-body">
|
||||
<div class="feature-project-body-inner">
|
||||
<div class="feature-project-prose">
|
||||
{% if image %}
|
||||
<figure class="feature-figure feature-clip">
|
||||
<img src="{{ url_for('static', filename=image) }}" alt="{{ caption or title }}" loading="lazy" width="1108" height="1017" />
|
||||
{% if caption %}<figcaption>{{ caption }}</figcaption>{% endif %}
|
||||
</figure>
|
||||
{% endif %}
|
||||
<div class="feature-body feature-clip" id="feature-{{ id }}-body">
|
||||
<div class="prose">
|
||||
{{ body | safe }}
|
||||
</div>
|
||||
{% if stack %}
|
||||
<div class="feature-project-stack">
|
||||
{% for item in stack %}
|
||||
<span class="feature-project-stack-item">{{ item }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="feature-stack"><span class="label">Built with</span> {{ stack | join(', ') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="feature-expand"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-controls="feature-{{ id }}-body"
|
||||
onclick="toggleFeature(this)"
|
||||
>
|
||||
<span class="visually-hidden when-closed">Read more about {{ title }}</span>
|
||||
<span class="visually-hidden when-open">Collapse {{ title }}</span>
|
||||
</button>
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -1,39 +1,27 @@
|
||||
{% macro project(title, classes, status, bgi, content, links) %}
|
||||
<div class="project {{ classes }}" data-aos="fade-up">
|
||||
<div class="projImageWrap">
|
||||
{% set tags = classes.split() | reject('equalto', 'pinned') | list %}
|
||||
<li class="project" id="project-{{ title | slug }}">
|
||||
<div class="project-thumb">
|
||||
{% if bgi|length > 0 %}
|
||||
{% set path = url_for('static', filename='photos/projects/' + bgi) %}
|
||||
<img src="{{ path }}" alt="Ref image for {{ title }} project" />
|
||||
<img src="{{ url_for('static', filename='photos/projects/' + bgi) }}" alt="" loading="lazy" />
|
||||
{% else %}
|
||||
<div class="projImagePlaceholder"></div>
|
||||
<div class="project-thumb-empty" aria-hidden="true"></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="projContent">
|
||||
<div class="projTitleRow">
|
||||
<div class="project-body">
|
||||
<h3>{{ title }}</h3>
|
||||
<span class="proj-status {{ status }}">{{ status }}</span>
|
||||
</div>
|
||||
{% if classes %}
|
||||
<div class="projTags">
|
||||
{% for cls in classes.split() %}
|
||||
{% if cls != 'pinned' %}<span class="proj-tag">{{ cls }}</span>{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="projDesc">{{ content }}</p>
|
||||
<p class="project-meta">
|
||||
<span class="status status-{{ status }}">{{ status }}</span>
|
||||
{% if tags %}<span class="sep" aria-hidden="true">·</span> {{ tags | join(', ') }}{% endif %}
|
||||
</p>
|
||||
<p class="project-desc">{{ content }}</p>
|
||||
{% if links %}
|
||||
<div class="projLinks">
|
||||
{% for i in links %}
|
||||
{% set src = 'icons/' + i[0] + '.svg' %}
|
||||
{% if i[1].startswith('https://') or i[1].startswith('http://') %}
|
||||
<a href="{{ i[1] }}" rel="noopener noreferrer" class="proj-link">
|
||||
<img class="projectLink" src="{{ url_for('static', filename=src) }}" alt="{{ i[0] }}" />
|
||||
<span>{{ i[2] }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
<p class="project-links">
|
||||
{% for i in links if i[1].startswith('https://') or i[1].startswith('http://') %}
|
||||
<a href="{{ i[1] }}" rel="noopener noreferrer">{{ i[2] }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -1,49 +1,33 @@
|
||||
{% block content %}
|
||||
<div class="foreground"></div>
|
||||
<div class="relative growBottom">
|
||||
<div id="nametag" class="flex" data-aos="fade-up">
|
||||
<div>
|
||||
<h1 class="textGrad">Work</h1>
|
||||
<h2>Projects & experiments</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="homeSubContent">
|
||||
<div class="foregroundContent">
|
||||
<div class="page">
|
||||
<header class="page-head">
|
||||
<h1 class="display">Work</h1>
|
||||
<p class="lede">
|
||||
Research, tooling, and experiments. Sometimes I forget to add things to this list, message me for missing details.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- Deep Dives -->
|
||||
<h2>Highlights</h2>
|
||||
<div class="feature-projects">
|
||||
<section class="section" aria-labelledby="highlights-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="highlights-heading">Highlights</h2>
|
||||
</div>
|
||||
{% from 'partials/feature_project.html' import feature_project %}
|
||||
{{ feature_project(
|
||||
'homelab',
|
||||
'AI Homelab',
|
||||
'Self-hosted GPU cluster serving MoE models beyond their rated VRAM, with a full LoRA fine-tuning pipeline on top',
|
||||
'<p>I\'ve refined a method to pool GPUs from different vendors and generations — an RTX 5070 and a Radeon RX 6600 XT — into a single ~20GB inference backend over Vulkan, so neither card\'s limits bottleneck the other. FreeToken, a bleeding-edge MoE inference server, fronts the cluster and handles the actual elastic inference, running Mixture-of-Experts models with far more total parameters than the cluster\'s VRAM would normally allow. My custom scripts resolve KV caching and system prompt compaction that make the setup harness-capable, so agentic tools like Claude Code can hit it over Anthropic- and OpenAI-compatible endpoints exactly like they would a paid API. GPU layer placement alone took generation from ~150s a call to ~88ms/token.</p><p>The same rig trains as well as it serves. I built a complete LoRA fine-tuning pipeline on it — base model, training config, and adapter. Used for fine-tuning Qwen3-1.7B on patent data, and training a Stable Diffusion LoRA set on curated, deduplicated image data.</p>',
|
||||
['RTX 5070', 'RX 6600 XT', 'Ollama', 'FreeToken', 'ComfyUI', 'PEFT / TRL'],
|
||||
'photos/deepdives/homelab-dashboard.png'
|
||||
) }}
|
||||
{% for id, f in var.features.items() %}
|
||||
{{ feature_project(id, f.title, f.tagline, f.body, f.stack, f.image, f.caption) }}
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
{{ feature_project(
|
||||
'siteinfra',
|
||||
'Self-Hosted Web Tooling & Maintenance',
|
||||
'I designed, deployed, and still actively operate this site\'s stack — the app, the edge, and its own health monitoring',
|
||||
'<p>This portfolio runs on infrastructure I designed, deployed, and still operate myself — the Flask app and its container, the Cloudflare edge and reverse proxy in front of it, DNS and tunneling, and the uptime monitor watching all of it (live at <a href="status">/status</a>). None of that is a managed host\'s job here; it\'s mine.</p><p>What actually proves it\'s production-capable is the maintenance loop, not the launch. I put it through a real adversarial review periodically — most recently a live pass with Claude Code against both the deployed site and the source — and fix what it finds. The latest pass closed two real issues same-day: a database-query pattern on an internal endpoint that could be hammered into needless load, and a self-built redirect that could momentarily hand a browser plaintext HTTP instead of HTTPS. Nothing catastrophic came up — no injection, no auth bypass, no data exposure — which is exactly the point of doing this on a schedule instead of waiting for something to break.</p>',
|
||||
['Flask', 'Docker', 'PostgreSQL', 'Reverse Proxy', 'Cloudflare']
|
||||
) }}
|
||||
</div>
|
||||
|
||||
<!-- Projects -->
|
||||
<h2>All Projects</h2>
|
||||
<div class="projectList">
|
||||
{% from 'partials/project.html' import project %} {% for i in
|
||||
var["projects"] %} {{ project(i, var["projects"][i]["classes"],
|
||||
var["projects"][i]["status"], var["projects"][i]["bgi"],
|
||||
var["projects"][i]["content"], var["projects"][i]["links"]) }} {% endfor
|
||||
%}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<section class="section" aria-labelledby="all-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="all-heading">All projects</h2>
|
||||
<p class="section-note">{{ var.projects | length }} entries</p>
|
||||
</div>
|
||||
<ol class="project-list">
|
||||
{% from 'partials/project.html' import project %}
|
||||
{% for title, item in var.projects.items() %}
|
||||
{{ project(title, item.classes, item.status, item.bgi, item.content, item.links) }}
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,100 +1,80 @@
|
||||
{% block content %}
|
||||
<div class="foreground"></div>
|
||||
<div class="foregroundContent">
|
||||
<h2 class='concentratedHead'>Service Status Monitor</h2>
|
||||
<div class="page">
|
||||
<header class="page-head">
|
||||
<h1 class="display">Status</h1>
|
||||
<p class="lede">
|
||||
Uptime for the services I host myself, checked every minute from the
|
||||
server. This page refreshes itself on the same schedule.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="info-box" role="region" aria-label="Status overview">
|
||||
<div class='flex spaceBetween'>
|
||||
<div>
|
||||
<span id="lastUpdate" aria-live="polite">Last checked: Loading...</span>
|
||||
<br />
|
||||
<span id="nextUpdate" aria-live="polite">Next check: --</span>
|
||||
</div>
|
||||
<button id="refreshBtn" onclick="refreshStatus()" aria-label="Refresh service status now">Refresh Now</button>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<h4>Status Legend</h4>
|
||||
<div class="legend-items">
|
||||
<div class="legend-item">
|
||||
<span class="state-dot online"></span>
|
||||
<span>Operational (response successful)</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="state-dot degraded"></span>
|
||||
<span>Degraded (timeout or errors)</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="state-dot offline"></span>
|
||||
<span>Offline (unreachable)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Overall Status Bar -->
|
||||
<div class="info-card summary-card" id="overallStatus" role="status" aria-live="polite">
|
||||
<div class="summary-content">
|
||||
<div class="summary-indicator">
|
||||
<section class="section status-summary" id="overallStatus" role="status" aria-live="polite">
|
||||
<div class="status-summary-main">
|
||||
<span class="summary-icon loading" aria-hidden="true">◐</span>
|
||||
<div>
|
||||
<h3 class="summary-title">Checking Systems...</h3>
|
||||
<h5 id="summary-subtitle">Loading service status</h5>
|
||||
<h2 class="summary-title">Checking systems</h2>
|
||||
<p class="summary-subtitle" id="summary-subtitle">Loading service status</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex spaceBetween">
|
||||
<div class="metric-box">
|
||||
<span class="metric-number" id="onlineCount" aria-label="Online services">--</span>
|
||||
<span class="metric-label">Online</span>
|
||||
</div>
|
||||
<div class="metric-box">
|
||||
<span class="metric-number" id="totalCount" aria-label="Total services">--</span>
|
||||
<span class="metric-label">Total</span>
|
||||
</div>
|
||||
<dl class="status-facts">
|
||||
<div>
|
||||
<dt>Online</dt>
|
||||
<dd><span id="onlineCount" aria-label="Online services">--</span> of <span id="totalCount" aria-label="Total services">--</span></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Last checked</dt>
|
||||
<dd id="lastUpdate">Loading</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Next check</dt>
|
||||
<dd id="nextUpdate">--</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<button id="refreshBtn" class="button" type="button" onclick="refreshStatus()" aria-label="Refresh service status now">Refresh now</button>
|
||||
</section>
|
||||
|
||||
<div class="card-grid" role="list" aria-label="Service status cards">
|
||||
<section class="section" aria-labelledby="services-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="services-heading">Services</h2>
|
||||
<p class="section-note">Uptime over 24 hours, 7 days, 30 days, and all time</p>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table class="status-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Service</th>
|
||||
<th scope="col">State</th>
|
||||
<th scope="col" class="num">Response</th>
|
||||
<th scope="col" class="num">Code</th>
|
||||
<th scope="col" class="num">Checks</th>
|
||||
<th scope="col">Uptime</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in var.services %}
|
||||
<a href="{{ service.url }}" class="info-card-link" id="status-{{ service.id }}" role="listitem">
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<h3>{{ service.name }}</h3>
|
||||
<div class="state-indicator">
|
||||
<span class="state-dot loading" aria-label="Status: Loading..."></span>
|
||||
<span class="state-text">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Response Time:</span>
|
||||
<span class="metric-value" id="time-{{ service.id }}" aria-live="polite">--</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Status Code:</span>
|
||||
<span class="metric-value" id="code-{{ service.id }}" aria-live="polite">--</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Total Checks:</span>
|
||||
<span class="metric-value" id="checks-{{ service.id }}" aria-live="polite">--</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="detail-label">Uptime:</div>
|
||||
<div class="detail-values" id="uptime-{{ service.id }}" aria-live="polite">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<tr id="status-{{ service.id }}">
|
||||
<th scope="row"><a href="{{ service.url }}" rel="noopener noreferrer">{{ service.name }}</a></th>
|
||||
<td>
|
||||
<span class="state-indicator">
|
||||
<span class="state-dot loading" aria-hidden="true"></span>
|
||||
<span class="state-text">Loading</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="num" id="time-{{ service.id }}">--</td>
|
||||
<td class="num" id="code-{{ service.id }}">--</td>
|
||||
<td class="num" id="checks-{{ service.id }}">--</td>
|
||||
<td class="uptime" id="uptime-{{ service.id }}">Loading</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<h4>About This Monitor</h4>
|
||||
<ul>
|
||||
<li><strong>Check Frequency:</strong> Services are checked automatically every minute from the server</li>
|
||||
<li><strong>Page Refresh:</strong> This page auto-refreshes every minute to show latest data</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p class="table-note">
|
||||
<span class="state-dot online" aria-hidden="true"></span> operational
|
||||
<span class="state-dot degraded" aria-hidden="true"></span> degraded or timed out
|
||||
<span class="state-dot offline" aria-hidden="true"></span> unreachable
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/status.js') }}"></script>
|
||||
|
||||
Reference in New Issue
Block a user