Generate orders from db
This commit is contained in:
parent
fd654153a4
commit
18b9c9de55
13 changed files with 403 additions and 114 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,7 +1,10 @@
|
|||
*.pyc
|
||||
.env
|
||||
*~
|
||||
db.sqlite3
|
||||
|
||||
Caddyfile
|
||||
|
||||
*.dump.sql
|
||||
*.dump.sql
|
||||
|
||||
.vscode
|
||||
26
db/models.py
26
db/models.py
|
|
@ -104,9 +104,24 @@ class OrderStatus(BaseModel):
|
|||
created_at = DateTimeField() # TIMESTAMP
|
||||
due_at = DateTimeField() # TIMESTAMP
|
||||
mastodon_id = TextField()
|
||||
punishment = ForeignKeyField(column_name='punishment_id', field='id', model=PunishmentStatus, null=True)
|
||||
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:
|
||||
table_name = 'order_status'
|
||||
|
||||
|
|
@ -115,6 +130,15 @@ class Repeat(BaseModel):
|
|||
orders = TextField()
|
||||
probability = FloatField()
|
||||
|
||||
orders_pool = ForeignKeyField(
|
||||
column_name='orders_pool_id',
|
||||
field='id',
|
||||
model=OrdersPool,
|
||||
null=False,
|
||||
unique=True,
|
||||
backref='repeat'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
table_name = 'repeat'
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@ def orders_pool_list(user_id):
|
|||
def orders_pool(user_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):
|
||||
return DomSubUsers.create(
|
||||
sub=sub,
|
||||
|
|
@ -43,23 +49,27 @@ def domsubusers_delete(sub, dom):
|
|||
def domsubusers_list(dom):
|
||||
return DomSubUsers.select().where(DomSubUsers.dom == dom)
|
||||
|
||||
def repeat_get():
|
||||
def repeat_get(orders_pool_id):
|
||||
try:
|
||||
return Repeat.get()
|
||||
return Repeat.get(orders_pool_id = orders_pool_id)
|
||||
except Repeat.DoesNotExist:
|
||||
return None
|
||||
|
||||
def repeat_increment():
|
||||
q = Repeat.update(count=Repeat.count + 1)
|
||||
return q.execute()
|
||||
def repeat_increment(id):
|
||||
r = Repeat.get(id=id)
|
||||
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(
|
||||
orders_pool_id=orders_pool_id,
|
||||
probability=probability,
|
||||
orders=orders
|
||||
)
|
||||
|
||||
def repeat_clear():
|
||||
def repeat_clear(id):
|
||||
q = Repeat.delete()
|
||||
q.execute()
|
||||
|
||||
|
|
@ -80,14 +90,19 @@ def skip_day_contains(date):
|
|||
q = SkipDay.select().where(SkipDay.date == date)
|
||||
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(
|
||||
orders_pool_id=orders_pool.id,
|
||||
user_id=user.id,
|
||||
mastodon_id=mastodon_id,
|
||||
created_at=created_at,
|
||||
due_at=due_at,
|
||||
text=text
|
||||
)
|
||||
|
||||
def order_status_by_id(order_status_id):
|
||||
return OrderStatus.get(id=order_status_id)
|
||||
|
||||
def order_status_outstanding():
|
||||
return OrderStatus.select().where(
|
||||
(OrderStatus.confirmed_at.is_null()) & (OrderStatus.punishment_id.is_null())
|
||||
|
|
|
|||
42
generate.py
42
generate.py
|
|
@ -10,30 +10,18 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
def pick_order(orders):
|
||||
picked = random.choices(orders,
|
||||
weights=[o['weight'] for o in orders],
|
||||
weights=[o.weight for o in orders],
|
||||
k=1)[0]
|
||||
|
||||
result = []
|
||||
repeat = 0.0
|
||||
|
||||
if('text' in picked):
|
||||
result.append(picked['text'])
|
||||
result.append(picked.name)
|
||||
|
||||
if('repeat' in picked):
|
||||
repeat = picked['repeat']
|
||||
for add_on in picked.add_ons:
|
||||
if add_on.probability > random.random():
|
||||
result.append(add_on.name)
|
||||
|
||||
if('add' in picked):
|
||||
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)
|
||||
return (result, picked.repeat)
|
||||
|
||||
def read_config():
|
||||
with open(ORDERS_YML) as stream:
|
||||
|
|
@ -43,31 +31,29 @@ def read_config():
|
|||
logger.error(exc)
|
||||
return orders
|
||||
|
||||
def generate_order():
|
||||
def generate_order(orders_pool):
|
||||
# Do we want to repeat?
|
||||
repeat = repeat_get()
|
||||
if repeat is not None:
|
||||
if orders_pool.repeat.count() != 0:
|
||||
repeat = orders_pool.repeat.get()
|
||||
if repeat.probability > random.random():
|
||||
repeat_increment()
|
||||
repeat_increment(repeat.id)
|
||||
return {
|
||||
"orders": json.loads(repeat.orders),
|
||||
"count": repeat.count
|
||||
}
|
||||
else:
|
||||
repeat_clear()
|
||||
|
||||
orders_config = read_config()
|
||||
repeat_clear(repeat.id)
|
||||
|
||||
if orders_config['orders_probability'] < random.random():
|
||||
if orders_pool.probability < random.random():
|
||||
# No orders today
|
||||
return { }
|
||||
|
||||
# Pick new orders
|
||||
(result, repeat_p) = pick_order(orders_config['orders'])
|
||||
(result, repeat_p) = pick_order(orders_pool.orders)
|
||||
|
||||
# Log the repeat
|
||||
if repeat_p > 0.0:
|
||||
repeat_put(repeat_p, json.dumps(result))
|
||||
repeat_put(orders_pool.id, repeat_p, json.dumps(result))
|
||||
|
||||
return {
|
||||
"orders": result
|
||||
|
|
|
|||
41
main.py
41
main.py
|
|
@ -2,15 +2,25 @@ import sys
|
|||
import logging
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from scheduling import OrderScheduler
|
||||
from generate import generate_order
|
||||
from orders import order_issue, order_check
|
||||
from db.queries import orders_pool_by_id
|
||||
from telegram.telegram import handle_commands
|
||||
from telegram.commands import commands
|
||||
from db.queries import initdb
|
||||
|
||||
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__':
|
||||
logging.basicConfig(
|
||||
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")
|
||||
|
||||
parser_immediate = subparsers.add_parser('immediate', help='Immediately generate a command')
|
||||
parser_check = subparsers.add_parser('check', help="Checks if any orders are outstanding")
|
||||
parser_initdb = subparsers.add_parser('initdb', help="Creates the database tables")
|
||||
parser_generate = subparsers.add_parser('generate', help='Generate a command')
|
||||
parser_generate.add_argument('orders_pool_id')
|
||||
|
||||
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()
|
||||
|
||||
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.run_until_complete(order_issue())
|
||||
loop.close()
|
||||
loop.run_until_complete(
|
||||
do_order_issue(args.orders_pool_id)
|
||||
)
|
||||
elif args.command == 'check':
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(order_check())
|
||||
loop.close()
|
||||
elif args.command == 'initdb':
|
||||
initdb()
|
||||
loop.run_until_complete(
|
||||
order_check(args.order_status_id)
|
||||
)
|
||||
else:
|
||||
loop = asyncio.new_event_loop()
|
||||
s = OrderScheduler(loop)
|
||||
|
|
|
|||
54
migrations/005_delete_repeat.py
Normal file
54
migrations/005_delete_repeat.py
Normal 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"
|
||||
55
migrations/006_create_repeat.py
Normal file
55
migrations/006_create_repeat.py
Normal 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')
|
||||
57
migrations/007_delete_order_status.py
Normal file
57
migrations/007_delete_order_status.py
Normal 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"
|
||||
58
migrations/008_add_order_status.py
Normal file
58
migrations/008_add_order_status.py
Normal 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')
|
||||
79
orders.py
79
orders.py
|
|
@ -4,7 +4,7 @@ import asyncio
|
|||
|
||||
from util import make_session
|
||||
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 telegram.telegram import Telegram
|
||||
from settings import MASTODON_USERNAME, ORDER_TIMEOUT, ENV
|
||||
|
|
@ -12,7 +12,8 @@ from util import timezone
|
|||
|
||||
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 += orders_str + "\n\n"
|
||||
if repeats > 1:
|
||||
|
|
@ -26,7 +27,7 @@ async def order_mastodon_post(session, orders_str, repeats, due_at):
|
|||
m = Mastodon(session)
|
||||
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 += orders_str + "\n\n"
|
||||
if repeats > 1:
|
||||
|
|
@ -37,23 +38,23 @@ async def order_telegram_post(session, orders_str, repeats, due_at, m_url):
|
|||
post += "\n⚠️ DEV"
|
||||
|
||||
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"
|
||||
if ENV == 'dev':
|
||||
post += "\n⚠️ DEV"
|
||||
|
||||
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:
|
||||
orders_info = generate_order()
|
||||
orders_info = generate_order(orders_pool)
|
||||
|
||||
if 'orders' not in orders_info:
|
||||
logger.info("No orders for today")
|
||||
await order_telegram_post_none(session)
|
||||
await order_telegram_post_none(session, orders_pool)
|
||||
return
|
||||
|
||||
orders_str = "\n".join(orders_info['orders'])
|
||||
|
|
@ -65,6 +66,7 @@ async def order_issue():
|
|||
|
||||
m_status = await order_mastodon_post(
|
||||
session,
|
||||
orders_pool,
|
||||
orders_str,
|
||||
repeats_count,
|
||||
due_at
|
||||
|
|
@ -72,20 +74,21 @@ async def order_issue():
|
|||
|
||||
await order_telegram_post(
|
||||
session,
|
||||
orders_pool,
|
||||
orders_str,
|
||||
repeats_count,
|
||||
due_at,
|
||||
m_status['url']
|
||||
)
|
||||
|
||||
order_status_put(
|
||||
return order_status_put(
|
||||
orders_pool,
|
||||
orders_pool.user,
|
||||
m_status['id'],
|
||||
created_at,
|
||||
due_at,
|
||||
orders_str
|
||||
)
|
||||
|
||||
return due_at
|
||||
)
|
||||
|
||||
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
|
||||
|
|
@ -112,6 +115,11 @@ async def punishment_telegram_post(session, punishment_str, m_url):
|
|||
await t.message_send(post)
|
||||
|
||||
async def punishment_issue(session, outstanding_order):
|
||||
# TODO: Generate a punishment
|
||||
|
||||
logger.info('TODO: Generate a punishment')
|
||||
return
|
||||
|
||||
punishment = generate_punishment()
|
||||
punishment_str = "\n".join(punishment)
|
||||
|
||||
|
|
@ -135,31 +143,32 @@ async def punishment_issue(session, outstanding_order):
|
|||
)
|
||||
|
||||
order_check_lock = asyncio.Lock()
|
||||
async def order_check():
|
||||
async def order_check(order_status_id):
|
||||
async with order_check_lock:
|
||||
async with make_session() as session:
|
||||
outstanding_orders = order_status_outstanding()
|
||||
for outstanding_order in outstanding_orders:
|
||||
m = Mastodon(session)
|
||||
context = await m.statusContext(outstanding_order.mastodon_id)
|
||||
order_status = order_status_by_id(order_status_id)
|
||||
|
||||
confirmed_at = None
|
||||
for d in context['descendants']:
|
||||
if (
|
||||
d['in_reply_to_id'] == outstanding_order.mastodon_id and
|
||||
d['account']['username'] == MASTODON_USERNAME and
|
||||
len(d['media_attachments']) > 0
|
||||
):
|
||||
confirmed_at = d['created_at']
|
||||
order_status_confirm(outstanding_order.id, confirmed_at)
|
||||
logger.info('Confirmed order %s' % (outstanding_order.id))
|
||||
break
|
||||
m = Mastodon(session)
|
||||
context = await m.statusContext(order_status.mastodon_id)
|
||||
|
||||
confirmed_at = None
|
||||
for d in context['descendants']:
|
||||
if (
|
||||
d['in_reply_to_id'] == order_status.mastodon_id and
|
||||
# TODO: Get mastodon username
|
||||
d['account']['username'] == MASTODON_USERNAME and
|
||||
len(d['media_attachments']) > 0
|
||||
):
|
||||
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:
|
||||
logger.info('Order %s remains unconfirmed' % (outstanding_order.id))
|
||||
if confirmed_at is None:
|
||||
logger.info('Order %s remains unconfirmed' % (order_status.id))
|
||||
|
||||
due_at = datetime.datetime.fromisoformat(outstanding_order.due_at)
|
||||
if(due_at < datetime.datetime.now(datetime.UTC)):
|
||||
logger.info('Time to issue a punishment for %s' % outstanding_order.id)
|
||||
due_at = datetime.datetime.fromisoformat(order_status.due_at)
|
||||
if(due_at < datetime.datetime.now(datetime.UTC)):
|
||||
logger.info('Time to issue a punishment for %s' % order_status.id)
|
||||
|
||||
await punishment_issue(session, outstanding_order)
|
||||
await punishment_issue(session, order_status)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ from scheduler.asyncio import Scheduler
|
|||
|
||||
from settings import TIMEZONE
|
||||
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
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SATURDAY = 5
|
||||
SUNDAY = 6
|
||||
WEEKDAYS = [0, 1, 2, 3, 4]
|
||||
WEEKENDS = [5, 6]
|
||||
|
||||
GRACE_PERIOD = datetime.timedelta(seconds=10)
|
||||
|
||||
|
|
@ -22,44 +22,53 @@ class OrderScheduler():
|
|||
|
||||
self.scheduler = Scheduler(loop=loop, tzinfo=self.tz)
|
||||
|
||||
# Regularly scheduled orders
|
||||
order_time_dt = order_time()
|
||||
self.scheduler.daily(order_time_dt, self.scheduled_order)
|
||||
self.scheduled_pools = {}
|
||||
for orders_pool in orders_pool_scheduled():
|
||||
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
|
||||
outstanding_orders = order_status_outstanding()
|
||||
for oo in outstanding_orders:
|
||||
self.scheduler.once(
|
||||
datetime.datetime.fromisoformat(oo.due_at) + GRACE_PERIOD,
|
||||
self.scheduled_check
|
||||
)
|
||||
self.outstanding_orders = {}
|
||||
# TODO: Schedule outstanding order checks
|
||||
|
||||
# TODO: Schedule keeping schedule up to date
|
||||
|
||||
logger.info(self.scheduler)
|
||||
|
||||
async def scheduled_order(self):
|
||||
# Skip weekends
|
||||
async def scheduled_order(self, orders_pool_id):
|
||||
orders_pool = orders_pool_by_id(orders_pool_id)
|
||||
|
||||
# Skip weekends or weekdays
|
||||
day_of_week = datetime.datetime.now(tz=self.tz).weekday()
|
||||
if (day_of_week in [SATURDAY, SUNDAY]):
|
||||
logger.info('Today is a weekend')
|
||||
if (
|
||||
(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
|
||||
|
||||
# Skip stored dates
|
||||
# TODO: Store these per user
|
||||
today = datetime.datetime.now(tz=self.tz).strftime("%Y-%m-%d")
|
||||
logger.info('Today %s' % today)
|
||||
if (skip_day_contains(today)):
|
||||
logger.info('Today is a skip day')
|
||||
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
|
||||
self.scheduler.once(
|
||||
due_at + GRACE_PERIOD,
|
||||
self.scheduled_check
|
||||
self.outstanding_orders[order_status.id] = self.scheduler.once(
|
||||
order_status.due_at + GRACE_PERIOD,
|
||||
self.scheduled_check,
|
||||
args=(order_status.id,)
|
||||
)
|
||||
|
||||
logger.info(self.scheduler)
|
||||
|
||||
async def scheduled_check(self):
|
||||
await order_check()
|
||||
async def scheduled_check(self, outstanding_order_id):
|
||||
await order_check(outstanding_order_id)
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ class Telegram:
|
|||
|
||||
return await response.json()
|
||||
|
||||
async def message_send(self, text, chat_id=None):
|
||||
chat_id_actual = chat_id if chat_id != None else TELEGRAM_CHAT_ID
|
||||
async def message_send(self, chat_id, text):
|
||||
chat_id_actual = chat_id
|
||||
text_actual = text
|
||||
if ENV == 'dev' and chat_id != TELEGRAM_CHAT_ID:
|
||||
text_actual = f"⚠️ Message intended for chat id {chat_id}\n\n" + text
|
||||
|
|
|
|||
4
util.py
4
util.py
|
|
@ -10,8 +10,8 @@ def make_session():
|
|||
def timezone():
|
||||
return pytz.timezone(TIMEZONE)
|
||||
|
||||
def order_time():
|
||||
order_time_arr = list(map(int, ORDER_TIME.split(':')))
|
||||
def order_time(str):
|
||||
order_time_arr = list(map(int, str.split(':')))
|
||||
return datetime.time(
|
||||
hour=order_time_arr[0],
|
||||
minute=order_time_arr[1],
|
||||
|
|
|
|||
Loading…
Reference in a new issue