Source code for stalker.models.structure

# -*- coding: utf-8 -*-
# Stalker a Production Asset Management System
# Copyright (C) 2009-2017 Erkan Ozgur Yilmaz
#
# This file is part of Stalker.
#
# Stalker is free software: you can redistribute it and/or modify
# it under the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License.
#
# Stalker is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with Stalker.  If not, see <http://www.gnu.org/licenses/>

from sqlalchemy import Table, Column, Integer, ForeignKey, Text
from sqlalchemy.orm import relationship, validates

from stalker.db.declarative import Base
from stalker.models.entity import Entity

from stalker.log import logging_level
import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging_level)


[docs]class Structure(Entity): """Defines folder structures for :class:`.Projects`. Structures are generally owned by :class:`.Project` objects. Whenever a :class:`.Project` is physically created, project folders are created by looking at :attr:`.Structure.custom_template` of the :class:`.Structure`, the :class:`.Project` object is generally given to the :class:`.Structure`. So it is possible to use a variable like "{{project}}" or derived variables like:: {% for seq in project.sequences %} {{do something here}} Every line of this rendered template will represent a folder and Stalker will create these folders on the attached :class:`.Repository`. :param templates: A list of :class:`.FilenameTemplate`\ s which defines a specific template for the given :attr:`.FilenameTemplate.target_entity_type`\ s. :type templates: list of :class:`.FilenameTemplate`\ s :param str custom_template: A string containing several lines of folder names. The folders are relative to the :class:`.Project` root. It can also contain a Jinja2 Template code. Which will be rendered to show the list of folders to be created with the project. The Jinja2 Template is going to have the {{project}} variable. The important point to be careful about is to list all the custom folders of the project in a new line in this string. For example a :class:`.Structure` for a :class:`.Project` can have the following :attr:`.Structure.custom_template`:: ASSETS {% for asset in project.assets %} {% set asset_root = 'ASSETS/' + asset.code %} {{asset_root}} {% for task in asset.tasks %} {% set task_root = asset_root + '/' + task.code %} {{task_root}} SEQUENCES {% for seq in project.sequences %}} {% set seq_root = 'SEQUENCES/' + {{seq.code}} %} {{seq_root}}/Edit {{seq_root}}/Edit/Export {{seq_root}}/Storyboard {% for shot in seq.shots %} {% set shot_root = seq_root + '/SHOTS/' + shot.code %} {{shot_root}} {% for task in shot.tasks %} {% set task_root = shot_root + '/' + task.code %} {{task_root}} The above example has gone far beyond deep than it is needed, where it started to define paths for :class:`.Asset`\ s. Even it is possible to create a :class:`.Project` structure like that, in general it is unnecessary. Because the above folders are going to be created but they are probably going to be empty for a while, because the :class:`.Asset`\ s are not created yet (or in fact no :class:`.Version`\ s are created for the :class:`.Task`\ s). Anyway, it is much suitable and desired to create this details by using :class:`.FilenameTemplate` objects. Which are specific to certain :attr:`.FilenameTemplate.target_entity_type`\ s. And by using the :attr:`.Structure.custom_template` attribute, Stalker can not place any source or output file of a :class:`.Version` in the :class:`.Repository` where as it can by using :class:`.FilenameTemplate`\ s. But for certain types of :class:`.Task`\ s it is may be good to previously create the folder structure just because in certain environments (programs) it is not possible to run a Python code that will place the file in to the Repository like in Photoshop. The ``custom_template`` parameter can be None or an empty string if it is not needed. A :class:`.Structure` can not be created without a ``type`` (__strictly_typed__ = True). By giving a ``type`` to the :class:`.Structure`, you can create one structure for **Commercials** and another project structure for **Movies** and another one for **Print** projects etc. and can reuse them with new :class:`.Project`\ s. """ #__strictly_typed__ = True __auto_name__ = False __tablename__ = "Structures" __mapper_args__ = {"polymorphic_identity": "Structure"} structure_id = Column( "id", Integer, ForeignKey("Entities.id"), primary_key=True, ) templates = relationship( "FilenameTemplate", secondary="Structure_FilenameTemplates" ) custom_template = Column("custom_template", Text) def __init__(self, templates=None, custom_template=None, **kwargs): super(Structure, self).__init__(**kwargs) if templates is None: templates = [] self.templates = templates self.custom_template = custom_template def __eq__(self, other): """the equality operator """ return super(Structure, self).__eq__(other) and \ isinstance(other, Structure) and \ self.templates == other.templates and \ self.custom_template == other.custom_template def __hash__(self): """the overridden __hash__ method """ return super(Structure, self).__hash__() @validates("custom_template") def _validate_custom_template(self, key, custom_template_in): """validates the given custom_template value """ if custom_template_in is None: custom_template_in = "" from stalker import __string_types__ if not isinstance(custom_template_in, __string_types__): raise TypeError( "%s.custom_template should be a string not %s" % ( self.__class__.__name__, custom_template_in.__class__.__name__ ) ) return custom_template_in @validates("templates") def _validate_templates(self, key, template_in): """validates the given template value """ from stalker.models.template import FilenameTemplate if not isinstance(template_in, FilenameTemplate): raise TypeError( "All the elements in the %s.templates should be a " "stalker.models.template.FilenameTemplate instance not %s" % (self.__class__.__name__, template_in.__class__.__name__) ) return template_in
# Structure_FilenameTemplates Table Structure_FilenameTemplates = Table( "Structure_FilenameTemplates", Base.metadata, Column("structure_id", Integer, ForeignKey("Structures.id"), primary_key=True), Column("filenametemplate_id", Integer, ForeignKey("FilenameTemplates.id"), primary_key=True) )