gear-orders/scheduling.py

135 lines
4.1 KiB
Python
Raw Normal View History

2025-11-14 04:03:20 +00:00
import logging
import datetime
import pytz
from scheduler.asyncio import Scheduler
2026-03-08 02:28:07 +00:00
from db.constants import TIMELINE_ORDER_ISSUED, TIMELINE_ORDER_NOT_ISSUED
2026-03-07 00:33:08 +00:00
from settings import TIMEZONE, SCHEDULE_SYNC_INTERVAL
2025-11-14 04:03:20 +00:00
from orders import order_issue, order_check
2026-03-08 02:28:07 +00:00
from db.queries import orders_pool_by_id, orders_pool_scheduled, orders_pool_since, skip_day_contains, order_status_outstanding, timeline_event_put
2026-03-07 00:33:08 +00:00
from util import order_time, sqlite_time
2025-11-14 04:03:20 +00:00
logger = logging.getLogger(__name__)
2026-03-04 23:33:17 +00:00
WEEKDAYS = [0, 1, 2, 3, 4]
WEEKENDS = [5, 6]
2025-11-14 04:03:20 +00:00
GRACE_PERIOD = datetime.timedelta(seconds=10)
2025-11-14 04:03:20 +00:00
class OrderScheduler():
def __init__(self, loop):
self.tz = pytz.timezone(TIMEZONE)
self.scheduler = Scheduler(loop=loop, tzinfo=self.tz)
2026-03-04 23:33:17 +00:00
self.scheduled_pools = {}
for orders_pool in orders_pool_scheduled():
2026-03-07 00:33:08 +00:00
self.schedule_pool(orders_pool)
2026-03-04 23:33:17 +00:00
2026-03-06 03:47:39 +00:00
for order_status in order_status_outstanding():
2026-03-07 00:33:08 +00:00
self.scheduler.once(
2026-03-06 03:47:39 +00:00
datetime.datetime.fromisoformat(order_status.due_at) + GRACE_PERIOD,
self.scheduled_check,
args=(order_status.id,)
)
2026-03-04 23:33:17 +00:00
2026-03-07 00:33:08 +00:00
self.last_update = datetime.datetime.now(datetime.UTC)
self.scheduler.cyclic(
datetime.timedelta(seconds=SCHEDULE_SYNC_INTERVAL),
self.update_schedule
)
2025-11-14 04:03:20 +00:00
logger.info(self.scheduler)
2026-03-07 00:33:08 +00:00
def schedule_pool(self, orders_pool):
if orders_pool.id in self.scheduled_pools:
self.scheduler.delete_job(self.scheduled_pools[orders_pool.id])
del self.scheduled_pools[orders_pool.id]
if orders_pool.scheduled:
self.scheduled_pools[orders_pool.id] = self.scheduler.daily(
[order_time(t) for t in orders_pool.time.split(",")],
2026-03-07 00:33:08 +00:00
self.scheduled_order,
args=(orders_pool.id,)
)
2026-03-04 23:33:17 +00:00
async def scheduled_order(self, orders_pool_id):
orders_pool = orders_pool_by_id(orders_pool_id)
# Skip weekends or weekdays
2025-11-14 04:03:20 +00:00
day_of_week = datetime.datetime.now(tz=self.tz).weekday()
2026-03-04 23:33:17 +00:00
if (
(not orders_pool.weekends and day_of_week in WEEKENDS)
or
(not orders_pool.weekdays and day_of_week in WEEKDAYS)
):
2026-03-08 02:28:07 +00:00
logger.info(f'{orders_pool} Not scheduled for today')
timeline_event_put(
TIMELINE_ORDER_NOT_ISSUED,
f"{orders_pool.name} is not scheduled for today",
user=orders_pool.user,
orders_pool=orders_pool
)
2025-11-14 04:03:20 +00:00
return
# Skip stored dates
2025-11-14 04:03:20 +00:00
today = datetime.datetime.now(tz=self.tz).strftime("%Y-%m-%d")
2026-03-06 02:16:31 +00:00
if (skip_day_contains(orders_pool.user, today)):
2026-03-08 02:28:07 +00:00
logger.info(f'{orders_pool} Today is a skip day')
timeline_event_put(
TIMELINE_ORDER_NOT_ISSUED,
f"Today is a skip day",
user=orders_pool.user,
orders_pool=orders_pool
)
2025-11-14 04:03:20 +00:00
return
2026-03-04 23:33:17 +00:00
logger.info(f'Issuing order for {orders_pool.name}[{orders_pool.user.telegram_username}]')
2025-11-14 04:03:20 +00:00
2026-03-08 02:28:07 +00:00
issue_result = await order_issue(orders_pool)
2025-11-14 04:03:20 +00:00
2026-03-08 02:28:07 +00:00
if 'order_status' in issue_result:
order_status = issue_result['order_status']
# Schedule check
2026-03-07 00:33:08 +00:00
self.scheduler.once(
2026-03-04 23:33:17 +00:00
order_status.due_at + GRACE_PERIOD,
self.scheduled_check,
args=(order_status.id,)
)
2026-03-08 02:28:07 +00:00
timeline_event_put(
TIMELINE_ORDER_ISSUED,
2026-03-08 18:44:59 +00:00
order_status.text.split("\n")[0],
2026-03-08 02:28:07 +00:00
user=orders_pool.user,
orders_pool=orders_pool,
order_status=order_status,
extra={
"mastodon_status_url": issue_result['mastodon_status']['url']
}
)
elif 'reason' in issue_result:
timeline_event_put(
TIMELINE_ORDER_NOT_ISSUED,
issue_result['reason'],
user=orders_pool.user,
orders_pool=orders_pool
)
2025-11-14 04:03:20 +00:00
2026-03-04 23:33:17 +00:00
async def scheduled_check(self, outstanding_order_id):
await order_check(outstanding_order_id)
2026-03-07 00:33:08 +00:00
async def update_schedule(self):
last_update_sqlite = sqlite_time(self.last_update)
updated = False
for orders_pool_updated in orders_pool_since(last_update_sqlite):
logger.info(f'Updating schedule for {orders_pool_updated}')
self.schedule_pool(orders_pool_updated)
updated = True
if updated:
logger.info(self.scheduler)
self.last_update = datetime.datetime.now(datetime.UTC)