# -*- coding: utf-8 -*-
import os
import logging
logger = logging.getLogger(__name__)
[docs]class Config(object):
"""config abstraction
Idea is coming from Sphinx config
**config.py File**
oyProjectManager uses the ``config.py`` to let one to customize the system
config.
The ``config.py`` file is searched in a couple of places through the
system:
* under "~/.oypmrc/" directory (not yet)
* under "$OYPROJECTMANAGER_PATH"
The first path is a folder in the users home dir. The second one is a path
defined by the ``OYPROJECTMANAGER_PATH`` environment variable.
Defining the ``config.py`` by using the environment variable gives the most
customizable and consistent setup through the studio. You can set
``OYPROJECTMANAGER_PATH`` to a shared folder in your fileserver where all
the users can access.
Because, ``config.py`` is a regular Python code which is executed by
oyProjectManager, you can do anything you were doing in a normal Python
script. This is very handy if you have another source of information which
is reachable by a Python script.
In config.py you can:
* Add new users
* Customize default project structure
* Customize file naming convention
* Customize path naming convention
* Customize the database file name
* Customize server paths
* Customize environments (host programs running oyProjectManager)
* and many more
If there is no ``OYPROJECTMANAGER_PATH`` variable in your current
environment or it is not showing an existing path or there is no
``config.py`` file the system will use the system defaults and it is
probably not going to work efficiently.
Sample usage is::
from oyProjectManager import config
conf = config.Config()
print conf.database_url # will return sqlite:///$REPO/project_manager.db
# print all the user names defined in the config.py
for user in conf.users:
print user.name
**About the Users**
The users in the oyProjectManager is held in the config file as a python
dictionary. You can add or remove users::
users_data = [{"name": "Erkan Ozgur Yilmaz",
"initials":"eoy",
"email": "eoyilmaz@company.com"}]
As seen in the above example the ``users_data`` variable is a python list
holding python dictionary, and the dictionary has keys like ``name``,
``initials`` and ``email``.
The users are created from the ``config.py`` file to be able to add them
easly (the UI will be written in next versions).
**Config Variables**
TODO: Add explanation of each variable
Variables which can be set in ``config.py`` as follows::
database_url = "sqlite:///$REPO/project_manager.db"
shot_number_prefix = "SH"
shot_number_padding = 3
rev_number_prefix = "r"
rev_number_padding = 2
ver_number_prefix = "v"
ver_number_padding = 3
default_fps = 25
default_take_name = "MAIN"
users_data = [{"name": "Administrator", "initials": "adm"}]
repository_env_key = "REPO"
repository = [
{
"name": "Default",
"windows_path": "~/Projects",
"linux_path": "~/Projects",
"osx_path": "~/Projects"
}
]
environments = [
{
"name":"Maya",
"extensions":["ma", "mb"]
},
{
"name":"Houdini",
"extensions":["hip"]
},
{
"name":"Nuke",
"extensions": ["nk"]
},
{
"name":"Photoshop",
"extensions": ["psd", "pdd"],
"export_extensions": ["tif", "tga", "bmp", "jpg", "iff"]
},
{
"name":"3DEqualizer",
"extensions": ["3te"]
},
{
"name":"Fusion",
"extensions": ["comp"]
}
]
resolution_presets = {
"PC Video": [640, 480, 1.0],
"NTSC": [720, 486, 0.91],
"NTSC 16:9": [720, 486, 1.21],
"PAL": [720, 576, 1.067],
"PAL 16:9": [720, 576, 1.46],
"HD 720": [1280, 720, 1.0],
"HD 1080": [1920, 1080, 1.0],
"1K Super 35": [1024, 778, 1.0],
"2K Super 35": [2048, 1556, 1.0],
"4K Super 35": [4096, 3112, 1.0],
"A4 Portrait": [2480, 3508, 1.0],
"A4 Landscape": [3508, 2480, 1.0],
"A3 Portrait": [3508, 4960, 1.0],
"A3 Landscape": [4960, 3508, 1.0],
"A2 Portrait": [4960, 7016, 1.0],
"A2 Landscape": [7016, 4960, 1.0],
"50x70cm Poster Portrait": [5905, 8268, 1.0],
"50x70cm Poster Landscape": [8268, 5905, 1.0],
"70x100cm Poster Portrait": [8268, 11810, 1.0],
"70x100cm Poster Landscape": [11810, 8268, 1.0],
"1k Square": [1024, 1024, 1.0],
"2K Square": [2048, 2048, 1.0],
"3K Square": [3072, 3072, 1.0],
"4K Square": [4096, 4096, 1.0],
}
default_resolution_preset = "HD 1080"
project_structure = "" # TODO: fix this
version_types = "" # TODO: fix this
"""
default_config_values = dict(
database_url = "sqlite:///$OYPROJECTMANAGER_PATH/project_manager.db",
status_list = [
'WTS',
'WIP',
'REV',
'APP',
'CMP'
],
status_list_long_names = [
'Waiting To Start',
'Work In Progress',
'For Review',
'Approved',
'Completed'
],
status_bg_colors = [
[192, 80, 77], #WTS
[255, 192, 0], #WIP
[ 89, 141, 213], #REV
[155, 187, 89], #APP
[155, 187, 89], #CMP
],
status_fg_colors = [
[255, 255, 255], #WTS
[ 0, 0, 0], #WIP
[ 0, 0, 0], #REV
[ 0, 0, 0], #APP
[ 0, 0, 0], #CMP
],
shot_number_prefix = "SH",
shot_number_padding = 3,
rev_number_prefix = "r",
rev_number_padding = 2,
ver_number_prefix = "v",
ver_number_padding = 3,
default_fps = 25,
default_asset_type_name = "Generic",
default_take_name = "MAIN",
users_data = [{"name": "Administrator", "initials": "adm"}],
# just use one repository for now
repository_env_key = "REPO",
repository = {
"name": "Default",
"windows_path": "~/Projects",
"linux_path": "~/Projects",
"osx_path": "~/Projects"
},
file_size_format = "%.2f MB",
time_format = '%d.%m.%Y %H:%M',
environments = [
{
"name":"Maya",
"extensions":["ma", "mb"]
},
{
"name":"Houdini",
"extensions":["hip"]
},
{
"name":"Nuke",
"extensions": ["nk"],
},
{
"name":"Photoshop",
"extensions": ["psd", "pdd"],
"export_extensions": ["tif", "tga", "bmp", "jpg", "iff"],
},
{
"name":"3DEqualizer",
"extensions": ["3te"]
},
{
"name":"Fusion",
"extensions": ["comp"]
}
],
resolution_presets = {
"PC Video": [640, 480, 1.0],
"NTSC": [720, 486, 0.91],
"NTSC 16:9": [720, 486, 1.21],
"PAL": [720, 576, 1.067],
"PAL 16:9": [720, 576, 1.46],
"HD 720": [1280, 720, 1.0],
"HD 1080": [1920, 1080, 1.0],
"1K Super 35": [1024, 778, 1.0],
"2K Super 35": [2048, 1556, 1.0],
"4K Super 35": [4096, 3112, 1.0],
"A4 Portrait": [2480, 3508, 1.0],
"A4 Landscape": [3508, 2480, 1.0],
"A3 Portrait": [3508, 4960, 1.0],
"A3 Landscape": [4960, 3508, 1.0],
"A2 Portrait": [4960, 7016, 1.0],
"A2 Landscape": [7016, 4960, 1.0],
"50x70cm Poster Portrait": [5905, 8268, 1.0],
"50x70cm Poster Landscape": [8268, 5905, 1.0],
"70x100cm Poster Portrait": [8268, 11810, 1.0],
"70x100cm Poster Landscape": [11810, 8268, 1.0],
"1k Square": [1024, 1024, 1.0],
"2k Square": [2048, 2048, 1.0],
"3k Square": [3072, 3072, 1.0],
"4k Square": [4096, 4096, 1.0],
},
default_resolution_preset = "HD 1080",
project_structure = """{% for sequence in project.sequences %}
{% set seq_path = project.full_path + '/Sequences/' + sequence.code %}
{{seq_path}}/Edit/Offline
{{seq_path}}/Edit/Sound
{{seq_path}}/References/Artworks
{{seq_path}}/References/Text/Scenario
{{seq_path}}/References/Photos_Images
{{seq_path}}/References/Videos
{{seq_path}}/References/Others
{% for shot in sequence.shots %}
{{seq_path}}/Shots/{{shot.code}}
{{seq_path}}/Shots/{{shot.code}}/Plate
{{seq_path}}/Shots/{{shot.code}}/Ref
{{seq_path}}/Shots/{{shot.code}}/Texture
{% endfor %}
{% endfor %}
""",
asset_thumbnail_path = "{{project.code}}/Assets/{{asset.type}}/{{asset.code}}/Thumbnail",
asset_thumbnail_filename = "{{asset.code}}_thumbnail.{{extension}}",
shot_thumbnail_path = "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{shot.code}}/Thumbnail",
shot_thumbnail_filename = "{{shot.code}}_thumbnail.{{extension}}",
thumbnail_format = "jpg",
thumbnail_quality = 70,
thumbnail_size = [320, 180],
version_types = [
{
"name": "Animation",
"code": "Anim",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Shot"
},
{
"name": "Camera",
"code": "Cam",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Shot"
},
{
"name": "Composition",
"code": "Comp",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Nuke", "Fusion"],
"type_for": "Shot"
},
# {
# "name": "Edit",
# "code": "Edit",
# "path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
# "filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
# "output_path": "{{version._path}}/Output/{{version.take_name}}",
# "extra_folders": "",
# "environments": ["Nuke", "Fusion"],
# "type_for": "Shot"
# },
{
"name":"FX",
"code": "FX",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": """{{version.path}}/anim
{{version.path}}/cache
{{version.path}}/exports
{{version.path}}/fx_scenes
{{version.path}}/maps
{{version.path}}/misc
{{version.path}}/obj
{{version.path}}/sim_in
{{version.path}}/sim_out""",
"environments": ["Maya", "Houdini"],
"type_for": "Shot"
},
{
"name":"Model",
"code": "Model",
"path": "{{project.code}}/Assets/{{asset.type}}/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Asset"
},
{
"name": "Other",
"code": "Other",
"path": "{{project.code}}/Assets/{{asset.type}}/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini", "Nuke", "Fusion", "Photoshop"],
"type_for": "Asset"
},
{
"name": "Previs",
"code": "Previs",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Shot"
},
{
"name": "Lighting",
"code": "Lighting",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Shot"
},
{
"name": "Rig",
"code": "Rig",
"path": "{{project.code}}/Assets/{{asset.type}}/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Asset"
},
{
"name": "Roto",
"code": "Roto",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Nuke", "Fusion"],
"type_for": "Shot"
},
{
"name": "Scene Assembly",
"code": "SceneAss",
"path":"{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Shot"
},
{
"name": "Matte",
"code": "Matte",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Photoshop"],
"type_for": "Shot"
},
{
"name": "Texture",
"code": "Texture",
"path": "{{project.code}}/Assets/{{asset.type}}/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Photoshop", "Nuke", "Fusion"],
"type_for": "Asset",
},
{
"name": "Illustration",
"code": "Illust",
"path": "{{project.code}}/Assets/{{asset.type}}/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Photoshop"],
"type_for": "Asset"
},
{
"name": "Look Development",
"code": "LookDev",
"path": "{{project.code}}/Assets/{{asset.type}}/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["Maya", "Houdini"],
"type_for": "Asset"
},
{
"name": "Match Move",
"code": "MM",
"path": "{{project.code}}/Sequences/{{sequence.code}}/Shots/{{version.base_name}}/{{type.code}}",
"filename": "{{version.base_name}}_{{version.take_name}}_{{type.code}}_v{{'%03d'|format(version.version_number)}}_{{version.created_by.initials}}{{version.extension}}",
"output_path": "{{version._path}}/Output/{{version.take_name}}",
"extra_folders": "",
"environments": ["3DEqualizer"],
"type_for": "Shot"
}
]
)
[docs] def __init__(self):
self.config_values = Config.default_config_values.copy()
self.user_config = {}
# the priority order is
# oyProjectManager.config
# config.py under .oyrc directory
# config.py under $OYPROJECTMANAGER_PATH
self._parse_settings()
def _parse_settings(self):
# for now just use $OYPROJECTMANAGER_PATH
ENV_KEY = "OYPROJECTMANAGER_PATH"
# try to get the environment variable
if not os.environ.has_key(ENV_KEY):
# don't do anything
logger.debug("no environment key found for user settings")
else:
logger.debug("environment key found")
resolved_path = os.path.expanduser(
os.path.join(
os.environ[ENV_KEY],
"config.py"
)
)
# using `while` is not safe to expand variables
# do the expansion for 5 times which is complex enough
# and I don't (hopefully) expect anybody to use
# more than 5 level deep environment variables
resolved_path = os.path.expandvars(
os.path.expandvars(
os.path.expandvars(
os.path.expandvars(
resolved_path
)
)
)
)
try:
try:
logger.debug("importing user config")
execfile(resolved_path, self.user_config)
except SyntaxError, err:
raise RuntimeError("There is a syntax error in your "
"configuration file: " + str(err))
# append the data to the current settings
logger.debug("updating system config")
for key in self.user_config:
if key in self.config_values:
self.config_values[key] = self.user_config[key]
except IOError:
logger.warning("The $OYPROJETMANAGER_PATH:" + resolved_path + \
" doesn't exists! skipping user config")
def __getattr__(self, name):
return self.config_values[name]
def __getitem__(self, name):
return getattr(self, name)
def __setitem__(self, name, value):
return setattr(self, name, value)
def __delitem__(self, name):
delattr(self, name)
def __contains__(self, name):
return name in self.config_values
@property
def last_user_id(self):
"""returns the last user id
It is not very much related with the config.py and user settings, but
it seems the most appropriate place is this one to get information from
individual users.
This should work fairly fast, because it uses the local filesystem not
the network thus the fileserver.
"""
# TODO: This should be replaced with beaker.session
file_name = 'last_user_id'
file_path = os.path.expanduser("~/.oypmrc/")
file_full_path = os.path.join(file_path, file_name)
last_user_id = None
try:
last_user_file = open(file_full_path)
except IOError:
pass
else:
last_user_id = int(last_user_file.readline().strip())
last_user_file.close()
return last_user_id
@last_user_id.setter
[docs] def last_user_id(self, user_id):
"""sets the user id for the last user
"""
if not isinstance(user_id, int):
raise RuntimeWarning("user_id for last_user_id should be an int")
return
file_name = 'last_user_id'
file_path = os.path.expanduser("~/.oypmrc/")
file_full_path = os.path.join(file_path, file_name)
logger.debug("saving user id to %s" % file_full_path)
# create the folder first
try:
os.makedirs(file_path)
except OSError:
# already created
pass
try:
last_user_file = open(file_full_path, 'w')
except IOError as e:
pass
else:
last_user_file.write(str(user_id))
last_user_file.close()