[2025-04-13] Docker setup
This commit is contained in:
parent
c05e550f74
commit
aa8c36d4b9
7 changed files with 23 additions and 0 deletions
12
build/Dockerfile
Normal file
12
build/Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
FROM python:3-slim
|
||||
|
||||
WORKDIR usr/src/app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["gunicorn","--config", "gunicorn_config.py", "year-progress:app"]
|
9
build/gunicorn_config.py
Normal file
9
build/gunicorn_config.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
import os
|
||||
|
||||
workers = int(os.environ.get('GUNICORN_PROCESSES', '2'))
|
||||
threads = int(os.environ.get('GUNICORN_THREADS', '4'))
|
||||
# timeout = int(os.environ.get('GUNICORN_TIMEOUT', '120'))
|
||||
bind = os.environ.get('GUNICORN_BIND', '0.0.0.0:8080')
|
||||
|
||||
forwarded_allow_ips = '*'
|
||||
secure_scheme_headers = { 'X-Forwarded-Proto': 'https' }
|
2
build/requirements.txt
Normal file
2
build/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Flask==3.1.0
|
||||
Gunicorn==23.0.0
|
46
build/templates/index.html
Normal file
46
build/templates/index.html
Normal file
|
@ -0,0 +1,46 @@
|
|||
<html>
|
||||
<head>
|
||||
<link href="{{ cssPath }}" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1></h1>
|
||||
<h2><strong><strong>It is currently <button>{{ nowTime }}</button> on <button>{{ nowDate }}</button> <a style="text-decoration: none;" href="">↻<a></strong></strong></h2>
|
||||
<h3>
|
||||
We are <button>{{ yearPercent }}%</button> of the way through {{ year }}<br>
|
||||
<progress max="100" value="{{ yearPercent }}">{{ yearPercent }}%</progress>
|
||||
</h3>
|
||||
<strong>To put that into context, if the year was:</strong><br>
|
||||
<br>
|
||||
<div class="left">
|
||||
⚽ a 90 minute football match, it would be the {{ footballMinute }}th minute
|
||||
</div><br>
|
||||
<div class="right">
|
||||
🕘 a 40 hour work week, it would be {{ workTime }} on {{ workDay }}
|
||||
</div><br>
|
||||
<div class="left">
|
||||
🏃 a marathon, you would have {{ marathonMile }} miles to go
|
||||
</div><br>
|
||||
<div class="right">
|
||||
🪲 Abbey Road, you would be listening to "{{ abbeyRoadSong }}"
|
||||
</div><br>
|
||||
<div class="left">
|
||||
🗡️ The Hobbit, Bilbo would be {{ hobbitChapter }}
|
||||
</div><br>
|
||||
<div class="right">
|
||||
✈️ a flight from London to New York, you would be about <a href="https://www.openstreetmap.org/?mlat={{ lat }}&mlon={{ long }}.00#map=4/{{ lat }}/{{ long }}">here</a>
|
||||
</div><br>
|
||||
<div class="left">
|
||||
🧬 the history of life on Earth, {{ homoSapiens }}
|
||||
</div><br>
|
||||
<br>
|
||||
<div class="footer">
|
||||
<a href="https://git.cnln.dev/andrew/year-progress/issues">📨 Suggestions?</a> |
|
||||
<a href="https://git.cnln.dev/andrew/year-progress">🧑💻 Source</a> |
|
||||
<a href="https://concrete.style">🎨 Style</a> |
|
||||
<a href="https://ko-fi.com/andrewconlin">☕ Buy me a coffee!</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
18
build/templates/init.html
Normal file
18
build/templates/init.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<html>
|
||||
<div style="visibility: hidden">
|
||||
<form id="form" method="POST" action="/">
|
||||
<input name="timestamp" id="timestamp" type="number"></input>
|
||||
</form>
|
||||
</div>
|
||||
</html>
|
||||
<script>
|
||||
window.onload = function(e){
|
||||
var d = new Date();
|
||||
var timestamp = Math.round(d.getTime() / 1000);
|
||||
input = document.getElementById("timestamp")
|
||||
input.value = timestamp;
|
||||
form = document.getElementById("form")
|
||||
form.submit();
|
||||
console.log(time);
|
||||
}
|
||||
</script>
|
222
build/year-progress.py
Normal file
222
build/year-progress.py
Normal file
|
@ -0,0 +1,222 @@
|
|||
import datetime
|
||||
import math
|
||||
|
||||
from flask import Flask, render_template, url_for, request
|
||||
app = Flask(__name__)
|
||||
|
||||
def num2day(num):
|
||||
match num:
|
||||
case 1:
|
||||
return "Monday"
|
||||
case 2:
|
||||
return "Tuesday"
|
||||
case 3:
|
||||
return "Wednesday"
|
||||
case 4:
|
||||
return "Thursday"
|
||||
case 5:
|
||||
return "Friday"
|
||||
|
||||
def latLongToCartesian(lat,long):
|
||||
lat = lat*math.pi/180
|
||||
long = long*math.pi/180
|
||||
x = math.cos(lat)*math.cos(long)
|
||||
y = math.cos(lat)*math.sin(long)
|
||||
z = math.sin(lat)
|
||||
return x,y,z
|
||||
|
||||
def cartesianToLatLong(x,y,z):
|
||||
long = math.atan2(y,x)
|
||||
hyp = math.sqrt(x*x + y*y)
|
||||
lat = math.atan2(z,hyp)
|
||||
|
||||
lat = lat*180/math.pi
|
||||
long = long*180/math.pi
|
||||
|
||||
return lat, long
|
||||
|
||||
def getPercentageLatLong(yearPercent,lat1,long1,lat2,long2):
|
||||
# http://www.geomidpoint.com/calculation.html
|
||||
x1, y1, z1 = latLongToCartesian(lat1,long1)
|
||||
x2, y2, z2 = latLongToCartesian(lat2,long2)
|
||||
|
||||
xmid = (x1*(1-yearPercent)+x2*yearPercent)
|
||||
ymid = (y1*(1-yearPercent)+y2*yearPercent)
|
||||
zmid = (z1*(1-yearPercent)+z2*yearPercent)
|
||||
|
||||
latmid, longmid = cartesianToLatLong(xmid,ymid,zmid)
|
||||
|
||||
return latmid, longmid
|
||||
|
||||
def workWeek(yearPercent):
|
||||
totalNumHours = 40*yearPercent
|
||||
numDays = totalNumHours/8
|
||||
hoursIntoDay = (numDays - math.floor(numDays))*8
|
||||
minutesIntoDay = round((hoursIntoDay - math.floor(hoursIntoDay))*60)
|
||||
if minutesIntoDay > 59:
|
||||
minutesIntoDay = 0
|
||||
hoursIntoDay += 1
|
||||
workDatetime = datetime.datetime(year=1970,month=1,day=1,hour=math.floor(hoursIntoDay)+9,minute=minutesIntoDay)
|
||||
workTime = workDatetime.strftime("%-I:%M%p")
|
||||
return num2day(math.floor(numDays+1)), workTime
|
||||
|
||||
def abbeyRoad(yearPercent):
|
||||
totalSeconds = 47*60+29 # 47 minutes 29 seconds
|
||||
numSeconds = math.floor(totalSeconds*yearPercent);
|
||||
# 4:19
|
||||
if numSeconds <= 4*60+19:
|
||||
return "Come Together"
|
||||
# 3:02
|
||||
elif numSeconds <= 259+(3*60+2):
|
||||
return "Something"
|
||||
# 3:27
|
||||
elif numSeconds <= 441+(3*60+27):
|
||||
return "Maxwell's Silver Hammer"
|
||||
# 3:27
|
||||
elif numSeconds <= 648+(3*60+27):
|
||||
return "Oh! Darling"
|
||||
# 2:50
|
||||
elif numSeconds <= 855+(2*60+50):
|
||||
return "Octopus's Garden"
|
||||
# 7:47
|
||||
elif numSeconds <= 1025+(7*60+47):
|
||||
return "I Want You (She's So Heavy)"
|
||||
# 3:05
|
||||
elif numSeconds <= 1492+(3*60+5):
|
||||
return "Here Comes The Sun"
|
||||
# 2:45
|
||||
elif numSeconds <= 1677+(2*60+45):
|
||||
return "Because"
|
||||
# 4:02
|
||||
elif numSeconds <= 1842+(4*60+2):
|
||||
return "You Never Give Me Your Money"
|
||||
# 2:26
|
||||
elif numSeconds <= 2084+(2*60+26):
|
||||
return "Sun King"
|
||||
# 1:06
|
||||
elif numSeconds <= 2230+(1*60+6):
|
||||
return "Mean Mr. Mustard"
|
||||
# 1:12
|
||||
elif numSeconds <= 2296+(1*60+12):
|
||||
return "Polythene Pam"
|
||||
# 1:58
|
||||
elif numSeconds <= 2368+(1*60+58):
|
||||
return "She Came In Through The Bathroom Window"
|
||||
# 1:31
|
||||
elif numSeconds <= 2486+(1*60+31):
|
||||
return "Golden Slumbers"
|
||||
# 1:36
|
||||
elif numSeconds <= 2577+(1*60+36):
|
||||
return "Carry That Weight"
|
||||
# 2:21
|
||||
elif numSeconds <= 2673+(2*60+21):
|
||||
return "The End"
|
||||
# 0:25
|
||||
else:
|
||||
return "Her Majesty"
|
||||
|
||||
def theHobbit(yearPercent):
|
||||
hobbitPage = math.floor(yearPercent * 273)
|
||||
if hobbitPage < 24:
|
||||
return "having an unexpected party"
|
||||
elif hobbitPage < 40:
|
||||
return "roasting mutton"
|
||||
elif hobbitPage < 49:
|
||||
return "having a short rest"
|
||||
elif hobbitPage < 62:
|
||||
return "going over hill and under hill"
|
||||
elif hobbitPage < 82:
|
||||
return "performing riddles in the dark"
|
||||
elif hobbitPage < 102:
|
||||
return "out of the frying pan, but into the fire"
|
||||
elif hobbitPage < 127:
|
||||
return "in some queer lodgings"
|
||||
elif hobbitPage < 157:
|
||||
return "fighting flies and spiders"
|
||||
elif hobbitPage < 171:
|
||||
return "taking barrels out of bond"
|
||||
elif hobbitPage < 183:
|
||||
return "getting a warm welcome"
|
||||
elif hobbitPage < 192:
|
||||
return "on the doorstep"
|
||||
elif hobbitPage < 211:
|
||||
return "getting inside information"
|
||||
elif hobbitPage < 222:
|
||||
return "not at home"
|
||||
elif hobbitPage < 231:
|
||||
return "oblivious to fire hitting water"
|
||||
elif hobbitPage < 241:
|
||||
return "watching the gathering of the clouds"
|
||||
elif hobbitPage < 247:
|
||||
return "a thief in the night"
|
||||
elif hobbitPage < 258:
|
||||
return "oblivious to the clouds bursting"
|
||||
elif hobbitPage < 266:
|
||||
return "on the return journey"
|
||||
else:
|
||||
return "at the last stage"
|
||||
|
||||
def transatlanticFlight(yearPercent):
|
||||
lat1 = 51.470020
|
||||
long1 = -0.454295
|
||||
|
||||
lat2 = 40.641766
|
||||
long2 = -73.780968
|
||||
|
||||
lat, long = getPercentageLatLong(yearPercent,lat1,long1,lat2,long2)
|
||||
return round(lat,2), round(long,2)
|
||||
|
||||
def getHomoSapiens(yearPercent):
|
||||
totalYears = 4.3e9
|
||||
yearsUntilHumans = totalYears*(1-yearPercent) - 300e3
|
||||
if yearsUntilHumans >= 0:
|
||||
return f"Homo Sapiens would evolve in ~{math.floor(yearsUntilHumans)} years"
|
||||
else:
|
||||
return f"Homo Sapiens have been around for ~{-math.floor(yearsUntilHumans)} years"
|
||||
|
||||
def getTemplate(timestamp):
|
||||
now = datetime.datetime.fromtimestamp(timestamp)
|
||||
nowTime = now.strftime("%-I:%M%p")
|
||||
nowDate = now.strftime("%A") + " " + now.strftime("%-d") + " " + now.strftime("%B")
|
||||
|
||||
delta = now - datetime.datetime(day=1,month=1,year=now.year)
|
||||
totalDays = datetime.datetime(day=1,month=1,year=now.year+1) - datetime.datetime(day=1,month=1,year=now.year)
|
||||
|
||||
yearPercent = delta/totalDays
|
||||
footballMinute = math.floor(yearPercent*90)
|
||||
workDay, workTime = workWeek(yearPercent)
|
||||
marathonMile = round(26.2 - math.floor(yearPercent*26.2),2)
|
||||
abbeyRoadSong = abbeyRoad(yearPercent)
|
||||
hobbitChapter = theHobbit(yearPercent)
|
||||
lat, long = transatlanticFlight(yearPercent)
|
||||
homoSapiens = getHomoSapiens(yearPercent)
|
||||
|
||||
return now.year, nowTime, nowDate, round(yearPercent*100,2), footballMinute, workDay, workTime, marathonMile, abbeyRoadSong, hobbitChapter, lat, long, homoSapiens
|
||||
|
||||
def index(timestamp):
|
||||
year, nowTime, nowDate, yearPercent, footballMinute, workDay, workTime, marathonMile, abbeyRoadSong, hobbitChapter, lat, long, homoSapiens = getTemplate(timestamp)
|
||||
cssPath = url_for("static", filename="style.css")
|
||||
return render_template("index.html",\
|
||||
cssPath=cssPath,\
|
||||
year=year,\
|
||||
nowTime=nowTime,\
|
||||
nowDate=nowDate,\
|
||||
yearPercent=yearPercent,\
|
||||
footballMinute=footballMinute,\
|
||||
workDay=workDay,\
|
||||
workTime=workTime,\
|
||||
marathonMile=marathonMile,\
|
||||
abbeyRoadSong=abbeyRoadSong,\
|
||||
hobbitChapter=hobbitChapter,\
|
||||
lat=lat,\
|
||||
long=long,\
|
||||
homoSapiens=homoSapiens)
|
||||
|
||||
@app.route("/", methods=["GET","POST"])
|
||||
def init():
|
||||
if request.method == "POST":
|
||||
timestamp = int(request.values.get('timestamp'))
|
||||
return index(timestamp)
|
||||
else:
|
||||
return render_template("init.html")
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue