Skip to content

Commit

Permalink
Merge branch 'develop' into enhancement/project_entity
Browse files Browse the repository at this point in the history
  • Loading branch information
martastain authored Jun 13, 2024
2 parents 31183cd + f41ff93 commit 8803741
Show file tree
Hide file tree
Showing 23 changed files with 1,211 additions and 530 deletions.
112 changes: 112 additions & 0 deletions client/ayon_kitsu/plugins/publish/collect_kitsu_family.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""
Requires:
none
Provides:
instance -> families ([])
"""
import pyblish.api

from ayon_core.lib import filter_profiles

from ayon_kitsu.pipeline import plugin


class CollectKitsuFamily(plugin.KitsuPublishInstancePlugin):
"""Adds explicitly 'kitsu' to families to upload instance to FTrack.
Uses selection by combination of hosts/families/tasks names via
profiles resolution.
Triggered everywhere, checks instance against configured.
Checks advanced filtering which works on 'families' not on main
'family', as some variants dynamically resolves addition of kitsu
based on 'families' (editorial drives it by presence of 'review')
"""

label = "Collect Kitsu Family"
order = pyblish.api.CollectorOrder + 0.4990

profiles = None

def process(self, instance):
if not self.profiles:
self.log.warning("No profiles present for adding Kitsu family")
return

host_name = instance.context.data["hostName"]
product_type = instance.data["productType"]
task_name = instance.data.get("task")

filtering_criteria = {
"host_names": host_name,
"product_types": product_type,
"task_names": task_name
}
profile = filter_profiles(
self.profiles,
filtering_criteria,
logger=self.log
)

add_kitsu_family = False
families = instance.data.setdefault("families", [])

if profile:
add_kitsu_family = profile["add_kitsu_family"]
additional_filters = profile.get("advanced_filtering")
if additional_filters:
families_set = set(families) | {product_type}
self.log.info(
"'{}' families used for additional filtering".format(
families_set))
add_kitsu_family = self._get_add_kitsu_f_from_addit_filters(
additional_filters,
families_set,
add_kitsu_family
)

result_str = "Not adding"
if add_kitsu_family:
result_str = "Adding"
if "kitsu" not in families:
families.append("kitsu")

self.log.debug("{} 'kitsu' family for instance with '{}'".format(
result_str, product_type
))

def _get_add_kitsu_f_from_addit_filters(
self, additional_filters, families, add_kitsu_family
):
"""Compares additional filters - working on instance's families.
Triggered for more detailed filtering when main family matches,
but content of 'families' actually matter.
(For example 'review' in 'families' should result in adding to
Kitsu)
Args:
additional_filters (dict) - from Setting
families (set[str]) - subfamilies
add_kitsu_family (bool) - add kitsu to families if True
"""

override_filter = None
override_filter_value = -1
for additional_filter in additional_filters:
filter_families = set(additional_filter["families"])
valid = filter_families <= set(families) # issubset
if not valid:
continue

value = len(filter_families)
if value > override_filter_value:
override_filter = additional_filter
override_filter_value = value

if override_filter:
add_kitsu_family = override_filter["add_kitsu_family"]

return add_kitsu_family
4 changes: 2 additions & 2 deletions client/ayon_kitsu/plugins/publish/integrate_kitsu_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class IntegrateKitsuNote(KitsuPublishContextPlugin):

order = pyblish.api.IntegratorOrder
label = "Kitsu Note and Status"
families = ["render", "image", "online", "plate", "kitsu"]
families = ["kitsu"]

# status settings
set_status_note = False
Expand Down Expand Up @@ -68,7 +68,7 @@ def process(self, context):
families = set(
[instance.data["family"]] + instance.data.get("families", [])
)
if "review" not in families:
if "review" not in families or "kitsu" not in families:
continue

kitsu_task = instance.data.get("kitsuTask")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class IntegrateKitsuReview(KitsuPublishInstancePlugin):

order = pyblish.api.IntegratorOrder + 0.01
label = "Kitsu Review"
families = ["render", "image", "online", "plate", "kitsu"]
families = ["kitsu"]
optional = True

def process(self, instance):
Expand Down
100 changes: 99 additions & 1 deletion server/kitsu/addon_helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
def required_values(entity: dict, keys: list[str], allow_empty_value=False):
import re
import unicodedata
from typing import Any

"""
A collection of helper functions for Ayon Addons
minimal dependencies, pytest unit tests
"""

def required_values(entity: dict[str, Any], keys: list[str], allow_empty_value: bool = False) -> list[Any]:

"""check the entity dict has the required keys and a value for each"""
values = []
for key in keys:
Expand All @@ -8,3 +18,91 @@ def required_values(entity: dict, keys: list[str], allow_empty_value=False):
raise ValueError(f"Value for '{key}' cannot be empty for entity: {entity}")
values.append(entity.get(key))
return values


## ========== KITSU -> AYON NAME CONVERSIONS =====================


def create_short_name(name: str) -> str:
"""create a shortname from the full name when a shortname is not present"""
code = name.lower()

if "_" in code:
subwords = code.split("_")
code = "".join([subword[0] for subword in subwords])[:4]
elif len(name) > 4:
vowels = ["a", "e", "i", "o", "u"]
filtered_word = "".join([char for char in code if char not in vowels])
code = filtered_word[:4]

# if there is a number at the end of the code, add it to the code
last_char = code[-1]
if last_char.isdigit():
code += last_char

return code


def to_username(first_name: str, last_name: str | None = None) -> str:
"""converts usernames from kitsu - converts accents"""

name = (
f"{first_name.strip()}.{last_name.strip()}" if last_name else first_name.strip()
)

name = name.lower()
name = remove_accents(name)
return to_entity_name(name)


def remove_accents(input_str: str) -> str:
"""swap accented characters for a-z equivilants ž => z"""

nfkd_form = unicodedata.normalize("NFKD", input_str)
result = "".join([c for c in nfkd_form if not unicodedata.combining(c)])

# manually replace exceptions
# @see https://stackoverflow.com/questions/3194516/replace-special-characters-with-ascii-equivalent
replacement_map = {
"Æ": "AE",
"Ð": "D",
"Ø": "O",
"Þ": "TH",
"ß": "ss",
"æ": "ae",
"ð": "d",
"ø": "o",
"þ": "th",
"Œ": "OE",
"œ": "oe",
"ƒ": "f",
}
for k, v in replacement_map.items():
if k in result:
result = result.replace(k, v)

# remove any unsupported characters
result = re.sub(r"[^a-zA-Z0-9_\.\-]", "", result)

return result


def to_entity_name(name: str) -> str:
r"""convert names so they will pass AYON Entity name validation
@see ayon_server.types.NAME_REGEX = r"^[a-zA-Z0-9_]([a-zA-Z0-9_\.\-]*[a-zA-Z0-9_])?$"
"""

if not name:
raise ValueError("Entity name cannot be empty")

name = name.strip()

# replace whitespace
name = re.sub(r"\s+", "_", name)
# remove any invalid characters
name = re.sub(r"[^a-zA-Z0-9_\.\-]", "", name)

# first and last characters cannot be . or -
name = re.sub(r"^[^a-zA-Z0-9_]+", "", name)
name = re.sub(r"[^a-zA-Z0-9_]+$", "", name)
return name
2 changes: 1 addition & 1 deletion server/kitsu/anatomy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ayon_server.settings.anatomy.statuses import Status
from ayon_server.settings.anatomy.task_types import TaskType

from .utils import create_short_name, remove_accents
from .addon_helpers import create_short_name, remove_accents

if TYPE_CHECKING:
from .. import KitsuAddon
Expand Down
Loading

0 comments on commit 8803741

Please sign in to comment.