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:
@@ -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 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>
|
||||
|
||||
<div class="prose about-bio">
|
||||
<p>
|
||||
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>
|
||||
<p>
|
||||
There is also a <a href="/resume">résumé</a> for paper-minded people.
|
||||
</p>
|
||||
</div>
|
||||
<div class="homeSubContent">
|
||||
<div class="foregroundContent">
|
||||
|
||||
<!-- Bio -->
|
||||
<div style="max-width: 52em; margin-bottom: 3em;">
|
||||
<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.
|
||||
</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>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<div class="concentratedHead">
|
||||
<h3>Career & Education Timeline</h3>
|
||||
</div>
|
||||
<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>
|
||||
<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">
|
||||
{% 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>
|
||||
</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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<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="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 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() %}
|
||||
<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>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</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">
|
||||
{% for i in var.books.selection %}
|
||||
<div role="listitem">
|
||||
<a href="#'{{i}}'" aria-label="View review of {{ i }}">
|
||||
<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 %}
|
||||
<li>
|
||||
<a href="#'{{ i }}'" title="{{ i }}">
|
||||
<img
|
||||
alt="{{ i }}"
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename)) }}"
|
||||
loading="lazy"
|
||||
width="80"
|
||||
height="120"
|
||||
/>
|
||||
</a>
|
||||
</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>
|
||||
<ul class="review-list">
|
||||
{% for i in var.books.books %}
|
||||
<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"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</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">
|
||||
{% 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'>
|
||||
<img
|
||||
class="bookcover"
|
||||
alt="{{ i }} cover"
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename))}}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<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 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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</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 class="neonBox">
|
||||
<h1 class="neon">ERROR {{error}}</h1>
|
||||
<br /><br />
|
||||
<h3 class="neon">{{message}}</h3>
|
||||
</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>
|
||||
</nav>
|
||||
</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>
|
||||
</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>
|
||||
{% 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>
|
||||
<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>
|
||||
|
||||
<br /> <hr> <br />
|
||||
<div class="page">
|
||||
|
||||
<div class="flex vertOnMobile">
|
||||
<div>
|
||||
<img
|
||||
src="{{ url_for('static', filename='photos/extradimensionalSq.avif') }}"
|
||||
id="homeIcon"
|
||||
fetchpriority=high
|
||||
alt="Biblically Accurate Seraphim"
|
||||
/>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
<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>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<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:
|
||||
</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>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
{#
|
||||
<div id="aboutCards" class="flex">
|
||||
<div class="chess">
|
||||
{% from 'partials/chess.html' import chess %} {{
|
||||
chess('asimonson1125') }}
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<img height='150px' alt='stabby' src="{{ url_for('static', filename='photos/electricityStabby.png') }}" loading="lazy" />
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
#}
|
||||
<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>
|
||||
{% endblock %}
|
||||
<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>
|
||||
<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>
|
||||
</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">
|
||||
<h3>{{ title }}</h3>
|
||||
<p class="feature-project-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">
|
||||
{{ body | safe }}
|
||||
</div>
|
||||
{% if stack %}
|
||||
<div class="feature-project-stack">
|
||||
{% for item in stack %}
|
||||
<span class="feature-project-stack-item">{{ item }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% 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-tagline">{{ tagline }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% 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 %}
|
||||
<p class="feature-stack"><span class="label">Built with</span> {{ stack | join(', ') }}</p>
|
||||
{% endif %}
|
||||
</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">
|
||||
<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>
|
||||
<div class="project-body">
|
||||
<h3>{{ title }}</h3>
|
||||
<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 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>
|
||||
|
||||
<section class="section" aria-labelledby="highlights-heading">
|
||||
<div class="section-head">
|
||||
<h2 id="highlights-heading">Highlights</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="homeSubContent">
|
||||
<div class="foregroundContent">
|
||||
|
||||
<!-- Deep Dives -->
|
||||
<h2>Highlights</h2>
|
||||
<div class="feature-projects">
|
||||
{% 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'
|
||||
) }}
|
||||
|
||||
{{ 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>
|
||||
{% from 'partials/feature_project.html' import feature_project %}
|
||||
{% for id, f in var.features.items() %}
|
||||
{{ feature_project(id, f.title, f.tagline, f.body, f.stack, f.image, f.caption) }}
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<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>
|
||||
</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'>
|
||||
<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>
|
||||
<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>
|
||||
<h2 class="summary-title">Checking systems</h2>
|
||||
<p class="summary-subtitle" id="summary-subtitle">Loading service status</p>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<!-- Overall Status Bar -->
|
||||
<div class="info-card summary-card" id="overallStatus" role="status" aria-live="polite">
|
||||
<div class="summary-content">
|
||||
<div class="summary-indicator">
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<div class="card-grid" role="list" aria-label="Service status cards">
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</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>
|
||||
<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 %}
|
||||
<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>
|
||||
<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