From a3b2f8af68bdeca09d7e99fed2a9dfe2ed4980c0 Mon Sep 17 00:00:00 2001 From: Kuldeep Date: Thu, 18 Dec 2025 16:17:44 +0530 Subject: [PATCH 1/3] update cached_schema for caching pipeline steps --- python/shotgun_globals/cached_schema.py | 115 +++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/python/shotgun_globals/cached_schema.py b/python/shotgun_globals/cached_schema.py index eaac6b33..b1135b1f 100644 --- a/python/shotgun_globals/cached_schema.py +++ b/python/shotgun_globals/cached_schema.py @@ -11,6 +11,7 @@ # make sure that py25 has access to with statement import os +from collections import defaultdict import sgtk from sgtk.platform.qt import QtCore @@ -65,7 +66,7 @@ def __init__(self): self._sg_schema_query_ids = {} self._sg_status_query_ids = {} - + self._step_map_cache = {} # load cached values from disk self._load_cached_schema() self._load_cached_status() @@ -852,6 +853,118 @@ def get_status_color(cls, status_code, project_id=None): return status_color + @classmethod + def _build_project_visible_steps_by_entity_type(cls, all_steps, project_id): + """ + Build a mapping of entity type to project-visible Pipeline Steps. + + :param list all_steps: List of Step dictionaries (as returned by a Shotgun find). + :param int project_id: Project id whose schema visibility is applied. + + This helper inspects the project's schema for each entity type and includes + only the Steps that are exposed via visible 'step_*' fields in that project. + The mapping is constructed from the provided ``all_steps`` list and filtered + by matching the field display name of visible 'step_*' fields to the Step + ``code``. The special display name "ALL TASKS" is ignored. + + - Input Steps are expected to be dicts containing at least: 'id', 'code', + 'entity_type' and optionally 'color'. + - Returned lists are sorted by the Step 'code'. + + :returns: dict[str, list[dict]] mapping entity type to visible Step dicts. + """ + self = cls.__get_instance() + steps_by_entity_and_code = defaultdict(dict) + + for step in all_steps: + step_code = step.get("code") + entity_type = step.get("entity_type") + if not step_code or not entity_type: + continue + steps_by_entity_and_code[entity_type][step_code] = step + step_map = defaultdict(list) + + for entity_type, step_lookup_by_code in steps_by_entity_and_code.items(): + try: + schema_fields = self._bundle.shotgun.schema_field_read( + entity_type, + project_entity={"type": "Project", "id": project_id}, + ) + except Exception as e: + self._bundle.log_error( + "Caught error attempting to read schema for entity type [%s] :\n%s" + % (entity_type, e) + ) + raise + visible_steps = [ + step_lookup_by_code[field_schema.get("name", {}).get("value")] + for field_name, field_schema in schema_fields.items() + if field_name + and field_name.startswith("step_") + and field_schema.get("visible", {}).get("value") + and field_schema.get("name", {}).get("value") + and field_schema.get("name", {}).get("value") != "ALL TASKS" + and field_schema.get("name", {}).get("value") in step_lookup_by_code + ] + if not visible_steps: + continue + visible_steps.sort(key=lambda x: x.get("code") or "") + step_map[entity_type].extend(visible_steps) + return step_map + + @classmethod + def get_pipeline_steps_by_entity_type( + cls, limit_to_project_visible=False, project_id=None + ): + """ + Return a cached mapping of entity type to Pipeline Steps. + + :param bool limit_to_project_visible: Filter to project-visible steps when True. + :param Optional[int] project_id: Project id; if None, current context's project is used. + + - When ``limit_to_project_visible`` is True and a project is available, + includes only Pipeline Steps that are visible in the project's schema + (based on visible 'step_*' fields). + - Otherwise, returns all Pipeline Steps grouped by their ``entity_type``. + - Results are cached per context using the key + ``(limit_to_project_visible and project_id is not None, project_id)``. + + :returns: dict[str, list[dict]] mapping entity type to ordered Step dicts. + """ + self = cls.__get_instance() + all_steps = self._bundle.shotgun.find( + "Step", + [], + ["code", "entity_type", "color"], + order=[{"field_name": "code", "direction": "asc"}], + ) + + effective_project_id = project_id or self._get_current_project_id() + key = ( + bool(limit_to_project_visible and effective_project_id), + effective_project_id, + ) + + # Return cached if available for this context + if key in self._step_map_cache: + return self._step_map_cache[key] + + if not key[0]: + # Not limiting: group all steps by entity_type + step_map = defaultdict(list) + for step in all_steps: + entity_type = step.get("entity_type") + if entity_type: + step_map[entity_type].append(step) + else: + # Limiting to project-visible steps + step_map = cls._build_project_visible_steps_by_entity_type( + all_steps, effective_project_id + ) + + self._step_map_cache[key] = step_map + return step_map + @classmethod def field_is_editable(cls, sg_entity_type, field_name, project_id=None): """ From 31564804c17362f344965805e84161a1cb2ef6cc Mon Sep 17 00:00:00 2001 From: Kuldeep Date: Thu, 18 Dec 2025 16:18:25 +0530 Subject: [PATCH 2/3] update cached_schema for caching pipeline steps --- python/shotgun_globals/cached_schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/shotgun_globals/cached_schema.py b/python/shotgun_globals/cached_schema.py index b1135b1f..e30b1a13 100644 --- a/python/shotgun_globals/cached_schema.py +++ b/python/shotgun_globals/cached_schema.py @@ -873,6 +873,7 @@ def _build_project_visible_steps_by_entity_type(cls, all_steps, project_id): :returns: dict[str, list[dict]] mapping entity type to visible Step dicts. """ + self = cls.__get_instance() steps_by_entity_and_code = defaultdict(dict) From 22a86673991365b6ae20924fa926f91eb809d480 Mon Sep 17 00:00:00 2001 From: Kuldeep Date: Thu, 18 Dec 2025 17:06:14 +0530 Subject: [PATCH 3/3] updated for cache file reference --- python/shotgun_globals/cached_schema.py | 77 +++++++++++++++---------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/python/shotgun_globals/cached_schema.py b/python/shotgun_globals/cached_schema.py index e30b1a13..1fb44e86 100644 --- a/python/shotgun_globals/cached_schema.py +++ b/python/shotgun_globals/cached_schema.py @@ -858,20 +858,23 @@ def _build_project_visible_steps_by_entity_type(cls, all_steps, project_id): """ Build a mapping of entity type to project-visible Pipeline Steps. - :param list all_steps: List of Step dictionaries (as returned by a Shotgun find). + :param list all_steps: List of Step dictionaries (e.g. from a Shotgun find). :param int project_id: Project id whose schema visibility is applied. - This helper inspects the project's schema for each entity type and includes - only the Steps that are exposed via visible 'step_*' fields in that project. - The mapping is constructed from the provided ``all_steps`` list and filtered - by matching the field display name of visible 'step_*' fields to the Step - ``code``. The special display name "ALL TASKS" is ignored. + This inspects the project's schema for each entity type and includes only + those Steps that are exposed via visible 'step_*' fields for the project. + A field is eligible when: + - its name starts with 'step_' + - it is marked visible for the project + - its display name is not "ALL TASKS" + - its display name matches a Step 'code' present in ``all_steps`` - - Input Steps are expected to be dicts containing at least: 'id', 'code', - 'entity_type' and optionally 'color'. - - Returned lists are sorted by the Step 'code'. + Notes: + - Input Steps must contain at least: 'id', 'code', 'entity_type' (and may include 'color'). + - The resulting order follows the schema fields iteration order; no extra sorting is applied. - :returns: dict[str, list[dict]] mapping entity type to visible Step dicts. + + :returns: dict[str, list[dict]] mapping entity type to visible Step dicts. """ self = cls.__get_instance() @@ -887,30 +890,42 @@ def _build_project_visible_steps_by_entity_type(cls, all_steps, project_id): for entity_type, step_lookup_by_code in steps_by_entity_and_code.items(): try: - schema_fields = self._bundle.shotgun.schema_field_read( - entity_type, - project_entity={"type": "Project", "id": project_id}, + entity_fields = cls.get_entity_fields( + entity_type, project_id=project_id ) - except Exception as e: - self._bundle.log_error( - "Caught error attempting to read schema for entity type [%s] :\n%s" - % (entity_type, e) + except Exception: + self._bundle.log_debug( + f"Could not get entity fields for entity type {entity_type}" ) - raise - visible_steps = [ - step_lookup_by_code[field_schema.get("name", {}).get("value")] - for field_name, field_schema in schema_fields.items() - if field_name - and field_name.startswith("step_") - and field_schema.get("visible", {}).get("value") - and field_schema.get("name", {}).get("value") - and field_schema.get("name", {}).get("value") != "ALL TASKS" - and field_schema.get("name", {}).get("value") in step_lookup_by_code - ] - if not visible_steps: + entity_fields = [] + + visible_step_codes = [] + for field_name in entity_fields: + if not field_name.startswith("step_"): + continue + try: + if cls.field_is_visible( + entity_type, field_name, project_id=project_id + ): + display_name = cls.get_field_display_name( + entity_type, field_name, project_id=project_id + ) + if ( + display_name + and display_name != "ALL TASKS" + and display_name in step_lookup_by_code + ): + visible_step_codes.append(display_name) + except Exception as e: + self._bundle.log_debug(e) + continue + + if not visible_step_codes: continue - visible_steps.sort(key=lambda x: x.get("code") or "") - step_map[entity_type].extend(visible_steps) + + step_map[entity_type].extend( + [step_lookup_by_code[code] for code in visible_step_codes] + ) return step_map @classmethod