# -*- 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 Column, Integer, ForeignKey, String
from sqlalchemy.orm import validates
from stalker.models.entity import Entity
from stalker.models.mixins import TargetEntityTypeMixin
from stalker.log import logging_level
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging_level)
[docs]class FilenameTemplate(Entity, TargetEntityTypeMixin):
"""Holds templates for filename and path conventions.
FilenameTemplate objects help to specify where to place a
:class:`~stalker.models.version.Version` related file.
Although, it is mainly used by Stalker to define
:class:`~stalker.models.version.Version` related file paths and file names
to place them in to proper places inside a
:class:`~stalker.models.project.Project`'s
:attr:`~stalker.models.project.Project.structure`, the idea behind is open
to endless possibilities.
Here is an example::
p1 = Project(name="Test Project") # shortened for this example
s1 = Structure(name="Commercial Project Structure") # shortened for this example
# this is going to be used by Stalker to decide the
# :stalker:`~stalker.models.link.Link`
# :stalker:`~stalker.models.link.Link.filename` and
# :stalker:`~stalker.models.link.Link.path` (which is the way Stalker
# links external files to Version instances)
f1 = FilenameTemplate(
name="Asset Version Template",
target_entity_type="Asset",
target_entity_type="Asset",
path="{{project.code}}/Assets/{{asset.type.code}}/{{asset.code}}/{{task.type.code}}",
filename="{{asset.code}}_{{version.take_name}}_{{task.type.code}}_v{{'%03d'|version.version_number}}{{link.extension}}"
)
s1.templates.append(f1)
p1.structure = s1
# now because we have defined a FilenameTemplate for Assets,
# Stalker is now able to produce a path and a filename for any Version
# related to this asset.
:param str target_entity_type: The class name that this FilenameTemplate
is designed for. You can also pass the class itself. So both of the
examples below can work::
new_filename_template1 = FilenameTemplate(target_entity_type="Asset")
new_filename_template2 = FilenameTemplate(target_entity_type=Asset)
A TypeError will be raised when it is skipped or it is None and a
ValueError will be raised when it is given as and empty string.
:param str path: A `Jinja2`_ template code which specifies the path of the
given item. It is relative to the repository root. A typical example
could be::
asset_path = "{{project.code}}/ASSETS/{{asset.code}}/{{task.code}}/"
:param str filename: A `Jinja2`_ template code which specifies the file
name of the given item. It is relative to the
:attr:`~stalker.models.template.FilenameTemplate.path`. A typical
example could be::
asset_filename = "{{asset.code}}_{{version.take}}_{{task.code}}_v"{{'%03d'|format(version.version)}}{{version.extension}}"
Could be set to an empty string or None, the default value is None.
It can be None, or an empty string, or it can be skipped.
.. _Jinja2: http://jinja.pocoo.org/docs/
"""
__auto_name__ = False
__strictly_typed__ = False
__tablename__ = "FilenameTemplates"
__mapper_args__ = {"polymorphic_identity": "FilenameTemplate"}
filenameTemplate_id = Column("id", Integer, ForeignKey("Entities.id"),
primary_key=True)
# _target_entity_type = Column("target_entity_type", String)
path = Column(
String,
doc="""The template code for the path of this FilenameTemplate."""
)
filename = Column(
String,
doc="""The template code for the file part of the FilenameTemplate."""
)
[docs] def __init__(self,
target_entity_type=None,
path=None,
filename=None,
**kwargs):
super(FilenameTemplate, self).__init__(**kwargs)
TargetEntityTypeMixin.__init__(self, target_entity_type, **kwargs)
self.path = path
self.filename = filename
@validates("path")
def _validate_path(self, key, path_in):
"""validates the given path attribute for several conditions
"""
# check if it is None
if path_in is None:
path_in = ""
if not isinstance(path_in, (str, unicode)):
raise TypeError("%s.path attribute should be string or unicode "
"not %s" % (self.__class__.__name__,
path_in.__class__.__name__))
return path_in
@validates("filename")
def _validate_filename(self, key, filename_in):
"""validates the given filename attribute for several conditions
"""
# check if it is None
if filename_in is None:
filename_in = ""
if not isinstance(filename_in, (str, unicode)):
raise TypeError("%s.filename attribute should be string or "
"unicode not %s" % (self.__class__.__name__,
filename_in.__class__.__name__))
return filename_in
def __eq__(self, other):
"""checks the equality of the given object to this one
"""
return super(FilenameTemplate, self).__eq__(other) and\
isinstance(other, FilenameTemplate) and\
self.target_entity_type == other.target_entity_type and\
self.path == other.path and\
self.filename == other.filename