Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,20 @@ select = [
"F63",
"F7",
"F82",
] # , "C901"] # uncomment to re-enable mccabe complexity - see https://github.com/TimMcCool/scratchattach/issues/566
"C901",
] # uncomment to re-enable mccabe complexity - see https://github.com/TimMcCool/scratchattach/issues/566

[tool.ruff.lint.mccabe]
# max-complexity = 10
max-complexity = 10

[tool.setuptools.packages.find]
where = ["."]
include = ["scratchattach*"]

[dependency-groups]
dev = ["cryptography>=46.0.3", "pytest>=9.0.2", "python-dotenv>=1.2.2"]
dev = [
"cryptography>=46.0.3",
"pytest>=9.0.2",
"python-dotenv>=1.2.2",
"ruff>=0.15.12",
]
70 changes: 39 additions & 31 deletions scratchattach/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ def main():
)

# Using walrus operator & ifs for artificial indentation
# TODO: there may be a better way to do this
if commands := parser.add_subparsers(dest="command"):
commands.add_parser("profile", help="View your profile")
commands.add_parser("sessions", help="View session list")
if login := commands.add_parser("login", help="Login to Scratch"):
login.add_argument("--sessid", dest="sessid", nargs="?", default=False, const=True,
help="Login by session ID")
login.add_argument("--sessid", dest="sessid", nargs="?", default=False, const=True, help="Login by session ID")
if group := commands.add_parser("group", help="View current session group"):
if group_commands := group.add_subparsers(dest="group_command"):
group_commands.add_parser("list", help="List all session groups")
Expand All @@ -46,10 +46,13 @@ def main():
parser.add_argument("-U", "--username", dest="username", help="Name of user to look at")
parser.add_argument("-P", "--project", dest="project_id", help="ID of project to look at")
parser.add_argument("-S", "--studio", dest="studio_id", help="ID of studio to look at")
parser.add_argument("-L", "--session_name", dest="session_name",
help="Name of (registered) session/login to look at")
parser.add_argument("-L", "--session_name", dest="session_name", help="Name of (registered) session/login to look at")

args = parser.parse_args(namespace=cli.ArgSpace())
handle_args(args, parser)


def handle_args(args: cli.ArgSpace, parser: argparse.ArgumentParser):
cli.ctx.args = args
cli.ctx.parser = parser

Expand All @@ -63,31 +66,36 @@ def main():
case "profile":
cmd.profile()
case None:
if args.username:
user = ctx.session.connect_user(args.username)
console.print(cli.try_get_img(user.icon, (30, 30)))
console.print(user)
return
if args.studio_id:
studio = ctx.session.connect_studio(args.studio_id)
console.print(cli.try_get_img(studio.thumbnail, (34, 20)))
console.print(studio)
return
if args.project_id:
project = ctx.session.connect_project(args.project_id)
console.print(cli.try_get_img(project.thumbnail, (30, 23)))
console.print(project)
return
if args.session_name:
if sess := ctx.db_get_sess(args.session_name):
console.print(sess)
else:
raise ValueError(f"No session logged in called {args.session_name!r} "
f"- try using `scratch sessions` to see available sessions")
return

parser.print_help()


if __name__ == '__main__':
handle_nocmd(args, parser)


def handle_nocmd(args: cli.ArgSpace, parser: argparse.ArgumentParser):
if args.username:
user = ctx.session.connect_user(args.username)
console.print(cli.try_get_img(user.icon, (30, 30)))
console.print(user)
return
if args.studio_id:
studio = ctx.session.connect_studio(args.studio_id)
console.print(cli.try_get_img(studio.thumbnail, (34, 20)))
console.print(studio)
return
if args.project_id:
project = ctx.session.connect_project(args.project_id)
console.print(cli.try_get_img(project.thumbnail, (30, 23)))
console.print(project)
return
if args.session_name:
if sess := ctx.db_get_sess(args.session_name):
console.print(sess)
else:
raise ValueError(
f"No session logged in called {args.session_name!r} - try using `scratch sessions` to see available sessions"
)
return

