Generate orders from db

This commit is contained in:
Johnny Gear 2026-03-04 17:33:17 -06:00
parent fd654153a4
commit 18b9c9de55
13 changed files with 403 additions and 114 deletions

5
.gitignore vendored
View file

@ -1,7 +1,10 @@
*.pyc
.env .env
*~ *~
db.sqlite3 db.sqlite3
Caddyfile Caddyfile
*.dump.sql *.dump.sql
.vscode

View file

@ -104,9 +104,24 @@ class OrderStatus(BaseModel):
created_at = DateTimeField() # TIMESTAMP created_at = DateTimeField() # TIMESTAMP
due_at = DateTimeField() # TIMESTAMP due_at = DateTimeField() # TIMESTAMP
mastodon_id = TextField() mastodon_id = TextField()
punishment = ForeignKeyField(column_name='punishment_id', field='id', model=PunishmentStatus, null=True)
text = TextField() text = TextField()
pool = ForeignKeyField(
column_name='orders_pool_id',
field='id',
model=OrdersPool,
backref='statuses',
null=True
)
user = ForeignKeyField(
column_name='user_id',
field='id',
model=User,
backref='statuses',
null=False
)
class Meta: class Meta:
table_name = 'order_status' table_name = 'order_status'
@ -115,6 +130,15 @@ class Repeat(BaseModel):
orders = TextField() orders = TextField()
probability = FloatField() probability = FloatField()
orders_pool = ForeignKeyField(
column_name='orders_pool_id',
field='id',
model=OrdersPool,
null=False,
unique=True,
backref='repeat'
)
class Meta: class Meta:
table_name = 'repeat' table_name = 'repeat'

View file

