psychosis results

This commit is contained in:
2026-08-24 19:31:12 -05:00
parent 8432cc0bf6
commit c6fa2c41a7
9 changed files with 338 additions and 131 deletions

View File

@@ -4,12 +4,17 @@ import os
import flask
from flask_minify import Minify
from werkzeug.middleware.proxy_fix import ProxyFix
import werkzeug.exceptions as HTTPerror
import config # noqa: F401 — side-effect: loads dev env vars
from monitor import monitor, SERVICES
app = flask.Flask(__name__)
# Trust the scheme/host Cloudflare sets, so redirects Werkzeug builds itself
# (e.g. the trailing-slash redirect on /api/goto) use https instead of the
# plaintext scheme it sees on its own socket.
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
# ── Static file fingerprinting ────────────────────────────────────────
@@ -42,6 +47,9 @@ def add_headers(response):
response.headers['X-XSS-Protection'] = '1; mode=block'
response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
if flask.request.is_secure:
response.headers['Strict-Transport-Security'] = 'max-age=31536000'
if flask.request.path.startswith('/static/'):
response.headers['Cache-Control'] = 'public, max-age=31536000, immutable'
elif flask.request.path in ['/sitemap.xml', '/robots.txt']:
@@ -55,7 +63,7 @@ def add_headers(response):
# ── Load page data ────────────────────────────────────────────────────
def load_json(path):
with open(path, "r") as f:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
@@ -64,7 +72,7 @@ books = load_json("./static/json/books.json")
skills = load_json("./static/json/skills.json")
pages = load_json("./static/json/pages.json")
pages['projects']['skillList'] = skills
pages['about']['skillList'] = skills
pages['projects']['projects'] = projects
pages['home']['books'] = books
pages['books']['books'] = books

View File

@@ -51,6 +51,26 @@ class ServiceMonitor:
for svc in SERVICES
}
self._last_check = None
# Cheap placeholder (no DB access) until the first check_all_services()
# run replaces it with the real, uptime-backed summary.
self._cached_summary = {
'last_check': None,
'next_check': None,
'services': [
{
'id': service_id,
'name': cached['name'],
'url': cached['url'],
'status': cached['status'],
'response_time': cached['response_time'],
'status_code': cached['status_code'],
'last_online': cached['last_online'],
'uptime': {'24h': None, '7d': None, '30d': None, 'all_time': None},
'total_checks': 0,
}
for service_id, cached in self._current.items()
],
}
self._ensure_schema()
# ── Database helpers ──────────────────────────────────────────
@@ -206,6 +226,7 @@ class ServiceMonitor:
if result['status'] == 'online':
cached['last_online'] = result['timestamp']
self._last_check = datetime.now().isoformat()
self._cached_summary = self._build_summary_locked()
# ── Uptime calculations ───────────────────────────────────────
@@ -271,9 +292,13 @@ class ServiceMonitor:
# ── Status summary ────────────────────────────────────────────
def get_status_summary(self):
"""Build a JSON-serializable status summary with uptime statistics."""
with self.lock:
def _build_summary_locked(self):
"""Build a JSON-serializable status summary with uptime statistics.
Hits the database (multiple queries per service) -- only call this
from within check_all_services() while self.lock is held, so it runs
once per check cycle rather than once per /api/status request.
"""
summary = {
'last_check': self._last_check,
'next_check': None,
@@ -304,6 +329,12 @@ class ServiceMonitor:
return summary
def get_status_summary(self):
"""Return the cached status summary, refreshed once per check cycle
(see check_all_services), so this never itself touches the database."""
with self.lock:
return self._cached_summary
# ── Background loop ───────────────────────────────────────────
def _purge_old_records(self):

View File

@@ -5,7 +5,6 @@ click==8.4.1
Flask==3.1.3
Flask-Minify==0.50
gunicorn==26.0.0
htmlminf==0.1.13
idna==3.16
itsdangerous==2.2.0
Jinja2==3.1.6

View File

@@ -199,7 +199,7 @@ tr {
min-height: 100vh;
}
.growBottom :last-child {
.growBottom > :last-child {
flex-grow: 1;
}
@@ -1595,6 +1595,57 @@ tr {
display: none;
}
.timeline.collapsed::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 8em;
background: linear-gradient(to bottom, transparent, rgba(var(--bg-card-rgb), 0.97));
pointer-events: none;
}
.timeline-expand-wrap {
position: relative;
z-index: 2;
display: flex;
justify-content: center;
margin-top: 1em;
margin-bottom: 2.5em;
}
.timeline-expand-wrap.overlap {
margin-top: -3.75em;
}
.timeline-expand-btn {
padding: 0.65em 2em;
border: 1px solid rgba(var(--accent-rgb), 0.5);
border-radius: 2em;
background: rgba(var(--accent-rgb), 0.2);
color: var(--text-heading);
font-family: 'Courier New', Courier, monospace;
font-size: 0.85rem;
font-weight: bold;
letter-spacing: 0.05em;
text-transform: uppercase;
flex-grow: 1;
cursor: pointer;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.timeline-expand-btn:hover {
background: rgba(var(--accent-rgb), 0.35);
border-color: var(--accent);
transform: translateY(-1px);
}
.timeline-expand-btn.hidden {
display: none;
}
/* Summary Card */
.summary-card {
padding: 2em;
@@ -1896,6 +1947,23 @@ tr {
margin-top: 1.5em;
}
.skills-certs-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2em;
align-items: start;
}
.skills-certs-grid .skills-col #skillTree {
font-size: large;
}
@media screen and (max-width: 900px) {
.skills-certs-grid {
grid-template-columns: 1fr;
}
}
.cert-group {
margin-bottom: 1.5em;
min-width: 0;

View File

@@ -29,10 +29,42 @@
"description": "Face it, you've been ducked",
"canonical": "/duck"
},
"certificates": {
"template": "certs.html",
"title": "Certifications & Awards - Andrew Simonson",
"description": "Data science, DevOps, and entrepreneurship certifications earned by Andrew Simonson.",
"canonical": "/certs"
"about": {
"template": "about.html",
"title": "About, Timeline & Certifications - Andrew Simonson",
"description": "Bio, career/education timeline, and certifications for Andrew Simonson - Data Scientist at Ecolab.",
"canonical": "/about",
"timeline": {
"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",
"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",
"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",
"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)",
"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",
"classes": "experience technical"
}
}
}
}