parser.print_help()


if __name__ == "__main__":
main()
43 changes: 26 additions & 17 deletions scratchattach/cli/cmd/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from rich.markup import escape
from rich.table import Table


def _list():
table = Table(title="All groups")
table.add_column("Name")
Expand All @@ -15,40 +16,41 @@ def _list():
db.cursor.execute("SELECT USERNAME FROM GROUP_USERS WHERE GROUP_NAME=?", (name,))
usernames = db.cursor.fetchall()

table.add_row(escape(name), escape(description),
'\n'.join(f"{i}. {u}" for i, (u,) in enumerate(usernames)))
table.add_row(escape(name), escape(description), "\n".join(f"{i}. {u}" for i, (u,) in enumerate(usernames)))

console.print(table)


def add(group_name: str):
accounts = input("Add accounts (split by space): ").split()
for account in accounts:
ctx.db_add_to_group(group_name, account)


def remove(group_name: str):
accounts = input("Remove accounts (split by space): ").split()
for account in accounts:
ctx.db_remove_from_group(group_name, account)


def new():
console.rule(f"New group {escape(ctx.args.group_name)}")
if ctx.db_group_exists(ctx.args.group_name):
raise ValueError(f"Group {escape(ctx.args.group_name)} already exists")

db.conn.execute("BEGIN")
db.cursor.execute("INSERT INTO GROUPS (NAME, DESCRIPTION) "
"VALUES (?, ?)", (ctx.args.group_name, input("Description: ")))
db.cursor.execute("INSERT INTO GROUPS (NAME, DESCRIPTION) VALUES (?, ?)", (ctx.args.group_name, input("Description: ")))
db.conn.commit()
add(ctx.args.group_name)

_group(ctx.args.group_name)


def _group(group_name: str):
"""
Display information about a group
"""
db.cursor.execute(
"SELECT NAME, DESCRIPTION FROM GROUPS WHERE NAME = ?", (group_name,))
db.cursor.execute("SELECT NAME, DESCRIPTION FROM GROUPS WHERE NAME = ?", (group_name,))
result = db.cursor.fetchone()
if result is None:
print("No group selected!!")
Expand All @@ -61,13 +63,13 @@ def _group(group_name: str):

table = Table(title=escape(group_name))
table.add_column(escape(name))
table.add_column('Usernames')
table.add_column("Usernames")

table.add_row(escape(description),
'\n'.join(f"{i}. {u}" for i, u in enumerate(usernames)))
table.add_row(escape(description), "\n".join(f"{i}. {u}" for i, u in enumerate(usernames)))

console.print(table)


def switch():
console.rule(f"Switching to {escape(ctx.args.group_name)}")
if not ctx.db_group_exists(ctx.args.group_name):
Expand All @@ -76,6 +78,7 @@ def switch():
ctx.current_group_name = ctx.args.group_name
_group(ctx.current_group_name)


def delete(group_name: str):
print(f"Deleting {group_name}")
if not ctx.db_group_exists(group_name):
Expand All @@ -85,17 +88,30 @@ def delete(group_name: str):

ctx.db_group_delete(group_name)


def copy(group_name: str, new_name: str):
print(f"Copying {group_name} as {new_name}")
if not ctx.db_group_exists(group_name):
raise ValueError(f"Group {group_name} does not exist")

ctx.db_group_copy(group_name, new_name)


def rename(group_name: str, new_name: str):
copy(group_name, new_name)
delete(group_name)


def delete_cmd():
if input("Are you sure? (y/N): ").lower() != "y":
return
delete(ctx.current_group_name)
new_current = ctx.db_first_group_name
print(f"Switching to {new_current}")
ctx.current_group_name = new_current
_group(new_current)


