Compare commits
2 Commits
15bd8ee02f
...
196873aac9
| Author | SHA1 | Date | |
|---|---|---|---|
|
196873aac9
|
|||
|
8e7512965c
|
@@ -23,4 +23,11 @@ def create_app(test_config=None):
|
||||
def hello():
|
||||
return "'Ello, Wurld!"
|
||||
|
||||
from . import db
|
||||
db.init_app(app)
|
||||
|
||||
from . import weather
|
||||
app.register_blueprint(weather.bp)
|
||||
app.add_url_rule('/', endpoint='index')
|
||||
|
||||
return app
|
||||
|
||||
46
weather/db.py
Normal file
46
weather/db.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
|
||||
import click
|
||||
from flask import current_app, g
|
||||
|
||||
|
||||
def get_db():
|
||||
if 'db' not in g:
|
||||
g.db = sqlite3.connect(
|
||||
current_app.config['DATABASE'],
|
||||
detect_types=sqlite3.PARSE_DECLTYPES
|
||||
)
|
||||
g.db.row_factory = sqlite3.Row
|
||||
|
||||
return g.db
|
||||
|
||||
def close_db(e=None):
|
||||
db = g.pop('db', None)
|
||||
|
||||
if db is not None:
|
||||
db.close()
|
||||
|
||||
def init_db():
|
||||
db = get_db()
|
||||
|
||||
with current_app.open_resource('schema.sql') as f:
|
||||
db.executescript(f.read().decode('utf8'))
|
||||
|
||||
|
||||
@click.command('init-db')
|
||||
def init_db_command():
|
||||
"""Refresh database"""
|
||||
init_db()
|
||||
click.echo('Initialized the database.')
|
||||
|
||||
|
||||
sqlite3.register_converter(
|
||||
"timestamp", lambda v: datetime.fromisoformat(v.decode())
|
||||
)
|
||||
|
||||
|
||||
def init_app(app):
|
||||
app.teardown_appcontext(close_db)
|
||||
app.cli.add_command(init_db_command)
|
||||
|
||||
43
weather/schema.sql
Normal file
43
weather/schema.sql
Normal file
@@ -0,0 +1,43 @@
|
||||
DROP TABLE IF EXISTS periods;
|
||||
DROP TABLE IF EXISTS reports;
|
||||
|
||||
CREATE TABLE "periods" (
|
||||
"id" Integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"report_id" Integer NOT NULL,
|
||||
"period_number" Integer NOT NULL,
|
||||
"name" Text,
|
||||
"start_time" DateTime NOT NULL,
|
||||
"end_time" DateTime NOT NULL,
|
||||
"is_daytime" Integer NOT NULL,
|
||||
"temperature" Integer NOT NULL,
|
||||
"temperature_unit" Text NOT NULL DEFAULT 'F',
|
||||
"precipitation_probability" Integer,
|
||||
"dewpoint_celsius" Numeric,
|
||||
"relative_humidity" Integer,
|
||||
"wind_speed" Text,
|
||||
"wind_direction" Text,
|
||||
"icon_url" Text,
|
||||
"short_forecast" Text,
|
||||
"detailed_forecast" Text,
|
||||
"created_at" DateTime,
|
||||
"updated_at" DateTime,
|
||||
CONSTRAINT "periods_reports_CASCADE_NO ACTION_report_id_id_0" FOREIGN KEY ( "report_id" ) REFERENCES "reports"( "id" )
|
||||
ON DELETE Cascade
|
||||
);
|
||||
|
||||
CREATE INDEX "periods_report_id_start_time_index" ON "periods"( "report_id", "start_time" );
|
||||
|
||||
CREATE TABLE "reports" (
|
||||
"id" Integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"type" Text NOT NULL,
|
||||
"reported_at" DateTime NOT NULL,
|
||||
"generated_at" DateTime NOT NULL,
|
||||
"latitude" Numeric NOT NULL,
|
||||
"longitude" Numeric NOT NULL,
|
||||
"elevation_meters" Numeric,
|
||||
"created_at" DateTime,
|
||||
"updated_at" DateTime,
|
||||
CONSTRAINT "check ""type"" in ('hourly', 'weekly')" CHECK ("type" in ('hourly', 'weekly')) );
|
||||
|
||||
CREATE INDEX "reports_type_reported_at_index" ON "reports"( "type", "reported_at" );
|
||||
CREATE UNIQUE INDEX "reports_type_reported_at_unique" ON "reports"( "type", "reported_at" );
|
||||
BIN
weather/static/Abel-Regular.woff2
Normal file
BIN
weather/static/Abel-Regular.woff2
Normal file
Binary file not shown.
54
weather/static/reset.css
Normal file
54
weather/static/reset.css
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 1. Use a more-intuitive box-sizing model */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 2. Remove default margin */
|
||||
*:not(dialog) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 3. Enable keyword animations */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
/* 4. Add accessible line-height */
|
||||
line-height: 1.5;
|
||||
/* 5. Improve text rendering */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* 6. Improve media defaults */
|
||||
img, picture, video, canvas, svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* 7. Inherit fonts for form controls */
|
||||
input, button, textarea, select {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/* 8. Avoid text overflows */
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 9. Improve line wrapping */
|
||||
p {
|
||||
text-wrap: pretty;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/*
|
||||
10. Create a root stacking context
|
||||
*/
|
||||
#root, #__next {
|
||||
isolation: isolate;
|
||||
}
|
||||
20
weather/static/style.css
Normal file
20
weather/static/style.css
Normal file
@@ -0,0 +1,20 @@
|
||||
@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;
|
||||
}
|
||||
28
weather/templates/base.html
Normal file
28
weather/templates/base.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
|
||||
<!-- Font preloads (should be done for each font file) -->
|
||||
<link href="{{ url_for('static', filename='Abel-Regular.woff2') }}" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
|
||||
<!-- CSS -->
|
||||
<link href="{{ url_for('static', filename='reset.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 -->
|
||||
|
||||
</head>
|
||||
<body class="font-abel antialiased">
|
||||
<div id="app" class="min-h-screen">
|
||||
{% for message in get_flashed_messages() %}
|
||||
<div class="flash">{{ message }}</div>
|
||||
{% endfor %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
9
weather/templates/weather/index.html
Normal file
9
weather/templates/weather/index.html
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
Hey what's going on on this side?
|
||||
|
||||
{% for period in periods %}
|
||||
<p>{{ period['start_time'] }} - {{ period['end_time'] }} | {{ period['temperature'] }}{{ period['temperature_unit'] }}</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
18
weather/weather.py
Normal file
18
weather/weather.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from flask import (
|
||||
Blueprint, flash, g, render_template, request, url_for
|
||||
)
|
||||
from werkzeug.exceptions import abort
|
||||
|
||||
from weather.db import get_db
|
||||
|
||||
bp = Blueprint('weather', __name__)
|
||||
|
||||
@bp.route('/')
|
||||
def index():
|
||||
db = get_db()
|
||||
periods = db.execute(
|
||||
'SELECT *'
|
||||
' FROM `periods`'
|
||||
' ORDER BY `id` DESC'
|
||||
).fetchall()
|
||||
return render_template('weather/index.html', periods=periods)
|
||||
Reference in New Issue
Block a user