diff --git a/src/trackteroid/configuration.py b/src/trackteroid/configuration.py index 9cee961..704be90 100644 --- a/src/trackteroid/configuration.py +++ b/src/trackteroid/configuration.py @@ -113,12 +113,15 @@ def _override_configuration(): # deletion for and resolves to True or False. ALLOWED_FOR_DELETION_RESOLVER = lambda session, type_name: True +DEFAULT_PROJECTIONS_RESOLVER = lambda session, type_name: [] + WARN_ON_INJECT = False ############################################################################################ _LOG = logging.getLogger(f"{LOGGING_NAMESPACE}.configuration") _CALLABLES_REQUIRE_FALLBACK = [ "RELATIONSHIPS_RESOLVER", + "DEFAULT_PROJECTIONS_RESOLVER" ] _override_configuration() diff --git a/src/trackteroid/entities/base.py b/src/trackteroid/entities/base.py index 332d234..4cc0f2e 100644 --- a/src/trackteroid/entities/base.py +++ b/src/trackteroid/entities/base.py @@ -60,6 +60,7 @@ from ..configuration import ( LOGGING_NAMESPACE, ALLOWED_FOR_DELETION_RESOLVER, + DEFAULT_PROJECTIONS_RESOLVER, WARN_ON_INJECT ) @@ -729,22 +730,6 @@ def resolve_subtype(self, entity_type): source=getattr(matched_types, "source", None) ) - def query_children(self, projections=None, session=None): - """Queries and returns the children of the collection. Only supported for - entities that have a "children" relation - - Args: - projections (`list` of `str`): Projections to fetch from the children - session (Session): Optional session to use. - - """ - projections_ = ["children.{}".format(_) for _ in self._entity.projections] - projections_ += ["children.{}".format(x) for x in (projections or [])] - projections_.append("children.object_type.name") - query = self.as_query(use_ids=True) - query.projections = list(set(projections_).union(query.projections)) # update not override - return query.get_all(session=session or self._session).children - # TODO: move to avoid circular import @staticmethod def _make_empty(entity_class, session): @@ -1745,11 +1730,9 @@ def delete(self): class _EntityBase(object, metaclass=ForwardDeclareCompare): + _ftrack_entity = None relationship = Relationship() - projections = ["id"] - _ftrack_entity = None - log = None def __new__(cls, *args, **kwargs): """ make it possible to swap the Entity class with the given class @@ -1763,7 +1746,6 @@ def __new__(cls, *args, **kwargs): """ if kwargs.get("_cls"): cls = kwargs["_cls"] - cls.log = logging.getLogger("{}.entities.{}".format(cls.__name__, LOGGING_NAMESPACE)) del kwargs["_cls"] if cls and args and isinstance(args[0], (EntityCollection, EmptyCollection)): @@ -1805,10 +1787,6 @@ def __new__(cls, *args, **kwargs): def __init__(self, _cls=None, ftrack_entity=None, **kwargs): self.ftrack_entity = ftrack_entity - if not _cls: - self.log = logging.getLogger( - "{}.entities.{}".format(self.__class__.__name__, LOGGING_NAMESPACE) - ) def __getitem__(self, item): # Try/Except against KeyError to keep compatibility with @@ -1853,6 +1831,14 @@ def __cmp__(self, other): else: return 1 + def projections(self, session): + return DEFAULT_PROJECTIONS_RESOLVER(session=session, type_name=self.__class__.__name__) + + @classmethod + @property + def log(cls): + return logging.getLogger(f"{LOGGING_NAMESPACE}.entities.{cls.__name__}") + def pre_create(self, **kwargs): return kwargs diff --git a/src/trackteroid/entities/entities.py b/src/trackteroid/entities/entities.py index 1d9610f..574b95d 100644 --- a/src/trackteroid/entities/entities.py +++ b/src/trackteroid/entities/entities.py @@ -41,7 +41,6 @@ class AssetVersion(Entity): - projections = ["id", "asset.name", "version"] relationship = Relationship() relationship.parent = "asset" @@ -140,7 +139,6 @@ def by_publish_state(self, target, publish_state): class TypedContext(Entity): - projections = ["id", "name"] relationship = Relationship() # only for autocompletion @@ -300,7 +298,6 @@ class AssetGroup(TypedContext): class Component(Entity): - projections = ["id", "name"] relationship = Relationship() relationship.parent = "version" @@ -361,19 +358,19 @@ def by_size(self, target, minimum=0, maximum=0): class FileComponent(Component): - projections = ["id", "name"] + pass class ContainerComponent(Component): - projections = ["id", "name"] + pass class NoteComponent(Component): - projections = ["note_id", "component_id", "component"] + pass class Project(Entity): - projections = ["id", "name"] + pass @Criteria.supported_targets(Entity) def by_name(self, target, *names): @@ -423,7 +420,6 @@ def create(self, name, project_schema, **kwargs): pass class ComponentLocation(Entity): - projections = ["id", "resource_identifier"] relationship = Relationship() relationship.parent = "component" @@ -456,7 +452,6 @@ def by_resource_identifier(self, target, *resource_identifiers): class Asset(Entity): - projections = ["id", "name"] relationship = Relationship() @@ -496,7 +491,6 @@ def create(self, name, type, **kwargs): pass class Status(Entity): - projections = ["id", "name"] relationship = Relationship() @@ -507,21 +501,19 @@ def by_name(self, target, *names): class State(Entity): - projections = ["id", "name"] relationship = Relationship() class AssetVersionLink(Entity): - projections = ["id", "from_id", "to_id"] + pass class ProjectSchema(Entity): - projections = ["id", "name"] + pass class User(Entity): - projections = ["id", "username", "is_active"] relationship = Relationship() @@ -540,7 +532,6 @@ def by_name(self, target, *names): class Timelog(Entity): - projections =["id", "comment", "start", "duration"] relationship = Relationship() relationship.parent = "user" @@ -567,19 +558,18 @@ def by_lifespan(self, target, start=None, end=None): class UserSecurityRole(Entity): - projections = ["id", "security_role", "projects"] + pass class UserSecurityRoleProject(Entity): - projections = ["id", "project"] + pass class SecurityRole(Entity): - projections = ["id", "name", "type"] + pass class Appointment(Entity): - projections = ["id", "resource", "resource.name"] relationship = Relationship() relationship.parent = "context" @@ -599,19 +589,19 @@ def pre_create(self, **kwargs): class ObjectType(Entity): - projections = ["id"] + pass class Context(Entity): - projections = ["id"] + pass class AssetType(Entity): - projections = ["id", "name"] + pass class Event(Entity): - projections = ["id", "action", "user", "data"] + pass relationship = Relationship() @@ -632,23 +622,23 @@ def by_data(self, target, *datas): class TypedContextLink(Entity): - projections = ["id", "from_id", "to_id"] + pass class TaskTypeSchema(Entity): - projections = ["id", "types.name"] + pass class Type(Entity): - projections = ["id", "name"] + pass class Metadata(Entity): - projections = ["id", "key", "value"] + pass class NoteCategory(Entity): - projections = ["id", "name"] + pass @Criteria.supported_targets() def by_name(self, target, *names): @@ -657,7 +647,7 @@ def by_name(self, target, *names): class Note(Entity): - projections = ["id", "content"] + relationship = Relationship() # FIX: why do we need to set a parent if the precreate set @@ -728,7 +718,7 @@ def by_id(self, target, *ids): class ReviewSession(Entity): - projections = ["id", "name"] + relationship = Relationship() def pre_create(self, **kwargs): @@ -748,7 +738,7 @@ def by_name(self, target, *names): class ReviewSessionObject(Entity): - projections = ["id", "name"] + relationship = Relationship() relationship.parent = "review_session" @@ -762,7 +752,7 @@ def pre_create(self, **kwargs): class ReviewSessionInvitee(Entity): - projections = ["id", "name", "email"] + relationship = Relationship() # TEST: Check if this is needed or how it is actually used. relationship.parent = "review_session" @@ -773,15 +763,11 @@ def pre_create(self, **kwargs): class ReviewSessionObjectStatus(Entity): - projections = ["id", "status"] + pass class Recipient(Entity): - # recipients are only used in notes and thus we can simply project - # everything - projections = ["resource_id", "note", "note_id", "recipient", "user"] - - relationship = Relationship() + pass class EntitySetting(Entity): @@ -789,7 +775,6 @@ class EntitySetting(Entity): class Group(Entity): - projections = ["id", "name"] @Criteria.supported_targets() def by_name(self, target, *names): @@ -798,9 +783,6 @@ def by_name(self, target, *names): class Job(Entity): - projections = ["id", "status", "data"] - - relationship = Relationship() @Criteria.supported_targets(Entity) def by_name(self, target, *names): @@ -876,11 +858,10 @@ def by_finish_date(self, target, finish_date): class Membership(Entity): - projections = ["id", "group"] + pass class Location(Entity): - projections = ["id", "name"] @Criteria.supported_targets() def by_name(self, target, *names): @@ -889,7 +870,6 @@ def by_name(self, target, *names): class List(Entity): - projections = ["id", "name"] relationship = Relationship() @@ -941,9 +921,6 @@ def create(self, task, **kwargs): pass class ListCategory(Entity): - projections = ["id", "name"] - - relationship = Relationship() @Criteria.supported_targets() def by_name(self, target, *names): @@ -956,7 +933,6 @@ class WorkflowSchema(Entity): class NoteLabel(Entity): - projections = ["id", "name"] @Criteria.supported_targets(Entity) def by_name(self, target, *names): @@ -971,4 +947,4 @@ class Resource(Entity): # automatically declare classes for all TypedContext objects for _type in OBJECT_TYPES.types: if _type not in locals() or issubclass(locals().get(_type, None), ForwardDeclaration): - locals()[_type] = type(str(_type), (TypedContext,), {"projections": ['id', 'name']}) + locals()[_type] = type(str(_type), (TypedContext,), {}) diff --git a/src/trackteroid/query/query.py b/src/trackteroid/query/query.py index 48d1693..e048661 100644 --- a/src/trackteroid/query/query.py +++ b/src/trackteroid/query/query.py @@ -115,7 +115,7 @@ def __init__(self, entity, session=SESSION, schema=SCHEMA.default): self.entity_type.relationship(session=session, schema=schema) - self.primary_key = self.entity_type.projections[0] if len(self.entity_type.projections) else "id" + self.primary_key = session.types[self.entity_type.__class__.__name__].primary_key_attributes[0] self.projections = [] self.criteria = [] self.session = session @@ -155,12 +155,9 @@ def __copy__(self): def __str__(self): """ returns the generated query string """ - projections = ", ".join(self.projections or self.entity_type.projections) + projections = ", ".join(self.projections or self.entity_type.projections(self.session)) - _query = "select {} from {}".format( - projections, - self.entity_type.__class__.__name__ - ) + _query = (f"select {projections} from " if projections else "") + f"{self.entity_type.__class__.__name__}" if self.criteria: # We sort the criteria based on the distance to the queried entity: @@ -310,7 +307,7 @@ def _set_projections(self, projections): else: _projections.append(projection) # extend default projections - self.projections = list(set(_projections + self.entity_type.__class__.projections)) + self.projections = list(set(_projections + self.entity_type.projections(self.session))) @property def valid(self): diff --git a/tests/test_authoring.py b/tests/test_authoring.py index c34cd5f..1cbf152 100644 --- a/tests/test_authoring.py +++ b/tests/test_authoring.py @@ -1,4 +1,6 @@ +import logging import random +import sys import uuid from collections import OrderedDict