Skip to content

Commit 9426f36

Browse files
committed
updates
1 parent 8a8eac1 commit 9426f36

23 files changed

+603
-2165
lines changed

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,12 @@ HOBOLINK_USERNAME=replace_me
55
HOBOLINK_PASSWORD=replace_me
66
HOBOLINK_TOKEN=replace_me
77

8+
BASIC_AUTH_USERNAME=admin
9+
BASIC_AUTH_PASSWORD=password
10+
11+
MAPBOX_ACCESS_TOKEN=replace_me
12+
13+
SENTRY_DSN=replace_me
14+
SENTRY_ENVIRONMENT=replace_me
15+
816
USE_MOCK_DATA=false

.flaskenv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FLASK_APP=app.main:create_app

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ docs/site/
55
pgadmin4/
66
mkdocs_env/
77
latest.dump
8+
server.crt
9+
server.key
810

911
# Created by https://www.toptal.com/developers/gitignore/api/python,pycharm,windows,osx,visualstudiocode
1012
# Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm,windows,osx,visualstudiocode

.pre-commit-config.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,14 @@ repos:
2222
rev: v2.12.0
2323
hooks:
2424
- id: hadolint-docker
25+
26+
- repo: https://github.com/rhysd/actionlint
27+
rev: v1.6.27
28+
hooks:
29+
- id: actionlint-docker
30+
31+
- repo: https://github.com/koalaman/shellcheck-precommit
32+
rev: v0.10.0
33+
hooks:
34+
- id: shellcheck
35+
files: ^(.*\.sh|run)$

.python-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.12.6
1+
3.12

Dockerfile

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
FROM python:3.12
2-
3-
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
4-
RUN /install.sh && rm /install.sh
1+
FROM python:3.12-slim
2+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
53

64
WORKDIR /app
7-
COPY requirements.txt ./requirements.txt
5+
ENV UV_PROJECT_ENVIRONMENT=/usr/local
86

9-
RUN /root/.cargo/bin/uv sync --system --no-cache
7+
RUN --mount=type=cache,target=/root/.cache/uv \
8+
--mount=type=bind,source=requirements.txt,target=requirements.txt \
9+
uv pip install -r requirements.txt --compile-bytecode --system
1010

11-
COPY ./ .
11+
COPY . /app
1212

13-
EXPOSE 80
13+
EXPOSE 80 443
1414

1515
CMD ["bash", "-c", "flask db migrate && gunicorn -c gunicorn_conf.py app.main:create_app\\(\\)"]

alembic/versions/rev001.py

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import sqlalchemy as sa
1313
from sqlalchemy import schema
14-
from sqlalchemy.engine.reflection import Inspector
1514

1615
from alembic import op
1716
from app.config import QUERIES_DIR
@@ -26,8 +25,6 @@
2625

2726
def upgrade():
2827
conn = op.get_bind()
29-
inspector = Inspector.from_engine(conn)
30-
tables = inspector.get_table_names()
3128

3229
# skip the following tables:
3330
# - hobolink
@@ -36,44 +33,53 @@ def upgrade():
3633
# - model_outputs
3734
# These are rewritten each time; their data doesn't need to be persisted.
3835

