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