Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ test_ci:
up:
docker compose --file compose.dev.yaml up --remove-orphans

up-all:
docker compose --file compose.dev.yaml --profile taskrunner up --remove-orphans

down:
docker compose --file compose.dev.yaml down --remove-orphans

Expand Down
Empty file removed featureflags/__init__.py
Empty file.
45 changes: 0 additions & 45 deletions featureflags/data.py

This file was deleted.

Empty file.
Empty file.
30 changes: 0 additions & 30 deletions featureflags/management/commands/create_feature_flag.py

This file was deleted.

27 changes: 0 additions & 27 deletions featureflags/migrations/0001_initial.py

This file was deleted.

Empty file.
41 changes: 0 additions & 41 deletions featureflags/models.py

This file was deleted.

9 changes: 6 additions & 3 deletions mottle/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"django.contrib.messages",
"django.contrib.staticfiles",
"web",
"featureflags",
"urlshortener",
"django_hosts",
"django_htmx",
Expand Down Expand Up @@ -303,11 +302,15 @@

PROXY_URL = env.str("PROXY_URL", None)

EVENTS_ENABLED = env.bool("EVENTS_ENABLED", True)
EVENT_SOURCES_FETCH_CONCURRENCY_LIMIT = env.int("EVENT_SOURCES_FETCH_CONCURRENCY_LIMIT", 100)
EVENTS_FETCH_CONCURRENCY_LIMIT = env.int("EVENTS_FETCH_CONCURRENCY_LIMIT", 100)
EVENT_ARTIST_NAME_MATCH_THRESHOLD = env.int("EVENT_ARTIST_NAME_MATCH_THRESHOLD", 85)
RESOLVE_SONGKICK_URLS = env.bool("RESOLVE_SONGKICK_URLS", False)

GEODJANGO_SRID = 4326

