Compare commits
46 Commits
efaf2fb169
...
nginx
| Author | SHA1 | Date | |
|---|---|---|---|
| 1db6f02fba | |||
| b7d448790d | |||
| 5254ad3172 | |||
| bd104f6aaf | |||
| fa77871d0d | |||
| e2206feaf4 | |||
| 30a9c89f41 | |||
| bdfffb46df | |||
| 536832f582 | |||
| ba7fb575ad | |||
| e09bbf19a9 | |||
| c6e05ed716 | |||
| 7731a2bafc | |||
| dd8b15e6e5 | |||
| 97ab6440a8 | |||
| 52b0b48f06 | |||
| 6996e5ad0f | |||
| 0ef8ad597f | |||
| 15c9aef875 | |||
| bd757e18ed | |||
| ead8c365e2 | |||
| 81c52d3758 | |||
| a8d22ceb50 | |||
| de64e4e944 | |||
| 2f58e0f734 | |||
| 8d9010801b | |||
| acea74be61 | |||
| d75572c39d | |||
| 1e9b2026cc | |||
| c60023f185 | |||
| 4a610023d4 | |||
| bda2e6aff5 | |||
| 5e856882e2 | |||
| 67e9f6d4ab | |||
| a75e425cdc | |||
| 5867b0831d | |||
| d563cfbf82 | |||
| e65ca46ac8 | |||
| b7a205d9e0 | |||
| b482b38a35 | |||
| 74bdbaa498 | |||
| b767d2fbe8 | |||
| 5655ad688f | |||
| 9aecf28d7b | |||
| 3974ce57d3 | |||
| 4afbbb2887 |
15
.dockerignore
Executable file → Normal file
@@ -1,15 +1,6 @@
|
|||||||
.git
|
react_OLD
|
||||||
.gitignore
|
|
||||||
.env
|
|
||||||
.venv
|
.venv
|
||||||
.vscode
|
.vscode
|
||||||
.claude
|
.git
|
||||||
CLAUDE.md
|
.git*
|
||||||
README.md
|
|
||||||
STATUS_MONITOR_README.md
|
|
||||||
Dockerfile
|
|
||||||
docker-compose.yml
|
|
||||||
notes.txt
|
|
||||||
react_OLD
|
|
||||||
__pycache__
|
__pycache__
|
||||||
*.pyc
|
|
||||||
|
|||||||
0
.gitattributes
vendored
Executable file → Normal file
@@ -1,2 +0,0 @@
|
|||||||
[core]
|
|
||||||
filemode = false
|
|
||||||
7
.gitignore
vendored
Executable file → Normal file
@@ -2,10 +2,3 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
notes.txt
|
notes.txt
|
||||||
react_OLD
|
react_OLD
|
||||||
envs.py
|
|
||||||
.env
|
|
||||||
status_history.json
|
|
||||||
|
|
||||||
.claude
|
|
||||||
CLAUDE.md
|
|
||||||
.aider*
|
|
||||||
|
|||||||
26
.vscode/launch.json
vendored
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Python Debugger: Flask",
|
|
||||||
"type": "debugpy",
|
|
||||||
"request": "launch",
|
|
||||||
"cwd": "${workspaceFolder}/src",
|
|
||||||
"module": "flask",
|
|
||||||
"env": {
|
|
||||||
"FLASK_APP": "app.py",
|
|
||||||
"FLASK_DEBUG": "1",
|
|
||||||
|
|
||||||
},
|
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
"--no-debugger",
|
|
||||||
"--no-reload"
|
|
||||||
],
|
|
||||||
"jinja": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
0
.vscode/settings.json
vendored
Executable file → Normal file
34
Dockerfile
Executable file → Normal file
@@ -1,10 +1,34 @@
|
|||||||
FROM python:3.10-bullseye
|
FROM ubuntu:lunar
|
||||||
LABEL maintainer="Andrew Simonson <asimonson1125@gmail.com>"
|
LABEL maintainer="Andrew Simonson <asimonson1125@gmail.com>"
|
||||||
|
|
||||||
WORKDIR /app
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
|
||||||
COPY src/ .
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y python3-pip nginx gunicorn supervisor
|
||||||
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
# Setup flask application
|
||||||
|
RUN mkdir -p /deploy/app
|
||||||
|
COPY src /deploy/app
|
||||||
|
RUN pip install -r /deploy/app/requirements.txt
|
||||||
|
|
||||||
CMD [ "gunicorn", "--bind", "0.0.0.0:8080", "app:app"]
|
# Setup nginx
|
||||||
|
RUN rm /etc/nginx/sites-enabled/default
|
||||||
|
COPY flask.conf /etc/nginx/sites-available/
|
||||||
|
RUN ln -s /etc/nginx/sites-available/flask.conf /etc/nginx/sites-enabled/flask.conf && \
|
||||||
|
echo "daemon off;" >> /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Setup supervisord
|
||||||
|
RUN mkdir -p /var/log/supervisor
|
||||||
|
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
COPY gunicorn.conf /etc/supervisor/conf.d/gunicorn.conf
|
||||||
|
|
||||||
|
# Permissions
|
||||||
|
# RUN adduser --disabled-password --gecos '' supervisor && \
|
||||||
|
RUN chmod -R 777 /var/* && \
|
||||||
|
chown -R root /var/*
|
||||||
|
|
||||||
|
# Entrypoint
|
||||||
|
USER root
|
||||||
|
|
||||||
|
# Start processes
|
||||||
|
CMD ["/usr/bin/supervisord"]
|
||||||
|
|||||||
11
README.md
@@ -1,11 +0,0 @@
|
|||||||
# I made a uhh website
|
|
||||||
So people can see how excellent my coding standards are.
|
|
||||||
|
|
||||||
* Style: 5/10
|
|
||||||
* Originality: 3/10
|
|
||||||
* Security: Yes*
|
|
||||||
* Viruses: not included
|
|
||||||
|
|
||||||
You gotta uhh `pip3 install -r requirements.txt` and `python3 app.py` that thing
|
|
||||||
|
|
||||||
Docker compose configured to expose at `localhost:8080`
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
# Service Status Monitor
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
Server-side monitoring system that checks the availability of asimonson.com services every 2 hours and provides uptime statistics.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Backend Components
|
|
||||||
|
|
||||||
#### 1. `monitor.py` - Service Monitoring Module
|
|
||||||
- **Purpose**: Performs automated health checks on all services
|
|
||||||
- **Check Interval**: Every 2 hours (7200 seconds)
|
|
||||||
- **Services Monitored**:
|
|
||||||
- asimonson.com
|
|
||||||
- files.asimonson.com
|
|
||||||
- git.asimonson.com
|
|
||||||
- pass.asimonson.com
|
|
||||||
- ssh.asimonson.com
|
|
||||||
|
|
||||||
**Features**:
|
|
||||||
- Tracks response times and HTTP status codes
|
|
||||||
- Stores check history (up to 720 checks = 60 days of data)
|
|
||||||
- Calculates uptime percentages for multiple time periods (24h, 7d, 30d, all-time)
|
|
||||||
- Persists data to `static/json/status_history.json`
|
|
||||||
- Runs in a background thread
|
|
||||||
|
|
||||||
#### 2. `app.py` - Flask Integration
|
|
||||||
- **New API Endpoint**: `/api/status`
|
|
||||||
- Returns current status for all services
|
|
||||||
- Includes uptime statistics
|
|
||||||
- Provides last check and next check times
|
|
||||||
- **Auto-start**: Monitoring begins when the Flask app starts
|
|
||||||
|
|
||||||
### Frontend Components
|
|
||||||
|
|
||||||
#### 1. `templates/status.html` - Status Page Template
|
|
||||||
- Displays real-time service status
|
|
||||||
- Shows uptime percentages (24h, 7d, 30d, all-time)
|
|
||||||
- Displays response times and status codes
|
|
||||||
- Shows total number of checks performed
|
|
||||||
- Manual refresh button
|
|
||||||
- Auto-refreshes every 5 minutes
|
|
||||||
|
|
||||||
#### 2. `static/js/status.js` - Frontend Logic
|
|
||||||
- Fetches status data from `/api/status` API
|
|
||||||
- Updates UI with service status and uptime
|
|
||||||
- Handles error states gracefully
|
|
||||||
- Auto-refresh every 5 minutes
|
|
||||||
|
|
||||||
#### 3. `static/css/App.css` - Styling
|
|
||||||
- Color-coded status indicators:
|
|
||||||
- Green: Operational
|
|
||||||
- Yellow: Degraded/Timeout
|
|
||||||
- Red: Offline
|
|
||||||
- Responsive grid layout
|
|
||||||
- Dark theme matching existing site design
|
|
||||||
|
|
||||||
## Data Storage
|
|
||||||
|
|
||||||
Status history is stored in `src/static/json/status_history.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"last_check": "2026-02-11T14:30:00",
|
|
||||||
"services": {
|
|
||||||
"main": {
|
|
||||||
"name": "asimonson.com",
|
|
||||||
"url": "https://asimonson.com",
|
|
||||||
"status": "online",
|
|
||||||
"response_time": 156,
|
|
||||||
"status_code": 200,
|
|
||||||
"last_online": "2026-02-11T14:30:00",
|
|
||||||
"checks": [
|
|
||||||
{
|
|
||||||
"timestamp": "2026-02-11T14:30:00",
|
|
||||||
"status": "online",
|
|
||||||
"response_time": 156,
|
|
||||||
"status_code": 200
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Status Types
|
|
||||||
|
|
||||||
- **online**: HTTP status 2xx-4xx, service responding
|
|
||||||
- **degraded**: HTTP status 5xx or slow response
|
|
||||||
- **timeout**: Request exceeded timeout limit (10 seconds)
|
|
||||||
- **offline**: Unable to reach service
|
|
||||||
- **unknown**: No checks performed yet
|
|
||||||
|
|
||||||
## Uptime Calculation
|
|
||||||
|
|
||||||
Uptime percentage = (number of online checks / total checks) × 100
|
|
||||||
|
|
||||||
Calculated for:
|
|
||||||
- Last 24 hours
|
|
||||||
- Last 7 days
|
|
||||||
- Last 30 days
|
|
||||||
- All-time (since monitoring began)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Starting the Server
|
|
||||||
```bash
|
|
||||||
cd src
|
|
||||||
python3 app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
The monitoring will start automatically and perform an initial check immediately, then every 2 hours thereafter.
|
|
||||||
|
|
||||||
### Accessing the Status Page
|
|
||||||
Navigate to: `https://asimonson.com/status`
|
|
||||||
|
|
||||||
### API Access
|
|
||||||
Direct API access: `https://asimonson.com/api/status`
|
|
||||||
|
|
||||||
Returns JSON with current status and uptime statistics for all services.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
To modify monitoring behavior, edit `src/monitor.py`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Change check interval (in seconds)
|
|
||||||
CHECK_INTERVAL = 7200 # 2 hours
|
|
||||||
|
|
||||||
# Modify service list
|
|
||||||
SERVICES = [
|
|
||||||
{
|
|
||||||
'id': 'main',
|
|
||||||
'name': 'asimonson.com',
|
|
||||||
'url': 'https://asimonson.com',
|
|
||||||
'timeout': 10 # seconds
|
|
||||||
},
|
|
||||||
# Add more services here
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- First deployment will show limited uptime data until enough checks accumulate
|
|
||||||
- Historical data is preserved across server restarts
|
|
||||||
- Maximum 720 checks stored per service (60 days at 2-hour intervals)
|
|
||||||
- Page auto-refreshes every 5 minutes to show latest server data
|
|
||||||
- Manual refresh button available for immediate updates
|
|
||||||
- All checks performed server-side (no client-side CORS issues)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
services:
|
|
||||||
portfolio:
|
|
||||||
image: 'asimonson1125/portfolio'
|
|
||||||
build:
|
|
||||||
context: ./
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
restart: 'no'
|
|
||||||
ports:
|
|
||||||
- 8080:8080
|
|
||||||
18
ecs-task.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"containerDefinitions": [
|
||||||
|
{
|
||||||
|
"name": "app",
|
||||||
|
"image": "asimonson1125/asimonson1125.github.io",
|
||||||
|
"essential": true,
|
||||||
|
"memory": 500,
|
||||||
|
"cpu": 10,
|
||||||
|
"portMappings": [
|
||||||
|
{
|
||||||
|
"containerPort": 80,
|
||||||
|
"hostPort": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"family": "Portfolio"
|
||||||
|
}
|
||||||
25
flask.conf
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name www.asimonson.com;
|
||||||
|
return 301 https://asimonson.com$request_uri;
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
listen 8080 http2;
|
||||||
|
server_name nginx-dev-asimonson.apps.okd4.csh.rit.edu;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/javascript text/css;
|
||||||
|
gunzip on;
|
||||||
|
|
||||||
|
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' *.cloudflare.com *.chesscomfiles.com *.chess.com *.googletagmanager.com cdn.jsdelivr.net www.google-analytics.com ajax.googleapis.com;";
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header X-Content-Type-Options 'nosniff';
|
||||||
|
add_header X-Frame-Options 'SAMEORIGIN';
|
||||||
|
|
||||||
|
location / {
|
||||||
|
include proxy_params;
|
||||||
|
proxy_pass http://localhost:5000/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
gunicorn.conf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[program:gunicorn]
|
||||||
|
command=/usr/bin/gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker app:app -b localhost:5000
|
||||||
|
directory=/deploy/app
|
||||||
146
src/app.py
Executable file → Normal file
@@ -1,120 +1,58 @@
|
|||||||
import flask
|
import flask
|
||||||
from flask_minify import Minify
|
from flask_minify import Minify
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import hashlib
|
proj = json.load(open("./static/json/projects.json", "r"))
|
||||||
import werkzeug.exceptions as HTTPerror
|
timeline = json.load(open("./static/json/timeline.json", "r"))
|
||||||
from config import *
|
pages = json.load(open("./static/json/pages.json", "r"))
|
||||||
from monitor import monitor, SERVICES
|
pages['about']['timeline'] = timeline
|
||||||
|
pages['projects']['projects'] = proj
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
# Compute content hashes for static file fingerprinting
|
|
||||||
static_file_hashes = {}
|
|
||||||
for dirpath, _, filenames in os.walk(app.static_folder):
|
|
||||||
for filename in filenames:
|
|
||||||
filepath = os.path.join(dirpath, filename)
|
|
||||||
relative = os.path.relpath(filepath, app.static_folder)
|
|
||||||
with open(filepath, 'rb') as f:
|
|
||||||
static_file_hashes[relative] = hashlib.md5(f.read()).hexdigest()[:8]
|
|
||||||
|
|
||||||
@app.context_processor
|
|
||||||
def override_url_for():
|
|
||||||
def versioned_url_for(endpoint, **values):
|
|
||||||
if endpoint == 'static':
|
|
||||||
filename = values.get('filename')
|
|
||||||
if filename and filename in static_file_hashes:
|
|
||||||
values['v'] = static_file_hashes[filename]
|
|
||||||
return flask.url_for(endpoint, **values)
|
|
||||||
return dict(url_for=versioned_url_for)
|
|
||||||
|
|
||||||
# Add security and caching headers
|
|
||||||
@app.after_request
|
|
||||||
def add_security_headers(response):
|
|
||||||
"""Add security and performance headers to all responses"""
|
|
||||||
# Security headers
|
|
||||||
response.headers['X-Content-Type-Options'] = 'nosniff'
|
|
||||||
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
|
||||||
response.headers['X-XSS-Protection'] = '1; mode=block'
|
|
||||||
response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
|
|
||||||
|
|
||||||
# Cache control for static assets
|
|
||||||
if flask.request.path.startswith('/static/'):
|
|
||||||
response.headers['Cache-Control'] = 'public, max-age=31536000, immutable'
|
|
||||||
elif flask.request.path in ['/sitemap.xml', '/robots.txt']:
|
|
||||||
response.headers['Cache-Control'] = 'public, max-age=86400'
|
|
||||||
else:
|
|
||||||
response.headers['Cache-Control'] = 'no-cache, must-revalidate'
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def load_json(path):
|
|
||||||
with open(path, "r") as f:
|
|
||||||
return json.load(f)
|
|
||||||
|
|
||||||
proj = load_json("./static/json/projects.json")
|
|
||||||
books = load_json("./static/json/books.json")
|
|
||||||
skillList = load_json("./static/json/skills.json")
|
|
||||||
timeline = load_json("./static/json/timeline.json")
|
|
||||||
pages = load_json("./static/json/pages.json")
|
|
||||||
|
|
||||||
pages['projects']['skillList'] = skillList
|
|
||||||
# pages['about']['timeline'] = timeline
|
|
||||||
pages['projects']['projects'] = proj
|
|
||||||
pages['home']['books'] = books
|
|
||||||
pages['books']['books'] = books
|
|
||||||
pages['status']['services'] = SERVICES
|
|
||||||
|
|
||||||
@app.route('/api/status')
|
|
||||||
def api_status():
|
|
||||||
"""API endpoint for service status"""
|
|
||||||
return flask.jsonify(monitor.get_status_summary())
|
|
||||||
|
|
||||||
@app.route('/api/goto/')
|
@app.route('/api/goto/')
|
||||||
@app.route('/api/goto/<location>')
|
@app.route('/api/goto/<location>')
|
||||||
def goto(location='home'):
|
def goto(location='home'):
|
||||||
if location not in pages:
|
|
||||||
flask.abort(404)
|
|
||||||
pagevars = pages[location]
|
pagevars = pages[location]
|
||||||
page = None
|
return [pagevars, flask.render_template(pagevars["template"], var=pagevars)]
|
||||||
try:
|
|
||||||
page = flask.render_template(pagevars["template"], var=pagevars)
|
|
||||||
except Exception:
|
|
||||||
e = HTTPerror.InternalServerError()
|
|
||||||
page = handle_http_error(e)
|
|
||||||
return [pagevars, page]
|
|
||||||
|
|
||||||
def funcGen(pagename, pages):
|
@app.route("/")
|
||||||
def dynamicRule():
|
def home():
|
||||||
try:
|
pagevars = pages["home"]
|
||||||
return flask.render_template('header.html', var=pages[pagename])
|
return flask.render_template("header.html", var=pagevars)
|
||||||
except Exception:
|
|
||||||
e = HTTPerror.InternalServerError()
|
|
||||||
print(e)
|
@app.route("/projects")
|
||||||
return handle_http_error(e)
|
def projects():
|
||||||
return dynamicRule
|
pagevars = pages["projects"]
|
||||||
|
return flask.render_template("header.html", var=pagevars)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/about")
|
||||||
|
def about():
|
||||||
|
pagevars = pages["about"]
|
||||||
|
return flask.render_template("header.html", var=pagevars)
|
||||||
|
|
||||||
for i in pages:
|
|
||||||
func = funcGen(i, pages)
|
|
||||||
app.add_url_rule(pages[i]['canonical'], i, func)
|
|
||||||
|
|
||||||
@app.route("/resume")
|
@app.route("/resume")
|
||||||
@app.route("/Resume.pdf")
|
@app.route("/Resume.pdf")
|
||||||
@app.route("/Resume_Simonson_Andrew.pdf")
|
|
||||||
def resume():
|
def resume():
|
||||||
return flask.send_file("./static/Resume_Simonson_Andrew.pdf")
|
return flask.send_file("./static/Resume.pdf")
|
||||||
|
|
||||||
@app.errorhandler(HTTPerror.HTTPException)
|
|
||||||
def handle_http_error(e):
|
@app.errorhandler(Exception)
|
||||||
|
def page404(e):
|
||||||
eCode = e.code
|
eCode = e.code
|
||||||
message = e.description
|
message = e.description
|
||||||
|
try:
|
||||||
|
message = e.length
|
||||||
|
finally:
|
||||||
pagevars = {
|
pagevars = {
|
||||||
"template": "error.html",
|
"template": "error.html",
|
||||||
"title": f"{eCode} - Simonson",
|
"title": f"{eCode} - Simonson",
|
||||||
"description": "Error on Andrew Simonson's Digital Portfolio",
|
"description": "Error on Andrew Simonson's Digital Portfolio",
|
||||||
"canonical": f"/{eCode}",
|
"canonical": "404",
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
flask.render_template(
|
flask.render_template(
|
||||||
@@ -127,25 +65,6 @@ def handle_http_error(e):
|
|||||||
eCode,
|
eCode,
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.errorhandler(Exception)
|
|
||||||
def handle_generic_error(e):
|
|
||||||
pagevars = {
|
|
||||||
"template": "error.html",
|
|
||||||
"title": "500 - Simonson",
|
|
||||||
"description": "Error on Andrew Simonson's Digital Portfolio",
|
|
||||||
"canonical": "/500",
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
flask.render_template(
|
|
||||||
"header.html",
|
|
||||||
var=pagevars,
|
|
||||||
error=500,
|
|
||||||
message="Internal Server Error",
|
|
||||||
title="500 - Simonson Portfolio",
|
|
||||||
),
|
|
||||||
500,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/sitemap.xml")
|
@app.route("/sitemap.xml")
|
||||||
@app.route("/robots.txt")
|
@app.route("/robots.txt")
|
||||||
@@ -157,7 +76,6 @@ if __name__ == "__main__":
|
|||||||
# import sass
|
# import sass
|
||||||
|
|
||||||
# sass.compile(dirname=("static/scss", "static/css"), output_style="compressed")
|
# sass.compile(dirname=("static/scss", "static/css"), output_style="compressed")
|
||||||
app.run(debug=False)
|
app.run()
|
||||||
else:
|
else:
|
||||||
Minify(app=app, html=True, js=True, cssless=True)
|
Minify(app=app, html=True, js=True, cssless=True)
|
||||||
monitor.start_monitoring()
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
from os import environ as env
|
|
||||||
# automatically updates some dev envs. need to remove for production.
|
|
||||||
try:
|
|
||||||
__import__('envs')
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
256
src/monitor.py
@@ -1,256 +0,0 @@
|
|||||||
"""
|
|
||||||
Service monitoring module
|
|
||||||
Checks service availability and tracks uptime statistics
|
|
||||||
"""
|
|
||||||
import requests
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from threading import Thread, Lock
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Service configuration
|
|
||||||
SERVICES = [
|
|
||||||
{
|
|
||||||
'id': 'main',
|
|
||||||
'name': 'asimonson.com',
|
|
||||||
'url': 'https://asimonson.com',
|
|
||||||
'timeout': 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id': 'files',
|
|
||||||
'name': 'files.asimonson.com',
|
|
||||||
'url': 'https://files.asimonson.com',
|
|
||||||
'timeout': 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id': 'git',
|
|
||||||
'name': 'git.asimonson.com',
|
|
||||||
'url': 'https://git.asimonson.com',
|
|
||||||
'timeout': 10
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
# Check interval: 30 mins
|
|
||||||
CHECK_INTERVAL = 1800
|
|
||||||
|
|
||||||
# File to store status history
|
|
||||||
STATUS_FILE = Path(__file__).parent / 'static' / 'json' / 'status_history.json'
|
|
||||||
|
|
||||||
class ServiceMonitor:
|
|
||||||
def __init__(self):
|
|
||||||
self.status_data = {}
|
|
||||||
self.lock = Lock()
|
|
||||||
self.load_history()
|
|
||||||
|
|
||||||
def load_history(self):
|
|
||||||
"""Load status history from file"""
|
|
||||||
if STATUS_FILE.exists():
|
|
||||||
try:
|
|
||||||
with open(STATUS_FILE, 'r') as f:
|
|
||||||
self.status_data = json.load(f)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error loading status history: {e}")
|
|
||||||
self.initialize_status_data()
|
|
||||||
else:
|
|
||||||
self.initialize_status_data()
|
|
||||||
|
|
||||||
def initialize_status_data(self):
|
|
||||||
"""Initialize empty status data structure"""
|
|
||||||
self.status_data = {
|
|
||||||
'last_check': None,
|
|
||||||
'services': {}
|
|
||||||
}
|
|
||||||
for service in SERVICES:
|
|
||||||
self.status_data['services'][service['id']] = {
|
|
||||||
'name': service['name'],
|
|
||||||
'url': service['url'],
|
|
||||||
'status': 'unknown',
|
|
||||||
'response_time': None,
|
|
||||||
'status_code': None,
|
|
||||||
'last_online': None,
|
|
||||||
'checks': [] # List of check results
|
|
||||||
}
|
|
||||||
|
|
||||||
def save_history(self):
|
|
||||||
"""Save status history to file"""
|
|
||||||
try:
|
|
||||||
STATUS_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
with open(STATUS_FILE, 'w') as f:
|
|
||||||
json.dump(self.status_data, f, indent=2)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error saving status history: {e}")
|
|
||||||
|
|
||||||
def check_service(self, service):
|
|
||||||
"""Check a single service and return status"""
|
|
||||||
start_time = time.time()
|
|
||||||
result = {
|
|
||||||
'timestamp': datetime.now().isoformat(),
|
|
||||||
'status': 'offline',
|
|
||||||
'response_time': None,
|
|
||||||
'status_code': None
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.head(
|
|
||||||
service['url'],
|
|
||||||
timeout=service['timeout'],
|
|
||||||
allow_redirects=True
|
|
||||||
)
|
|
||||||
|
|
||||||
elapsed = int((time.time() - start_time) * 1000) # ms
|
|
||||||
|
|
||||||
result['response_time'] = elapsed
|
|
||||||
result['status_code'] = response.status_code
|
|
||||||
|
|
||||||
# Consider 2xx and 3xx as online
|
|
||||||
if 200 <= response.status_code < 400:
|
|
||||||
result['status'] = 'online'
|
|
||||||
elif 400 <= response.status_code < 500:
|
|
||||||
# Client errors might still mean service is up
|
|
||||||
result['status'] = 'online'
|
|
||||||
else:
|
|
||||||
result['status'] = 'degraded'
|
|
||||||
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
result['status'] = 'timeout'
|
|
||||||
result['response_time'] = service['timeout'] * 1000
|
|
||||||
except Exception as e:
|
|
||||||
result['status'] = 'offline'
|
|
||||||
result['error'] = str(e)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def check_all_services(self):
|
|
||||||
"""Check all services and update status data"""
|
|
||||||
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Checking all services...")
|
|
||||||
|
|
||||||
# Perform all network checks concurrently and OUTSIDE the lock
|
|
||||||
results = {}
|
|
||||||
with ThreadPoolExecutor(max_workers=len(SERVICES)) as executor:
|
|
||||||
futures = {executor.submit(self.check_service, s): s for s in SERVICES}
|
|
||||||
for future in futures:
|
|
||||||
service = futures[future]
|
|
||||||
result = future.result()
|
|
||||||
results[service['id']] = result
|
|
||||||
print(f" {service['name']}: {result['status']} ({result['response_time']}ms)")
|
|
||||||
|
|
||||||
# Only acquire lock when updating the shared data structure
|
|
||||||
with self.lock:
|
|
||||||
for service in SERVICES:
|
|
||||||
result = results[service['id']]
|
|
||||||
service_data = self.status_data['services'][service['id']]
|
|
||||||
|
|
||||||
# Update current status
|
|
||||||
service_data['status'] = result['status']
|
|
||||||
service_data['response_time'] = result['response_time']
|
|
||||||
service_data['status_code'] = result['status_code']
|
|
||||||
|
|
||||||
if result['status'] == 'online':
|
|
||||||
service_data['last_online'] = result['timestamp']
|
|
||||||
|
|
||||||
# Add to check history (keep last 2880 checks = 60 days at 2hr intervals)
|
|
||||||
service_data['checks'].append(result)
|
|
||||||
if len(service_data['checks']) > 2880:
|
|
||||||
service_data['checks'] = service_data['checks'][-2880:]
|
|
||||||
|
|
||||||
self.status_data['last_check'] = datetime.now().isoformat()
|
|
||||||
self.save_history()
|
|
||||||
|
|
||||||
def _calculate_uptime_unlocked(self, service_id, hours=None):
|
|
||||||
"""Calculate uptime percentage for a service (assumes lock is held)"""
|
|
||||||
service_data = self.status_data['services'].get(service_id)
|
|
||||||
if not service_data or not service_data['checks']:
|
|
||||||
return None
|
|
||||||
|
|
||||||
checks = service_data['checks']
|
|
||||||
|
|
||||||
# Filter by time period if specified
|
|
||||||
if hours:
|
|
||||||
cutoff = datetime.now() - timedelta(hours=hours)
|
|
||||||
checks = [
|
|
||||||
c for c in checks
|
|
||||||
if datetime.fromisoformat(c['timestamp']) > cutoff
|
|
||||||
]
|
|
||||||
|
|
||||||
if not checks:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Require minimum data coverage for the time period
|
|
||||||
# Calculate expected number of checks for this period
|
|
||||||
expected_checks = (hours * 3600) / CHECK_INTERVAL
|
|
||||||
|
|
||||||
# Require at least 50% of expected checks to show this metric
|
|
||||||
minimum_checks = max(3, expected_checks * 0.5)
|
|
||||||
|
|
||||||
if len(checks) < minimum_checks:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
# For all-time, require at least 3 checks
|
|
||||||
if len(checks) < 3:
|
|
||||||
return None
|
|
||||||
|
|
||||||
online_count = sum(1 for c in checks if c['status'] == 'online')
|
|
||||||
uptime = (online_count / len(checks)) * 100
|
|
||||||
|
|
||||||
return round(uptime, 2)
|
|
||||||
|
|
||||||
def calculate_uptime(self, service_id, hours=None):
|
|
||||||
"""Calculate uptime percentage for a service"""
|
|
||||||
with self.lock:
|
|
||||||
return self._calculate_uptime_unlocked(service_id, hours)
|
|
||||||
|
|
||||||
def get_status_summary(self):
|
|
||||||
"""Get current status summary with uptime statistics"""
|
|
||||||
with self.lock:
|
|
||||||
summary = {
|
|
||||||
'last_check': self.status_data['last_check'],
|
|
||||||
'next_check': None,
|
|
||||||
'services': []
|
|
||||||
}
|
|
||||||
|
|
||||||
# Calculate next check time
|
|
||||||
if self.status_data['last_check']:
|
|
||||||
last_check = datetime.fromisoformat(self.status_data['last_check'])
|
|
||||||
next_check = last_check + timedelta(seconds=CHECK_INTERVAL)
|
|
||||||
summary['next_check'] = next_check.isoformat()
|
|
||||||
|
|
||||||
for service_id, service_data in self.status_data['services'].items():
|
|
||||||
service_summary = {
|
|
||||||
'id': service_id,
|
|
||||||
'name': service_data['name'],
|
|
||||||
'url': service_data['url'],
|
|
||||||
'status': service_data['status'],
|
|
||||||
'response_time': service_data['response_time'],
|
|
||||||
'status_code': service_data['status_code'],
|
|
||||||
'last_online': service_data['last_online'],
|
|
||||||
'uptime': {
|
|
||||||
'24h': self._calculate_uptime_unlocked(service_id, 24),
|
|
||||||
'7d': self._calculate_uptime_unlocked(service_id, 24 * 7),
|
|
||||||
'30d': self._calculate_uptime_unlocked(service_id, 24 * 30),
|
|
||||||
'all_time': self._calculate_uptime_unlocked(service_id)
|
|
||||||
},
|
|
||||||
'total_checks': len(service_data['checks'])
|
|
||||||
}
|
|
||||||
summary['services'].append(service_summary)
|
|
||||||
|
|
||||||
return summary
|
|
||||||
|
|
||||||
def start_monitoring(self):
|
|
||||||
"""Start background monitoring thread"""
|
|
||||||
def monitor_loop():
|
|
||||||
# Initial check
|
|
||||||
self.check_all_services()
|
|
||||||
|
|
||||||
# Periodic checks
|
|
||||||
while True:
|
|
||||||
time.sleep(CHECK_INTERVAL)
|
|
||||||
self.check_all_services()
|
|
||||||
|
|
||||||
thread = Thread(target=monitor_loop, daemon=True)
|
|
||||||
thread.start()
|
|
||||||
print(f"Service monitoring started (checks every {CHECK_INTERVAL/3600} hours)")
|
|
||||||
|
|
||||||
# Global monitor instance
|
|
||||||
monitor = ServiceMonitor()
|
|
||||||
BIN
src/requirements.txt
Executable file → Normal file
BIN
src/static/Resume.pdf
Normal file
0
src/static/chesscom-embed/default.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
src/static/chesscom-embed/diamonds.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
1070
src/static/css/App.css
Executable file → Normal file
0
src/static/css/checkbox.css
Executable file → Normal file
2
src/static/css/head.css
Executable file → Normal file
@@ -1 +1 @@
|
|||||||
.line:not(:first-child){position:absolute;top:0;left:0}@keyframes glitch1{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(2px);color:#4E9A26}98%{transform:translateX(3px);color:#AC1212}99%{transform:translateX(5px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch2{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(2px);color:#AC1212}99%{transform:translateX(-4px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch3{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(4px);color:#4E9A26}98%{transform:translateX(4px);color:#AC1212}99%{transform:translateX(-3px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch4{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-1px);color:#4E9A26}98%{transform:translateX(-4px);color:#AC1212}99%{transform:translateX(1px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch5{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-2px);color:#4E9A26}98%{transform:translateX(2px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch6{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(0px);color:#4E9A26}98%{transform:translateX(0px);color:#AC1212}99%{transform:translateX(0px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch7{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(0px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(4px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch8{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-4px);color:#4E9A26}98%{transform:translateX(2px);color:#AC1212}99%{transform:translateX(3px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch9{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(3px);color:#AC1212}99%{transform:translateX(3px);color:#fff}100%{transform:translateX(0)}}@keyframes glitch10{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(5px);color:#4E9A26}98%{transform:translateX(4px);color:#AC1212}99%{transform:translateX(4px);color:#fff}100%{transform:translateX(0)}}@keyframes clip{0%{clip-path:polygon(0 100%, 100% 100%, 100% 120%, 0 120%)}100%{clip-path:polygon(0 -20%, 100% -20%, 100% 0%, 0 0)}}
|
.line:not(:first-child){position:absolute;top:0;left:0}.line:nth-child(1){animation:clip 6000ms -600ms linear infinite,glitch1 2500ms -540ms linear infinite}@keyframes glitch1{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-3px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(2){animation:clip 6000ms -1200ms linear infinite,glitch2 2500ms -210ms linear infinite}@keyframes glitch2{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(-2px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(3){animation:clip 6000ms -1800ms linear infinite,glitch3 2500ms -866ms linear infinite}@keyframes glitch3{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(2px);color:#4E9A26}98%{transform:translateX(0px);color:#AC1212}99%{transform:translateX(4px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(4){animation:clip 6000ms -2400ms linear infinite,glitch4 2500ms -60ms linear infinite}@keyframes glitch4{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-3px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(5){animation:clip 6000ms -3000ms linear infinite,glitch5 2500ms -221ms linear infinite}@keyframes glitch5{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(0px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-4px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(6){animation:clip 6000ms -3600ms linear infinite,glitch6 2500ms -716ms linear infinite}@keyframes glitch6{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(5px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(7){animation:clip 6000ms -4200ms linear infinite,glitch7 2500ms -157ms linear infinite}@keyframes glitch7{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(-1px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-2px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(8){animation:clip 6000ms -4800ms linear infinite,glitch8 2500ms -955ms linear infinite}@keyframes glitch8{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(1px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(-4px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(9){animation:clip 6000ms -5400ms linear infinite,glitch9 2500ms -54ms linear infinite}@keyframes glitch9{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(0px);color:#4E9A26}98%{transform:translateX(-3px);color:#AC1212}99%{transform:translateX(-1px);color:#fff}100%{transform:translateX(0)}}.line:nth-child(10){animation:clip 6000ms -6000ms linear infinite,glitch10 2500ms -857ms linear infinite}@keyframes glitch10{0%{transform:translateX(0)}96%{transform:translateX(0);color:#fff}97%{transform:translateX(3px);color:#4E9A26}98%{transform:translateX(1px);color:#AC1212}99%{transform:translateX(2px);color:#fff}100%{transform:translateX(0)}}@keyframes clip{0%{clip-path:polygon(0 100%, 100% 100%, 100% 120%, 0 120%)}100%{clip-path:polygon(0 -20%, 100% -20%, 100% 0%, 0 0)}}
|
||||||
|
|||||||
0
src/static/favicon.ico
Executable file → Normal file
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
BIN
src/static/fonts/INDEPENDENT.ttf
Normal file
0
src/static/fonts/NeonFuture.ttf
Executable file → Normal file
0
src/static/fonts/RobotoCondensed-Regular.ttf
Executable file → Normal file
BIN
src/static/fonts/Starixo.otf
Normal file
0
src/static/fonts/SunsetClub.otf
Executable file → Normal file
0
src/static/icons/email.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
0
src/static/icons/github.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 759 B After Width: | Height: | Size: 759 B |
0
src/static/icons/globe.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
0
src/static/icons/instagram.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 683 B After Width: | Height: | Size: 683 B |
0
src/static/icons/linkedin.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 177 KiB |
0
src/static/icons/menu.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 764 B After Width: | Height: | Size: 764 B |
|
Before Width: | Height: | Size: 9.5 KiB |
@@ -1,432 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="216.75919mm"
|
|
||||||
height="216.7592mm"
|
|
||||||
viewBox="0 0 216.75919 216.7592"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
|
||||||
sodipodi:docname="workingModel.svg"
|
|
||||||
inkscape:export-filename="neonfinal.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#1a1a1a"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:zoom="0.61646116"
|
|
||||||
inkscape:cx="438.79488"
|
|
||||||
inkscape:cy="524.76947"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="32"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1" />
|
|
||||||
<defs
|
|
||||||
id="defs1">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient1"
|
|
||||||
inkscape:collect="always">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffc919;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop1" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#d2ae2d;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop2" />
|
|
||||||
</linearGradient>
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect17"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.9986979,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect16"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,8.0780818,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect9"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.6572028,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect8"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.5495738,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect8-2"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.5495738,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect9-9"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.6572028,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter9"
|
|
||||||
x="-0.030747933"
|
|
||||||
y="-0.028448841"
|
|
||||||
width="1.0614959"
|
|
||||||
height="1.0568977">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="1.1764713"
|
|
||||||
id="feGaussianBlur9" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter29"
|
|
||||||
x="-0.090988795"
|
|
||||||
y="-0.084185358"
|
|
||||||
width="1.1819776"
|
|
||||||
height="1.1683707">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="4.7058852"
|
|
||||||
id="feGaussianBlur29" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter30"
|
|
||||||
x="-0.23378194"
|
|
||||||
y="-0.21630154"
|
|
||||||
width="1.4675639"
|
|
||||||
height="1.4326031">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="13.071903"
|
|
||||||
id="feGaussianBlur30" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter5"
|
|
||||||
x="-0.041894038"
|
|
||||||
y="-0.041893957"
|
|
||||||
width="1.083788"
|
|
||||||
height="1.0837879">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="1.0664778"
|
|
||||||
id="feGaussianBlur5" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter6"
|
|
||||||
x="-0.23389403"
|
|
||||||
y="-0.23389395"
|
|
||||||
width="1.467788"
|
|
||||||
height="1.4677879">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="9.2941268"
|
|
||||||
id="feGaussianBlur6" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter8"
|
|
||||||
x="-0.44132139"
|
|
||||||
y="-0.4413213"
|
|
||||||
width="1.8826427"
|
|
||||||
height="1.8826426">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="10.040917"
|
|
||||||
id="feGaussianBlur8" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter10"
|
|
||||||
x="-0.01629857"
|
|
||||||
y="-0.01629849"
|
|
||||||
width="1.0325971"
|
|
||||||
height="1.032597">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="0.49999998"
|
|
||||||
id="feGaussianBlur10" />
|
|
||||||
</filter>
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect16-7"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,8.0780818,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect17-5"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.9986979,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient1"
|
|
||||||
id="radialGradient2"
|
|
||||||
cx="68.084846"
|
|
||||||
cy="82.277237"
|
|
||||||
fx="68.084846"
|
|
||||||
fy="82.277237"
|
|
||||||
r="51.629848"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-6.8045576,-27.991731)">
|
|
||||||
<circle
|
|
||||||
style="display:none;opacity:1;fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.09141;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;paint-order:markers stroke fill"
|
|
||||||
id="background"
|
|
||||||
cx="115.18415"
|
|
||||||
cy="136.37132"
|
|
||||||
r="108.37959"
|
|
||||||
inkscape:export-filename="withBackground.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#use6"
|
|
||||||
id="use7"
|
|
||||||
style="display:inline;opacity:1;filter:url(#filter8)"
|
|
||||||
inkscape:export-filename="orangOutline.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#star"
|
|
||||||
id="use6"
|
|
||||||
style="display:inline;opacity:0.7;mix-blend-mode:normal;filter:url(#filter6)"
|
|
||||||
inkscape:export-filename="../../Documents/GitHub/asimonson1125.github.io/src/static/icons/neonfinal3.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<g
|
|
||||||
id="star"
|
|
||||||
style="display:inline;opacity:1;filter:url(#filter5)"
|
|
||||||
transform="matrix(1.1617659,0,0,1.1617659,37.635304,39.028635)"
|
|
||||||
inkscape:export-filename="../../Documents/GitHub/asimonson1125.github.io/src/static/icons/neonfinal2.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96">
|
|
||||||
<path
|
|
||||||
style="opacity:1;mix-blend-mode:normal;fill:url(#radialGradient2);fill-opacity:1;fill-rule:evenodd;stroke:#ff9c00;stroke-width:0.860759;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter10)"
|
|
||||||
d="m 65.084856,67.277241 c -1.287779,9.10845 -5.183319,11.28276 -12.694774,12 -7.511459,0.71725 -34.305226,3 -34.305226,3 0,0 25.891549,1.71223 35,3 9.108454,1.28778 11.282748,4.48855 12,12 0.717252,7.511459 3,34.999999 3,34.999999 0,0 1.712221,-25.89154 3,-34.999999 1.28778,-9.10844 4.488549,-11.28275 11.999999,-12 7.51146,-0.71724 34.999995,-3 34.999995,-3 0,0 -25.891545,-1.71222 -34.999995,-3 -9.10845,-1.28777 -11.282748,-4.48854 -11.999999,-12 -0.717252,-7.51145 -3,-34.999998 -3,-34.999998 0,0 -1.71222,25.891548 -3,34.999998 z"
|
|
||||||
id="path3-6-8"
|
|
||||||
sodipodi:nodetypes="sscsscsscsscs" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="neonHighlights"
|
|
||||||
style="display:inline;opacity:0.75"
|
|
||||||
transform="translate(18.455527)">
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#neonTubing"
|
|
||||||
id="use30"
|
|
||||||
style="display:inline;mix-blend-mode:normal;filter:url(#filter30)"
|
|
||||||
transform="translate(-18.455527)" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#neonTubing"
|
|
||||||
id="use29"
|
|
||||||
style="display:inline;mix-blend-mode:normal;filter:url(#filter29)"
|
|
||||||
transform="translate(-18.455527)" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#neonTubing"
|
|
||||||
id="use9"
|
|
||||||
style="opacity:1;filter:url(#filter9)"
|
|
||||||
transform="translate(-18.455527)" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="neonTubing"
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
transform="translate(18.455527)">
|
|
||||||
<g
|
|
||||||
id="g1"
|
|
||||||
style="display:none;stroke:#f8e0c3;stroke-width:3;stroke-dasharray:none;stroke-opacity:1">
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 129.66155,74.179932 H 61.78218 a 9.6572028,9.6572028 135 0 0 -9.657203,9.657203 v 39.314015"
|
|
||||||
id="path1"
|
|
||||||
inkscape:path-effect="#path-effect9"
|
|
||||||
inkscape:original-d="M 129.66155,74.179932 H 52.124977 v 48.971218" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 114.02977,84.567913 H 72.042986 a 7.4885845,7.4885845 134.76763 0 0 -7.488338,7.549325 l 0.186961,23.049192"
|
|
||||||
id="path2"
|
|
||||||
inkscape:path-effect="#path-effect8"
|
|
||||||
inkscape:original-d="M 114.02977,84.567913 H 64.493412 l 0.248197,30.598517"
|
|
||||||
transform="translate(-0.40337459,1.3209286)" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 129.66155,74.179932 H 61.78218 a 9.6572028,9.6572028 135 0 0 -9.657203,9.657203 v 39.314015"
|
|
||||||
id="path1-2"
|
|
||||||
inkscape:path-effect="#path-effect9-9"
|
|
||||||
inkscape:original-d="M 129.66155,74.179932 H 52.124977 v 48.971218"
|
|
||||||
transform="rotate(180,96.802685,134.07745)" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 114.02977,84.567913 H 72.042986 a 7.4885845,7.4885845 134.76763 0 0 -7.488338,7.549325 l 0.186961,23.049192"
|
|
||||||
id="path2-7"
|
|
||||||
inkscape:path-effect="#path-effect8-2"
|
|
||||||
inkscape:original-d="M 114.02977,84.567913 H 64.493412 l 0.248197,30.598517"
|
|
||||||
transform="rotate(180,97.004375,133.41698)" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g33"
|
|
||||||
transform="translate(-7.6804235,-33.343613)"
|
|
||||||
style="display:inline;stroke:#f8e0c3;stroke-width:3;stroke-dasharray:none;stroke-opacity:1">
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 142.67883,102.44998 H 73.832338 a 10.424106,10.424106 142.5 0 0 -10.068913,7.72615 l -6.688692,24.96254"
|
|
||||||
id="path10"
|
|
||||||
inkscape:path-effect="#path-effect17"
|
|
||||||
inkscape:original-d="M 142.67883,102.44998 H 65.83364 l -8.758907,32.68869"
|
|
||||||
transform="translate(-6.0989631,2.1674006)" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 156.95436,91.412872 H 56.426396 A 10.527561,10.527561 142.5 0 0 46.257553,99.2157 l -11.945382,44.58078"
|
|
||||||
id="path11"
|
|
||||||
inkscape:path-effect="#path-effect16"
|
|
||||||
inkscape:original-d="M 156.95436,91.412872 H 48.348314 L 34.312171,143.79648" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 142.67883,102.44998 H 73.832338 a 10.424106,10.424106 142.5 0 0 -10.068913,7.72615 l -6.688692,24.96254"
|
|
||||||
id="path10-6"
|
|
||||||
inkscape:path-effect="#path-effect17-5"
|
|
||||||
inkscape:original-d="M 142.67883,102.44998 H 65.83364 l -8.758907,32.68869"
|
|
||||||
transform="rotate(180,105.40367,165.67111)" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 156.95436,91.412872 H 56.426396 a 10.496229,10.496229 142.41749 0 0 -10.146361,7.808817 l -13.97988,52.781241"
|
|
||||||
id="path11-2"
|
|
||||||
inkscape:path-effect="#path-effect16-7"
|
|
||||||
inkscape:original-d="M 156.95436,91.412872 H 48.348314 L 32.300155,152.00293"
|
|
||||||
transform="rotate(180,103.61221,167.4007)"
|
|
||||||
sodipodi:nodetypes="ccc" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 213 KiB |
@@ -1,432 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="216.75919mm"
|
|
||||||
height="216.7592mm"
|
|
||||||
viewBox="0 0 216.75919 216.7592"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
|
||||||
sodipodi:docname="workingModel.svg"
|
|
||||||
inkscape:export-filename="neonfinal.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#1a1a1a"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:zoom="0.61646116"
|
|
||||||
inkscape:cx="438.79488"
|
|
||||||
inkscape:cy="524.76947"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="32"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1" />
|
|
||||||
<defs
|
|
||||||
id="defs1">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient1"
|
|
||||||
inkscape:collect="always">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffc919;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop1" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#d2ae2d;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop2" />
|
|
||||||
</linearGradient>
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect17"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.9986979,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect16"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,8.0780818,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect9"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.6572028,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect8"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.5495738,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect8-2"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.5495738,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect9-9"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.6572028,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter9"
|
|
||||||
x="-0.030747933"
|
|
||||||
y="-0.028448841"
|
|
||||||
width="1.0614959"
|
|
||||||
height="1.0568977">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="1.1764713"
|
|
||||||
id="feGaussianBlur9" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter29"
|
|
||||||
x="-0.090988795"
|
|
||||||
y="-0.084185358"
|
|
||||||
width="1.1819776"
|
|
||||||
height="1.1683707">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="4.7058852"
|
|
||||||
id="feGaussianBlur29" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter30"
|
|
||||||
x="-0.23378194"
|
|
||||||
y="-0.21630154"
|
|
||||||
width="1.4675639"
|
|
||||||
height="1.4326031">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="13.071903"
|
|
||||||
id="feGaussianBlur30" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter5"
|
|
||||||
x="-0.041894038"
|
|
||||||
y="-0.041893957"
|
|
||||||
width="1.083788"
|
|
||||||
height="1.0837879">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="1.0664778"
|
|
||||||
id="feGaussianBlur5" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter6"
|
|
||||||
x="-0.23389403"
|
|
||||||
y="-0.23389395"
|
|
||||||
width="1.467788"
|
|
||||||
height="1.4677879">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="9.2941268"
|
|
||||||
id="feGaussianBlur6" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter8"
|
|
||||||
x="-0.44132139"
|
|
||||||
y="-0.4413213"
|
|
||||||
width="1.8826427"
|
|
||||||
height="1.8826426">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="10.040917"
|
|
||||||
id="feGaussianBlur8" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
id="filter10"
|
|
||||||
x="-0.01629857"
|
|
||||||
y="-0.01629849"
|
|
||||||
width="1.0325971"
|
|
||||||
height="1.032597">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="0.49999998"
|
|
||||||
id="feGaussianBlur10" />
|
|
||||||
</filter>
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect16-7"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,8.0780818,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="fillet_chamfer"
|
|
||||||
id="path-effect17-5"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.9986979,0,1 @ F,0,0,1,0,0,0,1"
|
|
||||||
radius="0"
|
|
||||||
unit="px"
|
|
||||||
method="auto"
|
|
||||||
mode="F"
|
|
||||||
chamfer_steps="1"
|
|
||||||
flexible="false"
|
|
||||||
use_knot_distance="true"
|
|
||||||
apply_no_radius="true"
|
|
||||||
apply_with_radius="true"
|
|
||||||
only_selected="false"
|
|
||||||
hide_knots="false" />
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient1"
|
|
||||||
id="radialGradient2"
|
|
||||||
cx="68.084846"
|
|
||||||
cy="82.277237"
|
|
||||||
fx="68.084846"
|
|
||||||
fy="82.277237"
|
|
||||||
r="51.629848"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-6.8045576,-27.991731)">
|
|
||||||
<circle
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.09141;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;paint-order:markers stroke fill"
|
|
||||||
id="background"
|
|
||||||
cx="115.18415"
|
|
||||||
cy="136.37132"
|
|
||||||
r="108.37959"
|
|
||||||
inkscape:export-filename="withBackground.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#use6"
|
|
||||||
id="use7"
|
|
||||||
style="display:inline;opacity:1;filter:url(#filter8)"
|
|
||||||
inkscape:export-filename="orangOutline.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#star"
|
|
||||||
id="use6"
|
|
||||||
style="display:inline;opacity:0.7;mix-blend-mode:normal;filter:url(#filter6)"
|
|
||||||
inkscape:export-filename="../../Documents/GitHub/asimonson1125.github.io/src/static/icons/neonfinal3.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<g
|
|
||||||
id="star"
|
|
||||||
style="display:inline;opacity:1;filter:url(#filter5)"
|
|
||||||
transform="matrix(1.1617659,0,0,1.1617659,37.635304,39.028635)"
|
|
||||||
inkscape:export-filename="../../Documents/GitHub/asimonson1125.github.io/src/static/icons/neonfinal2.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96">
|
|
||||||
<path
|
|
||||||
style="opacity:1;mix-blend-mode:normal;fill:url(#radialGradient2);fill-opacity:1;fill-rule:evenodd;stroke:#ff9c00;stroke-width:0.860759;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter10)"
|
|
||||||
d="m 65.084856,67.277241 c -1.287779,9.10845 -5.183319,11.28276 -12.694774,12 -7.511459,0.71725 -34.305226,3 -34.305226,3 0,0 25.891549,1.71223 35,3 9.108454,1.28778 11.282748,4.48855 12,12 0.717252,7.511459 3,34.999999 3,34.999999 0,0 1.712221,-25.89154 3,-34.999999 1.28778,-9.10844 4.488549,-11.28275 11.999999,-12 7.51146,-0.71724 34.999995,-3 34.999995,-3 0,0 -25.891545,-1.71222 -34.999995,-3 -9.10845,-1.28777 -11.282748,-4.48854 -11.999999,-12 -0.717252,-7.51145 -3,-34.999998 -3,-34.999998 0,0 -1.71222,25.891548 -3,34.999998 z"
|
|
||||||
id="path3-6-8"
|
|
||||||
sodipodi:nodetypes="sscsscsscsscs" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="neonHighlights"
|
|
||||||
style="display:inline;opacity:0.75"
|
|
||||||
transform="translate(18.455527)">
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#neonTubing"
|
|
||||||
id="use30"
|
|
||||||
style="display:inline;mix-blend-mode:normal;filter:url(#filter30)"
|
|
||||||
transform="translate(-18.455527)" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#neonTubing"
|
|
||||||
id="use29"
|
|
||||||
style="display:inline;mix-blend-mode:normal;filter:url(#filter29)"
|
|
||||||
transform="translate(-18.455527)" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#neonTubing"
|
|
||||||
id="use9"
|
|
||||||
style="opacity:1;filter:url(#filter9)"
|
|
||||||
transform="translate(-18.455527)" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="neonTubing"
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
transform="translate(18.455527)">
|
|
||||||
<g
|
|
||||||
id="g1"
|
|
||||||
style="display:none;stroke:#f8e0c3;stroke-width:3;stroke-dasharray:none;stroke-opacity:1">
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 129.66155,74.179932 H 61.78218 a 9.6572028,9.6572028 135 0 0 -9.657203,9.657203 v 39.314015"
|
|
||||||
id="path1"
|
|
||||||
inkscape:path-effect="#path-effect9"
|
|
||||||
inkscape:original-d="M 129.66155,74.179932 H 52.124977 v 48.971218" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 114.02977,84.567913 H 72.042986 a 7.4885845,7.4885845 134.76763 0 0 -7.488338,7.549325 l 0.186961,23.049192"
|
|
||||||
id="path2"
|
|
||||||
inkscape:path-effect="#path-effect8"
|
|
||||||
inkscape:original-d="M 114.02977,84.567913 H 64.493412 l 0.248197,30.598517"
|
|
||||||
transform="translate(-0.40337459,1.3209286)" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 129.66155,74.179932 H 61.78218 a 9.6572028,9.6572028 135 0 0 -9.657203,9.657203 v 39.314015"
|
|
||||||
id="path1-2"
|
|
||||||
inkscape:path-effect="#path-effect9-9"
|
|
||||||
inkscape:original-d="M 129.66155,74.179932 H 52.124977 v 48.971218"
|
|
||||||
transform="rotate(180,96.802685,134.07745)" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#f8e0c3;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 114.02977,84.567913 H 72.042986 a 7.4885845,7.4885845 134.76763 0 0 -7.488338,7.549325 l 0.186961,23.049192"
|
|
||||||
id="path2-7"
|
|
||||||
inkscape:path-effect="#path-effect8-2"
|
|
||||||
inkscape:original-d="M 114.02977,84.567913 H 64.493412 l 0.248197,30.598517"
|
|
||||||
transform="rotate(180,97.004375,133.41698)" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g33"
|
|
||||||
transform="translate(-7.6804235,-33.343613)"
|
|
||||||
style="display:inline;stroke:#f8e0c3;stroke-width:3;stroke-dasharray:none;stroke-opacity:1">
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 142.67883,102.44998 H 73.832338 a 10.424106,10.424106 142.5 0 0 -10.068913,7.72615 l -6.688692,24.96254"
|
|
||||||
id="path10"
|
|
||||||
inkscape:path-effect="#path-effect17"
|
|
||||||
inkscape:original-d="M 142.67883,102.44998 H 65.83364 l -8.758907,32.68869"
|
|
||||||
transform="translate(-6.0989631,2.1674006)" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 156.95436,91.412872 H 56.426396 A 10.527561,10.527561 142.5 0 0 46.257553,99.2157 l -11.945382,44.58078"
|
|
||||||
id="path11"
|
|
||||||
inkscape:path-effect="#path-effect16"
|
|
||||||
inkscape:original-d="M 156.95436,91.412872 H 48.348314 L 34.312171,143.79648" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 142.67883,102.44998 H 73.832338 a 10.424106,10.424106 142.5 0 0 -10.068913,7.72615 l -6.688692,24.96254"
|
|
||||||
id="path10-6"
|
|
||||||
inkscape:path-effect="#path-effect17-5"
|
|
||||||
inkscape:original-d="M 142.67883,102.44998 H 65.83364 l -8.758907,32.68869"
|
|
||||||
transform="rotate(180,105.40367,165.67111)" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#ededed;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 156.95436,91.412872 H 56.426396 a 10.496229,10.496229 142.41749 0 0 -10.146361,7.808817 l -13.97988,52.781241"
|
|
||||||
id="path11-2"
|
|
||||||
inkscape:path-effect="#path-effect16-7"
|
|
||||||
inkscape:original-d="M 156.95436,91.412872 H 48.348314 L 32.300155,152.00293"
|
|
||||||
transform="rotate(180,103.61221,167.4007)"
|
|
||||||
sodipodi:nodetypes="ccc" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
14
src/static/js/checkbox.js
Executable file → Normal file
@@ -25,17 +25,3 @@ function toggleCheckbox(dir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function activeSkill(obj) {
|
|
||||||
if (obj.parentElement.classList.contains("activeSkill")) {
|
|
||||||
obj.parentElement.classList.remove("activeSkill");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// document.querySelectorAll(".skill").forEach((x) => {
|
|
||||||
// x.classList.remove("activeSkill");
|
|
||||||
// });
|
|
||||||
while (obj.parentElement.classList.contains("skill")) {
|
|
||||||
obj = obj.parentElement;
|
|
||||||
obj.classList.add("activeSkill");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
2
src/static/js/chessbed.js
Executable file → Normal file
@@ -10,7 +10,7 @@ async function addChessEmbed(username) {
|
|||||||
if (user.status === 200) {
|
if (user.status === 200) {
|
||||||
user = await user.json();
|
user = await user.json();
|
||||||
stats = await stats.json();
|
stats = await stats.json();
|
||||||
const ratings = {
|
ratings = {
|
||||||
rapid: stats.chess_rapid.last.rating,
|
rapid: stats.chess_rapid.last.rating,
|
||||||
blitz: stats.chess_blitz.last.rating,
|
blitz: stats.chess_blitz.last.rating,
|
||||||
bullet: stats.chess_bullet.last.rating,
|
bullet: stats.chess_bullet.last.rating,
|
||||||
|
|||||||
30
src/static/js/idler.js
Executable file → Normal file
@@ -69,39 +69,23 @@ function windowResized() {
|
|||||||
function draw() {
|
function draw() {
|
||||||
background(24);
|
background(24);
|
||||||
|
|
||||||
// Update all balls
|
|
||||||
for (let i = 0; i < balls.length; i++) {
|
for (let i = 0; i < balls.length; i++) {
|
||||||
balls[i].update();
|
balls[i].update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimize line drawing with early distance checks
|
|
||||||
const maxDist = 150;
|
|
||||||
const maxDistSquared = maxDist * maxDist; // Avoid sqrt in distance calculation
|
|
||||||
|
|
||||||
for (let i = 0; i < balls.length - 1; i++) {
|
for (let i = 0; i < balls.length - 1; i++) {
|
||||||
const ball1 = balls[i];
|
|
||||||
for (let j = i + 1; j < balls.length; j++) {
|
for (let j = i + 1; j < balls.length; j++) {
|
||||||
const ball2 = balls[j];
|
let distance = dist(balls[i].x, balls[i].y, balls[j].x, balls[j].y);
|
||||||
|
if (distance < 100){
|
||||||
// Quick rejection test using squared distance (faster than sqrt)
|
|
||||||
const dx = ball2.x - ball1.x;
|
|
||||||
const dy = ball2.y - ball1.y;
|
|
||||||
const distSquared = dx * dx + dy * dy;
|
|
||||||
|
|
||||||
if (distSquared < maxDistSquared) {
|
|
||||||
const distance = Math.sqrt(distSquared); // Only calculate sqrt if needed
|
|
||||||
|
|
||||||
if (distance < 100) {
|
|
||||||
stroke(150);
|
stroke(150);
|
||||||
line(ball1.x, ball1.y, ball2.x, ball2.y);
|
line(balls[i].x, balls[i].y, balls[j].x, balls[j].y);
|
||||||
} else {
|
}
|
||||||
|
else if (distance < 150) {
|
||||||
stroke(100);
|
stroke(100);
|
||||||
const chance = 0.3 ** (((random(0.2) + 0.8) * distance) / 150);
|
let chance = 0.3 ** (((random(0.2) + 0.8) * distance) / 150);
|
||||||
if (chance < 0.5) {
|
if (chance < 0.5) {
|
||||||
stroke(50);
|
stroke(50);
|
||||||
}
|
}
|
||||||
line(ball1.x, ball1.y, ball2.x, ball2.y);
|
line(balls[i].x, balls[i].y, balls[j].x, balls[j].y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
124
src/static/js/responsive.js
Executable file → Normal file
@@ -1,9 +1,77 @@
|
|||||||
function toggleMenu(collapse=false) {
|
window.onload = function () {
|
||||||
if (window.innerWidth < 1400) {
|
onLoaded();
|
||||||
|
};
|
||||||
|
function onLoaded() {
|
||||||
|
document.body.scrollTop = 0; // For Safari
|
||||||
|
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
|
||||||
|
|
||||||
|
window.onresize = function () {
|
||||||
|
resizer();
|
||||||
|
};
|
||||||
|
resizer();
|
||||||
|
if (window.innerWidth < 1200) {
|
||||||
|
const e = document.querySelector(".navControl");
|
||||||
|
e.style.maxHeight = "0px";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizer() {
|
||||||
|
const e = document.querySelector(".navControl");
|
||||||
|
if (window.innerWidth > 1200) {
|
||||||
|
// desktop view
|
||||||
|
scrollFunction();
|
||||||
|
window.onscroll = function () {
|
||||||
|
scrollFunction();
|
||||||
|
};
|
||||||
|
e.style.maxHeight = `${e.scrollHeight + 10}px`;
|
||||||
|
} else {
|
||||||
|
// mobile view
|
||||||
|
window.onscroll = "";
|
||||||
|
document.querySelector(".header").style.backgroundColor = "#1a1a1a";
|
||||||
|
document.querySelectorAll(".header .name h1").forEach(function (x) {
|
||||||
|
x.style.fontSize = "1.5rem";
|
||||||
|
});
|
||||||
|
// document.querySelector('.header > h1').style.color = "#ecebeb";
|
||||||
|
document.querySelector(".header").style.borderBottomWidth = "3px";
|
||||||
|
e.style.maxHeight = "0px";
|
||||||
|
document.querySelectorAll(".navElement *").forEach((x) => {
|
||||||
|
x.style.paddingTop = ".3rem";
|
||||||
|
x.style.paddingBottom = ".3rem";
|
||||||
|
x.style.fontSize = "1rem";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollFunction() {
|
||||||
|
if (document.body.scrollTop > 10 || document.documentElement.scrollTop > 10) {
|
||||||
|
document.querySelector(".header").style.backgroundColor = "#1a1a1a";
|
||||||
|
document.querySelectorAll(".header .name h1").forEach(function (x) {
|
||||||
|
x.style.fontSize = "1.5rem";
|
||||||
|
});
|
||||||
|
document.querySelectorAll(".navElement *").forEach((x) => {
|
||||||
|
x.style.paddingTop = ".3rem";
|
||||||
|
x.style.paddingBottom = ".3rem";
|
||||||
|
x.style.fontSize = "1rem";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.querySelector(".header").style.backgroundColor = "rgba(0,0,0,0)";
|
||||||
|
document.querySelectorAll(".header .name h1").forEach(function (x) {
|
||||||
|
x.style.fontSize = "2rem";
|
||||||
|
});
|
||||||
|
// document.querySelector('.header > h1').style.color = "#ecebeb";
|
||||||
|
document.querySelectorAll(".navElement *").forEach((x) => {
|
||||||
|
x.style.paddingTop = ".5rem";
|
||||||
|
x.style.paddingBottom = ".5rem";
|
||||||
|
x.style.fontSize = "1.2rem";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMenu() {
|
||||||
|
if (window.innerWidth < 1200) {
|
||||||
const e = document.querySelector(".navControl");
|
const e = document.querySelector(".navControl");
|
||||||
const bar = document.querySelector(".header");
|
const bar = document.querySelector(".header");
|
||||||
const isCollapsed = !e.style.maxHeight || e.style.maxHeight === "0px";
|
if (e.style.maxHeight === "0px") {
|
||||||
if (isCollapsed && !collapse) {
|
|
||||||
e.style.maxHeight = `${e.scrollHeight + 10}px`;
|
e.style.maxHeight = `${e.scrollHeight + 10}px`;
|
||||||
bar.style.borderBottomWidth = "0px";
|
bar.style.borderBottomWidth = "0px";
|
||||||
} else {
|
} else {
|
||||||
@@ -13,55 +81,31 @@ function toggleMenu(collapse=false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function goto(location, { push = true } = {}) {
|
async function goto(location, {push=true, toggle=true}={}) {
|
||||||
let a;
|
let a = await fetch("/api/goto/" + location, {
|
||||||
try {
|
|
||||||
a = await fetch("/api/goto/" + location, {
|
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
});
|
});
|
||||||
if (!a.ok) {
|
|
||||||
console.error(`Navigation failed: HTTP ${a.status}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Navigation fetch failed:", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.dispatchEvent(new Event('beforenavigate'));
|
|
||||||
|
|
||||||
const response = await a.json();
|
const response = await a.json();
|
||||||
const metadata = response[0];
|
const metadata = response[0];
|
||||||
const content = response[1];
|
const content = response[1];
|
||||||
const root = document.getElementById("root");
|
let root = document.getElementById("root");
|
||||||
root.innerHTML = content;
|
root.innerHTML = content;
|
||||||
root.querySelectorAll("script").forEach((oldScript) => {
|
root.querySelectorAll("script").forEach((x) => {
|
||||||
const newScript = document.createElement("script");
|
eval(x.innerHTML);
|
||||||
Array.from(oldScript.attributes).forEach(attr => {
|
|
||||||
newScript.setAttribute(attr.name, attr.value);
|
|
||||||
});
|
});
|
||||||
newScript.textContent = oldScript.textContent;
|
document.querySelector("title").textContent = metadata['title'];
|
||||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
window.scrollTo(0, 0);
|
||||||
});
|
if(toggle){
|
||||||
|
toggleMenu();
|
||||||
if (!window.location.href.includes("#")) {
|
|
||||||
window.scrollTo({top: 0, left: 0, behavior:"instant"});
|
|
||||||
} else {
|
|
||||||
const eid = decodeURIComponent(window.location.hash.substring(1));
|
|
||||||
const el = document.getElementById(eid);
|
|
||||||
if (el) el.scrollIntoView();
|
|
||||||
}
|
}
|
||||||
|
if(push){
|
||||||
toggleMenu(collapse=true);
|
history.pushState(null, null, metadata['canonical']);
|
||||||
document.querySelector("title").textContent = metadata["title"];
|
|
||||||
if (push) {
|
|
||||||
history.pushState(null, null, metadata["canonical"]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function backButton() {
|
function backButton() {
|
||||||
const location = window.location.pathname;
|
const location = window.location.pathname;
|
||||||
goto(location.substring(1), { push: false }); // remove slash, goto already does that
|
goto(location.substring(1), {push:false}); // remove slash, goto already does that
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,264 +0,0 @@
|
|||||||
// Fetch and display service status from API
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch status data from server
|
|
||||||
*/
|
|
||||||
async function fetchStatus() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/status');
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
}
|
|
||||||
const data = await response.json();
|
|
||||||
updateStatusDisplay(data);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching status:', error);
|
|
||||||
showError('Failed to fetch service status. Please try again later.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the status display with fetched data
|
|
||||||
*/
|
|
||||||
function updateStatusDisplay(data) {
|
|
||||||
// Update last check time
|
|
||||||
if (data.last_check) {
|
|
||||||
const lastCheck = new Date(data.last_check);
|
|
||||||
const timeString = lastCheck.toLocaleString();
|
|
||||||
document.getElementById('lastUpdate').textContent = `Last checked: ${timeString}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update next check time
|
|
||||||
if (data.next_check) {
|
|
||||||
const nextCheck = new Date(data.next_check);
|
|
||||||
const timeString = nextCheck.toLocaleString();
|
|
||||||
const nextCheckEl = document.getElementById('nextUpdate');
|
|
||||||
if (nextCheckEl) {
|
|
||||||
nextCheckEl.textContent = `Next check: ${timeString}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update each service
|
|
||||||
data.services.forEach(service => {
|
|
||||||
updateServiceCard(service);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update overall status
|
|
||||||
updateOverallStatus(data.services);
|
|
||||||
|
|
||||||
// Re-enable refresh button
|
|
||||||
const refreshBtn = document.getElementById('refreshBtn');
|
|
||||||
if (refreshBtn) {
|
|
||||||
refreshBtn.disabled = false;
|
|
||||||
refreshBtn.textContent = 'Refresh Now';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a single service card
|
|
||||||
*/
|
|
||||||
function updateServiceCard(service) {
|
|
||||||
const card = document.getElementById(`status-${service.id}`);
|
|
||||||
if (!card) return;
|
|
||||||
|
|
||||||
const stateDot = card.querySelector('.state-dot');
|
|
||||||
const stateText = card.querySelector('.state-text');
|
|
||||||
const timeDisplay = document.getElementById(`time-${service.id}`);
|
|
||||||
const codeDisplay = document.getElementById(`code-${service.id}`);
|
|
||||||
const uptimeDisplay = document.getElementById(`uptime-${service.id}`);
|
|
||||||
const checksDisplay = document.getElementById(`checks-${service.id}`);
|
|
||||||
|
|
||||||
// Update response time
|
|
||||||
if (service.response_time !== null) {
|
|
||||||
timeDisplay.textContent = `${service.response_time}ms`;
|
|
||||||
} else {
|
|
||||||
timeDisplay.textContent = '--';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update status code
|
|
||||||
if (service.status_code !== null) {
|
|
||||||
codeDisplay.textContent = service.status_code;
|
|
||||||
} else {
|
|
||||||
codeDisplay.textContent = service.status === 'unknown' ? 'Unknown' : 'Error';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update status indicator
|
|
||||||
card.classList.remove('online', 'degraded', 'offline', 'unknown');
|
|
||||||
|
|
||||||
switch (service.status) {
|
|
||||||
case 'online':
|
|
||||||
stateDot.className = 'state-dot online';
|
|
||||||
stateText.textContent = 'Operational';
|
|
||||||
card.classList.add('online');
|
|
||||||
break;
|
|
||||||
case 'degraded':
|
|
||||||
case 'timeout':
|
|
||||||
stateDot.className = 'state-dot degraded';
|
|
||||||
stateText.textContent = service.status === 'timeout' ? 'Timeout' : 'Degraded';
|
|
||||||
card.classList.add('degraded');
|
|
||||||
break;
|
|
||||||
case 'offline':
|
|
||||||
stateDot.className = 'state-dot offline';
|
|
||||||
stateText.textContent = 'Offline';
|
|
||||||
card.classList.add('offline');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
stateDot.className = 'state-dot loading';
|
|
||||||
stateText.textContent = 'Unknown';
|
|
||||||
card.classList.add('unknown');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update uptime statistics
|
|
||||||
if (uptimeDisplay && service.uptime) {
|
|
||||||
const uptimeHTML = [];
|
|
||||||
|
|
||||||
// Helper function to get color class based on uptime percentage
|
|
||||||
const getUptimeClass = (value) => {
|
|
||||||
if (value === null) return 'text-muted';
|
|
||||||
if (value >= 99) return 'text-excellent';
|
|
||||||
if (value >= 95) return 'text-good';
|
|
||||||
if (value >= 90) return 'text-fair';
|
|
||||||
return 'text-poor';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to format uptime value
|
|
||||||
const formatUptime = (value, label) => {
|
|
||||||
const display = value !== null ? `${value}%` : '--';
|
|
||||||
const colorClass = getUptimeClass(value);
|
|
||||||
return `${label}: <strong class="${colorClass}">${display}</strong>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add all uptime metrics
|
|
||||||
uptimeHTML.push(formatUptime(service.uptime['24h'], '24h'));
|
|
||||||
uptimeHTML.push(formatUptime(service.uptime['7d'], '7d'));
|
|
||||||
uptimeHTML.push(formatUptime(service.uptime['30d'], '30d'));
|
|
||||||
uptimeHTML.push(formatUptime(service.uptime.all_time, 'All'));
|
|
||||||
|
|
||||||
uptimeDisplay.innerHTML = uptimeHTML.join(' | ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update total checks
|
|
||||||
if (checksDisplay && service.total_checks !== undefined) {
|
|
||||||
checksDisplay.textContent = service.total_checks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update overall status bar
|
|
||||||
*/
|
|
||||||
function updateOverallStatus(services) {
|
|
||||||
const overallBar = document.getElementById('overallStatus');
|
|
||||||
const icon = overallBar.querySelector('.summary-icon');
|
|
||||||
const title = overallBar.querySelector('.summary-title');
|
|
||||||
const subtitle = document.getElementById('summary-subtitle');
|
|
||||||
const onlineCount = document.getElementById('onlineCount');
|
|
||||||
const totalCount = document.getElementById('totalCount');
|
|
||||||
|
|
||||||
// Count service statuses
|
|
||||||
const total = services.length;
|
|
||||||
const online = services.filter(s => s.status === 'online').length;
|
|
||||||
const degraded = services.filter(s => s.status === 'degraded' || s.status === 'timeout').length;
|
|
||||||
const offline = services.filter(s => s.status === 'offline').length;
|
|
||||||
|
|
||||||
// Update counts
|
|
||||||
onlineCount.textContent = online;
|
|
||||||
totalCount.textContent = total;
|
|
||||||
|
|
||||||
// Remove all status classes
|
|
||||||
overallBar.classList.remove('online', 'degraded', 'offline');
|
|
||||||
icon.classList.remove('operational', 'partial', 'major', 'loading');
|
|
||||||
|
|
||||||
// Determine overall status
|
|
||||||
if (online === total) {
|
|
||||||
// All systems operational
|
|
||||||
overallBar.classList.add('online');
|
|
||||||
icon.classList.add('operational');
|
|
||||||
icon.textContent = '\u2713';
|
|
||||||
title.textContent = 'All Systems Operational';
|
|
||||||
subtitle.textContent = `All ${total} services are running normally`;
|
|
||||||
} else if (offline >= Math.ceil(total / 2)) {
|
|
||||||
// Major outage (50% or more offline)
|
|
||||||
overallBar.classList.add('offline');
|
|
||||||
icon.classList.add('major');
|
|
||||||
icon.textContent = '\u2715';
|
|
||||||
title.textContent = 'Major Outage';
|
|
||||||
subtitle.textContent = `${offline} service${offline !== 1 ? 's' : ''} offline, ${degraded} degraded`;
|
|
||||||
} else if (offline > 0 || degraded > 0) {
|
|
||||||
// Partial outage
|
|
||||||
overallBar.classList.add('degraded');
|
|
||||||
icon.classList.add('partial');
|
|
||||||
icon.textContent = '\u26A0';
|
|
||||||
title.textContent = 'Partial Outage';
|
|
||||||
if (offline > 0 && degraded > 0) {
|
|
||||||
subtitle.textContent = `${offline} offline, ${degraded} degraded`;
|
|
||||||
} else if (offline > 0) {
|
|
||||||
subtitle.textContent = `${offline} service${offline !== 1 ? 's' : ''} offline`;
|
|
||||||
} else {
|
|
||||||
subtitle.textContent = `${degraded} service${degraded !== 1 ? 's' : ''} degraded`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Unknown state
|
|
||||||
icon.classList.add('loading');
|
|
||||||
icon.textContent = '\u25D0';
|
|
||||||
title.textContent = 'Status Unknown';
|
|
||||||
subtitle.textContent = 'Waiting for service data';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show error message
|
|
||||||
*/
|
|
||||||
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');
|
|
||||||
if (container) {
|
|
||||||
container.insertBefore(errorDiv, container.firstChild);
|
|
||||||
setTimeout(() => errorDiv.remove(), 5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manual refresh
|
|
||||||
*/
|
|
||||||
function refreshStatus() {
|
|
||||||
const refreshBtn = document.getElementById('refreshBtn');
|
|
||||||
if (refreshBtn) {
|
|
||||||
refreshBtn.disabled = true;
|
|
||||||
refreshBtn.textContent = 'Checking...';
|
|
||||||
}
|
|
||||||
fetchStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize on page load
|
|
||||||
*/
|
|
||||||
var statusIntervalId = null;
|
|
||||||
|
|
||||||
function initStatusPage() {
|
|
||||||
// Clear any existing interval from a previous SPA navigation
|
|
||||||
if (statusIntervalId !== null) {
|
|
||||||
clearInterval(statusIntervalId);
|
|
||||||
}
|
|
||||||
fetchStatus();
|
|
||||||
// Auto-refresh every 5 minutes to get latest data
|
|
||||||
statusIntervalId = setInterval(fetchStatus, 300000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up interval when navigating away via SPA
|
|
||||||
document.addEventListener('beforenavigate', () => {
|
|
||||||
if (statusIntervalId !== null) {
|
|
||||||
clearInterval(statusIntervalId);
|
|
||||||
statusIntervalId = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start when page loads
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', initStatusPage);
|
|
||||||
} else {
|
|
||||||
initStatusPage();
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
{
|
|
||||||
"selection": [
|
|
||||||
"The Rational Optimist",
|
|
||||||
"The End of the World is Just the Beginning",
|
|
||||||
"When to Rob a Bank",
|
|
||||||
"Freakonomics",
|
|
||||||
"The Accidental Superpower",
|
|
||||||
"Verbal Judo",
|
|
||||||
"Zero To One"
|
|
||||||
],
|
|
||||||
"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."
|
|
||||||
},
|
|
||||||
"The Rational Optimist": {
|
|
||||||
"filename": "ratOpt.jpg",
|
|
||||||
"link": "https://www.amazon.com/Rational-Optimist-Prosperity-Evolves-P-s/dp/0061452068",
|
|
||||||
"review": "The story of humanity's growth with a refined variation of a lens I very much believe deserves greater publicity. Long, yes, but ultimately one of the most important books for me to have read."
|
|
||||||
},
|
|
||||||
"Freakonomics": {
|
|
||||||
"filename": "freakonomics.jpeg",
|
|
||||||
"link": "https://freakonomics.com/books/",
|
|
||||||
"review": "The original on cracked economics. Dozens of case studies heavy on the unexpected results of incentives. Mind Expanding. Very nice."
|
|
||||||
},
|
|
||||||
"Superfreakonomics": {
|
|
||||||
"filename": "superfreakonomics.jpeg",
|
|
||||||
"link": "https://freakonomics.com/books/",
|
|
||||||
"review": "More of the goods? Excellent. A number of case studies on finding simple solutions to wicked problems through changing the attack vector in brainstorming. Another fun read."
|
|
||||||
},
|
|
||||||
"When to Rob a Bank": {
|
|
||||||
"filename": "whenToRobABank.jpeg",
|
|
||||||
"link": "https://freakonomics.com/books/",
|
|
||||||
"review": "Collection of blog posts. Good for short reading sessions. I read this one first, I thought the Freakonomics mode of thinking was hilarious. Classic."
|
|
||||||
},
|
|
||||||
"Think like a Freak": {
|
|
||||||
"filename": "thinkLikeAFreak.jpg",
|
|
||||||
"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."
|
|
||||||
},
|
|
||||||
"The Tyranny of Metrics": {
|
|
||||||
"filename": "TyrannyOfMetrics.jpg",
|
|
||||||
"link": "https://www.amazon.com/Tyranny-Metrics-Jerry-Z-Muller/dp/0691174954",
|
|
||||||
"review": "Library find. Very appreciated read given my field of study. Adds a new lens on the cost of information and how it impacts us from the cube office to the oval office."
|
|
||||||
},
|
|
||||||
"The Accidental Superpower": {
|
|
||||||
"filename": "theAccidentalSuperpower.jpeg",
|
|
||||||
"link": "https://zeihan.com/",
|
|
||||||
"review": "My intro to geopolitics, brilliant hook that made possibly my fastest read ever. The straightforward the lines of reasoning and grounding in concrete realities that few would consider controversial is incredible, even when the conclusions seem hyper-dramatic."
|
|
||||||
},
|
|
||||||
"The Absent Superpower": {
|
|
||||||
"filename": "theAbsentSuperpower.jpeg",
|
|
||||||
"link": "https://zeihan.com/",
|
|
||||||
"review": "Hadn't realized the author was the same as Accidental when I bought it. It covered a lot of the same stuff but with a heavy emphasis on fracking technology which was a bit slow but it solidified the veracity of Zeihan's worldview."
|
|
||||||
},
|
|
||||||
"Disunited Nations": {
|
|
||||||
"filename": "disunitedNations.jpeg",
|
|
||||||
"link": "https://zeihan.com/disunited-nations/",
|
|
||||||
"review": "Chapter profiles of key countries in the world provides lots of interesting supporting details, but being the third book of its kind made the rehashing a bit of a drag."
|
|
||||||
},
|
|
||||||
"The End of the World is Just the Beginning": {
|
|
||||||
"filename": "theEndOfTheWorldIsJustTheBeginning.jpeg",
|
|
||||||
"link": "https://zeihan.com/end-of-the-world/",
|
|
||||||
"review": "Separates itself from the others by Zeihan in its breakdown of specific resources of every category, from nickel to corn. Being generalized, specific, and an enjoyable read all at the same time. Galaxy Brain stuff."
|
|
||||||
},
|
|
||||||
"The Storm Before the Calm": {
|
|
||||||
"filename": "theStormBeforeTheCalm.jpeg",
|
|
||||||
"link": "https://www.amazon.com/Storm-Before-Calm-Americas-Discord/dp/1101911786/",
|
|
||||||
"review": "Repetitive and almost faith-based. The cycles and cultural inclinations used as the backbone for predictions may be accurate, but the evidence for this was slim. Nonetheless, the cultural archetypes laid out here have stuck with me, albeit not for political fortune-telling purposes."
|
|
||||||
},
|
|
||||||
"No, They Can't": {
|
|
||||||
"filename": "no-they-cant.jpeg",
|
|
||||||
"link": "https://www.goodreads.com/book/show/13260131-no-they-can-t",
|
|
||||||
"review": "I much preferred Give Me A Break (which I read first). Sossel's writing style in this book is less developed - it feels aimless and with an intent to lecture."
|
|
||||||
},
|
|
||||||
"Give Me a Break": {
|
|
||||||
"filename": "giveMeABreak.jpeg",
|
|
||||||
"link": "https://www.amazon.com/Give-Me-Break-Exposed-Hucksters-ebook/dp/B000FC2NF8/",
|
|
||||||
"review": "I expected a boring autobiography-type book, but instead is a glimpse inside Stossel's work that transformed itself as it transformed his view. Was very happy to see a figure of similar personal ideology. Probably made it a little too easy to swallow that pill."
|
|
||||||
},
|
|
||||||
"Reign of Terror": {
|
|
||||||
"filename": "reignofterror.jpg",
|
|
||||||
"link": "https://www.amazon.com/Reign-Terror-Destabilized-America-Produced/dp/1984879774",
|
|
||||||
"review": "Packed with real news events and first person accounts, Reign of Terror chronicles the story of politics and intelligence agencies during the War on Terror. In typical journalist fashion, the populist (read: racist) cause for the events is mostly conjecture to fit a progressive narrative. Nonetheless, a comprehensive history of malpractice in public office."
|
|
||||||
},
|
|
||||||
"Zero To One": {
|
|
||||||
"filename": "zeroToOne.jpeg",
|
|
||||||
"link": "https://www.amazon.com/Zero-One-Notes-Startups-Future/dp/0804139296",
|
|
||||||
"review": "Initially very frustrating read. Took a bit of internal review to realize that this has to be read with an entrepreneurial perspective, not a consumer one. After that, it's quite eye-opening in ways that would've been obvious without the lens of life experience. The optimistic takes are very much appreciated and their justifications are solid."
|
|
||||||
},
|
|
||||||
"Courage is Calling": {
|
|
||||||
"filename": "courageIsCalling.jpeg",
|
|
||||||
"link": "https://www.amazon.com/Courage-Calling-Fortune-Favors-Brave/dp/B094PMPCBT/",
|
|
||||||
"review": "Average on the readability scale, but inspiring all the same. As a primer in stoicism it gets a 8/10 for not enough gigachad energy to match the gigachad content."
|
|
||||||
},
|
|
||||||
"Discipline Is Destiny": {
|
|
||||||
"filename": "disciplineIsDestiny.jpg",
|
|
||||||
"link": "https://www.amazon.com/Discipline-Destiny-Power-Self-Control-Virtues/dp/0593191692",
|
|
||||||
"review": "Much like the first in its series - small chapters (very helpful) each with inspiring insider stories of figures of history. Anyone capable of learning from these figures would benefit greatly from implementing the virtues in this series."
|
|
||||||
},
|
|
||||||
"Right Thing, Right Now": {
|
|
||||||
"filename": "rightthingrightnow.png",
|
|
||||||
"link": "https://www.amazon.com/Right-Thing-Now-Values-Character/dp/0593191714",
|
|
||||||
"review": "As the third in its series, the virtue of justice derives a large portion of its meaning from the previous two. While still an good read with a valuable influence for personal growth, it lacks a distinction between justice as a virtue and fighting for the right cause. Some sections preach for ideological purity while others insist on pragmatism which is a pretty important detail regarding justice."
|
|
||||||
},
|
|
||||||
"On Grand Strategy": {
|
|
||||||
"filename": "onGrandStrategy.jpeg",
|
|
||||||
"link": "https://www.amazon.com/Grand-Strategy-John-Lewis-Gaddis/dp/1594203512",
|
|
||||||
"review": "Book for the academically-inclined. Not fun to read. Big words scary. It's insightful to be sure but I wouldn't read it again. The message on conceptual contradictions has stuck with me. Quite the brain food."
|
|
||||||
},
|
|
||||||
"David and Goliath": {
|
|
||||||
"filename": "davidAndGoliath.png",
|
|
||||||
"link": "https://www.amazon.com/David-Goliath-Underdogs-Misfits-Battling/dp/0316239852/",
|
|
||||||
"review": "Book contains takes that may not be hot, but *are* incredibly based. In a sentence: Goliath is only the giant from the wrong perspectives. The only reason it's not one of my favorites is that it's tamer than the aggressively standoffish and hilarious."
|
|
||||||
},
|
|
||||||
"The Scout Mindset": {
|
|
||||||
"filename": "scoutMindset.png",
|
|
||||||
"link": "https://www.amazon.com/Scout-Mindset-People-Things-Clearly-ebook/dp/B07L2HQ26K/",
|
|
||||||
"review": "Felt like a list of things that I already do that I should be more mindful of. Maybe that's just me. There was some interesting mental probablism sprinkled in the first half but the second half did not have much new to say. Good but not eye-opening."
|
|
||||||
},
|
|
||||||
"Verbal Judo": {
|
|
||||||
"filename": "verbalJudo.png",
|
|
||||||
"link": "https://www.amazon.com/Verbal-Judo-Second-Gentle-Persuasion-ebook/dp/B00FJ3CMI6/",
|
|
||||||
"review": "Book tries to hook you into reading it even when you're already halfway through reading it. And it works! Definitely a good book to review occasionally to keep yourself grounded during tense moments."
|
|
||||||
},
|
|
||||||
"You Can Read Anyone": {
|
|
||||||
"filename": "youCanReadAnyone.jpg",
|
|
||||||
"link": "https://www.amazon.com/YOU-READ-ANYONE-David-Lieberman-ebook/dp/B001J6OV0Y",
|
|
||||||
"review": "Not as page-turning as many of the others and clearly not as memorable. The techniques pique curiosity but are difficult to use without practice."
|
|
||||||
},
|
|
||||||
"The Parasitic Mind": {
|
|
||||||
"filename": "theParasiticMind.jpeg",
|
|
||||||
"link": "https://www.amazon.com/Parasitic-Mind-Infectious-Killing-Common/dp/1684512298/",
|
|
||||||
"review": "The humor is the most memorable part but the concepts are no slouches. The contemporary culture war basis makes it tricky to talk about, but it absolutely should be discussed."
|
|
||||||
},
|
|
||||||
"Profiles in Courage": {
|
|
||||||
"filename": "profilesInCourage.jpeg",
|
|
||||||
"link": "https://www.amazon.com/Profiles-Courage-John-F-Kennedy/dp/0060854936",
|
|
||||||
"review": "Another book that was hard to really get into but still provided fascinating commentary on some very important figures that have faded from public memory."
|
|
||||||
},
|
|
||||||
"Where Good Ideas Come From": {
|
|
||||||
"filename": "where-good-ideas-come-from.png",
|
|
||||||
"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/",
|
|
||||||
"review": "Another read with challenging academic vernacular. I had a agreeable-hate relationship with the biblical storytelling that made me somewhat dread reading yet also question the nature of religion. Another example of books with good advice being not fun to get into."
|
|
||||||
},
|
|
||||||
"Beyond Order: 12 More Rules for Life": {
|
|
||||||
"filename": "BeyondOrder.jpg",
|
|
||||||
"link": "https://www.amazon.com/Beyond-Order-More-Rules-Life/dp/0593084640",
|
|
||||||
"review": "It bothers how much these books make me stop and think because it throws me out of the focus of actually reading. More solid advice with less religion than the predecessor but retaining the 'difficult to want to read' badge."
|
|
||||||
},
|
|
||||||
"The Hitchhiker's Guide to the Galaxy": {
|
|
||||||
"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."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
28
src/static/json/pages.json
Executable file → Normal file
@@ -5,34 +5,16 @@
|
|||||||
"description": "Andrew Simonson's Digital Portfolio home",
|
"description": "Andrew Simonson's Digital Portfolio home",
|
||||||
"canonical": "/"
|
"canonical": "/"
|
||||||
},
|
},
|
||||||
"status": {
|
|
||||||
"template": "status.html",
|
|
||||||
"title":"Andrew Simonson - Status Page",
|
|
||||||
"description": "Status page for my services",
|
|
||||||
"canonical": "/status"
|
|
||||||
},
|
|
||||||
"projects": {
|
"projects": {
|
||||||
"template": "projects.html",
|
"template": "projects.html",
|
||||||
"title": "Andrew Simonson - Projects",
|
"title": "Andrew Simonson - Projects",
|
||||||
"description": "Recent projects by Andrew Simonson on his lovely portfolio website :)",
|
"description": "Recent projects by Andrew Simonson on his lovely portfolio website :)",
|
||||||
"canonical": "/projects"
|
"canonical": "/projects"
|
||||||
},
|
},
|
||||||
"books": {
|
"about": {
|
||||||
"template": "books.html",
|
"template": "about.html",
|
||||||
"title": "Andrew Simonson - Bookshelf",
|
"title": "Andrew Simonson - About Me",
|
||||||
"description": "Some of the books I've read",
|
"description": "About Andrew Simonson",
|
||||||
"canonical": "/books"
|
"canonical": "/about"
|
||||||
},
|
|
||||||
"duck": {
|
|
||||||
"template": "duck.html",
|
|
||||||
"title":"You've been ducked!",
|
|
||||||
"description": "Face it, you've been ducked",
|
|
||||||
"canonical": "/duck"
|
|
||||||
},
|
|
||||||
"certificates": {
|
|
||||||
"template": "certs.html",
|
|
||||||
"title": "Certificates and Awards",
|
|
||||||
"description": "Certificates and Awards Listing",
|
|
||||||
"canonical": "/certs"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
123
src/static/json/projects.json
Executable file → Normal file
@@ -1,47 +1,19 @@
|
|||||||
{
|
{
|
||||||
"Antietam-Conococheague Watershed Monitoring": {
|
"Lower 48 Alt. Energy Map": {
|
||||||
"status": "complete",
|
|
||||||
"classes": "geospacial",
|
|
||||||
"bgi": "watershedTemps.png",
|
|
||||||
"content": "Geospacial analysis of Maryland's Antietam and Conococheague sub-watersheds, monitoring water quality and temperatures through the summer months for reporting to governmental review boards for environmental protection"
|
|
||||||
},
|
|
||||||
"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."
|
|
||||||
},
|
|
||||||
"RIT Hotspots": {
|
|
||||||
"status": "incomplete",
|
|
||||||
"classes": "pinned geospacial 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.",
|
|
||||||
"links": [
|
|
||||||
["github", "https://github.com/asimonson1125/hotspotsrit", "git repo"]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Calorimetry Analysis Engineering": {
|
|
||||||
"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",
|
|
||||||
"links": []
|
|
||||||
},
|
|
||||||
"Geography of Alternative Energy": {
|
|
||||||
"status": "complete",
|
"status": "complete",
|
||||||
"classes": "pinned geospacial",
|
"classes": "pinned geospacial",
|
||||||
"bgi": "energyGeography.png",
|
"bgi": "geovisF.png",
|
||||||
"content": "An ArcGIS geospacial analysis comparing the difference in effectiveness of wind, solar, and geothermal energy across the continental 48 United States.",
|
"content": "ArcGIS Map of the most effective alternative energy sources in the continental United States",
|
||||||
"links": [
|
"links": [
|
||||||
[
|
[
|
||||||
"globe",
|
"globe",
|
||||||
"https://ritarcgis.maps.arcgis.com/apps/dashboards/17d5bda01edc4a2eb6205a4922d889c9",
|
"https://ritarcgis.maps.arcgis.com/apps/dashboards/17d5bda01edc4a2eb6205a4922d889c9",
|
||||||
"Dashboard"
|
"ArcGIS"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"OccupyRIT": {
|
"OccupyRIT": {
|
||||||
"status": "complete",
|
"status": "WIP",
|
||||||
"classes": "pinned programming",
|
"classes": "pinned programming",
|
||||||
"bgi": "occupyRIT.png",
|
"bgi": "occupyRIT.png",
|
||||||
"content": "Collects RIT Gym Occupancy data, determining busiest workout times",
|
"content": "Collects RIT Gym Occupancy data, determining busiest workout times",
|
||||||
@@ -49,17 +21,33 @@
|
|||||||
["github", "https://github.com/asimonson1125/Occupy-RIT", "git repo"]
|
["github", "https://github.com/asimonson1125/Occupy-RIT", "git repo"]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Portfolio Website": {
|
"Chesscom Embeds": {
|
||||||
|
"status": "complete",
|
||||||
|
"classes": "programming",
|
||||||
|
"bgi": "chessbed.png",
|
||||||
|
"content": "A template for creating Chess.com user profile embeds",
|
||||||
|
"links": [
|
||||||
|
["github", "https://github.com/asimonson1125/chesscom-embed", "git repo"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Resume": {
|
||||||
"status": "WIP",
|
"status": "WIP",
|
||||||
"classes": "programming",
|
"classes": "programming",
|
||||||
"content": "This website is my personal sandbox where I've integrated some of my data projects via docker cluster. It is self hosted and zero-trust secure while remaining dynamic and free of the tech debt that comes with pre-designed sites and excessive framework application. Yeah, I can do E2E.",
|
"bgi": "resume.png",
|
||||||
|
"content": "My Resume, made in LaTeX with a custom design derived by the AltaCV template on OverLeaf",
|
||||||
"links": [
|
"links": [
|
||||||
["globe", "https://asimonson.com", "Homepage"],
|
["github", "https://github.com/asimonson1125/Resume", "git repo"],
|
||||||
[
|
["globe", "https://asimonson.com/Resume.pdf", "Resume"]
|
||||||
"github",
|
|
||||||
"https://github.com/asimonson1125/asimonson1125.github.io",
|
|
||||||
"git repo"
|
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"Digital Portfolio": {
|
||||||
|
"status": "WIP",
|
||||||
|
"classes": "programming",
|
||||||
|
"bgi": "website.png",
|
||||||
|
"content": "My personal portfolio website (you're on it now!)",
|
||||||
|
"links": [
|
||||||
|
["github", "https://github.com/asimonson1125/asimonson1125.github.io", "git repo"],
|
||||||
|
["globe", "https://asimonson.com", "site link"]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Slate": {
|
"Slate": {
|
||||||
@@ -68,21 +56,62 @@
|
|||||||
"bgi": "slate.png",
|
"bgi": "slate.png",
|
||||||
"content": "Slate is a web app designed to help event coordinators schedule events by congregating participant calendar data. Includes Computer Science House account integration",
|
"content": "Slate is a web app designed to help event coordinators schedule events by congregating participant calendar data. Includes Computer Science House account integration",
|
||||||
"links": [
|
"links": [
|
||||||
["globe", "https://slate.csh.rit.edu", "site link"],
|
["github", "https://github.com/asimonson1125/Slate", "git repo"],
|
||||||
["github", "https://github.com/asimonson1125/Slate", "git repo"]
|
["globe", "https://slate.csh.rit.edu", "site link"]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Monte Carlo Engine for NationsGame": {
|
"HvZ Bot": {
|
||||||
|
"status": "complete",
|
||||||
|
"classes": "programming",
|
||||||
|
"bgi": "",
|
||||||
|
"content": "A Discord bot to handle role management and statistics for RIT's Humans vs. Zombies games",
|
||||||
|
"links": [
|
||||||
|
["github", "https://github.com/asimonson1125/HvZ-bot", "git repo"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"FinTech": {
|
||||||
|
"status": "WIP",
|
||||||
|
"classes": "pinned programming",
|
||||||
|
"bgi": "",
|
||||||
|
"content": "A team derived from the RIT Financial Management Association dedicated to learning about financial management of equities using automated solutions developed by students",
|
||||||
|
"links": [
|
||||||
|
["github", "https://github.com/LukeHorigan/Financial-Management-Assocation-", "git repo"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Browser Trivia Bot": {
|
||||||
|
"status": "complete",
|
||||||
|
"classes": "programming",
|
||||||
|
"bgi": "",
|
||||||
|
"content": "A tampermonkey tool used to automatically answer and submit online trivia forms, which can be tailored to different site layouts. Source currently private.",
|
||||||
|
"links": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NationsGame Rolls Sim": {
|
||||||
"status": "complete",
|
"status": "complete",
|
||||||
"classes": "programming",
|
"classes": "programming",
|
||||||
"bgi": "ceoOfYugo.png",
|
"bgi": "ceoOfYugo.png",
|
||||||
"content": "A simulator for the browser game, NationsGame, to analyze unit composition and predict in-game victors and unit statistics. Unfortunately, NationsGame is now defunct. Limited screenshots of functionality.",
|
"content": "A simulator for the browser game, NationsGame, to analyze unit composition and predict in-game victors and unit statistics. Unfortunately, NationsGame is now defunct. Limited screenshots of functionality.",
|
||||||
"links": [
|
"links": [
|
||||||
[
|
["github", "https://github.com/asimonson1125/NG-Rolls-Simulator", "git repo"]
|
||||||
"github",
|
|
||||||
"https://github.com/asimonson1125/NG-Rolls-Simulator",
|
|
||||||
"git repo"
|
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"VEXcode Button Engine": {
|
||||||
|
"status": "complete",
|
||||||
|
"classes": "programming",
|
||||||
|
"bgi": "vexcodeButtons.jpeg",
|
||||||
|
"content": "VEXcode button library + examples and template for the VEX V5 brain",
|
||||||
|
"links": [
|
||||||
|
["github", "https://github.com/asimonson1125/VEXcode-Button-Generator", "git repo"],
|
||||||
|
["globe", "https://www.vexforum.com/t/vexcode-button-generator/72450", "Forum post"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"WinKeylogger": {
|
||||||
|
"status": "complete",
|
||||||
|
"classes": "programming",
|
||||||
|
"bgi": "",
|
||||||
|
"content": "A C++ keylogger for windows based off a Udemy course with my custom modifications and powershell script",
|
||||||
|
"links": [
|
||||||
|
["github", "https://github.com/asimonson1125/WinKeylogger", "git repo"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"Tools": {
|
|
||||||
"Microsoft Azure": {
|
|
||||||
"Databricks": {},
|
|
||||||
"Data Factory": {},
|
|
||||||
"Stream Analytics": {}
|
|
||||||
},
|
|
||||||
"Databricks": {},
|
|
||||||
"Apache Spark": {},
|
|
||||||
"Visual Basic for Applications (Excel)": {}
|
|
||||||
},
|
|
||||||
"Data and AI": {
|
|
||||||
"Python": {
|
|
||||||
"PyTorch/TensorFlow": {},
|
|
||||||
"Numpy/Pandas": {},
|
|
||||||
"Scikit/Sklearn": {},
|
|
||||||
"Selenium/BS4": {},
|
|
||||||
"Pyspark": {}
|
|
||||||
},
|
|
||||||
"R": {},
|
|
||||||
"SQL": {}
|
|
||||||
},
|
|
||||||
"Frontend": {
|
|
||||||
"Flask (Python)": {},
|
|
||||||
"React (Javascript)": {},
|
|
||||||
"SASS/SCSS": {}
|
|
||||||
},
|
|
||||||
"Backend & DevOps": {
|
|
||||||
"Backend": {
|
|
||||||
"Rust": {},
|
|
||||||
"C#": {}
|
|
||||||
},
|
|
||||||
"DevOps": {
|
|
||||||
"Docker": {},
|
|
||||||
"Microsoft Azure": {},
|
|
||||||
"Kubernetes/Openshift": {},
|
|
||||||
"Cloudflare": {},
|
|
||||||
"Bash": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
src/static/json/timeline.json
Executable file → Normal file
@@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"Rochester Institute of Technology": {
|
"Rochester Institute of Technology": {
|
||||||
"classes": "pinned education technical",
|
"classes": "pinned education technical",
|
||||||
"date": "08/2021 - 12/2024",
|
"date": "08/2021 - 05/2025",
|
||||||
"content": "Studying in Rochester Institute of Technology's Computer Science BS program with a minor in International Relations."
|
"content": "Studying in Rochester Institute of Technology's Computer Science BS program with a minor in International Relations."
|
||||||
},
|
},
|
||||||
"Pretzel & Pizza Creations": {
|
"Pretzel & Pizza Creations": {
|
||||||
|
|||||||
BIN
src/static/photos/AcademicTeam.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
src/static/photos/WeThePeople.jpg
Normal file
|
After Width: | Height: | Size: 813 KiB |
BIN
src/static/photos/WeThePeople_Districts.jpg
Normal file
|
After Width: | Height: | Size: 898 KiB |
BIN
src/static/photos/WeThePeople_NationalsSetup.jpg
Normal file
|
After Width: | Height: | Size: 463 KiB |
|
Before Width: | Height: | Size: 987 B |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 620 B |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 320 KiB |
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 235 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 71 KiB |
0
src/static/photos/projects/ceoOfYugo.png → src/static/photos/ceoOfYugo.png
Executable file → Normal file
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
BIN
src/static/photos/chessbed.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 203 KiB |