Source code for stalker.models.structure

# -*- coding: utf-8 -*-
# Stalker a Production Asset Management System
# Copyright (C) 2009-2013 Erkan Ozgur Yilmaz
# 
# This file is part of Stalker.
# 
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
# 
# This library 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 GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA

from sqlalchemy import Table, Column, Integer, ForeignKey, String
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): """Holds data about how the physical files are arranged in the :class:`~stalker.models.repository.Repository`. Structures are generally owned by :class:`~stalker.models.project.Project` objects. Whenever a :class:`~stalker.models.project.Project` is physically created, project folders are created by looking at :attr:`~stalker.models.structure.Structure.custom_template` of the :class:`~stalker.models.structure.Structure`, the :class:`~stalker.models.project.Project` object is generally given to the :class:`~stalker.models.structure.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:`~stalker.models.repository.Repository`. :param templates: A list of :class:`~stalker.models.template.FilenameTemplate`\ s which defines a specific template for the given :attr:`~stalker.models.template.FilenameTemplate.target_entity_type`\ s. :type templates: list of :class:`~stalker.models.template.FilenameTemplate`\ s :param str custom_template: A string containing several lines of folder names. The folders are relative to the :class:`~stalker.models.project.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:`~stalker.models.structure.Structure` for a :class:`~stalker.models.project.Project` can have the following :attr:`~stalker.models.structure.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:`~stalker.models.asset.Asset`\ s. Even it is possible to create a :class:`~stalker.models.project.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:`~stalker.models.asset.Asset`\ s are not created yet (or in fact no :class:`~stalker.models.version.Version`\ s are created for the :class:`~stalker.models.task.Task`\ s). Anyway, it is much suitable and desired to create this details by using :class:`~stalker.models.template.FilenameTemplate` objects. Which are specific to certain :attr:`~stalker.models.template.FilenameTemplate.target_entity_type`\ s. And by using the :attr:`~stalker.models.structure.Structure.custom_template` attribute, Stalker can not place any source or output file of a :class:`~stalker.models.version.Version` in the :class:`~stalker.models.repository.Repository` where as it can by using :class:`~stalker.models.template.FilenameTemplate`\ s. But for certain types of :class:`~stalker.models.task.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:`~stalker.models.structure.Structure` can not be created without a ``type`` (__strictly_typed__ = True). By giving a ``type`` to the :class:`~stalker.models.structure.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:`~stalker.models.project.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", String)
[docs] 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 @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 = "" if not isinstance(custom_template_in, (str, unicode)): 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 " "list of " "stalker.models.template.FilenameTemplate " "instances not %s" % (self.__class__.__name__, template_in.__class__.__name__)) return template_in # STRUCTURE_FILENAMETEMPLATES
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) )