def group():
match ctx.args.group_command:
case "list":
Expand All @@ -109,14 +125,7 @@ def group():
case "remove":
remove(ctx.current_group_name)
case "delete":
if input("Are you sure? (y/N): ").lower() != "y":
return
delete(ctx.current_group_name)
new_current = ctx.db_first_group_name
print(f"Switching to {new_current}")
ctx.current_group_name = new_current
_group(new_current)

delete_cmd()
case "copy":
copy(ctx.current_group_name, ctx.args.group_name)
case "rename":
Expand Down
33 changes: 20 additions & 13 deletions scratchattach/cli/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Holds objects that should be available for the whole CLI system
Also provides wrappers for some SQL info.
"""

import argparse
from dataclasses import dataclass, field

Expand Down Expand Up @@ -54,9 +55,8 @@ def session(self):

@property
def current_group_name(self):
return db.cursor \
.execute("SELECT * FROM CURRENT WHERE GROUP_NAME IS NOT NULL") \
.fetchone()[0]
# FIXME: raises error when there are no groups
return db.cursor.execute("SELECT * FROM CURRENT WHERE GROUP_NAME IS NOT NULL").fetchone()[0]

@current_group_name.setter
def current_group_name(self, value: str):
Expand All @@ -75,14 +75,12 @@ def db_session_exists(name: str) -> bool:

@staticmethod
def db_users_in_group(name: str) -> list[str]:
return [i for (i,) in db.cursor.execute(
"SELECT USERNAME FROM GROUP_USERS WHERE GROUP_NAME = ?", (name,)).fetchall()]
return [i for (i,) in db.cursor.execute("SELECT USERNAME FROM GROUP_USERS WHERE GROUP_NAME = ?", (name,)).fetchall()]

def db_remove_from_group(self, group_name: str, username: str):
if username in self.db_users_in_group(group_name):
db.conn.execute("BEGIN")
db.cursor.execute("DELETE FROM GROUP_USERS "
"WHERE USERNAME = ? AND GROUP_NAME = ?", (username, group_name))
db.cursor.execute("DELETE FROM GROUP_USERS WHERE USERNAME = ? AND GROUP_NAME = ?", (username, group_name))
db.conn.commit()

@staticmethod
Expand All @@ -96,8 +94,7 @@ def db_add_to_group(self, group_name: str, username: str):
if username in self.db_users_in_group(group_name) or not self.db_session_exists(username):
return
db.conn.execute("BEGIN")
db.cursor.execute("INSERT INTO GROUP_USERS (GROUP_NAME, USERNAME) "
"VALUES (?, ?)", (group_name, username))
db.cursor.execute("INSERT INTO GROUP_USERS (GROUP_NAME, USERNAME) VALUES (?, ?)", (group_name, username))
db.conn.commit()

@staticmethod
Expand All @@ -113,11 +110,21 @@ def db_group_delete(group_name: str):
def db_group_copy(group_name: str, copy_name: str):
db.conn.execute("BEGIN")
# copy group metadata
db.cursor.execute("INSERT INTO GROUPS (NAME, DESCRIPTION) "
"SELECT ?, DESCRIPTION FROM GROUPS WHERE NAME = ?", (copy_name, group_name,))
db.cursor.execute(
"INSERT INTO GROUPS (NAME, DESCRIPTION) SELECT ?, DESCRIPTION FROM GROUPS WHERE NAME = ?",
(
copy_name,
group_name,
),
)
# copy sessions
db.cursor.execute("INSERT INTO GROUP_USERS (GROUP_NAME, USERNAME) "
"SELECT ?, USERNAME FROM GROUP_USERS WHERE GROUP_NAME = ?", (copy_name, group_name,))
db.cursor.execute(
"INSERT INTO GROUP_USERS (GROUP_NAME, USERNAME) SELECT ?, USERNAME FROM GROUP_USERS WHERE GROUP_NAME = ?",
(
copy_name,
group_name,
),
)

db.conn.commit()

Expand Down
Loading
Loading