View File

@@ -4,11 +4,35 @@
<div id="nametag" class="flex" data-aos="fade-up">
<div>
<h1 class="textGrad">About</h1>
<h2>My story, in order</h2>
<h2>Bio, timeline &amp; credentials</h2>
</div>
</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="concentratedHead">
<h3>Career &amp; Education Timeline</h3>
</div>
@@ -26,7 +50,7 @@
<div class="timeitem-inner boxed">
<div class="timeitem-header">
<span class="datetext">{{ item.date }}</span>
<strong>{{ name }}</strong>
<strong>{{ item.title }}</strong>
</div>
<div class="timeitem-content">
{{ item.content | safe }}
@@ -42,32 +66,146 @@
</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 &amp; 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 &middot; 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 &mdash; Python for Data Science</a></li>
<li><a href="https://courses.edx.org/certificates/f29d0e65fc024c6e95121619e329a286" class="cert-item">DSE210x &mdash; Probability and Statistics in Data Science using Python</a></li>
<li><a href="https://courses.edx.org/certificates/cccc2bd2ed61470e8492d6da1be530c5" class="cert-item">DSE220x &mdash; Machine Learning Fundamentals</a></li>
<li><a href="https://courses.edx.org/certificates/4dfd6563a1f84caaa8922a02a5125f29" class="cert-item">DSE230x &mdash; 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>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const buttons = document.querySelectorAll('.filter-btn');
(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;
buttons.forEach(function(btn) {
btn.addEventListener('click', function() {
var filter = this.getAttribute('data-filter');
buttons.forEach(function(b) { b.classList.remove('active'); b.setAttribute('aria-pressed', 'false'); });
this.classList.add('active');
this.setAttribute('aria-pressed', 'true');
var currentFilter = 'all';
var expanded = false;
function applyVisibility() {
var shown = 0;
var matched = 0;
items.forEach(function(item) {
var cats = item.getAttribute('data-categories') || '';
if (filter === 'all' || cats.indexOf(filter) !== -1) {
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');
}
});
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);
}
}
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');
this.setAttribute('aria-pressed', 'true');
currentFilter = this.getAttribute('data-filter');
expanded = false;
applyVisibility();
});
});
});
if (expandBtn) {
expandBtn.addEventListener('click', function() {
expanded = !expanded;
applyVisibility();
});
}
applyVisibility();
})();
</script>
{% endblock %}

View File

@@ -1,50 +0,0 @@
{% block content %}
<div class="foreground"></div>
<div class="foregroundContent">
<h1>Certifications</h1>
<p class="page-subtitle">
Comprehensive list verifiable on
<a href="https://www.linkedin.com/in/simonsonandrew/details/certifications/">LinkedIn</a>
</p>
<p>Computer Science BS and Data Science MS from Rochester Institute of Technology</p>
<div class="boxed cert-group">
<p class="cert-group-provider">UCSanDiegoX &middot; edX</p>
<h2 class="concentratedHead">Data Science MicroMasters Program</h2>
<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 &mdash; Python for Data Science</a></li>
<li><a href="https://courses.edx.org/certificates/f29d0e65fc024c6e95121619e329a286" class="cert-item">DSE210x &mdash; Probability and Statistics in Data Science using Python</a></li>
<li><a href="https://courses.edx.org/certificates/cccc2bd2ed61470e8492d6da1be530c5" class="cert-item">DSE220x &mdash; Machine Learning Fundamentals</a></li>
<li><a href="https://courses.edx.org/certificates/4dfd6563a1f84caaa8922a02a5125f29" class="cert-item">DSE230x &mdash; Big Data Analytics Using Spark</a></li>
</ul>
</div>
<div class="cert-grid">
<div class="boxed cert-group">
<h3 class="concentratedHead">One-Off Courses</h3>
<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>
<h3 class="concentratedHead">Entrepreneurial Certifications</h3>
<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>
{% endblock %}

View File

@@ -110,6 +110,9 @@
<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>

View File

@@ -10,38 +10,16 @@
<div class="homeSubContent">
<div class="foregroundContent">
<!-- About / Intro -->
<!-- Intro -->
<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="certs">certifications page</a>
and a
<a href="Resume_Simonson_Andrew.pdf" target="_blank">resume</a>
for unexplained reasons.
Curious who's behind these?
<a href="about">About &amp; certifications</a>
·
<a href="Resume_Simonson_Andrew.pdf" target="_blank">Resume</a>
</p>
</div>
<!-- Skills -->
<div id="skills" style="margin-bottom: 3.5em;">
<h2>Technologies</h2>
{% from 'partials/skills.html' import skills %} {{
skills(var['skillList']) }}
</div>
<!-- Projects -->
<h2>Projects</h2>
<div class="projectList">