Add manage_beli.py management script
CLI to add/list/refresh/remove Beli profiles without a frontend. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
"""
|
||||
Manage Beli profiles and restaurant data.
|
||||
|
||||
Usage:
|
||||
python scripts/manage_beli.py add <user_email> <beli_email> <beli_password>
|
||||
python scripts/manage_beli.py list
|
||||
python scripts/manage_beli.py refresh <profile_id>
|
||||
python scripts/manage_beli.py refresh-all
|
||||
python scripts/manage_beli.py remove <profile_id>
|
||||
|
||||
Examples:
|
||||
python scripts/manage_beli.py add ryan@torrtle.co me@gmail.com mybelipass
|
||||
python scripts/manage_beli.py list
|
||||
python scripts/manage_beli.py refresh 3f2a1b...
|
||||
python scripts/manage_beli.py refresh-all
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from tortoise import Tortoise
|
||||
|
||||
load_dotenv()
|
||||
|
||||
DATABASE_URL = os.getenv(
|
||||
"DATABASE_URL", "postgres://raggr:changeme@localhost:5432/raggr"
|
||||
)
|
||||
|
||||
TORTOISE_CONFIG = {
|
||||
"connections": {"default": DATABASE_URL},
|
||||
"apps": {
|
||||
"models": {
|
||||
"models": [
|
||||
"blueprints.users.models",
|
||||
"blueprints.beli.models",
|
||||
"blueprints.email.models",
|
||||
"blueprints.conversation.models",
|
||||
"blueprints.scheduled_messages.models",
|
||||
"blueprints.cron.models",
|
||||
],
|
||||
"default_connection": "default",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def _init():
|
||||
await Tortoise.init(config=TORTOISE_CONFIG)
|
||||
|
||||
|
||||
async def _close():
|
||||
await Tortoise.close_connections()
|
||||
|
||||
|
||||
async def cmd_add(user_email: str, beli_email: str, beli_password: str):
|
||||
from blueprints.beli.logic import MAX_PROFILES_PER_USER
|
||||
from blueprints.beli.models import BeliProfile
|
||||
from blueprints.users.models import User
|
||||
from utils.beli_service import authenticate
|
||||
|
||||
user = await User.get_or_none(email=user_email)
|
||||
if not user:
|
||||
print(f"Error: no user with email '{user_email}'")
|
||||
return False
|
||||
|
||||
count = await BeliProfile.filter(user=user).count()
|
||||
if count >= MAX_PROFILES_PER_USER:
|
||||
print(
|
||||
f"Error: user already has {MAX_PROFILES_PER_USER} Beli profiles (maximum)"
|
||||
)
|
||||
return False
|
||||
|
||||
if await BeliProfile.exists(user=user, beli_email=beli_email):
|
||||
print(f"Error: profile for '{beli_email}' already exists for this user")
|
||||
return False
|
||||
|
||||
print(f"Authenticating with Beli as {beli_email}...")
|
||||
try:
|
||||
session = await authenticate(beli_email, beli_password)
|
||||
except Exception as e:
|
||||
print(f"Error: Beli authentication failed — {e}")
|
||||
return False
|
||||
|
||||
profile = await BeliProfile.create(
|
||||
user=user,
|
||||
beli_email=beli_email,
|
||||
beli_password=beli_password,
|
||||
beli_user_id=session["user_id"],
|
||||
beli_refresh_token=session["refresh_token"],
|
||||
enabled=True,
|
||||
)
|
||||
print(f"Profile created: {profile.id}")
|
||||
print(f" Beli user ID : {profile.beli_user_id}")
|
||||
print(f" Beli email : {profile.beli_email}")
|
||||
print(f" Owner : {user_email}")
|
||||
return True
|
||||
|
||||
|
||||
async def cmd_list():
|
||||
from blueprints.beli.models import BeliProfile, BeliRestaurant
|
||||
|
||||
profiles = await BeliProfile.all().prefetch_related("user").order_by("created_at")
|
||||
if not profiles:
|
||||
print("No Beli profiles found.")
|
||||
return
|
||||
|
||||
for p in profiles:
|
||||
count = await BeliRestaurant.filter(profile=p).count()
|
||||
refreshed = (
|
||||
p.last_refreshed_at.strftime("%Y-%m-%d %H:%M UTC")
|
||||
if p.last_refreshed_at
|
||||
else "never"
|
||||
)
|
||||
status = "enabled" if p.enabled else "DISABLED"
|
||||
print(f"{p.id} [{status}]")
|
||||
print(f" Owner : {p.user.email}")
|
||||
print(f" Beli : {p.beli_email} (user_id={p.beli_user_id})")
|
||||
print(f" Restaurants: {count} | Last refresh: {refreshed}")
|
||||
if p.last_error:
|
||||
print(f" Last error : {p.last_error}")
|
||||
print()
|
||||
|
||||
|
||||
async def cmd_refresh(profile_id: str):
|
||||
from blueprints.beli.logic import refresh_profile
|
||||
from blueprints.beli.models import BeliProfile
|
||||
|
||||
profile = await BeliProfile.get_or_none(id=profile_id)
|
||||
if not profile:
|
||||
print(f"Error: no profile with id '{profile_id}'")
|
||||
return False
|
||||
|
||||
print(f"Refreshing profile {profile_id} ({profile.beli_email})...")
|
||||
result = await refresh_profile(profile_id)
|
||||
if result.get("ok"):
|
||||
print(f"Done — {result['upserted']} restaurants upserted")
|
||||
elif result.get("skipped"):
|
||||
print("Skipped (profile disabled)")
|
||||
else:
|
||||
print(f"Failed: {result.get('error')}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
async def cmd_refresh_all():
|
||||
from blueprints.beli.models import BeliProfile
|
||||
from blueprints.beli.logic import refresh_profile
|
||||
|
||||
profiles = (
|
||||
await BeliProfile.filter(enabled=True).order_by("last_refreshed_at").all()
|
||||
)
|
||||
if not profiles:
|
||||
print("No enabled Beli profiles found.")
|
||||
return
|
||||
|
||||
print(f"Refreshing {len(profiles)} profile(s)...")
|
||||
for p in profiles:
|
||||
print(f"\n {p.beli_email} ({p.id})")
|
||||
result = await refresh_profile(str(p.id))
|
||||
if result.get("ok"):
|
||||
print(f" → {result['upserted']} restaurants upserted")
|
||||
else:
|
||||
print(f" → failed: {result.get('error')}")
|
||||
|
||||
|
||||
async def cmd_remove(profile_id: str):
|
||||
from blueprints.beli.models import BeliProfile, BeliRestaurant
|
||||
|
||||
profile = await BeliProfile.get_or_none(id=profile_id)
|
||||
if not profile:
|
||||
print(f"Error: no profile with id '{profile_id}'")
|
||||
return False
|
||||
|
||||
deleted = await BeliRestaurant.filter(profile=profile).count()
|
||||
await BeliRestaurant.filter(profile=profile).delete()
|
||||
await profile.delete()
|
||||
print(
|
||||
f"Removed profile {profile_id} ({profile.beli_email}) and {deleted} restaurants"
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def usage():
|
||||
print(__doc__)
|
||||
|
||||
|
||||
async def main():
|
||||
if len(sys.argv) < 2:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
cmd = sys.argv[1]
|
||||
|
||||
await _init()
|
||||
try:
|
||||
if cmd == "add":
|
||||
if len(sys.argv) != 5:
|
||||
print(
|
||||
"Usage: manage_beli.py add <user_email> <beli_email> <beli_password>"
|
||||
)
|
||||
sys.exit(1)
|
||||
ok = await cmd_add(sys.argv[2], sys.argv[3], sys.argv[4])
|
||||
sys.exit(0 if ok else 1)
|
||||
|
||||
elif cmd == "list":
|
||||
await cmd_list()
|
||||
|
||||
elif cmd == "refresh":
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: manage_beli.py refresh <profile_id>")
|
||||
sys.exit(1)
|
||||
ok = await cmd_refresh(sys.argv[2])
|
||||
sys.exit(0 if ok else 1)
|
||||
|
||||
elif cmd == "refresh-all":
|
||||
await cmd_refresh_all()
|
||||
|
||||
elif cmd == "remove":
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: manage_beli.py remove <profile_id>")
|
||||
sys.exit(1)
|
||||
ok = await cmd_remove(sys.argv[2])
|
||||
sys.exit(0 if ok else 1)
|
||||
|
||||
else:
|
||||
print(f"Unknown command: {cmd}")
|
||||
usage()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
await _close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user