Skip to content

Commit db4bb29

Browse files
committed
Simplify and remove appscheduler
1 parent 8f48907 commit db4bb29

4 files changed

Lines changed: 29 additions & 64 deletions

File tree

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ dependencies = [
1818
"stamina",
1919
"uvicorn",
2020
"whitenoise",
21-
"apscheduler",
2221
# Dev dependencies
2322
"djlint",
2423
"flakytest",

surfcamsapi/asgi.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,30 @@
77
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
88
"""
99

10+
import asyncio
1011
import os
1112

1213
from django.core.asgi import get_asgi_application
1314

14-
from .scheduler import start_scheduler
15+
from .scheduler import run_scheduler
1516

1617
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "surfcamsapi.settings")
1718

18-
# Get the ASGI application
1919
django_application = get_asgi_application()
2020

21-
# Start the scheduler
22-
scheduler = None
23-
2421

2522
async def application(scope, receive, send):
26-
global scheduler
27-
if scheduler is None:
28-
scheduler = await start_scheduler()
29-
30-
await django_application(scope, receive, send)
23+
if scope["type"] == "lifespan":
24+
scheduler_task = None
25+
while True:
26+
message = await receive()
27+
if message["type"] == "lifespan.startup":
28+
scheduler_task = asyncio.create_task(run_scheduler())
29+
await send({"type": "lifespan.startup.complete"})
30+
elif message["type"] == "lifespan.shutdown":
31+
if scheduler_task is not None:
32+
scheduler_task.cancel()
33+
await send({"type": "lifespan.shutdown.complete"})
34+
return
35+
else:
36+
await django_application(scope, receive, send)

surfcamsapi/scheduler.py

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import asyncio
2-
import datetime
2+
import logging
33

44
import httpx
5-
from apscheduler.schedulers.asyncio import AsyncIOScheduler
65

7-
scheduler = None
6+
logger = logging.getLogger(__name__)
87

8+
INTERVAL_SECONDS = 2 * 60 * 60 # 2 hours
99

10-
async def daily_job():
10+
11+
async def check_cams():
1112
from django.conf import settings
1213

1314
from cams.models import Cam
@@ -26,44 +27,29 @@ async def check_cam_status(cam):
2627
else {},
2728
timeout=10.0,
2829
)
29-
# Check if the response was successful
3030
response.raise_for_status()
31-
# If cam was offline before, update it
3231
if cam.offline_since is not None:
3332
cam.offline_since = None
3433
except (httpx.RequestError, httpx.HTTPStatusError):
3534
from django.utils import timezone
3635

37-
# Only set offline_since if it's not already set
3836
if cam.offline_since is None:
3937
cam.offline_since = timezone.now()
4038
return cam
4139

42-
# Process cams in batches of 10
4340
cam_list = [cam async for cam in cams]
4441
for i in range(0, len(cam_list), 10):
4542
batch = cam_list[i : i + 10]
4643
await asyncio.gather(*(check_cam_status(cam) for cam in batch))
4744

48-
# Save all cams at once
4945
await Cam.objects.abulk_update(cam_list, ["offline_since"])
5046

5147

52-
async def start_scheduler():
53-
global scheduler
54-
if scheduler is not None:
55-
return scheduler
56-
57-
scheduler = AsyncIOScheduler()
58-
59-
scheduler.add_job(
60-
daily_job,
61-
"interval",
62-
hours=2,
63-
next_run_time=datetime.datetime.now(),
64-
id="daily_job",
65-
replace_existing=True,
66-
)
67-
68-
scheduler.start()
69-
return scheduler
48+
async def run_scheduler():
49+
"""Run check_cams in a loop. Designed to be used as a background task."""
50+
while True:
51+
try:
52+
await check_cams()
53+
except Exception:
54+
logger.exception("Scheduled check_cams failed")
55+
await asyncio.sleep(INTERVAL_SECONDS)

uv.lock

Lines changed: 0 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)