diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..e90f3e5
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,7 @@
+[flake8]
+# Thanks https://www.reddit.com/r/learnpython/comments/rr6y69/comment/hqeqt68/?utm_source=share&utm_medium=web2x&context=3
+ignore = E203, W503, E501
+
+max-line-length = 88
+max-complexity = 39
+extend-ignore = E203
diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml
new file mode 100644
index 0000000..1561b4f
--- /dev/null
+++ b/.github/workflows/black.yml
@@ -0,0 +1,12 @@
+---
+# Thanks https://black.readthedocs.io/en/stable/integrations/github_actions.html
+name: Lint (black)
+
+on: [push, pull_request]
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: psf/black@24.1.1
diff --git a/freezing/web/__init__.py b/freezing/web/__init__.py
index e9089bc..d6acb77 100644
--- a/freezing/web/__init__.py
+++ b/freezing/web/__init__.py
@@ -1,6 +1,3 @@
-import os
-import os.path
-
from flask import Flask, session, g
from freezing.model import init_model, meta
@@ -25,7 +22,6 @@
alt_scoring,
tribes,
)
-from freezing.web.utils import auth
# Register our blueprints
diff --git a/freezing/web/config.py b/freezing/web/config.py
index 870e24f..d7e02e2 100644
--- a/freezing/web/config.py
+++ b/freezing/web/config.py
@@ -90,9 +90,9 @@ def init_logging(loglevel: int = logging.INFO, color: bool = False):
logging.root,
]
- for l in loggers:
- if l is logging.root:
- l.setLevel(logging.DEBUG)
+ for logger in loggers:
+ if logger is logging.root:
+ logger.setLevel(logging.DEBUG)
else:
- l.setLevel(logging.INFO)
- l.addHandler(ch)
+ logger.setLevel(logging.INFO)
+ logger.addHandler(ch)
diff --git a/freezing/web/data.py b/freezing/web/data.py
index 80d80a4..b13b055 100644
--- a/freezing/web/data.py
+++ b/freezing/web/data.py
@@ -1,13 +1,13 @@
"""
Functions for interacting with the datastore and the strava apis.
"""
+
from __future__ import division, unicode_literals
import re
import logging
from instagram import InstagramAPIError, InstagramClientError
-from polyline.codec import PolylineCodec
from sqlalchemy import and_
from geoalchemy import WKTSpatialElement
@@ -29,7 +29,7 @@
Team,
)
-from freezing.web import app, config
+from freezing.web import config
from freezing.web.autolog import log
from freezing.web.exc import (
InvalidAuthorizationToken,
diff --git a/freezing/web/templates/base.html b/freezing/web/templates/base.html
index 5f0904f..826d2ff 100644
--- a/freezing/web/templates/base.html
+++ b/freezing/web/templates/base.html
@@ -139,6 +139,8 @@
London Bridge Rides
Loopy for Arlington
Meh-diocrity
+ Fire
+ Flag
Michigander Snow Day Rides
Monuments and Memorials
diff --git a/freezing/web/utils/auth.py b/freezing/web/utils/auth.py
index c9d807a..8b3b7d3 100644
--- a/freezing/web/utils/auth.py
+++ b/freezing/web/utils/auth.py
@@ -1,6 +1,7 @@
"""
Some authentication-related utility functions/classes.
"""
+
import logging
from functools import wraps, update_wrapper
from datetime import timedelta
diff --git a/freezing/web/utils/dates.py b/freezing/web/utils/dates.py
index 377f7a0..f23f58b 100644
--- a/freezing/web/utils/dates.py
+++ b/freezing/web/utils/dates.py
@@ -1,9 +1,6 @@
-import pytz
-
-from datetime import datetime
from dateutil import parser as date_parser
-from freezing.web import app, config
+from freezing.web import config
def parse_competition_timestamp(ts):
diff --git a/freezing/web/utils/genericboard.py b/freezing/web/utils/genericboard.py
index af88efb..95b5021 100644
--- a/freezing/web/utils/genericboard.py
+++ b/freezing/web/utils/genericboard.py
@@ -1,13 +1,11 @@
import decimal
import os
-import enum
from datetime import datetime
from typing import List, Dict, Any, Tuple
import yaml
from marshmallow import fields
-from marshmallow_enum import EnumField
from freezing.model import meta
from freezing.model.msg import BaseSchema, BaseMessage
diff --git a/freezing/web/utils/gviz_api.py b/freezing/web/utils/gviz_api.py
index 6a0609d..b8001c3 100755
--- a/freezing/web/utils/gviz_api.py
+++ b/freezing/web/utils/gviz_api.py
@@ -42,8 +42,6 @@
class DataTableException(Exception):
"""The general exception object thrown by DataTable."""
- pass
-
class DataTableJSONEncoder(json.JSONEncoder):
"""JSON encoder that handles date/time/datetime objects correctly."""
diff --git a/freezing/web/utils/hashboard.py b/freezing/web/utils/hashboard.py
index 3809b7c..944732b 100644
--- a/freezing/web/utils/hashboard.py
+++ b/freezing/web/utils/hashboard.py
@@ -1,4 +1,3 @@
-import decimal
import os
from typing import List
diff --git a/freezing/web/utils/insta.py b/freezing/web/utils/insta.py
index 70be98d..6c83574 100644
--- a/freezing/web/utils/insta.py
+++ b/freezing/web/utils/insta.py
@@ -1,12 +1,13 @@
"""
Utility functions for working with images.
"""
+
import os
import urllib
import shutil
from instagram.client import InstagramAPI
-from freezing.web import app, exc
+from freezing.web import exc
from freezing.web.config import config
THUMBNAIL_DIMS = (150, 150)
diff --git a/freezing/web/utils/wktutils.py b/freezing/web/utils/wktutils.py
index a56559e..e3bf992 100644
--- a/freezing/web/utils/wktutils.py
+++ b/freezing/web/utils/wktutils.py
@@ -1,5 +1,4 @@
import re
-from decimal import Decimal
from collections import namedtuple
_point_rx = re.compile("^POINT\((.+)\)$")
diff --git a/freezing/web/views/alt_scoring.py b/freezing/web/views/alt_scoring.py
index 144d1dc..296b81f 100644
--- a/freezing/web/views/alt_scoring.py
+++ b/freezing/web/views/alt_scoring.py
@@ -8,7 +8,11 @@
from freezing.model import meta
from freezing.web import config
-from freezing.web.views.shared_sql import *
+from freezing.web.views.shared_sql import (
+ team_sleaze_query,
+ team_segment_query,
+ indiv_freeze_query,
+)
blueprint = Blueprint("alt_scoring", __name__)
@@ -18,9 +22,9 @@
def team_riders():
q = text(
"""
- select b.name, count(a.athlete_id) as ride_days from daily_scores a join teams b
- on a.team_id = b.id where a.distance > 1 and b.leaderboard_exclude=0 group by a.team_id order by ride_days desc;
- """
+ select b.name, count(a.athlete_id) as ride_days from daily_scores a join teams b
+ on a.team_id = b.id where a.distance > 1 and b.leaderboard_exclude=0 group by a.team_id order by ride_days desc;
+ """
)
team_riders = [
(x["name"], x["ride_days"]) for x in meta.scoped_session().execute(q).fetchall()
diff --git a/freezing/web/views/api.py b/freezing/web/views/api.py
index d8c78d3..40eb1ae 100644
--- a/freezing/web/views/api.py
+++ b/freezing/web/views/api.py
@@ -3,19 +3,17 @@
import json
import arrow
-import geojson
from flask import Blueprint, jsonify, request
import pytz
from sqlalchemy import text
-from stravalib import unithelper as uh
from freezing.model import meta
from freezing.model.orm import RidePhoto, Ride, RideTrack, Athlete
-from freezing.web import app, config
+from freezing.web import config
from freezing.web.autolog import log
from freezing.web.serialize import RidePhotoSchema
-from freezing.web.utils import auth, dates
+from freezing.web.utils import auth
from freezing.web.utils.wktutils import parse_linestring
blueprint = Blueprint("api", __name__)
diff --git a/freezing/web/views/chartdata.py b/freezing/web/views/chartdata.py
index c295093..341bc56 100644
--- a/freezing/web/views/chartdata.py
+++ b/freezing/web/views/chartdata.py
@@ -3,12 +3,13 @@
@author: hans
"""
+
import json
import copy
from collections import defaultdict
from datetime import datetime, timedelta
-from flask import current_app, request, Blueprint, jsonify
+from flask import current_app, Blueprint, jsonify
from sqlalchemy import text
from dateutil import rrule
diff --git a/freezing/web/views/general.py b/freezing/web/views/general.py
index e1ac1de..f876d12 100644
--- a/freezing/web/views/general.py
+++ b/freezing/web/views/general.py
@@ -3,7 +3,6 @@
@author: hans
"""
-from datetime import timedelta
from flask import (
render_template,
diff --git a/freezing/web/views/people.py b/freezing/web/views/people.py
index dcbc246..9e05488 100644
--- a/freezing/web/views/people.py
+++ b/freezing/web/views/people.py
@@ -1,4 +1,4 @@
-from datetime import date, timedelta
+from datetime import timedelta
from datetime import datetime
from flask import render_template, Blueprint, abort
diff --git a/freezing/web/views/photos.py b/freezing/web/views/photos.py
index 5e95d79..7651d47 100644
--- a/freezing/web/views/photos.py
+++ b/freezing/web/views/photos.py
@@ -1,12 +1,9 @@
import math
-from datetime import date, timedelta
-from datetime import datetime
-from flask import render_template, Blueprint, app, send_file, request
-from sqlalchemy import text
+from flask import render_template, Blueprint, send_file, request
from freezing.model import meta
-from freezing.model.orm import Team, Athlete, RidePhoto, Ride
+from freezing.model.orm import RidePhoto, Ride
from freezing.web import config
from freezing.web.utils import insta
diff --git a/freezing/web/views/pointless.py b/freezing/web/views/pointless.py
index f97613e..7192236 100644
--- a/freezing/web/views/pointless.py
+++ b/freezing/web/views/pointless.py
@@ -1,12 +1,10 @@
-import os
import operator
-from datetime import date, datetime, timezone
+from datetime import datetime, timezone
from collections import defaultdict
import re
-from flask import render_template, Blueprint, abort, redirect, url_for
+from flask import render_template, Blueprint, abort
from sqlalchemy import text
-import yaml
from freezing.model import meta
from freezing.web.config import config
@@ -280,7 +278,7 @@ def ross_hill_loop():
@blueprint.route("/coffeeride")
def coffeeride():
year = datetime.now().year
- tdata = _get_hashtag_tdata("coffeeride".format(year), "coffeeride", 2)
+ tdata = _get_hashtag_tdata("coffeeride{}".format(year), "coffeeride", 2)
return render_template(
"pointless/coffeeride.html", data={"tdata": tdata, "year": year}
)
diff --git a/freezing/web/views/shared_sql.py b/freezing/web/views/shared_sql.py
index 1e2b600..52f04ae 100644
--- a/freezing/web/views/shared_sql.py
+++ b/freezing/web/views/shared_sql.py
@@ -1,56 +1,57 @@
"""
SQL queries used in more than one class. DRY 4EVA
"""
+
from sqlalchemy import text
def team_sleaze_query():
return text(
"""
- select T.id, T.name as team_name, count(*) as num_sleaze_days
- from daily_scores D
- join lbd_athletes A on A.id = D.athlete_id
- join teams T on T.id = A.team_id
- where D.distance >= 1 and D.distance < 2
- group by T.id, T.name
- order by num_sleaze_days desc
- ;
- """
+ select T.id, T.name as team_name, count(*) as num_sleaze_days
+ from daily_scores D
+ join lbd_athletes A on A.id = D.athlete_id
+ join teams T on T.id = A.team_id
+ where D.distance >= 1 and D.distance < 2
+ group by T.id, T.name
+ order by num_sleaze_days desc
+ ;
+ """
)
def indiv_sleaze_query():
return text(
"""
- select D.athlete_id, A.display_name as athlete_name, count(*) as num_sleaze_days
- from daily_scores D
- join lbd_athletes A on A.id = D.athlete_id
- where D.distance >= 1 and D.distance < 2
- group by D.athlete_id, athlete_name
- order by num_sleaze_days desc
- ;
- """
+ select D.athlete_id, A.display_name as athlete_name, count(*) as num_sleaze_days
+ from daily_scores D
+ join lbd_athletes A on A.id = D.athlete_id
+ where D.distance >= 1 and D.distance < 2
+ group by D.athlete_id, athlete_name
+ order by num_sleaze_days desc
+ ;
+ """
)
def indiv_freeze_query():
return text(
"""
- select athlete_id, athlete_name, SUM(max_daily_freeze_points) as freeze_points_total
- from (
- select athlete_id, athlete_name, ride_date, MAX(freeze_points) as max_daily_freeze_points
- from (
- select R.athlete_id, A.display_name as athlete_name, date(R.start_date) as ride_date, (11*(ATAN((R.distance+4)-2*PI())+1.4)-2.66)*(1.2+ATAN((32-W.ride_temp_start)/5)) as freeze_points
- from rides R
- join ride_weather W on W.ride_id = R.id
- join lbd_athletes A on A.id = R.athlete_id
- ) FP
- group by athlete_id, athlete_name, ride_date
- ) FPMax
- group by athlete_id, athlete_name
- order by freeze_points_total desc
- ;
- """
+ select athlete_id, athlete_name, SUM(max_daily_freeze_points) as freeze_points_total
+ from (
+ select athlete_id, athlete_name, ride_date, MAX(freeze_points) as max_daily_freeze_points
+ from (
+ select R.athlete_id, A.display_name as athlete_name, date(R.start_date) as ride_date, (11*(ATAN((R.distance+4)-2*PI())+1.4)-2.66)*(1.2+ATAN((32-W.ride_temp_start)/5)) as freeze_points
+ from rides R
+ join ride_weather W on W.ride_id = R.id
+ join lbd_athletes A on A.id = R.athlete_id
+ ) FP
+ group by athlete_id, athlete_name, ride_date
+ ) FPMax
+ group by athlete_id, athlete_name
+ order by freeze_points_total desc
+ ;
+ """
)
@@ -58,62 +59,62 @@ def indiv_segment_query(join_miles=False):
if join_miles:
return text(
"""
- select aa.id, aa.athlete_name, aa.segment_rides, bb.dist from (select A.id, A.display_name as athlete_name, count(E.id) as segment_rides
- from lbd_athletes A
- join rides R on R.athlete_id = A.id
- join ride_efforts E on E.ride_id = R.id
- where E.segment_id = :segment_id
- group by A.id, A.display_name) aa
- join
- (select athlete_id, sum(distance) as dist from rides R group by athlete_id) bb
- on aa.id = bb.athlete_id
- order by aa.segment_rides desc;
- """
+ select aa.id, aa.athlete_name, aa.segment_rides, bb.dist from (select A.id, A.display_name as athlete_name, count(E.id) as segment_rides
+ from lbd_athletes A
+ join rides R on R.athlete_id = A.id
+ join ride_efforts E on E.ride_id = R.id
+ where E.segment_id = :segment_id
+ group by A.id, A.display_name) aa
+ join
+ (select athlete_id, sum(distance) as dist from rides R group by athlete_id) bb
+ on aa.id = bb.athlete_id
+ order by aa.segment_rides desc;
+ """
)
else:
return text(
"""
- select A.id, A.display_name as athlete_name, count(E.id) as segment_rides
- from lbd_athletes A
- join rides R on R.athlete_id = A.id
- join ride_efforts E on E.ride_id = R.id
- where E.segment_id = :segment_id
- group by A.id, A.display_name
- order by segment_rides desc
- ;
- """
+ select A.id, A.display_name as athlete_name, count(E.id) as segment_rides
+ from lbd_athletes A
+ join rides R on R.athlete_id = A.id
+ join ride_efforts E on E.ride_id = R.id
+ where E.segment_id = :segment_id
+ group by A.id, A.display_name
+ order by segment_rides desc
+ ;
+ """
)
def team_segment_query():
return text(
"""
- select T.id, T.name as team_name, count(E.id) as segment_rides
- from rides R
- join lbd_athletes A on A.id = R.athlete_id
- join teams T on T.id = A.team_id
- join ride_efforts E on E.ride_id = R.id
- where E.segment_id = :segment_id and T.leaderboard_exclude=0
- group by T.id, T.name
- order by segment_rides desc
- ;
- """
+ select T.id, T.name as team_name, count(E.id) as segment_rides
+ from rides R
+ join lbd_athletes A on A.id = R.athlete_id
+ join teams T on T.id = A.team_id
+ join ride_efforts E on E.ride_id = R.id
+ where E.segment_id = :segment_id and T.leaderboard_exclude=0
+ group by T.id, T.name
+ order by segment_rides desc
+ ;
+ """
)
def team_leaderboard_query():
return text(
"""
- select
- T.id as team_id,
- T.name as team_name,
- sum(DS.points) as total_score,
- sum(DS.distance) as total_distance
- from
- daily_scores DS join teams T on T.id = DS.team_id
- where not T.leaderboard_exclude
- group by T.id, T.name
- order by total_score desc
- ;
- """
+ select
+ T.id as team_id,
+ T.name as team_name,
+ sum(DS.points) as total_score,
+ sum(DS.distance) as total_distance
+ from
+ daily_scores DS join teams T on T.id = DS.team_id
+ where not T.leaderboard_exclude
+ group by T.id, T.name
+ order by total_score desc
+ ;
+ """
)
diff --git a/leaderboards/opmdays.yml b/leaderboards/opmdays.yml
index d61be96..da57cd6 100644
--- a/leaderboards/opmdays.yml
+++ b/leaderboards/opmdays.yml
@@ -2,7 +2,8 @@
title: Michigander - You call THAT a snow day?! - Prize
description: |
Riders who have ridden on the most offcial OPM snow days, or if no snow days are declared, on Michigan Day (Monday, January 18th). Ties to be broken by total mileage.
- Current official snow days: None
+
+ Current official snow days: Tuesday, January 16th
fields:
- name: athlete_id
visible: false
@@ -21,6 +22,11 @@ query: |
sum(R.distance) as distance
from lbd_athletes A
join rides R on R.athlete_id=A.id
+ /*
+ 2024 had an OPM snow day on Jan 16, so disable this for now.
+
where date(R.start_date) in (makedate(year(now()), 18))
+ */
+ where date(R.start_date) in ('2024-01-16')
group by athlete_id, athlete_name
order by days desc, distance desc;
diff --git a/requirements-test.txt b/requirements-test.txt
index 483cbb7..2df0352 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,5 +1,5 @@
-black==23.12.1
-flake8==6.1.0
+black==24.1.1
+flake8==7.0.0
pur==7.3.1
-pytest==7.4.4
+pytest==8.0.0
pytest-mock==3.12.0
diff --git a/requirements.txt b/requirements.txt
index 1ae6872..65ce7f6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,6 +6,7 @@ SQLAlchemy==1.3.24
# Thanks https://stackoverflow.com/a/77214086/424301 - Flask did not pin Werkzeug dep
Werkzeug==3.0.1
arrow==0.15.5
+autoflake==2.2.1
beautifulsoup4==4.8.2
colorlog==4.1.0
envparse==0.2.0
diff --git a/resources/passenger/passenger_wsgi.py b/resources/passenger/passenger_wsgi.py
index 2ab1ea3..03bbd5b 100644
--- a/resources/passenger/passenger_wsgi.py
+++ b/resources/passenger/passenger_wsgi.py
@@ -1,3 +1,8 @@
+#!/usr/bin/env python3
+import sys
+import os
+import logging
+
"""
This is a file designed to be used with Phusion Passenger.
@@ -6,8 +11,7 @@
We assume this repository is part of the
(This file is not intended for use when using freezing-web in Docker container.)
"""
-import sys, os
-import logging
+
BASE_DIR = os.path.join(os.path.dirname(__file__))
INTERP = os.path.join(BASE_DIR, "env", "bin", "python")
@@ -18,7 +22,7 @@
os.environ["APP_SETTINGS"] = os.path.join(BASE_DIR, "settings.cfg")
-from freezing.web import app as application
+from freezing.web import app as application # noqa
ch = logging.FileHandler(os.path.join(BASE_DIR, "application.log"))
ch.setLevel(logging.INFO)
@@ -32,9 +36,9 @@
logging.root,
]
-for l in loggers:
- l.setLevel(logging.INFO)
- l.addHandler(ch)
+for logger in loggers:
+ logger.setLevel(logging.INFO)
+ logger.addHandler(ch)
# Uncomment next two lines to enable debugging
# from werkzeug.debug import DebuggedApplication