SCHEDULE = {
"PLAYLIST_UPDATES": env.str("SCHEDULE_PLAYLIST_UPDATES", ""),
"EVENT_UPDATES": env.str("SCHEDULE_EVENT_UPDATES", ""),
"PLAYLIST_UPDATES": env.str("SCHEDULE_PLAYLIST_UPDATES", None),
"EVENT_UPDATES": env.str("SCHEDULE_EVENT_UPDATES", None),
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies = [
[dependency-groups]
dev = [
"mypy==1.18.2",
"django-stubs==5.2.7",
"django-stubs==5.2.8",
"lxml-stubs==0.5.1",
"pytest==9.0.1",
"pytest-asyncio==1.3.0",
Expand Down
25 changes: 13 additions & 12 deletions taskrunner/schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,54 @@
from typing import cast

from croniter.croniter import croniter
from django.conf import settings
from django_q.models import Schedule
from django_q.utils import localtime

from featureflags.data import FeatureFlag

logger = logging.getLogger(__name__)

PLAYLIST_UPDATES_NAME = "playlist_updates"
PLAYLIST_UPDATES_FUNC = "web.tasks.check_playlists_for_updates"
PLAYLIST_UPDATES_SCHEDULE = FeatureFlag.playlist_updates_schedule()

EVENT_UPDATES_NAME = "event_updates"
EVENT_UPDATES_FUNC = "web.tasks.check_artists_for_event_updates"
EVENT_UPDATES_SCHEDULE = FeatureFlag.event_updates_schedule()


def get_next_run(cron_schedule: str) -> datetime:
return cast("datetime", croniter(cron_schedule, localtime()).get_next(datetime))


def playlist_updates() -> None:
if PLAYLIST_UPDATES_SCHEDULE is None:
raise RuntimeError("playlist_updates schedule is not set")
schedule = settings.SCHEDULE.get("PLAYLIST_UPDATES")
if schedule is None:
logger.warning("PLAYLIST_UPDATES schedule is not set. Skipping task creation")
return
Comment on lines 23 to +27

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip scheduling when cron env vars missing

Both schedule helpers now return early when settings.SCHEDULE[...] is None. Because SCHEDULE_PLAYLIST_UPDATES/SCHEDULE_EVENT_UPDATES default to None (settings.py lines 313-315) and neither compose files nor .env define them, starting the taskrunner will hit this branch and skip creating the django-q schedules, so playlist and event updates never run. Previously the absence of a schedule raised and failed startup; now the job setup silently disappears whenever those env vars are unset.

Useful? React with 👍 / 👎.


Schedule.objects.update_or_create(
name=PLAYLIST_UPDATES_NAME,
defaults={
"func": PLAYLIST_UPDATES_FUNC,
"schedule_type": Schedule.CRON,
"cron": PLAYLIST_UPDATES_SCHEDULE,
"cron": schedule,
"cluster": "long_running",
"next_run": get_next_run(PLAYLIST_UPDATES_SCHEDULE),
"next_run": get_next_run(schedule),
},
)


def event_updates() -> None:
if EVENT_UPDATES_SCHEDULE is None:
raise RuntimeError("event_updates schedule is not set")
schedule = settings.SCHEDULE.get("EVENT_UPDATES")
if schedule is None:
logger.warning("EVENT_UPDATES schedule is not set. Skipping task creation")
return

Schedule.objects.update_or_create(
name=EVENT_UPDATES_NAME,
defaults={
"func": EVENT_UPDATES_FUNC,
"schedule_type": Schedule.CRON,
"cron": EVENT_UPDATES_SCHEDULE,
"cron": schedule,
"cluster": "long_running",
"next_run": get_next_run(EVENT_UPDATES_SCHEDULE),
"next_run": get_next_run(schedule),
},
)
14 changes: 7 additions & 7 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions web/events/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from datetime import date, datetime
from typing import Any, Optional

from django.conf import settings
from sentry_sdk import capture_exception, capture_message

from featureflags.data import FeatureFlag
from urlshortener.models import ShortURL

from .constants import (
Expand Down Expand Up @@ -515,7 +515,7 @@ async def extract_songkick_event(event_data: dict[str, Any]) -> Event:

urls = [u.split("?")[0] for u in urls]

if await FeatureFlag.resolve_songkick_urls():
if settings.RESOLVE_SONGKICK_URLS:
calls = [asend_get_request(async_songkick_client, u, redirect_url=True, raise_for_lte_300=False) for u in urls]
results = await asyncio.gather(*calls, return_exceptions=True)

Expand Down
8 changes: 3 additions & 5 deletions web/management/commands/get_event_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from typing import Any, cast

from asgiref.sync import async_to_sync
from django.conf import settings
from django.core.management import CommandError
from django.core.management.base import BaseCommand
from tekore.model import FullPlaylistTrack

from featureflags.data import FeatureFlag
from taskrunner.tasks import get_event_updates
from web.data import TrackData
from web.spotify import get_client_token
Expand All @@ -34,7 +34,7 @@ def add_arguments(self, parser: Any) -> None:
parser.add_argument(
"--concurrency-limit",
type=int,
default=FeatureFlag.event_fetching_concurrency_limit(),
default=settings.EVENTS_FETCH_CONCURRENCY_LIMIT,
help="Limit the number of concurrent executions for event fetching (how many artists to check at once)",
)
parser.add_argument(
Expand All @@ -47,9 +47,7 @@ def add_arguments(self, parser: Any) -> None:
def handle(self, *_: Any, **options: str) -> None:
playlist_id: str | None = options.get("playlist_id")
force_refetch: bool = cast("bool", options.get("force_refetch", False))
concurrency_limit: int = cast(
"int", options.get("concurrency_limit", FeatureFlag.event_fetching_concurrency_limit())
)
concurrency_limit: int = cast("int", options.get("concurrency_limit", settings.EVENTS_FETCH_CONCURRENCY_LIMIT))
compile_notifications: bool = cast("bool", options.get("compile_notifications", False))

if playlist_id:
Expand Down
8 changes: 3 additions & 5 deletions web/management/commands/track_artists_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
from typing import Any, cast

from asgiref.sync import async_to_sync
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from tekore.model import FullPlaylistTrack

from featureflags.data import FeatureFlag
from taskrunner.tasks import task_track_artists_events
from web.data import TrackData
from web.models import SpotifyUser
Expand All @@ -32,7 +32,7 @@ def add_arguments(self, parser: Any) -> None:
parser.add_argument(
"--concurrency-limit",
type=int,
default=FeatureFlag.event_sources_fetching_concurrency_limit(),
default=settings.EVENT_SOURCES_FETCH_CONCURRENCY_LIMIT,
help="Limit the number of concurrent executions for event fetching (how many artists to check at once)",
)

Expand All @@ -41,9 +41,7 @@ def handle(self, *_: Any, **options: str) -> None:
artist_id: str | None = options.get("artist_id") # pyright: ignore[reportAssignmentType]
playlist_id: str | None = options.get("playlist_id") # pyright: ignore[reportAssignmentType]
force_reevaluate: bool = cast("bool", options.get("force_reevaluate", False)) # pyright: ignore[reportAssignmentType]
concurrency_limit: int = cast(
"int", options.get("concurrency_limit", FeatureFlag.event_fetching_concurrency_limit())
)
concurrency_limit: int = cast("int", options.get("concurrency_limit", settings.EVENTS_FETCH_CONCURRENCY_LIMIT))

if not user_id:
raise CommandError("You must provide a user ID")
Expand Down
Loading