@ -28,6 +28,12 @@ def orders_pool_list(user_id):
def orders_pool(user_id, set_id): def orders_pool(user_id, set_id):
return OrdersPool.get(OrdersPool.user_id == user_id, OrdersPool.id == set_id) return OrdersPool.get(OrdersPool.user_id == user_id, OrdersPool.id == set_id)
def orders_pool_by_id(pool_id):
return OrdersPool.get(OrdersPool.id == pool_id)
def orders_pool_scheduled():
return OrdersPool.select().where(OrdersPool.scheduled == True)
def domsubusers_add(sub, dom): def domsubusers_add(sub, dom):
return DomSubUsers.create( return DomSubUsers.create(
sub=sub, sub=sub,
@ -43,23 +49,27 @@ def domsubusers_delete(sub, dom):
def domsubusers_list(dom): def domsubusers_list(dom):
return DomSubUsers.select().where(DomSubUsers.dom == dom) return DomSubUsers.select().where(DomSubUsers.dom == dom)
def repeat_get(): def repeat_get(orders_pool_id):
try: try:
return Repeat.get() return Repeat.get(orders_pool_id = orders_pool_id)
except Repeat.DoesNotExist: except Repeat.DoesNotExist:
return None return None
def repeat_increment(): def repeat_increment(id):
q = Repeat.update(count=Repeat.count + 1) r = Repeat.get(id=id)
return q.execute() q = Repeat.update(
count = r.count + 1
)
q.execute()
def repeat_put(probability, orders): def repeat_put(orders_pool_id, probability, orders):
return Repeat.create( return Repeat.create(
orders_pool_id=orders_pool_id,
probability=probability, probability=probability,
orders=orders orders=orders
) )
def repeat_clear(): def repeat_clear(id):
q = Repeat.delete() q = Repeat.delete()
q.execute() q.execute()
@ -80,14 +90,19 @@ def skip_day_contains(date):
q = SkipDay.select().where(SkipDay.date == date) q = SkipDay.select().where(SkipDay.date == date)
return len(q) > 0 return len(q) > 0
def order_status_put(mastodon_id, created_at, due_at, text): def order_status_put(orders_pool, user, mastodon_id, created_at, due_at, text):
return OrderStatus.create( return OrderStatus.create(
orders_pool_id=orders_pool.id,
user_id=user.id,
mastodon_id=mastodon_id, mastodon_id=mastodon_id,
created_at=created_at, created_at=created_at,
due_at=due_at, due_at=due_at,
text=text text=text
) )
def order_status_by_id(order_status_id):
return OrderStatus.get(id=order_status_id)
def order_status_outstanding(): def order_status_outstanding():
return OrderStatus.select().where( return OrderStatus.select().where(
(OrderStatus.confirmed_at.is_null()) & (OrderStatus.punishment_id.is_null()) (OrderStatus.confirmed_at.is_null()) & (OrderStatus.punishment_id.is_null())

View file

@ -10,30 +10,18 @@ logger = logging.getLogger(__name__)
def pick_order(orders): def pick_order(orders):
picked = random.choices(orders, picked = random.choices(orders,
weights=[o['weight'] for o in orders], weights=[o.weight for o in orders],
k=1)[0] k=1)[0]
result = [] result = []
repeat = 0.0
if('text' in picked): result.append(picked.name)
result.append(picked['text'])
if('repeat' in picked): for add_on in picked.add_ons:
repeat = picked['repeat'] if add_on.probability > random.random():
result.append(add_on.name)
if('add' in picked): return (result, picked.repeat)
for addition in picked['add']:
if addition['probability'] > random.random():
result.append(addition['text'])
if('pick' in picked):
(new_result, new_repeat) = pick_order(picked['pick'])
result += new_result
if new_repeat > 0.0:
repeat = new_repeat
return (result, repeat)
def read_config(): def read_config():
with open(ORDERS_YML) as stream: with open(ORDERS_YML) as stream:
@ -43,31 +31,29 @@ def read_config():
logger.error(exc) logger.error(exc)
return orders return orders
def generate_order(): def generate_order(orders_pool):
# Do we want to repeat? # Do we want to repeat?
repeat = repeat_get() if orders_pool.repeat.count() != 0:
if repeat is not None: repeat = orders_pool.repeat.get()
if repeat.probability > random.random(): if repeat.probability > random.random():
repeat_increment() repeat_increment(repeat.id)
return { return {
"orders": json.loads(repeat.orders), "orders": json.loads(repeat.orders),
"count": repeat.count "count": repeat.count
} }
else: else:
repeat_clear() repeat_clear(repeat.id)
orders_config = read_config()
if orders_config['orders_probability'] < random.random(): if orders_pool.probability < random.random():
# No orders today # No orders today
return { } return { }
# Pick new orders # Pick new orders
(result, repeat_p) = pick_order(orders_config['orders']) (result, repeat_p) = pick_order(orders_pool.orders)
# Log the repeat # Log the repeat
if repeat_p > 0.0: if repeat_p > 0.0:
repeat_put(repeat_p, json.dumps(result)) repeat_put(orders_pool.id, repeat_p, json.dumps(result))
return { return {
"orders": result "orders": result

41
main.py
View file

@ -2,15 +2,25 @@ import sys
import logging import logging
import argparse import argparse
import asyncio import asyncio
import json
from scheduling import OrderScheduler from scheduling import OrderScheduler
from generate import generate_order
from orders import order_issue, order_check from orders import order_issue, order_check
from db.queries import orders_pool_by_id
from telegram.telegram import handle_commands from telegram.telegram import handle_commands
from telegram.commands import commands from telegram.commands import commands
from db.queries import initdb
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
async def do_order_issue(order_pool_id):
orders_pool = orders_pool_by_id(args.orders_pool_id)
order_status = await order_issue(orders_pool)
if order_status is not None:
logger.info(f'Issued order id {order_status.id}')
if __name__=='__main__': if __name__=='__main__':
logging.basicConfig( logging.basicConfig(
format="%(asctime)s %(module)s [%(levelname)-4.4s] %(message)s", format="%(asctime)s %(module)s [%(levelname)-4.4s] %(message)s",
@ -23,22 +33,31 @@ if __name__=='__main__':
subparsers = parser.add_subparsers(help="Sub-command help", dest="command") subparsers = parser.add_subparsers(help="Sub-command help", dest="command")
parser_immediate = subparsers.add_parser('immediate', help='Immediately generate a command') parser_generate = subparsers.add_parser('generate', help='Generate a command')
parser_check = subparsers.add_parser('check', help="Checks if any orders are outstanding") parser_generate.add_argument('orders_pool_id')
parser_initdb = subparsers.add_parser('initdb', help="Creates the database tables")
parser_issue = subparsers.add_parser('issue', help='Issue a command')
parser_issue.add_argument('orders_pool_id')
parser_issue = subparsers.add_parser('check', help='Check on the status of an order')
parser_issue.add_argument('order_status_id')
args = parser.parse_args() args = parser.parse_args()
if args.command == 'immediate': if args.command == 'generate':
orders_pool = orders_pool_by_id(args.orders_pool_id)
logger.info('Orders - %s', json.dumps(generate_order(orders_pool)))
elif args.command == 'issue':
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
loop.run_until_complete(order_issue()) loop.run_until_complete(
loop.close() do_order_issue(args.orders_pool_id)
)
elif args.command == 'check': elif args.command == 'check':
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
loop.run_until_complete(order_check()) loop.run_until_complete(
loop.close() order_check(args.order_status_id)
elif args.command == 'initdb': )
initdb()
else: else:
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
s = OrderScheduler(loop) s = OrderScheduler(loop)

View file

@ -0,0 +1,54 @@
"""Peewee migrations -- 005_delete_repeat.py.
Some examples (model - class or model name)::
> Model = migrator.orm['table_name'] # Return model in current state by name
> Model = migrator.ModelClass # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python function with the given args
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.add_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
> migrator.add_constraint(model, name, sql)
> migrator.drop_index(model, *col_names)
> migrator.drop_not_null(model, *field_names)
> migrator.drop_constraints(model, *constraints)
"""
from contextlib import suppress
import peewee as pw
from peewee_migrate import Migrator
with suppress(ImportError):
import playhouse.postgres_ext as pw_pext
def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your migrations here."""
migrator.remove_model('repeat')
def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your rollback migrations here."""
@migrator.create_model
class Repeat(pw.Model):
id = pw.AutoField()
count = pw.IntegerField(default=0)
orders = pw.TextField()
probability = pw.FloatField()
class Meta:
table_name = "repeat"

View file

@ -0,0 +1,55 @@
"""Peewee migrations -- 006_create_repeat.py.
Some examples (model - class or model name)::
> Model = migrator.orm['table_name'] # Return model in current state by name
> Model = migrator.ModelClass # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python function with the given args
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.add_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
> migrator.add_constraint(model, name, sql)
> migrator.drop_index(model, *col_names)
> migrator.drop_not_null(model, *field_names)
> migrator.drop_constraints(model, *constraints)
"""
from contextlib import suppress
import peewee as pw
from peewee_migrate import Migrator
with suppress(ImportError):
import playhouse.postgres_ext as pw_pext
def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your migrations here."""
@migrator.create_model
class Repeat(pw.Model):
id = pw.AutoField()
count = pw.IntegerField(default=0)
orders = pw.TextField()
probability = pw.FloatField()
orders_pool = pw.ForeignKeyField(column_name='orders_pool_id', field='id', model=migrator.orm['orders_pool'], unique=True)
class Meta:
table_name = "repeat"
def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your rollback migrations here."""
migrator.remove_model('repeat')

View file

@ -0,0 +1,57 @@
"""Peewee migrations -- 007_delete_order_status.py.
Some examples (model - class or model name)::
> Model = migrator.orm['table_name'] # Return model in current state by name
> Model = migrator.ModelClass # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python function with the given args
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.add_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
> migrator.add_constraint(model, name, sql)
> migrator.drop_index(model, *col_names)
> migrator.drop_not_null(model, *field_names)
> migrator.drop_constraints(model, *constraints)
"""
from contextlib import suppress
import peewee as pw
from peewee_migrate import Migrator
with suppress(ImportError):
import playhouse.postgres_ext as pw_pext
def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your migrations here."""
migrator.remove_model('order_status')
def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your rollback migrations here."""
@migrator.create_model
class OrderStatus(pw.Model):
id = pw.AutoField()
confirmed_at = pw.DateTimeField(null=True)
created_at = pw.DateTimeField()
due_at = pw.DateTimeField()
mastodon_id = pw.TextField()
punishment = pw.ForeignKeyField(column_name='punishment_id', field='id', model=migrator.orm['punishment_status'], null=True)
text = pw.TextField()
class Meta:
table_name = "order_status"

View file

@ -0,0 +1,58 @@
"""Peewee migrations -- 008_add_order_status.py.
Some examples (model - class or model name)::
> Model = migrator.orm['table_name'] # Return model in current state by name
> Model = migrator.ModelClass # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python function with the given args
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.add_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
> migrator.add_constraint(model, name, sql)
> migrator.drop_index(model, *col_names)
> migrator.drop_not_null(model, *field_names)
> migrator.drop_constraints(model, *constraints)
"""
from contextlib import suppress
import peewee as pw
from peewee_migrate import Migrator
with suppress(ImportError):
import playhouse.postgres_ext as pw_pext
def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your migrations here."""
@migrator.create_model
class OrderStatus(pw.Model):
id = pw.AutoField()
confirmed_at = pw.DateTimeField(null=True)
created_at = pw.DateTimeField()
due_at = pw.DateTimeField()
mastodon_id = pw.TextField()
text = pw.TextField()
pool = pw.ForeignKeyField(column_name='orders_pool_id', field='id', model=migrator.orm['orders_pool'], null=True)
user = pw.ForeignKeyField(column_name='user_id', field='id', model=migrator.orm['user'])
class Meta:
table_name = "order_status"
def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
"""Write your rollback migrations here."""
migrator.remove_model('order_status')

View file

@ -4,7 +4,7 @@ import asyncio
from util import make_session from util import make_session
from generate import generate_order, generate_punishment from generate import generate_order, generate_punishment
from db.queries import order_status_put, punishment_status_put, order_status_outstanding, order_status_confirm from db.queries import order_status_by_id, order_status_put, punishment_status_put, order_status_outstanding, order_status_confirm
from mastodon import Mastodon from mastodon import Mastodon
from telegram.telegram import Telegram from telegram.telegram import Telegram
from settings import MASTODON_USERNAME, ORDER_TIMEOUT, ENV from settings import MASTODON_USERNAME, ORDER_TIMEOUT, ENV
@ -12,7 +12,8 @@ from util import timezone
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
async def order_mastodon_post(session, orders_str, repeats, due_at): async def order_mastodon_post(session, orders_pool, orders_str, repeats, due_at):
# TODO: Get user's mastodon username
post = "Here are today's orders for @%s -\n\n" % MASTODON_USERNAME post = "Here are today's orders for @%s -\n\n" % MASTODON_USERNAME
post += orders_str + "\n\n" post += orders_str + "\n\n"
if repeats > 1: if repeats > 1:
@ -26,7 +27,7 @@ async def order_mastodon_post(session, orders_str, repeats, due_at):
m = Mastodon(session) m = Mastodon(session)
return await m.statusPost(post) return await m.statusPost(post)
async def order_telegram_post(session, orders_str, repeats, due_at, m_url): async def order_telegram_post(session, orders_pool, orders_str, repeats, due_at, m_url):
post = "Here are your orders -\n\n" post = "Here are your orders -\n\n"
post += orders_str + "\n\n" post += orders_str + "\n\n"
if repeats > 1: if repeats > 1:
@ -37,23 +38,23 @@ async def order_telegram_post(session, orders_str, repeats, due_at, m_url):
post += "\n⚠️ DEV" post += "\n⚠️ DEV"
t = Telegram(session) t = Telegram(session)
await t.message_send(post) await t.message_send(orders_pool.user.telegram_chat_id, post)
async def order_telegram_post_none(session): async def order_telegram_post_none(session, orders_pool):
post = "No orders for today" post = "No orders for today"
if ENV == 'dev': if ENV == 'dev':
post += "\n⚠️ DEV" post += "\n⚠️ DEV"
t = Telegram(session) t = Telegram(session)
await t.message_send(post) await t.message_send(orders_pool.user.telegram_chat_id, post)
async def order_issue(): async def order_issue(orders_pool):
async with make_session() as session: async with make_session() as session:
orders_info = generate_order() orders_info = generate_order(orders_pool)
if 'orders' not in orders_info: if 'orders' not in orders_info:
logger.info("No orders for today") logger.info("No orders for today")
await order_telegram_post_none(session) await order_telegram_post_none(session, orders_pool)
return return
orders_str = "\n".join(orders_info['orders']) orders_str = "\n".join(orders_info['orders'])
@ -65,6 +66,7 @@ async def order_issue():
m_status = await order_mastodon_post( m_status = await order_mastodon_post(
session, session,
orders_pool,
orders_str, orders_str,
repeats_count, repeats_count,
due_at due_at
@ -72,20 +74,21 @@ async def order_issue():
await order_telegram_post( await order_telegram_post(
session, session,
orders_pool,
orders_str, orders_str,
repeats_count, repeats_count,
due_at, due_at,
m_status['url'] m_status['url']
) )
order_status_put( return order_status_put(
orders_pool,
orders_pool.user,
m_status['id'], m_status['id'],
created_at, created_at,
due_at, due_at,
orders_str orders_str
) )
return due_at
async def punishment_mastodon_post(session, punishment_str, reply_id=None): async def punishment_mastodon_post(session, punishment_str, reply_id=None):
post = "@%s has failed to post proof of compliance. Here is the punishment -\n\n" % MASTODON_USERNAME post = "@%s has failed to post proof of compliance. Here is the punishment -\n\n" % MASTODON_USERNAME
@ -112,6 +115,11 @@ async def punishment_telegram_post(session, punishment_str, m_url):
await t.message_send(post) await t.message_send(post)
async def punishment_issue(session, outstanding_order): async def punishment_issue(session, outstanding_order):
# TODO: Generate a punishment
logger.info('TODO: Generate a punishment')
return
punishment = generate_punishment() punishment = generate_punishment()
punishment_str = "\n".join(punishment) punishment_str = "\n".join(punishment)
@ -135,31 +143,32 @@ async def punishment_issue(session, outstanding_order):
) )
order_check_lock = asyncio.Lock() order_check_lock = asyncio.Lock()
async def order_check(): async def order_check(order_status_id):
async with order_check_lock: async with order_check_lock:
async with make_session() as session: async with make_session() as session:
outstanding_orders = order_status_outstanding() order_status = order_status_by_id(order_status_id)
for outstanding_order in outstanding_orders:
m = Mastodon(session)
context = await m.statusContext(outstanding_order.mastodon_id)
confirmed_at = None m = Mastodon(session)
for d in context['descendants']: context = await m.statusContext(order_status.mastodon_id)
if (
d['in_reply_to_id'] == outstanding_order.mastodon_id and confirmed_at = None
d['account']['username'] == MASTODON_USERNAME and for d in context['descendants']:
len(d['media_attachments']) > 0 if (
): d['in_reply_to_id'] == order_status.mastodon_id and
confirmed_at = d['created_at'] # TODO: Get mastodon username
order_status_confirm(outstanding_order.id, confirmed_at) d['account']['username'] == MASTODON_USERNAME and
logger.info('Confirmed order %s' % (outstanding_order.id)) len(d['media_attachments']) > 0
break ):
confirmed_at = d['created_at']
order_status_confirm(order_status.id, confirmed_at)
logger.info('Confirmed order %s' % (order_status.id))
break
if confirmed_at is None: if confirmed_at is None:
logger.info('Order %s remains unconfirmed' % (outstanding_order.id)) logger.info('Order %s remains unconfirmed' % (order_status.id))
due_at = datetime.datetime.fromisoformat(outstanding_order.due_at) due_at = datetime.datetime.fromisoformat(order_status.due_at)
if(due_at < datetime.datetime.now(datetime.UTC)): if(due_at < datetime.datetime.now(datetime.UTC)):
logger.info('Time to issue a punishment for %s' % outstanding_order.id) logger.info('Time to issue a punishment for %s' % order_status.id)
await punishment_issue(session, outstanding_order) await punishment_issue(session, order_status)

View file

@ -6,13 +6,13 @@ from scheduler.asyncio import Scheduler
from settings import TIMEZONE from settings import TIMEZONE
from orders import order_issue, order_check from orders import order_issue, order_check
from db.queries import order_status_outstanding, skip_day_contains from db.queries import order_status_outstanding, orders_pool_by_id, orders_pool_scheduled, skip_day_contains
from util import order_time from util import order_time
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
SATURDAY = 5 WEEKDAYS = [0, 1, 2, 3, 4]
SUNDAY = 6 WEEKENDS = [5, 6]
GRACE_PERIOD = datetime.timedelta(seconds=10) GRACE_PERIOD = datetime.timedelta(seconds=10)
@ -22,44 +22,53 @@ class OrderScheduler():
self.scheduler = Scheduler(loop=loop, tzinfo=self.tz) self.scheduler = Scheduler(loop=loop, tzinfo=self.tz)
# Regularly scheduled orders self.scheduled_pools = {}
order_time_dt = order_time() for orders_pool in orders_pool_scheduled():
self.scheduler.daily(order_time_dt, self.scheduled_order) self.scheduled_pools[orders_pool.id] = self.scheduler.daily(
order_time(orders_pool.time),
self.scheduled_order,
args=(orders_pool.id,)
)
# Schedule any outstanding orders self.outstanding_orders = {}
outstanding_orders = order_status_outstanding() # TODO: Schedule outstanding order checks
for oo in outstanding_orders:
self.scheduler.once( # TODO: Schedule keeping schedule up to date
datetime.datetime.fromisoformat(oo.due_at) + GRACE_PERIOD,
self.scheduled_check
)
logger.info(self.scheduler) logger.info(self.scheduler)
async def scheduled_order(self): async def scheduled_order(self, orders_pool_id):
# Skip weekends orders_pool = orders_pool_by_id(orders_pool_id)
# Skip weekends or weekdays
day_of_week = datetime.datetime.now(tz=self.tz).weekday() day_of_week = datetime.datetime.now(tz=self.tz).weekday()
if (day_of_week in [SATURDAY, SUNDAY]): if (
logger.info('Today is a weekend') (not orders_pool.weekends and day_of_week in WEEKENDS)
or
(not orders_pool.weekdays and day_of_week in WEEKDAYS)
):
logger.info(f'{orders_pool.name}[{orders_pool.user.telegram_username}] Not scheduled for today')
return return
# Skip stored dates # Skip stored dates
# TODO: Store these per user
today = datetime.datetime.now(tz=self.tz).strftime("%Y-%m-%d") today = datetime.datetime.now(tz=self.tz).strftime("%Y-%m-%d")
logger.info('Today %s' % today) logger.info('Today %s' % today)
if (skip_day_contains(today)): if (skip_day_contains(today)):
logger.info('Today is a skip day') logger.info('Today is a skip day')
return return
logger.info(f'Issuing order for {orders_pool.name}[{orders_pool.user.telegram_username}]')
due_at = await order_issue() order_status = await order_issue(orders_pool)
if due_at is not None: if order_status is not None:
# Schedule check # Schedule check
self.scheduler.once( self.outstanding_orders[order_status.id] = self.scheduler.once(
due_at + GRACE_PERIOD, order_status.due_at + GRACE_PERIOD,
self.scheduled_check self.scheduled_check,
args=(order_status.id,)
) )
logger.info(self.scheduler) async def scheduled_check(self, outstanding_order_id):
await order_check(outstanding_order_id)
async def scheduled_check(self):
await order_check()

View file

@ -39,8 +39,8 @@ class Telegram:
return await response.json() return await response.json()
async def message_send(self, text, chat_id=None): async def message_send(self, chat_id, text):
chat_id_actual = chat_id if chat_id != None else TELEGRAM_CHAT_ID chat_id_actual = chat_id
text_actual = text text_actual = text
if ENV == 'dev' and chat_id != TELEGRAM_CHAT_ID: if ENV == 'dev' and chat_id != TELEGRAM_CHAT_ID:
text_actual = f"⚠️ Message intended for chat id {chat_id}\n\n" + text text_actual = f"⚠️ Message intended for chat id {chat_id}\n\n" + text

View file

@ -10,8 +10,8 @@ def make_session():
def timezone(): def timezone():
return pytz.timezone(TIMEZONE) return pytz.timezone(TIMEZONE)
def order_time(): def order_time(str):
order_time_arr = list(map(int, ORDER_TIME.split(':'))) order_time_arr = list(map(int, str.split(':')))
return datetime.time( return datetime.time(
hour=order_time_arr[0], hour=order_time_arr[0],
minute=order_time_arr[1], minute=order_time_arr[1],