a terrible but somewhat functional state

This commit is contained in:
2026-01-11 17:50:32 -07:00
parent 9dbd72f4ee
commit 7b5a49979e
12 changed files with 9814 additions and 26 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+319
View File
@@ -0,0 +1,319 @@
{
"@context": [
"https://geojson.org/geojson-ld/geojson-context.jsonld",
{
"@version": "1.1",
"wx": "https://api.weather.gov/ontology#",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#"
}
],
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-111.6091,
40.1254
],
[
-111.6126,
40.1473
],
[
-111.6411,
40.1446
],
[
-111.6376,
40.1227
],
[
-111.6091,
40.1254
]
]
]
},
"properties": {
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2026-01-11T01:15:56+00:00",
"updateTime": "2026-01-10T21:04:44+00:00",
"validTimes": "2026-01-10T15:00:00+00:00/P7DT10H",
"elevation": {
"unitCode": "wmoUnit:m",
"value": 1399.9464
},
"periods": [
{
"number": 1,
"name": "Tonight",
"startTime": "2026-01-10T18:00:00-07:00",
"endTime": "2026-01-11T06:00:00-07:00",
"isDaytime": false,
"temperature": 21,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "SE",
"icon": "https://api.weather.gov/icons/land/night/few?size=medium",
"shortForecast": "Mostly Clear",
"detailedForecast": "Mostly clear, with a low around 21. Southeast wind around 2 mph."
},
{
"number": 2,
"name": "Sunday",
"startTime": "2026-01-11T06:00:00-07:00",
"endTime": "2026-01-11T18:00:00-07:00",
"isDaytime": true,
"temperature": 43,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "S",
"icon": "https://api.weather.gov/icons/land/day/sct?size=medium",
"shortForecast": "Mostly Sunny",
"detailedForecast": "Mostly sunny. High near 43, with temperatures falling to around 39 in the afternoon. South wind around 2 mph."
},
{
"number": 3,
"name": "Sunday Night",
"startTime": "2026-01-11T18:00:00-07:00",
"endTime": "2026-01-12T06:00:00-07:00",
"isDaytime": false,
"temperature": 22,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "E",
"icon": "https://api.weather.gov/icons/land/night/few/haze?size=medium",
"shortForecast": "Mostly Clear then Haze",
"detailedForecast": "Haze after 5am. Mostly clear. Low around 22, with temperatures rising to around 24 overnight. East wind around 2 mph."
},
{
"number": 4,
"name": "Monday",
"startTime": "2026-01-12T06:00:00-07:00",
"endTime": "2026-01-12T18:00:00-07:00",
"isDaytime": true,
"temperature": 45,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "NW",
"icon": "https://api.weather.gov/icons/land/day/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Sunny, with a high near 45. Northwest wind around 2 mph."
},
{
"number": 5,
"name": "Monday Night",
"startTime": "2026-01-12T18:00:00-07:00",
"endTime": "2026-01-13T06:00:00-07:00",
"isDaytime": false,
"temperature": 24,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "ENE",
"icon": "https://api.weather.gov/icons/land/night/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Mostly clear, with a low around 24. East northeast wind around 2 mph."
},
{
"number": 6,
"name": "Tuesday",
"startTime": "2026-01-13T06:00:00-07:00",
"endTime": "2026-01-13T18:00:00-07:00",
"isDaytime": true,
"temperature": 40,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "WNW",
"icon": "https://api.weather.gov/icons/land/day/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Mostly sunny, with a high near 40."
},
{
"number": 7,
"name": "Tuesday Night",
"startTime": "2026-01-13T18:00:00-07:00",
"endTime": "2026-01-14T06:00:00-07:00",
"isDaytime": false,
"temperature": 22,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "E",
"icon": "https://api.weather.gov/icons/land/night/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Partly cloudy, with a low around 22."
},
{
"number": 8,
"name": "Wednesday",
"startTime": "2026-01-14T06:00:00-07:00",
"endTime": "2026-01-14T18:00:00-07:00",
"isDaytime": true,
"temperature": 42,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "WNW",
"icon": "https://api.weather.gov/icons/land/day/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Sunny, with a high near 42."
},
{
"number": 9,
"name": "Wednesday Night",
"startTime": "2026-01-14T18:00:00-07:00",
"endTime": "2026-01-15T06:00:00-07:00",
"isDaytime": false,
"temperature": 24,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "ESE",
"icon": "https://api.weather.gov/icons/land/night/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Clear, with a low around 24."
},
{
"number": 10,
"name": "Thursday",
"startTime": "2026-01-15T06:00:00-07:00",
"endTime": "2026-01-15T18:00:00-07:00",
"isDaytime": true,
"temperature": 40,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "WNW",
"icon": "https://api.weather.gov/icons/land/day/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Sunny, with a high near 40."
},
{
"number": 11,
"name": "Thursday Night",
"startTime": "2026-01-15T18:00:00-07:00",
"endTime": "2026-01-16T06:00:00-07:00",
"isDaytime": false,
"temperature": 18,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "E",
"icon": "https://api.weather.gov/icons/land/night/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Clear, with a low around 18."
},
{
"number": 12,
"name": "Friday",
"startTime": "2026-01-16T06:00:00-07:00",
"endTime": "2026-01-16T18:00:00-07:00",
"isDaytime": true,
"temperature": 40,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "N",
"icon": "https://api.weather.gov/icons/land/day/haze?size=medium",
"shortForecast": "Haze",
"detailedForecast": "Haze. Sunny, with a high near 40."
},
{
"number": 13,
"name": "Friday Night",
"startTime": "2026-01-16T18:00:00-07:00",
"endTime": "2026-01-17T06:00:00-07:00",
"isDaytime": false,
"temperature": 22,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "ENE",
"icon": "https://api.weather.gov/icons/land/night/haze/few?size=medium",
"shortForecast": "Haze then Mostly Clear",
"detailedForecast": "Haze before 11pm. Mostly clear, with a low around 22."
},
{
"number": 14,
"name": "Saturday",
"startTime": "2026-01-17T06:00:00-07:00",
"endTime": "2026-01-17T18:00:00-07:00",
"isDaytime": true,
"temperature": 45,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": 0
},
"windSpeed": "2 mph",
"windDirection": "SSW",
"icon": "https://api.weather.gov/icons/land/day/few?size=medium",
"shortForecast": "Sunny",
"detailedForecast": "Sunny, with a high near 45."
}
]
}
}
+1 -4
View File
@@ -19,15 +19,12 @@ def create_app(test_config=None):
except OSError: except OSError:
pass pass
@app.route('/hello')
def hello():
return "'Ello, Wurld!"
from . import db from . import db
db.init_app(app) db.init_app(app)
from . import weather from . import weather
app.register_blueprint(weather.bp) app.register_blueprint(weather.bp)
app.add_url_rule('/', endpoint='index') app.add_url_rule('/', endpoint='index')
app.add_url_rule('/api', endpoint='api')
return app return app
-1
View File
@@ -5,7 +5,6 @@ CREATE TABLE "periods" (
"id" Integer NOT NULL PRIMARY KEY AUTOINCREMENT, "id" Integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"report_id" Integer NOT NULL, "report_id" Integer NOT NULL,
"period_number" Integer NOT NULL, "period_number" Integer NOT NULL,
"name" Text,
"start_time" DateTime NOT NULL, "start_time" DateTime NOT NULL,
"end_time" DateTime NOT NULL, "end_time" DateTime NOT NULL,
"is_daytime" Integer NOT NULL, "is_daytime" Integer NOT NULL,
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

+1
View File
@@ -0,0 +1 @@
Image by <a href="https://pixabay.com/users/couleur-1195798/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=5459972">Couleur</a> from <a href="https://pixabay.com//?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=5459972">Pixabay</a>
+90 -13
View File
@@ -1,20 +1,97 @@
@font-face { #clock {
font-family: "Abel"; font-size: 2em;
src: url(Abel-Regular.woff2) format("woff2"); padding-top: 1em;
font-weight: 400; text-align: center;
font-style: normal;
font-display: swap;
} }
.font-abel { .frosted {
font-family: "Abel", sans-serif; backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
background-color: rgba(78, 86, 106, 0.75);
border: 1px solid rgba(255, 255, 255, 0.125);
} }
.antialiased { .page-container {
-webkit-font-smoothing: antialiased; display: grid;
-moz-osx-font-smoothing: grayscale; grid-template-columns: 1fr;
grid-template-rows: 1.4fr 0.6fr;
gap: 2em 0em;
grid-auto-flow: row;
grid-template-areas:
"currentForecast"
"hourlyReport"
"weeklyReport";
max-width: 72em;
margin: 0 auto;
padding: 1.5em 0 2.5em;
} }
.min-h-screen { .currentForecast {
min-height: 100vh; display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas:
"forecast secondaryInfo";
grid-area: currentForecast;
}
.forecast {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(3, max-content);
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas:
"shortDescription"
"longDescription"
"currentTemp"
"waterConditions";
grid-area: forecast;
}
.shortDescription {
font-size: 3em;
grid-area: shortDescription;
}
.longDescription {
font-size: 1.35em;
grid-area: longDescription;
}
.currentTemp { grid-area: currentTemp; }
.currentTemp > .temperature { font-size: 6em; }
.currentTemp > .unit { font-size: 2em; }
.waterConditions { grid-area: waterConditions; }
.secondaryInfo {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
gap: 2em 0;
grid-auto-flow: row;
grid-template-areas:
"windContainer"
"solarClock";
grid-area: secondaryInfo;
}
.windContainer { grid-area: windContainer; }
.solarClock { grid-area: solarClock; }
.hourlyReport { grid-area: hourlyReport; }
.weeklyReport {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr;
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas:
". . . . . .";
grid-area: weeklyReport;
} }
+36
View File
@@ -0,0 +1,36 @@
@font-face {
font-family: "Abel";
src: url(Abel-Regular.woff2) format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
.font-abel { font-family: "Abel", sans-serif; }
.antialiased {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.min-h-screen { min-height: 100vh; }
.grid { display: grid; }
.grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.grid-cols-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.gap-x-4 { column-gap: 0.5rem; }
.flex { display: flex; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.p-4 { padding: 1rem; }
.my-6 { margin-top: 1.25rem; margin-bottom: 1.25rem; }
+10 -4
View File
@@ -11,18 +11,24 @@
<!-- CSS --> <!-- CSS -->
<link href="{{ url_for('static', filename='reset.css') }}" rel="stylesheet" media="screen"> <link href="{{ url_for('static', filename='reset.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='tailwind.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='style.css') }}" rel="stylesheet" media="screen"> <link href="{{ url_for('static', filename='style.css') }}" rel="stylesheet" media="screen">
<!-- JS that must be executed before the document is loaded --> <!-- JS that must be executed before the document is loaded -->
</head> </head>
<body class="font-abel antialiased"> <body class="font-abel antialiased">
<div id="app" class="min-h-screen"> <div id="app" class="min-h-screen" style="background-image: url({{ url_for('static', filename=condition_image) }}); background-size: cover; background-position: center;">
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div> <div id="clock">0:00:00 AM</div>
{% endfor %}
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
<script>
let clock = document.getElementById('clock');
setInterval(() => {
clock.innerText = new Date().toLocaleTimeString();
}, 1000)
</script>
</body> </body>
</html> </html>
+52 -3
View File
@@ -1,7 +1,56 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
{% for period in periods %} <div class="page-container">
<p>{{ period['start_time'] }} - {{ period['end_time'] }} | {{ period['temperature'] }}{{ period['temperature_unit'] }}</p> <div class="currentForecast">
{% endfor %} <div class="forecast">
<div class="shortDescription">{{ current_conditions['short_forecast'] }}</div>
<div class="longDescription">{{ current_conditions['detailed_forecast'] }}</div>
<div class="currentTemp">
<span class="temperature">{{ current_conditions['temperature'] }}</span><span class="unit">°{{ current_conditions['temperature_unit'] }}</span>
</div>
<div class="waterConditions">{{ current_conditions['precipitation_probability'] }}% percip | {{ current_conditions['relative_humidity'] }}% humidity (relative)</div>
</div>
<div class="secondaryInfo">
<div class="windContainer frosted">
<div class="flex items-center justify-between">
<div class="flex items-center">Wind Status</div>
<div class="flex items-center">{{ current_conditions['wind_speed'] }}</div>
</div>
<div class="flex items-center justify-center">
</div>
<div class="flex items-center justify-center">
Direction: {{ current_conditions['wind_direction'] }}
</div>
</div>
<div class="solarClock frosted">
<div class="flex items-center justify-between">
<div class="flex items-center">Sunrise</div>
<div class="flex items-center">Sunset</div>
</div>
<div class="flex items-center justify-center">
</div>
<div class="flex items-center justify-between">
<div class="flex items-center">Time AM</div>
<div class="flex items-center">Time PM</div>
</div>
</div>
</div>
</div>
<div class="hourlyForecast frosted my-6">
<h3>Hourly Forecast</h3>
<div class="grid grid-cols-6 gap-x-4">
{{ current_conditions | tojson(2) }}
</div>
</div>
<div class="weeklyReport frosted">
<h3>Weekly Forecast</h3>
<div class="grid grid-cols-7 gap-x-4">
{% for period in periods %}
<div>{{ period['start_time'] }} - {{ period['end_time'] }} | {{ period['temperature'] }}{{ period['temperature_unit'] }}</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %} {% endblock %}
+32 -1
View File
@@ -10,9 +10,40 @@ bp = Blueprint('weather', __name__)
@bp.route('/') @bp.route('/')
def index(): def index():
db = get_db() db = get_db()
latest_period = dict(db.execute(
'SELECT `id` FROM `reports` WHERE `type` = "hourly" ORDER BY `reported_at` DESC'
).fetchone())
current_conditions = dict(db.execute(
f"SELECT * FROM `periods` WHERE `report_id` = {latest_period['id']} LIMIT 1"
).fetchone())
# TODO: add conditions to check for day/night
condition_image = f"images/{current_conditions['short_forecast'].lower()}.jpg"
periods = db.execute( periods = db.execute(
'SELECT *' 'SELECT *'
' FROM `periods`' ' FROM `periods`'
' ORDER BY `id` DESC' ' ORDER BY `id` DESC'
' LIMIT 7'
).fetchall() ).fetchall()
return render_template('weather/index.html', periods=periods)
return render_template(
'weather/index.html',
current_conditions=current_conditions,
condition_image=condition_image,
periods=periods
)
@bp.route('/api')
def api():
db = get_db()
latest_period = dict(db.execute(
'SELECT `id` FROM `reports` WHERE `type` = "hourly" ORDER BY `reported_at` DESC'
).fetchone())
current_conditions = dict(db.execute(
f"SELECT * FROM `periods` WHERE `report_id` = {latest_period['id']} LIMIT 1"
).fetchone())
return current_conditions