diff --git a/.gitignore b/.gitignore index 53880c8..031164c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,33 +1,31 @@ # Local config files -.env* *.cfg - +.env* /data/ # Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] *$py.class +*.py[cod] +__pycache__/ # C extensions *.so # Distribution / packaging +*.egg +*.egg-info/ .Python -env/ +.eggs/ +.installed.cfg +.venv/ build/ develop-eggs/ dist/ downloads/ eggs/ -.eggs/ lib/ lib64/ parts/ sdist/ -var/ +venv/ wheels/ -*.egg-info/ -.installed.cfg -*.egg - diff --git a/Dockerfile b/Dockerfile index 26d04ed..d9e7286 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,4 +41,9 @@ COPY --from=buildstep /build/wheels /tmp/wheels RUN pip3 install /tmp/wheels/* +RUN mkdir /app +ADD alembic.ini /app + +WORKDIR /app + CMD freezing-sync diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..e3d9515 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,56 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = freezing.model:migrations + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# max length of characters to apply to the +# "slug" field +#truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# This is read from Flask config instead, +# or set the SQLALCHEMY_URL environment variable +# sqlalchemy.url = driver://user:pass@localhost/dbname + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/example.cfg b/example.cfg index 0c382bf..ae45a49 100644 --- a/example.cfg +++ b/example.cfg @@ -3,49 +3,48 @@ DEBUG=true -# The URL to the database. Note that the pymysql driver must be explicitly specified. -SQLALCHEMY_URL=mysql+pymysql://freezing:freezing@127.0.0.1/freezing?charset=utf8mb4&binary_prefix=true - -# Configuration for the Strava client. These settings come from your App setup. -STRAVA_CLIENT_ID=? -STRAVA_CLIENT_SECRET=? -STRAVA_ACTIVITY_CACHE_DIR=/path/to/cache/activities +BEANSTALKD_HOST=127.0.0.1 +BEANSTALKD_PORT=11300 -# Visit https://www.visualcrossing.com/ for your own -VISUAL_CROSSING_API_KEY=? -VISUAL_CROSSING_CACHE_DIR=/path/to/cache/visualcrossing +DATADOG_API_KEY=? +DATADOG_APP_KEY=? +DATADOG_HOST=127.0.0.1 +DATADOG_PORT=8125 -# Comma-separated list of Strava Clubs that are the participating teams. -TEAMS=1234,1235 +ENVIRONMENT=localdev -# Comma-separated list of teams that should be included for overall stats but are not "playing the game" -OBSERVER_TEAMS=324147 +# Any keywords to match on to exclude rides (default: "#NoBAFS"). Note: these are not case-sensitive. +EXCLUDE_KEYWORDS='#NoBAFS' # The main team id. If people join this before they join the competition teams, they will get on the leaderboards. -MAIN_TEAM=324147 +MAIN_TEAM=1 +# Comma-separated list of teams that should be included for overall stats but are not "playing the game" +OBSERVER_TEAMS=2 +# Comma-separated list of Strava Clubs that are the participating teams. +TEAMS=3,4 -# The competition title -COMPETITION_TITLE=BikeArlington Freezing Saddles 2019 +# The URL to the database. Note that the pymysql driver must be explicitly specified. +SQLALCHEMY_URL='mysql+pymysql://freezing:please-change-me-as-this-is-a-default@127.0.0.1/freezing?charset=utf8mb4&binary_prefix=true' # The start date of the competition -- WITH TIME ZONE -START_DATE=2020-01-01T00:00:00-05:00 - +START_DATE=2025-01-01T00:00:00-05:00 # The end date of the competition -- WITH TIME ZONE. # The sync will stop fetching rides after this date (plus grace period) -END_DATE=2020-03-19T23:59:59-04:00 - -# The hostname for a beanstalkd server. -BEANSTALKD_HOST=localhost - -# The port for beanstalkd server (default 11300) -BEANSTALKD_PORT=11300 +END_DATE=2025-03-19T23:59:59-04:00 -# How long (days) can people upload rides after competition> -UPLOAD_GRACE_PERIOD= +STRAVA_ACTIVITY_CACHE_DIR=data/cache/activities -# Any keywords to match on to exclude rides (default: "#NoBAFS"). Note: these are not case-sensitive. -EXCLUDE_KEYWORDS=#NoBAFS +# Configuration for the Strava client. These settings come from your App setup. +STRAVA_CLIENT_ID=STRAVA_CLIENT_ID +STRAVA_CLIENT_SECRET=STRAVA_CLIENT_SECRET # Python Time zone for competition days. # See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones -TIMEZONE=America/New_York +TIMEZONE=TIMEZONE:-America/New_York + +# How long (days) can people upload rides after competition? +UPLOAD_GRACE_PERIOD=7 + +# Visit https://www.visualcrossing.com/ for your own API key +VISUAL_CROSSING_API_KEY=? +VISUAL_CROSSING_CACHE_DIR=data/cache/weather diff --git a/freezing/sync/data/activity.py b/freezing/sync/data/activity.py index 6fd025a..28a5429 100644 --- a/freezing/sync/data/activity.py +++ b/freezing/sync/data/activity.py @@ -6,7 +6,7 @@ import arrow from freezing.model import meta from freezing.model.orm import Athlete, Ride, RideEffort, RideError, RideGeo, RidePhoto -from geoalchemy import WKTSpatialElement +from geoalchemy2.elements import WKTElement from sqlalchemy import and_, func from sqlalchemy.orm import joinedload from stravalib import unithelper @@ -571,7 +571,7 @@ def write_ride(self, activity: Activity) -> Ride: """ session = meta.scoped_session() if activity.start_latlng: - start_geo = WKTSpatialElement( + start_geo = WKTElement( "POINT({lon} {lat})".format( lat=activity.start_latlng.lat, lon=activity.start_latlng.lon ) @@ -580,7 +580,7 @@ def write_ride(self, activity: Activity) -> Ride: start_geo = None if activity.end_latlng: - end_geo = WKTSpatialElement( + end_geo = WKTElement( "POINT({lon} {lat})".format( lat=activity.end_latlng.lat, lon=activity.end_latlng.lon ) diff --git a/freezing/sync/data/athlete.py b/freezing/sync/data/athlete.py index 06df535..d890464 100644 --- a/freezing/sync/data/athlete.py +++ b/freezing/sync/data/athlete.py @@ -11,7 +11,7 @@ RidePhoto, Team, ) -from geoalchemy import WKTSpatialElement +from geoalchemy2.elements import WKTElement from sqlalchemy import and_ from sqlalchemy.orm import joinedload from stravalib import model as sm diff --git a/freezing/sync/data/streams.py b/freezing/sync/data/streams.py index 4a86672..72cc758 100644 --- a/freezing/sync/data/streams.py +++ b/freezing/sync/data/streams.py @@ -3,7 +3,7 @@ from freezing.model import meta from freezing.model.orm import Athlete, Ride, RideTrack -from geoalchemy import WKTSpatialElement +from geoalchemy2.elements import WKTElement from polyline.codec import PolylineCodec from sqlalchemy import and_, or_, update from sqlalchemy.orm import joinedload @@ -151,7 +151,7 @@ def write_ride_streams(self, streams: List[Stream], ride: Ride): RideTrack.__table__.delete().where(RideTrack.ride_id == ride.id) ) - gps_track = WKTSpatialElement(wktutils.linestring_wkt(lonlat_points)) + gps_track = WKTElement(wktutils.linestring_wkt(lonlat_points)) ride_track = RideTrack() ride_track.gps_track = gps_track diff --git a/requirements.txt b/requirements.txt index df322e7..8851f2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ APScheduler==3.10.4 -GeoAlchemy @ https://github.com/hozn/GeoAlchemy/archive/0.7.3dev1.tar.gz +GeoAlchemy2==0.16.0 PyMySQL==1.1.1 colorlog==4.1.0 datadog==0.33.0 envparse==0.2.0 -freezing-model @ https://github.com/freezingsaddles/freezing-model/archive/0.8.6.tar.gz +freezing-model @ https://github.com/freezingsaddles/freezing-model/archive/0.10.4.tar.gz greenstalk==1.0.1 polyline==1.4.0 pytz==2024.2 diff --git a/setup.py b/setup.py index 44cedea..bc3de96 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,8 @@ # -*- coding: utf-8 -*- -import os.path from setuptools import setup -version = "1.4.7" +version = "1.5.2" long_description = """ freezing-sync is the component responsible for fetching activities, weather data, etc. @@ -11,8 +10,9 @@ install_requires = [ "APScheduler", - "GeoAlchemy", + "GeoAlchemy2", "PyMySQL", + "SQLAlchemy", "colorlog", "datadog", "envparse",