39-
if "boathouses" not in tables:
40-
op.execute(schema.CreateSequence(schema.Sequence("boathouses_id_seq")))
41-
op.create_table(
42-
"boathouses",
43-
sa.Column(
44-
"id",
45-
sa.Integer(),
46-
autoincrement=True,
47-
nullable=False,
48-
server_default=sa.text("nextval('boathouses_id_seq'::regclass)"),
49-
),
50-
sa.Column("boathouse", sa.String(length=255), nullable=False),
51-
sa.Column("reach", sa.Integer(), nullable=True),
52-
sa.Column("latitude", sa.Numeric(), nullable=True),
53-
sa.Column("longitude", sa.Numeric(), nullable=True),
54-
sa.Column("overridden", sa.Boolean(), nullable=True),
55-
sa.Column("reason", sa.String(length=255), nullable=True),
56-
sa.PrimaryKeyConstraint("boathouse"),
57-
)
58-
with open(QUERIES_DIR + "/override_event_triggers_v1.sql", "r") as f:
59-
sql = sa.text(f.read())
60-
conn.execute(sql)
61-
if "live_website_options" not in tables:
62-
op.create_table(
63-
"live_website_options",
64-
sa.Column("id", sa.Integer(), nullable=False),
65-
sa.Column("flagging_message", sa.Text(), nullable=True),
66-
sa.Column("boating_season", sa.Boolean(), nullable=False),
67-
sa.PrimaryKeyConstraint("id"),
68-
)
69-
if "override_history" not in tables:
70-
op.create_table(
71-
"override_history",
72-
sa.Column("time", sa.TIMESTAMP(), nullable=True),
73-
sa.Column("boathouse", sa.TEXT(), nullable=True),
74-
sa.Column("overridden", sa.BOOLEAN(), nullable=True),
75-
sa.Column("reason", sa.TEXT(), nullable=True),
76-
)
36+
op.execute(schema.CreateSequence(schema.Sequence("boathouses_id_seq")))
37+
op.create_table(
38+
"boathouses",
39+
sa.Column(
40+
"id",
41+
sa.Integer(),
42+
autoincrement=True,
43+
nullable=False,
44+
server_default=sa.text("nextval('boathouses_id_seq'::regclass)"),
45+
),
46+
sa.Column("boathouse", sa.String(length=255), nullable=False),
47+
sa.Column("reach", sa.Integer(), nullable=True),
48+
sa.Column("latitude", sa.Numeric(), nullable=True),
49+
sa.Column("longitude", sa.Numeric(), nullable=True),
50+
sa.Column("overridden", sa.Boolean(), nullable=True),
51+
sa.Column("reason", sa.String(length=255), nullable=True),
52+
sa.PrimaryKeyConstraint("boathouse"),
53+
)
54+
55+
with open(f"{QUERIES_DIR}/override_event_triggers_v1.sql", "r") as f:
56+
sql = sa.text(f.read())
57+
conn.execute(sql)
58+
59+
op.create_table(
60+
"live_website_options",
61+
sa.Column("id", sa.Integer(), nullable=False),
62+
sa.Column("flagging_message", sa.Text(), nullable=True),
63+
sa.Column("boating_season", sa.Boolean(), nullable=False),
64+
sa.PrimaryKeyConstraint("id"),
65+
)
66+
op.create_table(
67+
"override_history",
68+
sa.Column("time", sa.TIMESTAMP(), nullable=True),
69+
sa.Column("boathouse", sa.TEXT(), nullable=True),
70+
sa.Column("overridden", sa.BOOLEAN(), nullable=True),
71+
sa.Column("reason", sa.TEXT(), nullable=True),
72+
)
73+
74+
with open(f"{QUERIES_DIR}/define_reach.sql", "r") as f:
75+
sql = sa.text(f.read())
76+
conn.execute(sql)
77+
with open(f"{QUERIES_DIR}/define_boathouse.sql", "r") as f:
78+
sql = sa.text(f.read())
79+
conn.execute(sql)
80+
with open(f"{QUERIES_DIR}/define_default_options.sql", "r") as f:
81+
sql = sa.text(f.read())
82+
conn.execute(sql)
7783

7884

7985
def downgrade():

app/admin/views/misc.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ class LogoutView(BaseView):
1212
@expose("/")
1313
def index(self):
1414
body = self.render("admin/logout.html")
15-
status = 401
16-
cache.clear()
15+
status = 200
1716
return body, status
1817

1918

app/config.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,7 @@ def SQLALCHEMY_DATABASE_URI(self) -> str:
192192

193193
DEFAULT_WIDGET_VERSION: int = 2
194194

195-
MAPBOX_ACCESS_TOKEN: str = os.getenv(
196-
"MAPBOX_ACCESS_TOKEN",
197-
# This is a token that's floating around the web in a lot of quickstart
198-
# examples for LeafletJS, and seems to work. ¯\_(ツ)_/¯
199-
#
200-
# You should not use it ideally, but as a default for very quick runs
201-
# and demos, it should be OK.
202-
"pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw",
203-
) # noqa
195+
MAPBOX_ACCESS_TOKEN: str = os.getenv("MAPBOX_ACCESS_TOKEN")
204196

205197
SENTRY_DSN: str | None = os.getenv("SENTRY_DSN")
206198
SENTRY_ENVIRONMENT: str | None = os.getenv("SENTRY_ENVIRONMENT")

app/main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ def register_jinja_env(app: Flask):
182182
@app.template_filter("strftime")
183183
def strftime(value: datetime.datetime, fmt: str = "%Y-%m-%d %I:%M:%S %p") -> str:
184184
"""Render datetimes with a default format for the frontend."""
185-
return value.strftime(fmt)
185+
if value:
186+
return value.strftime(fmt)
186187

187188
def _load_svg(file_name: str):
188189
"""Load an svg file from `static/images/`."""
@@ -372,6 +373,7 @@ def pip_compile(ctx: click.Context):
372373
)
373374
@mail_on_fail
374375
def email_90_day_data_command(async_: bool = False):
376+
"""Send email containing database dump."""
375377
from app.data.celery import send_database_exports_task
376378

377379
if async_:

0 commit comments

Comments
 (0)