Skip to content
30 changes: 5 additions & 25 deletions server/kitsu/push.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
create_task,
delete_folder,
delete_task,
delete_project,
get_folder_by_kitsu_id,
get_task_by_kitsu_id,
get_user_by_kitsu_id,
update_project,

update_folder,
update_task,
)
Expand Down Expand Up @@ -230,16 +230,13 @@ async def sync_person(
existing_users: dict[str, Any],
entity_dict: "EntityDict",
):

first_name, entity_id= required_values(entity_dict, ["first_name", "id"])
last_name = entity_dict.get("last_name", '')
first_name, entity_id = required_values(entity_dict, ["first_name", "id"])
last_name = entity_dict.get("last_name", "")

# == check should Person entity be synced ==
# do not sync Kitsu API bots
if entity_dict.get("is_bot"):
logging.info(
f"skipping sync_person for Kitsu Bot: {first_name} {last_name}"
)
logging.info(f"skipping sync_person for Kitsu Bot: {first_name} {last_name}")
return

logging.info(f"sync_person: {first_name} {last_name}")
Expand Down Expand Up @@ -330,23 +327,6 @@ async def sync_project(
await update_project(project.name, **anatomy_data)


async def delete_project(
addon: "KitsuAddon",
user: "UserEntity",
project: "ProjectEntity",
entity_dict: "EntityDict",
):
logging.info("delete_project")
session = await Session.create(user)
headers = {"Authorization": f"Bearer {session.token}"}
# Check if group already exists
async with httpx.AsyncClient() as client:
await client.delete(
f"{entity_dict['ayon_server_url']}/api/projects/{project.name}",
headers=headers,
)


async def sync_folder(
addon: "KitsuAddon",
user: "UserEntity",
Expand Down Expand Up @@ -645,7 +625,7 @@ async def remove_entities(
continue

if entity_dict["type"] == "Project":
if settings.delete_ayon_projects.enabled:
if settings.sync_settings.delete_projects:
Copy link
Member

@MustafaJafar MustafaJafar Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, Is this a bug fix?

I can tell that the setting for enabling deleting projects is at ayon+settings://kitsu/sync_settings/delete_projects
not at ayon+settings://kitsu/delete_ayon_projects/enabled

await update_project(
addon,
user,
Expand Down
199 changes: 97 additions & 102 deletions server/kitsu/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
UserEntity,
)
from ayon_server.events import dispatch_event
from ayon_server.exceptions import ForbiddenException
from ayon_server.lib.postgres import Postgres


Expand Down Expand Up @@ -86,7 +87,7 @@ async def get_task_by_kitsu_id(
"""Get an Ayon TaskEntity by its Kitsu ID"""

if existing_tasks and (kitsu_id in existing_tasks):
folder_id = existing_tasks[kitsu_id]
task_id = existing_tasks[kitsu_id]

else:
res = await Postgres.fetch(
Expand All @@ -98,9 +99,9 @@ async def get_task_by_kitsu_id(
)
if not res:
return None
folder_id = res[0]["id"]
task_id = res[0]["id"]

return await TaskEntity.load(project_name, folder_id)
return await TaskEntity.load(project_name, task_id)


async def create_folder(
Expand All @@ -119,16 +120,7 @@ async def create_folder(
project_name=project_name,
payload=payload,
)
await folder.save()
event = {
"topic": "entity.folder.created",
"description": f"Folder {folder.name} created",
"summary": {"entityId": folder.id, "parentId": folder.parent_id},
"project": project_name,
}

await dispatch_event(**event)
return folder
return await create_entity(project_name, folder)


async def update_folder(
Expand All @@ -138,32 +130,14 @@ async def update_folder(
**kwargs,
) -> bool:
folder = await FolderEntity.load(project_name, folder_id)
changed = False

payload: dict[str, Any] = {**kwargs, **create_name_and_label(name)}
kwargs: dict[str, Any] = {**kwargs, **create_name_and_label(name)}

for key in ["name", "label"]:
if key in payload and getattr(folder, key) != payload[key]:
setattr(folder, key, payload[key])
changed = True

for key, value in payload["attrib"].items():
if getattr(folder.attrib, key) != value:
setattr(folder.attrib, key, value)
if key not in folder.own_attrib:
folder.own_attrib.append(key)
changed = True
if changed:
await folder.save()
event = {
"topic": "entity.folder.updated",
"description": f"Folder {folder.name} updated",
"summary": {"entityId": folder.id, "parentId": folder.parent_id},
"project": project_name,
}
await dispatch_event(**event)

return changed
return await update_entity(
project_name,
folder,
kwargs,
attr_whitelist=["name", "label"],
)


async def delete_folder(
Expand All @@ -173,18 +147,7 @@ async def delete_folder(
**kwargs,
) -> None:
folder = await FolderEntity.load(project_name, folder_id)

# do we need this?
await folder.ensure_delete_access(user)

await folder.delete()
event = {
"topic": "entity.folder.deleted",
"description": f"Folder {folder.name} deleted",
"summary": {"entityId": folder.id, "parentId": folder.parent_id},
"project": project_name,
}
await dispatch_event(**event)
await delete_entity(project_name, folder, user)


async def create_task(
Expand All @@ -197,16 +160,7 @@ async def create_task(
project_name=project_name,
payload=payload,
)

await task.save()
event = {
"topic": "entity.task.created",
"description": f"Task {task.name} created",
"summary": {"entityId": task.id, "parentId": task.parent_id},
"project": project_name,
}
await dispatch_event(**event)
return task
return await create_entity(project_name, task)


async def update_task(
Expand All @@ -216,32 +170,14 @@ async def update_task(
**kwargs,
) -> bool:
task = await TaskEntity.load(project_name, task_id)
changed = False

payload = {**kwargs, **create_name_and_label(name)}
kwargs = {**kwargs, **create_name_and_label(name)}

# keys that can be updated
for key in ["name", "label", "status", "task_type", "assignees"]:
if key in payload and getattr(task, key) != payload[key]:
setattr(task, key, payload[key])
changed = True
if "attrib" in payload:
for key, value in payload["attrib"].items():
if getattr(task.attrib, key) != value:
setattr(task.attrib, key, value)
if key not in task.own_attrib:
task.own_attrib.append(key)
changed = True
if changed:
await task.save()
event = {
"topic": "entity.task.updated",
"description": f"Task {task.name} updated",
"summary": {"entityId": task.id, "parentId": task.parent_id},
"project": project_name,
}
await dispatch_event(**event)
return changed
return await update_entity(
project_name,
task,
kwargs,
attr_whitelist=["name", "label", "status", "task_type", "assignees"],
)


async def delete_task(
Expand All @@ -251,18 +187,7 @@ async def delete_task(
**kwargs,
) -> None:
task = await TaskEntity.load(project_name, task_id)

# do we need this?
await task.ensure_delete_access(user)

await task.delete()
event = {
"topic": "entity.task.deleted",
"description": f"Task {task.name} deleted",
"summary": {"entityId": task.id, "parentId": task.parent_id},
"project": project_name,
}
await dispatch_event(**event)
await delete_entity(project_name, task, user)


async def update_project(
Expand All @@ -281,25 +206,62 @@ async def update_project(
)


async def update_entity(project_name, entity, kwargs, attr_whitelist: list[str] | None = None):
"""updates the entity for given attribute whitelist, saves changes and dispatches an update event"""
async def delete_project(project_name: str, user: "UserEntity"):
project = await ProjectEntity.load(project_name)
if not user.is_manager:
raise ForbiddenException("You need to be a manager in order to delete projects")

return await delete_entity(project_name, project, user)


## ====================================================


async def create_entity(project_name: str, entity):
"""create a new entity and dispatch a create event, returns the entity"""
await entity.save()

summary = {
key: getattr(entity, key)
for key in {
"id",
"parent_id",
"name",
}
if hasattr(entity, key)
}

event = {
"topic": f"entity.{entity.entity_type}.created",
"description": f"{entity.entity_type} {entity.name} created",
"summary": summary,
"project": project_name,
}
await dispatch_event(**event)
return entity


async def update_entity(
project_name, entity, kwargs, attr_whitelist: list[str] | None = None
) -> bool:
"""updates the entity for given attribute whitelist, saves changes and dispatches an update event"""
changed = False
if attr_whitelist is None:
attr_whitelist = []

# keys that can be updated
for key in attr_whitelist:
if key in kwargs and getattr(entity, key) != kwargs[key]:
setattr(entity, key, kwargs[key])
logging.info(f"setattr {key}")
logging.debug(f"setattr {key} {getattr(entity, key)} => {kwargs[key]}")
changed = True
if "attrib" in kwargs:
for key, value in kwargs["attrib"].items():
if getattr(entity.attrib, key) != value:
setattr(entity.attrib, key, value)
if key not in entity.own_attrib:
entity.own_attrib.append(key)
logging.info(
logging.debug(
f"setattr attrib.{key} {getattr(entity.attrib, key)} => {value}"
)
changed = True
Expand All @@ -320,6 +282,39 @@ async def update_entity(project_name, entity, kwargs, attr_whitelist: list[str]
"summary": summary,
"project": project_name,
}
logging.info(f"dispatch_event: {event}")
logging.debug(f"dispatch_event: {event}")
await dispatch_event(**event)
return changed


async def delete_entity(
project_name: str,
entity,
user: "UserEntity",
) -> None:
"""delete the given entity after checking user permission, dispatches a delete event"""

# check user permission to delete this entity
if hasattr(entity, "ensure_delete_access") and callable(
entity.ensure_delete_access
):
await entity.ensure_delete_access(user)

await entity.delete()

summary = {}
if hasattr(entity, "id"):
summary["id"] = entity.id
if hasattr(entity, "parent_id"):
summary["parent_id"] = entity.parent_id
if hasattr(entity, "name"):
summary["name"] = entity.name

event = {
"topic": f"entity.{entity.entity_type}.deleted",
"description": f"{entity.entity_type} {entity.name} deleted",
"summary": summary,
"project": project_name,
}
logging.debug(f"dispatch_event: {event}")
await dispatch_event(**event)
Loading