├── dpa ├── ui │ ├── __init__.py │ ├── app │ │ ├── __init__.py │ │ ├── session.py │ │ └── entity │ │ │ └── __init__.py │ ├── dk │ │ └── __init__.py │ ├── icon │ │ ├── __init__.py │ │ └── factory.py │ ├── mari │ │ ├── __init__.py │ │ └── importprod.py │ ├── maya │ │ ├── __init__.py │ │ ├── export.py │ │ └── importref.py │ ├── action │ │ └── __init__.py │ ├── notify │ │ ├── __init__.py │ │ ├── fail.py │ │ └── ooto.py │ ├── product │ │ └── __init__.py │ ├── ptask │ │ ├── __init__.py │ │ └── version.py │ └── example │ │ ├── hello.py │ │ └── goodbye.py ├── app │ ├── __init__.py │ ├── cli.py │ └── server.py ├── ffmpeg │ ├── __init__.py │ └── action.py ├── houdini │ ├── __init__.py │ ├── entity │ │ └── geomcache.py │ ├── pyqt_houdini.py │ └── session.py ├── mari │ ├── __init__.py │ ├── entity │ │ └── __init__.py │ └── shelf.py ├── maya │ ├── __init__.py │ ├── entity │ │ ├── __init__.py │ │ ├── geom.py │ │ ├── camera.py │ │ ├── workfile.py │ │ └── geomcache.py │ └── shelf.py ├── nuke │ ├── __init__.py │ ├── scripts │ │ ├── __init__.py │ │ └── nukeWrangling.py │ ├── utils.py │ └── session.py ├── open │ └── __init__.py ├── shell │ └── __init__.py ├── stats │ └── __init__.py ├── sync │ └── __init__.py ├── user │ ├── tests │ │ └── __init__.py │ └── action │ │ ├── __init__.py │ │ └── info.py ├── location │ ├── tests │ │ └── __init__.py │ ├── action │ │ ├── __init__.py │ │ └── info.py │ └── __init__.py ├── notify │ ├── action │ │ ├── __init__.py │ │ ├── ooto.py │ │ ├── base.py │ │ └── fail.py │ └── __init__.py ├── product │ ├── action │ │ └── __init__.py │ ├── grouping │ │ └── __init__.py │ ├── subscription │ │ └── action │ │ │ └── __init__.py │ ├── category │ │ └── __init__.py │ └── representation │ │ └── status.py ├── ptask │ ├── action │ │ ├── __init__.py │ │ ├── source.py │ │ └── complete.py │ ├── env.py │ ├── cli │ │ └── __init__.py │ └── history.py ├── data │ ├── config │ │ ├── maya │ │ │ ├── maps │ │ │ │ ├── shaders.cfg │ │ │ │ ├── import_tif.cfg │ │ │ │ ├── import_ptx.cfg │ │ │ │ ├── import_tex.cfg │ │ │ │ └── import_tx.cfg │ │ │ ├── prman.cfg │ │ │ ├── camera │ │ │ │ ├── import_fbx.cfg │ │ │ │ ├── export.cfg │ │ │ │ └── import_ma.cfg │ │ │ ├── geom │ │ │ │ └── export.cfg │ │ │ ├── workfile │ │ │ │ ├── import_fbx.cfg │ │ │ │ ├── export.cfg │ │ │ │ └── import_ma.cfg │ │ │ ├── geomcache │ │ │ │ ├── import_abc.cfg │ │ │ │ └── export.cfg │ │ │ └── shelves.cfg │ │ ├── mari │ │ │ ├── geom │ │ │ │ ├── import_obj.cfg │ │ │ │ └── channels.cfg │ │ │ ├── shelves.cfg │ │ │ └── maps │ │ │ │ └── export.cfg │ │ ├── ptask │ │ │ └── sync.cfg │ │ ├── ui │ │ │ └── actions │ │ │ │ ├── ooto.cfg │ │ │ │ ├── ptask │ │ │ │ └── version.cfg │ │ │ │ ├── fail.cfg │ │ │ │ └── examples.cfg │ │ ├── restful │ │ │ └── urls.cfg │ │ ├── project │ │ │ └── master.cfg │ │ ├── nuke │ │ │ └── toolbars.cfg │ │ └── houdini │ │ │ └── shelves.cfg │ ├── images │ │ ├── logos │ │ │ ├── dk_full.png │ │ │ └── dk_logo.png │ │ └── icons │ │ │ ├── dir_32x32.png │ │ │ ├── dir_64x64.png │ │ │ ├── dk_32x32.png │ │ │ ├── dk_64x64.png │ │ │ ├── edit_32x32.png │ │ │ ├── edit_64x64.png │ │ │ ├── ooto_32x32.png │ │ │ ├── ooto_64x64.png │ │ │ ├── sub_32x32.png │ │ │ ├── sub_64x64.png │ │ │ ├── export_32x32.png │ │ │ ├── export_64x64.png │ │ │ ├── import_32x32.png │ │ │ ├── import_64x64.png │ │ │ ├── playblast_32x32.png │ │ │ ├── playblast_64x64.png │ │ │ ├── version_32x32.png │ │ │ ├── version_64x64.png │ │ │ ├── warning_32x32.png │ │ │ └── warning_64x64.png │ ├── bash │ │ └── README │ └── plugins │ │ ├── maya │ │ └── pipe_shelf.py │ │ ├── mari │ │ ├── pipe_shelf.py │ │ └── init.py │ │ └── nuke │ │ ├── menu.py │ │ └── init.py ├── __init__.py ├── singleton │ └── __init__.py ├── cli │ ├── action │ │ └── __init__.py │ └── __init__.py ├── imgres │ └── test │ │ └── test_imgres.py ├── queue │ └── __init__.py ├── restful │ └── __init__.py └── logging │ └── __init__.py ├── dpa_site ├── __init__.py └── sitecustomize.py ├── MANIFEST.in ├── docs └── source │ ├── python │ ├── modules.rst │ ├── dpa.env.vars.rst │ ├── dpa.ptask.env.rst │ ├── dpa.ptask.spec.rst │ ├── dpa.shell.output.rst │ ├── dpa.ptask.workspace.rst │ ├── dpa.restful.config.rst │ ├── dpa.shell.formatter.rst │ ├── dpa_site.sitecustomize.rst │ ├── dpa.cache.rst │ ├── dpa.user.tests.test_user.rst │ ├── dpa_site.dpa_import_hook.rst │ ├── dpa.config.rst │ ├── dpa.frange.rst │ ├── dpa.imgres.rst │ ├── dpa.argparse.rst │ ├── dpa.ptask.version.rst │ ├── dpa.location.tests.test_location.rst │ ├── dpa.ptask.assignment.rst │ ├── dpa.env.rst │ ├── dpa.user.rst │ ├── dpa.restful.rst │ ├── dpa.location.rst │ ├── dpa.shell.rst │ ├── dpa.user.tests.rst │ ├── dpa_site.rst │ ├── dpa.location.tests.rst │ ├── dpa.rst │ └── dpa.ptask.rst │ ├── templates │ ├── modules.rst │ ├── tool_template.rst │ ├── module_template.rst │ ├── tests.test_suite_template.rst │ └── tests.rst │ └── index.rst ├── tox.ini ├── requirements.txt ├── .gitignore ├── bin ├── dpa_mari_server ├── dpa_houdini_server ├── dpa_maya_server ├── dpa_houdini ├── dpa_uncompress ├── dpa_ribrender └── dpa ├── LICENSE └── setup.py /dpa/ui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ffmpeg/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/houdini/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/mari/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/maya/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/nuke/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/open/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/shell/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/stats/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/sync/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/dk/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/icon/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/mari/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/maya/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa_site/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/mari/entity/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/maya/entity/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/nuke/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/action/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/notify/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/product/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ui/ptask/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/user/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/location/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/notify/action/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/product/action/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/product/grouping/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/ptask/action/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dpa/data/config/maya/maps/shaders.cfg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dpa/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.110' 2 | -------------------------------------------------------------------------------- /dpa/product/subscription/action/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include dpa/data * 2 | -------------------------------------------------------------------------------- /dpa/user/action/__init__.py: -------------------------------------------------------------------------------- 1 | from .info import * 2 | -------------------------------------------------------------------------------- /dpa/location/action/__init__.py: -------------------------------------------------------------------------------- 1 | from .info import * 2 | -------------------------------------------------------------------------------- /dpa/data/config/maya/prman.cfg: -------------------------------------------------------------------------------- 1 | 2 | woff: ['NO2013'] 3 | 4 | -------------------------------------------------------------------------------- /dpa/data/images/logos/dk_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/logos/dk_full.png -------------------------------------------------------------------------------- /dpa/data/images/logos/dk_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/logos/dk_logo.png -------------------------------------------------------------------------------- /docs/source/python/modules.rst: -------------------------------------------------------------------------------- 1 | python 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 10 6 | 7 | dpa 8 | dpa_site 9 | -------------------------------------------------------------------------------- /dpa/data/images/icons/dir_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/dir_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/dir_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/dir_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/dk_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/dk_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/dk_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/dk_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/edit_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/edit_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/edit_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/edit_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/ooto_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/ooto_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/ooto_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/ooto_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/sub_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/sub_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/sub_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/sub_64x64.png -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27 3 | 4 | [testenv] 5 | commands = {envpython} -m unittest discover -s python 6 | deps = 7 | -------------------------------------------------------------------------------- /dpa/data/images/icons/export_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/export_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/export_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/export_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/import_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/import_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/import_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/import_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/playblast_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/playblast_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/playblast_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/playblast_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/version_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/version_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/version_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/version_64x64.png -------------------------------------------------------------------------------- /dpa/data/images/icons/warning_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/warning_32x32.png -------------------------------------------------------------------------------- /dpa/data/images/icons/warning_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clemson-DPA/dpa-pipe/HEAD/dpa/data/images/icons/warning_64x64.png -------------------------------------------------------------------------------- /dpa/ui/maya/export.py: -------------------------------------------------------------------------------- 1 | 2 | from PySide import QtGui, QtCore 3 | 4 | # ----------------------------------------------------------------------------- 5 | -------------------------------------------------------------------------------- /docs/source/templates/modules.rst: -------------------------------------------------------------------------------- 1 | python 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 10 6 | 7 | module_template 8 | tests 9 | tool_template 10 | -------------------------------------------------------------------------------- /docs/source/python/dpa.env.vars.rst: -------------------------------------------------------------------------------- 1 | dpa.env.vars module 2 | =================== 3 | 4 | .. automodule:: dpa.env.vars 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.ptask.env.rst: -------------------------------------------------------------------------------- 1 | dpa.ptask.env module 2 | ==================== 3 | 4 | .. automodule:: dpa.ptask.env 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.ptask.spec.rst: -------------------------------------------------------------------------------- 1 | dpa.ptask.spec module 2 | ===================== 3 | 4 | .. automodule:: dpa.ptask.spec 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/templates/tool_template.rst: -------------------------------------------------------------------------------- 1 | tool_template module 2 | ==================== 3 | 4 | .. automodule:: tool_template 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.shell.output.rst: -------------------------------------------------------------------------------- 1 | dpa.shell.output module 2 | ======================= 3 | 4 | .. automodule:: dpa.shell.output 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/templates/module_template.rst: -------------------------------------------------------------------------------- 1 | module_template module 2 | ====================== 3 | 4 | .. automodule:: module_template 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.ptask.workspace.rst: -------------------------------------------------------------------------------- 1 | dpa.ptask.workspace module 2 | ========================== 3 | 4 | .. automodule:: dpa.ptask.workspace 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.restful.config.rst: -------------------------------------------------------------------------------- 1 | dpa.restful.config module 2 | ========================= 3 | 4 | .. automodule:: dpa.restful.config 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.shell.formatter.rst: -------------------------------------------------------------------------------- 1 | dpa.shell.formatter module 2 | ========================== 3 | 4 | .. automodule:: dpa.shell.formatter 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /dpa/data/config/mari/geom/import_obj.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | force_ptex: 6 | 7 | type: bool 8 | default: False 9 | help: Force Ptex over UV. 10 | label: Force Ptex 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cinesync==1.0 2 | colorama==0.2.7 3 | ordereddict==1.1 4 | parsedatetime==1.4 5 | python-dateutil==2.2 6 | PyYAML==3.10 7 | requests==2.2 8 | rpyc==3.3.0 9 | Sphinx==1.2 10 | sphinx-rtd-theme==0.1 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa_site.sitecustomize.rst: -------------------------------------------------------------------------------- 1 | dpa_site.sitecustomize module 2 | ============================= 3 | 4 | .. automodule:: dpa_site.sitecustomize 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.cache.rst: -------------------------------------------------------------------------------- 1 | dpa.cache package 2 | ================= 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: dpa.cache 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa.user.tests.test_user.rst: -------------------------------------------------------------------------------- 1 | dpa.user.tests.test_user module 2 | =============================== 3 | 4 | .. automodule:: dpa.user.tests.test_user 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa_site.dpa_import_hook.rst: -------------------------------------------------------------------------------- 1 | dpa_site.dpa_import_hook module 2 | =============================== 3 | 4 | .. automodule:: dpa_site.dpa_import_hook 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.config.rst: -------------------------------------------------------------------------------- 1 | dpa.config package 2 | ================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: dpa.config 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa.frange.rst: -------------------------------------------------------------------------------- 1 | dpa.frange package 2 | ================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: dpa.frange 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa.imgres.rst: -------------------------------------------------------------------------------- 1 | dpa.imgres package 2 | ================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: dpa.imgres 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa.argparse.rst: -------------------------------------------------------------------------------- 1 | dpa.argparse package 2 | ==================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: dpa.argparse 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/templates/tests.test_suite_template.rst: -------------------------------------------------------------------------------- 1 | tests.test_suite_template module 2 | ================================ 3 | 4 | .. automodule:: tests.test_suite_template 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /dpa/data/config/ptask/sync.cfg: -------------------------------------------------------------------------------- 1 | excludes: [ 2 | "/cheesyq", 3 | "/import", 4 | "/products", 5 | "/.ptask_type", 6 | "/.[0-9]*/", 7 | "/houdini/cache", 8 | "/houdini/backup", 9 | "/maya/project/*", 10 | ] 11 | 12 | -------------------------------------------------------------------------------- /dpa/data/config/ui/actions/ooto.cfg: -------------------------------------------------------------------------------- 1 | 2 | options: 3 | 4 | message: 5 | type: text 6 | label: Message 7 | default: "" 8 | help: Please enter a detailed OOTO message 9 | required: True 10 | 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa.ptask.version.rst: -------------------------------------------------------------------------------- 1 | dpa.ptask.version package 2 | ========================= 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: dpa.ptask.version 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa.location.tests.test_location.rst: -------------------------------------------------------------------------------- 1 | dpa.location.tests.test_location module 2 | ======================================= 3 | 4 | .. automodule:: dpa.location.tests.test_location 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/python/dpa.ptask.assignment.rst: -------------------------------------------------------------------------------- 1 | dpa.ptask.assignment package 2 | ============================ 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: dpa.ptask.assignment 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /dpa/data/config/maya/camera/import_fbx.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | reference: 6 | type: bool 7 | default: True 8 | help: Create a reference to the fbx. 9 | label: "Create reference" 10 | 11 | -------------------------------------------------------------------------------- /dpa/data/config/maya/geom/export.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | obj_export: 6 | 7 | type: bool 8 | default: True 9 | help: Export the entity as an obj file. 10 | label: "Export to OBJ file" 11 | 12 | -------------------------------------------------------------------------------- /dpa/data/config/maya/workfile/import_fbx.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | reference: 6 | type: bool 7 | default: True 8 | help: Create a reference to the fbx. 9 | label: "Create reference" 10 | 11 | -------------------------------------------------------------------------------- /dpa/data/config/maya/workfile/export.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | bake_references: 6 | type: bool 7 | default: True 8 | help: Bake file references into the exported workfile product 9 | label: "Bake References" 10 | 11 | -------------------------------------------------------------------------------- /docs/source/python/dpa.env.rst: -------------------------------------------------------------------------------- 1 | dpa.env package 2 | =============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | dpa.env.vars 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: dpa.env 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Ignore compiled python files 3 | *.py[cod] 4 | 5 | # Ignore vim temporary and swap files 6 | *~ 7 | [._]*.s[a-w][a-z] 8 | [._]s[a-w][a-z] 9 | 10 | # Documentation build 11 | docs/build 12 | 13 | # Setuptools artifacts 14 | *.egg 15 | *.egg-info 16 | dist/* 17 | 18 | # Tox artifacts 19 | .tox 20 | -------------------------------------------------------------------------------- /docs/source/python/dpa.user.rst: -------------------------------------------------------------------------------- 1 | dpa.user package 2 | ================ 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | dpa.user.tests 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: dpa.user 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /docs/source/templates/tests.rst: -------------------------------------------------------------------------------- 1 | tests package 2 | ============= 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | tests.test_suite_template 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: tests 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /dpa/data/config/ui/actions/ptask/version.cfg: -------------------------------------------------------------------------------- 1 | 2 | options: 3 | 4 | description: 5 | type: text 6 | default: "" 7 | help: Please describe the work done in this version. 8 | label: "Describe the work done in this version" 9 | required: True 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /dpa/data/config/maya/geomcache/import_abc.cfg: -------------------------------------------------------------------------------- 1 | 2 | global: 3 | 4 | options: 5 | 6 | merge_under_selection: 7 | 8 | type: bool 9 | default: True 10 | help: Merge the alembic cache under the current selection. 11 | label: "Merge under selection" 12 | 13 | -------------------------------------------------------------------------------- /docs/source/python/dpa.restful.rst: -------------------------------------------------------------------------------- 1 | dpa.restful package 2 | =================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | dpa.restful.config 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: dpa.restful 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /docs/source/python/dpa.location.rst: -------------------------------------------------------------------------------- 1 | dpa.location package 2 | ==================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | dpa.location.tests 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: dpa.location 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /docs/source/python/dpa.shell.rst: -------------------------------------------------------------------------------- 1 | dpa.shell package 2 | ================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | dpa.shell.formatter 10 | dpa.shell.output 11 | 12 | Module contents 13 | --------------- 14 | 15 | .. automodule:: dpa.shell 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/source/python/dpa.user.tests.rst: -------------------------------------------------------------------------------- 1 | dpa.user.tests package 2 | ====================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | dpa.user.tests.test_user 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: dpa.user.tests 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /docs/source/python/dpa_site.rst: -------------------------------------------------------------------------------- 1 | dpa_site package 2 | ================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | dpa_site.dpa_import_hook 10 | dpa_site.sitecustomize 11 | 12 | Module contents 13 | --------------- 14 | 15 | .. automodule:: dpa_site 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/source/python/dpa.location.tests.rst: -------------------------------------------------------------------------------- 1 | dpa.location.tests package 2 | ========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | dpa.location.tests.test_location 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: dpa.location.tests 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /bin/dpa_mari_server: -------------------------------------------------------------------------------- 1 | #!/opt/foundry/mari2/mari 2 | 3 | import sys 4 | 5 | from dpa.app.cli import ClApp 6 | 7 | # ------------------------------------------------------------------------------ 8 | class ClMari(ClApp): 9 | 10 | name = 'clmari' 11 | 12 | # ------------------------------------------------------------------------------ 13 | if __name__ == "__main__": 14 | sys.exit(ClMari.cli()) 15 | 16 | -------------------------------------------------------------------------------- /bin/dpa_houdini_server: -------------------------------------------------------------------------------- 1 | #!/opt/hfs13/bin/hython 2 | 3 | import sys 4 | 5 | from dpa.app.cli import ClApp 6 | 7 | # ------------------------------------------------------------------------------ 8 | class ClHoudini(ClApp): 9 | 10 | name = 'clhoudini' 11 | 12 | # ------------------------------------------------------------------------------ 13 | if __name__ == "__main__": 14 | sys.exit(ClHoudini.cli()) 15 | 16 | -------------------------------------------------------------------------------- /dpa/data/config/restful/urls.cfg: -------------------------------------------------------------------------------- 1 | default: 2 | create: [POST, "http://{server}/api/{data_type}/.{data_format}"] 3 | delete: [DELETE, "http://{server}/api/{data_type}/{primary_key}/.{data_format}"] 4 | get: [GET, "http://{server}/api/{data_type}/{primary_key}/.{data_format}"] 5 | list: [GET, "http://{server}/api/{data_type}/.{data_format}"] 6 | update: [PUT, "http://{server}/api/{data_type}/{primary_key}/.{data_format}"] 7 | 8 | -------------------------------------------------------------------------------- /dpa/data/config/ui/actions/fail.cfg: -------------------------------------------------------------------------------- 1 | 2 | options: 3 | 4 | subject: 5 | type: str 6 | label: Subject 7 | default: "" 8 | help: Please enter a subject for this FAIL 9 | required: True 10 | 11 | message: 12 | type: text 13 | label: Message 14 | default: "" 15 | help: Please enter a detailed message describing this FAIL 16 | required: True 17 | 18 | -------------------------------------------------------------------------------- /dpa/data/config/project/master.cfg: -------------------------------------------------------------------------------- 1 | 2 | hierarchy: 3 | 4 | none : [project] 5 | project: [phase, template] 6 | phase: [build, shot] 7 | build: [stage] 8 | shot: [stage] 9 | stage: [work] 10 | template: [project, build] 11 | 12 | templates: 13 | 14 | project: [ 15 | templates=project_anim_short, 16 | ] 17 | 18 | build: [ 19 | templates=build_rig, 20 | templates=build_no_rig, 21 | ] 22 | 23 | -------------------------------------------------------------------------------- /dpa/data/config/maya/camera/export.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | ma_export: 6 | 7 | type: bool 8 | default: True 9 | help: Export the camera as a maya file. 10 | label: "Export to Maya file" 11 | 12 | fbx_export: 13 | 14 | type: bool 15 | default: False 16 | help: Export the camera as an fbx file. 17 | label: "Export to FBX file" 18 | 19 | -------------------------------------------------------------------------------- /docs/source/python/dpa.rst: -------------------------------------------------------------------------------- 1 | dpa package 2 | =========== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | dpa.argparse 10 | dpa.cache 11 | dpa.config 12 | dpa.env 13 | dpa.frange 14 | dpa.imgres 15 | dpa.location 16 | dpa.ptask 17 | dpa.restful 18 | dpa.shell 19 | dpa.user 20 | 21 | Module contents 22 | --------------- 23 | 24 | .. automodule:: dpa 25 | :members: 26 | :undoc-members: 27 | :show-inheritance: 28 | -------------------------------------------------------------------------------- /docs/source/python/dpa.ptask.rst: -------------------------------------------------------------------------------- 1 | dpa.ptask package 2 | ================= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | dpa.ptask.assignment 10 | dpa.ptask.version 11 | 12 | Submodules 13 | ---------- 14 | 15 | .. toctree:: 16 | 17 | dpa.ptask.env 18 | dpa.ptask.spec 19 | dpa.ptask.workspace 20 | 21 | Module contents 22 | --------------- 23 | 24 | .. automodule:: dpa.ptask 25 | :members: 26 | :undoc-members: 27 | :show-inheritance: 28 | -------------------------------------------------------------------------------- /bin/dpa_maya_server: -------------------------------------------------------------------------------- 1 | #!/usr/autodesk/maya2014-x64/bin/mayapy 2 | 3 | import sys 4 | 5 | import maya.standalone 6 | maya.standalone.initialize() 7 | 8 | from dpa.app.cli import ClApp 9 | 10 | # ------------------------------------------------------------------------------ 11 | class ClMaya(ClApp): 12 | 13 | name = 'clmaya' 14 | 15 | # ------------------------------------------------------------------------------ 16 | if __name__ == "__main__": 17 | sys.exit(ClMaya.cli()) 18 | 19 | -------------------------------------------------------------------------------- /dpa_site/sitecustomize.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # The dpa import hook creates special behavior for python imports within the 4 | # dpa namespace. It allows python packages to be spread out across the 5 | # production hierarchy. This makes it possible to test python modules at the 6 | # shot level before forcing the code onto the entire production. Because the 7 | # dpa_import hook is limited to the 'dpa' namespace, it will not affect 8 | # built-in or 3rd party python package imports. 9 | import dpa_import_hook 10 | 11 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. DPA Pipeline documentation master file, created by 2 | sphinx-quickstart on Wed Jun 11 11:51:09 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to DPA Pipeline's documentation! 7 | ======================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | Python API 15 | Python Templates 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | 24 | -------------------------------------------------------------------------------- /dpa/data/config/maya/maps/import_tif.cfg: -------------------------------------------------------------------------------- 1 | 2 | global: 3 | 4 | options: 5 | 6 | linearize: 7 | type: bool 8 | default: False 9 | help: Linearize (Apply SRGB) 10 | label: "Linearize" 11 | 12 | disable_file_load: 13 | type: bool 14 | default: True 15 | help: Disable texture file load 16 | label: Disable file load 17 | 18 | connect: 19 | type: bool 20 | default: True 21 | help: Connect the shaders with the corresponding file node 22 | label: Connect nodes 23 | 24 | -------------------------------------------------------------------------------- /dpa/data/config/mari/geom/channels.cfg: -------------------------------------------------------------------------------- 1 | channels: 2 | diffuse: 3 | layers: 4 | adjustment: 5 | ColorCorrection: Filter/sRGB2Linear 6 | color: [0.5, 0.5, 0.5, 1.0] 7 | depth: 16 8 | specular: 9 | layers: 10 | adjustment: 11 | ColorCorrection: Filter/sRGB2Linear 12 | color: [0.5, 0.5, 0.5, 1.0] 13 | depth: 16 14 | bump: 15 | color: [0.5, 0.5, 0.5, 1.0] 16 | depth: 16 17 | displacement: 18 | color: [0.5, 0.5, 0.5, 1.0] 19 | depth: 16 20 | transparency: 21 | color: [0.0, 0.0, 0.0, 1.0] 22 | depth: 16 23 | -------------------------------------------------------------------------------- /dpa/data/config/maya/camera/import_ma.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | instances: 6 | type: int 7 | default: 1 8 | help: The number of instances to import 9 | label: "Instances" 10 | 11 | instance_start: 12 | type: int 13 | default: 0 14 | help: The instance number to start with when importing multiple instances. The '0' instance will not have an instance number appended. 15 | label: "Instance number start." 16 | 17 | exportable: 18 | type: bool 19 | default: True 20 | help: "Creates an export set during import if set to True." 21 | label: "Exportable." 22 | 23 | -------------------------------------------------------------------------------- /dpa/data/config/maya/workfile/import_ma.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | instances: 6 | type: int 7 | default: 1 8 | help: The number of instances to import 9 | label: "Instances" 10 | 11 | instance_start: 12 | type: int 13 | default: 0 14 | help: The instance number to start with when importing multiple instances. The '0' instance will not have an instance number appended. 15 | label: "Instance number start" 16 | 17 | exportable: 18 | type: bool 19 | default: True 20 | help: "Creates an export set during import if set to True." 21 | label: "Exportable" 22 | 23 | group_reference: 24 | type: bool 25 | default: False 26 | help: "Create a group for all references." 27 | label: "Group references" 28 | 29 | -------------------------------------------------------------------------------- /dpa/singleton/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # ----------------------------------------------------------------------------- 3 | 4 | from threading import Lock 5 | 6 | # ----------------------------------------------------------------------------- 7 | class Singleton(object): 8 | 9 | _instance = None 10 | _initialized = False 11 | 12 | # ------------------------------------------------------------------------- 13 | def __new__(cls): 14 | 15 | lock = Lock() 16 | with lock: 17 | 18 | if cls._instance is None: 19 | cls._instance = super(Singleton, cls).__new__(cls) 20 | 21 | return cls._instance 22 | 23 | # ------------------------------------------------------------------------- 24 | def __init__(self): 25 | 26 | if self.__class__._initialized: 27 | return 28 | 29 | self.init() 30 | self.__class__._initialized = True 31 | 32 | -------------------------------------------------------------------------------- /dpa/data/config/maya/geomcache/export.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | abc_export: 6 | 7 | type: bool 8 | default: True 9 | help: Export the entity as an alembic file. 10 | label: Export to Alembic file 11 | 12 | abc_options: 13 | 14 | type: group 15 | help: Alembic export options 16 | label: Alembic options 17 | open: False 18 | options: 19 | uv_write: 20 | type: bool 21 | default: True 22 | help: Write uvs to the cache 23 | label: UV Write 24 | 25 | world_space: 26 | type: bool 27 | default: True 28 | help: Export the geom 29 | label: World Space 30 | 31 | fbx_export: 32 | 33 | type: bool 34 | default: False 35 | help: Export the entity as an fbx file 36 | label: Export to FBX file 37 | 38 | -------------------------------------------------------------------------------- /dpa/data/config/maya/maps/import_ptx.cfg: -------------------------------------------------------------------------------- 1 | 2 | global: 3 | 4 | options: 5 | 6 | shader: 7 | type: list 8 | default: PtexSurface 9 | help: Shader Options 10 | label: "Pick a shader" 11 | required: True 12 | choices: [PtexSurface] 13 | multiple: False 14 | 15 | rendermanShader: 16 | type: bool 17 | default: True 18 | help: Turn on renderman shader 19 | label: "Use Renderman Shader and attributes" 20 | 21 | linearize: 22 | type: bool 23 | default: False 24 | help: Linearize (Apply SRGB) 25 | label: "Linearize" 26 | 27 | disable_file_load: 28 | type: bool 29 | default: True 30 | help: Disable texture file load 31 | label: Disable file load 32 | 33 | connect: 34 | type: bool 35 | default: True 36 | help: Connect the shaders with the corresponding file node 37 | label: Connect nodes 38 | -------------------------------------------------------------------------------- /dpa/ui/example/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Hello World in pyqt.""" 3 | 4 | # ----------------------------------------------------------------------------- 5 | # Imports: 6 | # ----------------------------------------------------------------------------- 7 | 8 | import sys 9 | from PyQt4 import QtGui 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Classes: 13 | # ----------------------------------------------------------------------------- 14 | class HelloWorldWidget(QtGui.QLabel): 15 | 16 | # ------------------------------------------------------------------------- 17 | def __init__(self, parent=None): 18 | """A label with a greeting.""" 19 | 20 | super(HelloWorldWidget, self).__init__(parent) 21 | 22 | self.setText("Hello World!") 23 | 24 | # ----------------------------------------------------------------------------- 25 | if __name__ == "__main__": 26 | 27 | app = QtGui.QApplication(sys.argv) 28 | win = HelloWorldWidget() 29 | win.show() 30 | sys.exit(app.exec_()) 31 | 32 | -------------------------------------------------------------------------------- /dpa/ptask/env.py: -------------------------------------------------------------------------------- 1 | 2 | from dpa.env import Env 3 | from dpa.env.vars import DpaVars 4 | 5 | class PTaskEnv(Env): 6 | 7 | @classmethod 8 | def current(cls): 9 | 10 | env = PTaskEnv() 11 | env.get() 12 | return env 13 | 14 | def __init__(self): 15 | 16 | super(PTaskEnv, self).__init__() 17 | 18 | # XXX document the accessor names 19 | 20 | # library path 21 | self.add( 22 | DpaVars.path(default=DpaVars.path_base().get()), 23 | name='path' 24 | ) 25 | 26 | self.add( 27 | DpaVars.ld_library_path( 28 | default=DpaVars.ld_library_path_base().get()), 29 | name='ld_library_path' 30 | ) 31 | 32 | self.add( 33 | DpaVars.python_path(default=DpaVars.python_path_base().get()), 34 | name='python_path' 35 | ) 36 | 37 | self.add(DpaVars.ptask_spec(), name='ptask_spec') 38 | 39 | self.add(DpaVars.ptask_path(), name='ptask_path') 40 | 41 | self.add(DpaVars.ptask_version(), name='ptask_version') 42 | 43 | -------------------------------------------------------------------------------- /dpa/data/bash/README: -------------------------------------------------------------------------------- 1 | 2 | The bash file in this directory is used to activate the dpa pipeline in a shell 3 | environment. It can be sourced directly or it can be called from within a bash 4 | function. If you plan to use a virtualenv for your dpa-pipe installation, you 5 | can set the $DPA_VIRTUAL_ENV variable before sourcing this file and it will 6 | activate the virtualenv for you. Also included here is a bash function called 7 | 'pipedown' that will deactivate the pipeline within your shell. 8 | 9 | Example of activating the pipeline in ~/.bashrc: 10 | 11 | ... 12 | export DPA_VIRTUAL_ENV="/path/to/virtualenv" # <<< change path 13 | source __DPA_FILESYSTEM_ROOT__/bash/activate.bash 14 | ... 15 | 16 | Alternatively, you can define a function in your .bashrc to activate: 17 | 18 | ... 19 | export DPA_VIRTUAL_ENV="/path/to/virtualenv" # <<< change path 20 | pipeup () { 21 | # path to the 22 | source __DPA_FILESYSTEM_ROOT__/bash/activate.bash 23 | } 24 | ... 25 | 26 | Then you can type: 27 | 28 | # activate the pipeline 29 | > pipeup 30 | 31 | # deactivate the pipeline 32 | > pipedown 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Clemson University - Digital Production Arts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /dpa/cli/action/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------------------------------------------- 3 | # Imports: 4 | # ---------------------------------------------------------------------------- 5 | 6 | from abc import ABCMeta 7 | import argparse 8 | import sys 9 | 10 | from dpa.action import Action 11 | from dpa.logging import Logger 12 | from dpa.ptask.area import PTaskArea 13 | 14 | # ---------------------------------------------------------------------------- 15 | # Classes: 16 | # ---------------------------------------------------------------------------- 17 | class CommandLineAction(Action): 18 | """A base class for command line actions.""" 19 | 20 | __metaclass__ = ABCMeta 21 | 22 | target_type = "cli" 23 | 24 | # ------------------------------------------------------------------------ 25 | # Methods: 26 | # ------------------------------------------------------------------------ 27 | def log_action(self): 28 | 29 | if not self.__class__.logging: 30 | return 31 | 32 | # log the command 33 | msg = "({s})".format(s=PTaskArea.current().spec) 34 | msg += " " + " ".join(sys.argv) 35 | 36 | self.logger.info(msg) 37 | 38 | -------------------------------------------------------------------------------- /dpa/ui/example/goodbye.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Goodbye World in pyqt.""" 3 | 4 | # ----------------------------------------------------------------------------- 5 | # Imports: 6 | # ----------------------------------------------------------------------------- 7 | 8 | import sys 9 | from PySide import QtCore, QtGui 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Classes: 13 | # ----------------------------------------------------------------------------- 14 | class GoodbyeWorldWidget(QtGui.QPushButton): 15 | """A simple button that quits the application.""" 16 | 17 | # ------------------------------------------------------------------------- 18 | def __init__(self, parent=None): 19 | """Constructor.""" 20 | 21 | super(GoodbyeWorldWidget, self).__init__(parent) 22 | 23 | self.setText("Goodbye World!") 24 | 25 | self.clicked.connect(QtCore.QCoreApplication.quit) 26 | 27 | # ----------------------------------------------------------------------------- 28 | if __name__ == "__main__": 29 | 30 | app = QtGui.QApplication(sys.argv) 31 | win = GoodbyeWorldWidget() 32 | win.show() 33 | sys.exit(app.exec_()) 34 | 35 | -------------------------------------------------------------------------------- /dpa/data/plugins/maya/pipe_shelf.py: -------------------------------------------------------------------------------- 1 | 2 | from dpa.maya.shelf import MayaShelf 3 | from dpa.ptask.area import PTaskArea 4 | 5 | # ----------------------------------------------------------------------------- 6 | 7 | CONFIG_FILE = 'config/maya/shelves.cfg' 8 | INITIALIZED_SHELVES = [] 9 | 10 | # ----------------------------------------------------------------------------- 11 | def initializePlugin(mobject): 12 | 13 | # read the config, get all the shelf definitions 14 | ptask_area = PTaskArea.current() 15 | maya_shelves_config = ptask_area.config( 16 | config_file=CONFIG_FILE, composite_ancestors=True) 17 | 18 | for (shelf_name, shelf_config) in maya_shelves_config.iteritems(): 19 | 20 | shelf = MayaShelf(shelf_name) 21 | if shelf.exists: 22 | shelf.delete() 23 | 24 | shelf.create() 25 | 26 | # add all the shelf buttons 27 | for (button_key, button_config) in shelf_config.iteritems(): 28 | shelf.add_button(**button_config) 29 | 30 | INITIALIZED_SHELVES.append(shelf) 31 | 32 | # ----------------------------------------------------------------------------- 33 | def uninitializePlugin(mobject): 34 | 35 | for shelf in INITIALIZED_SHELVES: 36 | if shelf.exists: 37 | shelf.delete() 38 | 39 | -------------------------------------------------------------------------------- /dpa/data/config/maya/maps/import_tex.cfg: -------------------------------------------------------------------------------- 1 | 2 | global: 3 | 4 | options: 5 | 6 | shader: 7 | type: list 8 | default: aiStandardSurface 9 | help: Shader Options 10 | label: "Pick a shader" 11 | required: True 12 | choices: [aiStandard,aiStandardSurface,PxrSurface] 13 | multiple: False 14 | 15 | arnoldShader: 16 | type: bool 17 | default: True 18 | help: Turn on arnold shader 19 | label: "Use Arnold Shader and attributes" 20 | 21 | rendermanShader: 22 | type: bool 23 | default: False 24 | help: Turn on renderman shader 25 | label: "Use Renderman Shader and attributes" 26 | 27 | linearize: 28 | type: bool 29 | default: False 30 | help: Linearize (Apply SRGB) 31 | label: "Linearize" 32 | 33 | disable_file_load: 34 | type: bool 35 | default: True 36 | help: Disable texture file load 37 | label: Disable file load 38 | 39 | connect: 40 | type: bool 41 | default: True 42 | help: Connect the shaders with the corresponding file node 43 | label: Connect nodes 44 | -------------------------------------------------------------------------------- /dpa/data/config/maya/maps/import_tx.cfg: -------------------------------------------------------------------------------- 1 | 2 | global: 3 | 4 | options: 5 | 6 | shader: 7 | type: list 8 | default: aiStandardSurface 9 | help: Shader Options 10 | label: "Pick a shader" 11 | required: True 12 | choices: [aiStandard,aiStandardSurface,PxrSurface] 13 | multiple: False 14 | 15 | arnoldShader: 16 | type: bool 17 | default: True 18 | help: Turn on arnold shader 19 | label: "Use Arnold Shader and attributes" 20 | 21 | rendermanShader: 22 | type: bool 23 | default: False 24 | help: Turn on renderman shader 25 | label: "Use Renderman Shader and attributes" 26 | 27 | linearize: 28 | type: bool 29 | default: False 30 | help: Linearize (Apply SRGB) 31 | label: "Linearize" 32 | 33 | disable_file_load: 34 | type: bool 35 | default: True 36 | help: Disable texture file load 37 | label: Disable file load 38 | 39 | connect: 40 | type: bool 41 | default: True 42 | help: Connect the shaders with the corresponding file node 43 | label: Connect nodes 44 | -------------------------------------------------------------------------------- /dpa/data/plugins/mari/pipe_shelf.py: -------------------------------------------------------------------------------- 1 | 2 | from dpa.mari.shelf import MariShelf 3 | from dpa.ptask.area import PTaskArea 4 | 5 | # ----------------------------------------------------------------------------- 6 | 7 | CONFIG_FILE = 'config/mari/shelves.cfg' 8 | INITIALIZED_SHELVES = [] 9 | 10 | # ----------------------------------------------------------------------------- 11 | def initializePlugin(): 12 | 13 | # read the config, get all the shelf definitions 14 | ptask_area = PTaskArea.current() 15 | mari_shelves_config = ptask_area.config( 16 | config_file=CONFIG_FILE, composite_ancestors=True) 17 | 18 | for (shelf_name, shelf_config) in mari_shelves_config.iteritems(): 19 | 20 | shelf = MariShelf(shelf_name) 21 | if shelf.exists: 22 | shelf.delete() 23 | 24 | shelf.create() 25 | 26 | # add all the shelf buttons 27 | for (button_key, button_config) in shelf_config.iteritems(): 28 | shelf.add_button(**button_config) 29 | 30 | INITIALIZED_SHELVES.append(shelf) 31 | 32 | # ----------------------------------------------------------------------------- 33 | def uninitializePlugin(): 34 | 35 | for shelf in INITIALIZED_SHELVES: 36 | if shelf.exists: 37 | shelf.delete() 38 | 39 | 40 | initializePlugin() -------------------------------------------------------------------------------- /dpa/ui/icon/factory.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | 4 | from dpa.ptask.area import PTaskArea 5 | 6 | # ----------------------------------------------------------------------------- 7 | class IconFactory(object): 8 | 9 | ICON_SCHEME = "icon:///" 10 | 11 | # ------------------------------------------------------------------------- 12 | @classmethod 13 | def is_icon_path(cls, path): 14 | return path.startswith(cls.ICON_SCHEME) 15 | 16 | # ------------------------------------------------------------------------- 17 | def disk_path(self, uri): 18 | 19 | if not uri: 20 | return "" 21 | 22 | # XXX need to properly parse uri 23 | rel_path = uri.replace(self.__class__.ICON_SCHEME, "") 24 | 25 | image_paths = self.ptask_area.ancestor_paths( 26 | relative_file=rel_path, include_install=True) 27 | 28 | for image_path in image_paths: 29 | if os.path.exists(image_path): 30 | return image_path 31 | 32 | return rel_path 33 | 34 | # ------------------------------------------------------------------------- 35 | @property 36 | def ptask_area(self): 37 | 38 | if not hasattr(self, '_ptask_area'): 39 | self._ptask_area = PTaskArea.current() 40 | 41 | return self._ptask_area 42 | 43 | -------------------------------------------------------------------------------- /dpa/nuke/scripts/nukeWrangling.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import nuke 4 | import os, sys 5 | import pwd 6 | 7 | if __name__ == '__main__': 8 | 9 | if len(sys.argv) <= 1: 10 | print "\nInvalid file Path." 11 | sys.exit(0) 12 | nodes = [] 13 | for item in sys.argv[1:]: 14 | filepath,startFrame,endFrame = item.split(',') 15 | start = int(startFrame) 16 | end = int(endFrame) 17 | readNode = nuke.nodes.Read(file=filepath) 18 | readNode.knob("first").setValue(start) 19 | readNode.knob("last").setValue(end) 20 | readNode.knob("on_error").setValue(3) 21 | readNode.knob("colorspace").setValue(1) 22 | nodes.append(readNode) 23 | if len(nodes) == 0: 24 | print "\nNo items to read found" 25 | sys.exit(0) 26 | print "Number of nodes: " + str(len(nodes)) 27 | viewer = nuke.nodes.Viewer(inputs=nodes) 28 | root = nuke.toNode('root') 29 | root.knob('first_frame').setValue(start) 30 | root.knob('last_frame').setValue(end) 31 | savePath = "/tmp/nukeWrangling_" + str(pwd.getpwuid(os.getuid())[0]) 32 | os.system( "mkdir -p " + savePath ) 33 | savePath = savePath + "/tmpFile.nk" 34 | if os.path.exists(savePath): 35 | os.remove(savePath) 36 | script = nuke.toNode('root').name() 37 | nuke.scriptSaveAs(savePath) 38 | os.chmod(savePath,777) 39 | os.system('nuke ' + savePath) 40 | -------------------------------------------------------------------------------- /dpa/data/plugins/nuke/menu.py: -------------------------------------------------------------------------------- 1 | 2 | import nuke 3 | 4 | from dpa.ptask.area import PTaskArea 5 | from dpa.ui.icon.factory import IconFactory 6 | 7 | from dpa.nuke.nodes import add_commands, on_load 8 | 9 | # ----------------------------------------------------------------------------- 10 | 11 | NUKE_TOOLBAR_CONFIG = 'config/nuke/toolbars.cfg' 12 | 13 | # ----------------------------------------------------------------------------- 14 | def load_toolbars(): 15 | """Load all custom toolbars via config files.""" 16 | 17 | ptask_area = PTaskArea.current() 18 | nuke_toolbar_config = ptask_area.config( 19 | config_file=NUKE_TOOLBAR_CONFIG, composite_ancestors=True) 20 | 21 | for (toolbar_name, toolbar_config) in nuke_toolbar_config.iteritems(): 22 | 23 | toolbar = nuke.toolbar(toolbar_name) 24 | for (item_key, item_config) in toolbar_config.iteritems(): 25 | 26 | name = item_config.get('label', item_key) 27 | command = item_config.get('command', "print 'No op'") 28 | icon = item_config.get('image', None) 29 | tooltip = item_config.get('annotation', "") 30 | 31 | if icon: 32 | icon = IconFactory().disk_path(icon) 33 | 34 | toolbar.addCommand(name=name, command=command, icon=icon, 35 | tooltip=tooltip) 36 | 37 | # ----------------------------------------------------------------------------- 38 | 39 | load_toolbars() 40 | 41 | print "Loading DPA Nuke nodes..." 42 | add_commands() 43 | nuke.addOnScriptLoad(dpa.nuke.nodes.on_load) 44 | 45 | -------------------------------------------------------------------------------- /dpa/product/category/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from dpa.restful import RestfulObject, RestfulObjectError 3 | from dpa.restful.mixins import ListMixin 4 | 5 | # ----------------------------------------------------------------------------- 6 | class ProductCategory(ListMixin, RestfulObject): 7 | """Product Category API. 8 | 9 | .name 10 | .description 11 | 12 | """ 13 | 14 | data_type = "product-categories" 15 | 16 | # ------------------------------------------------------------------------- 17 | def __eq__(self, other): 18 | return self.name == other.name 19 | 20 | # ------------------------------------------------------------------------- 21 | def __ne__(self, other): 22 | return self.name != other.name 23 | 24 | # ------------------------------------------------------------------------- 25 | def __repr__(self): 26 | """:returns: Unique string represntation of the product.""" 27 | return self.__class__.__name__ + "('" + self.name + "')" 28 | 29 | # ------------------------------------------------------------------------- 30 | @property 31 | def name(self): 32 | return self._data.get('name') 33 | 34 | # ------------------------------------------------------------------------- 35 | @property 36 | def description(self): 37 | return self._data.get('description') 38 | 39 | # ----------------------------------------------------------------------------- 40 | class ProductCategoryError(RestfulObjectError): 41 | pass 42 | 43 | ProductCategory.exception_class = ProductCategoryError 44 | 45 | -------------------------------------------------------------------------------- /dpa/data/config/mari/shelves.cfg: -------------------------------------------------------------------------------- 1 | # shelf name 2 | Pipeline: 3 | 4 | import: 5 | command: | 6 | from dpa.ui.product.subscription._import import SubscriptionImportWizard 7 | wiz = SubscriptionImportWizard() 8 | wiz.exec_() 9 | 10 | label: Import 11 | annotation: Import products into the current session. 12 | image: "icon:///images/icons/import_32x32.png" 13 | 14 | export: 15 | command: | 16 | from dpa.ui.app.entity.export import EntityExportWizard 17 | wiz = EntityExportWizard() 18 | wiz.exec_() 19 | 20 | label: Export 21 | annotation: Export entities from the current session as products. 22 | image: "icon:///images/icons/export_32x32.png" 23 | 24 | version: 25 | command: | 26 | from dpa.ui.ptask.version import PTaskVersionDialog 27 | PTaskVersionDialog().show() 28 | 29 | label: Version 30 | annotation: Version the current ptask. 31 | image: "icon:///images/icons/version_32x32.png" 32 | 33 | ooto: 34 | command: | 35 | from dpa.ui.notify.ooto import OotoDialog 36 | OotoDialog().show() 37 | 38 | label: OOTO 39 | annotation: Send an OOTO notification 40 | image: "icon:///images/icons/ooto_32x32.png" 41 | 42 | fail: 43 | command: | 44 | from dpa.ui.notify.fail import FailDialog 45 | FailDialog().show() 46 | 47 | label: FAIL 48 | annotation: Send a FAIL notification 49 | image: "icon:///images/icons/warning_32x32.png" 50 | 51 | -------------------------------------------------------------------------------- /dpa/data/config/nuke/toolbars.cfg: -------------------------------------------------------------------------------- 1 | 2 | # toolbar name 3 | Pipeline: 4 | 5 | version: 6 | command: | 7 | from dpa.ui.ptask.version import PTaskVersionDialog 8 | PTaskVersionDialog().show() 9 | 10 | label: Version 11 | annotation: Version the current ptask. 12 | image: "icon:///images/icons/version_32x32.png" 13 | 14 | ooto: 15 | command: | 16 | from dpa.ui.notify.ooto import OotoDialog 17 | OotoDialog().show() 18 | 19 | label: OOTO 20 | annotation: Send an OOTO notification 21 | image: "icon:///images/icons/ooto_32x32.png" 22 | 23 | fail: 24 | command: | 25 | from dpa.ui.notify.fail import FailDialog 26 | FailDialog().show() 27 | 28 | label: FAIL 29 | annotation: Send a FAIL notification 30 | image: "icon:///images/icons/warning_32x32.png" 31 | 32 | dark_knight: 33 | command: | 34 | from dpa.ui.dk.nuke_ import NukeDarkKnightDialog 35 | NukeDarkKnightDialog().exec_() 36 | 37 | label: DARK KNIGHT 38 | annotation: Opens the dark knight render submission dialog. 39 | image: "icon:///images/icons/dk_32x32.png" 40 | 41 | refresh_subs: 42 | command: | 43 | from dpa.nuke.nodes import update_all_read_sub_nodes, populate_sub_cache 44 | populate_sub_cache(refresh=True) 45 | update_all_read_sub_nodes() 46 | 47 | label: REFRESH SUBS 48 | annotation: Refresh all ReadSub nodes with the latest subscriptions 49 | image: "icon:///images/icons/sub_32x32.png" 50 | 51 | -------------------------------------------------------------------------------- /bin/dpa_houdini: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Temporary wrapper script for launching alternate Houdini versions. 4 | # 5 | # This is for people who want to test out Houdini 13 with the DPA pipeline 6 | # environment already pulled down. It clobbers the current user environment, 7 | # which, at the time of writing, should be set up for Houdini 12. 8 | # 9 | # This does NOT solve the problem of not having Houdini 13's bin dir on the 10 | # user's $PATH, so this is only a temporary solution. It is useful only for 11 | # launching the "main" Houdini application. 12 | # 13 | # Author: Paul Kilgo (pkilgo@clemson.edu) 14 | # 15 | set -eu 16 | 17 | # Set Houdini location to site-configured default if it doesn't exist. 18 | HFS="/opt/hfs16" 19 | 20 | # Source Houdini setup. 21 | pushd "${HFS}" >/dev/null 22 | set +u; source houdini_setup_bash -q; set -u 23 | popd >/dev/null 24 | 25 | # Set up environment variables for DPA plugins. 26 | userdir="${HOME}/houdini${HOUDINI_MAJOR_RELEASE}.${HOUDINI_MINOR_RELEASE}" 27 | 28 | HOUDINI_OTLSCAN_PATH="${HFS}/houdini/otls:/group/dpa/plugins/houdiniPlugins" 29 | #HOUDINI_OTLSCAN_PATH="${HOUDINI_OTLSCAN_PATH}:${HFS}/houdini/otls:/group/dpa/plugins/houdiniPlugins" 30 | HOUDINI_OTLSCAN_PATH="${HOUDINI_OTLSCAN_PATH}:${userdir}/otls" 31 | 32 | HOUDINI_TOOLBAR_PATH="${HFS}/houdini/toolbar:/group/dpa/plugins/houdiniPlugins" 33 | #HOUDINI_TOOLBAR_PATH="${HOUDINI_TOOLBAR_PATH}:${HFS}/houdini/toolbar:/group/dpa/plugins/houdiniPlugins" 34 | HOUDINI_TOOLBAR_PATH="${HOUDINI_TOOLBAR_PATH}:${userdir}/toolbar" 35 | 36 | export HOUDINI_OTLSCAN_PATH HOUDINI_TOOLBAR_PATH 37 | unset userdir 38 | 39 | alias mantra="${HFS}/bin/mantra" 40 | alias mplay="${HFS}/bin/mplay" 41 | 42 | 43 | exec "${HFS}/bin/houdini" "$@" 44 | -------------------------------------------------------------------------------- /dpa/ptask/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Module: dpa.argparse 3 | # Contact: Josh Tomlinson (jtomlin) 4 | # ----------------------------------------------------------------------------- 5 | 6 | # ----------------------------------------------------------------------------- 7 | # Imports: 8 | # ----------------------------------------------------------------------------- 9 | 10 | import argparse 11 | import sys 12 | 13 | from dpa.ptask.area import PTaskArea 14 | from dpa.ptask.spec import PTaskSpec 15 | 16 | # ----------------------------------------------------------------------------- 17 | # Classes: 18 | # ----------------------------------------------------------------------------- 19 | class ParsePTaskSpecArg(argparse.Action): 20 | """argparse.Action subclass. parses a ptask spec. 21 | 22 | Use this class as an argument to the 'action' argument when calling 23 | add_argument on an argparse parser. When the command line arguments are 24 | parsed, the resulting namespace will include a parsed ptask spec object 25 | assigned to the argument's destination. 26 | 27 | This action will parse and resolve a ptask spec relative to the currently 28 | set ptask environment. 29 | 30 | """ 31 | 32 | # ------------------------------------------------------------------------- 33 | def __call__(self, parser, namespace, in_spec, option_string=None): 34 | 35 | # assume the current ptask if not supplied 36 | if not in_spec: 37 | in_spec = "." 38 | 39 | cur_spec = PTaskArea.current().spec 40 | in_spec = in_spec.strip().strip(PTaskSpec.SEPARATOR) 41 | full_spec = PTaskSpec.get(in_spec, relative_to=cur_spec) 42 | 43 | setattr(namespace, self.dest, full_spec) 44 | 45 | -------------------------------------------------------------------------------- /dpa/houdini/entity/geomcache.py: -------------------------------------------------------------------------------- 1 | 2 | import os.path 3 | import re 4 | 5 | from dpa.app.entity import Entity, EntityRegistry, EntityError 6 | 7 | # ----------------------------------------------------------------------------- 8 | class GeomcacheEntity(Entity): 9 | 10 | category = "geomcache" 11 | 12 | # ------------------------------------------------------------------------- 13 | @classmethod 14 | def import_product_representation(cls, session, representation, *args, 15 | **kwargs): 16 | """Import this entity to the session.""" 17 | 18 | if representation.type == 'abc': 19 | cls._abc_import(session, representation, *args, **kwargs) 20 | else: 21 | raise EntityError( 22 | "Unknown type for {cat} import: {typ}".format( 23 | cat=cls.category, typ=representation.type)) 24 | 25 | # ------------------------------------------------------------------------- 26 | @classmethod 27 | def _abc_import(cls, session, representation, *args, **kwargs): 28 | 29 | product = representation.product_version.product 30 | abc_file = cls.get_import_file(session, product.name, 31 | product.category, representation) 32 | 33 | alembic_node = session.hou.node('obj').createNode('alembicarchive') 34 | alembic_node.setParms({"fileName": abc_file}) 35 | alembic_node.parm('buildHierarchy').pressButton() 36 | 37 | 38 | # ------------------------------------------------------------------------- 39 | def export(self, *args, **kwargs): 40 | """Export this entity to a product.""" 41 | 42 | raise EntityError("Houdini geom export not yet supported.") 43 | 44 | # ----------------------------------------------------------------------------- 45 | EntityRegistry().register('houdini', GeomcacheEntity) 46 | 47 | -------------------------------------------------------------------------------- /dpa/app/cli.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import time 4 | 5 | from dpa.app.server import AppServer 6 | from dpa.cli.action import CommandLineAction 7 | 8 | # ------------------------------------------------------------------------------ 9 | class ClApp(CommandLineAction): 10 | 11 | name = "clapp" 12 | 13 | # -------------------------------------------------------------------------- 14 | @classmethod 15 | def setup_cl_args(cls, parser): 16 | 17 | parser.add_argument( 18 | "port", 19 | type=int, 20 | help="Port number to serve." 21 | ) 22 | 23 | # -------------------------------------------------------------------------- 24 | def __init__(self, port): 25 | 26 | super(ClApp, self).__init__(port) 27 | 28 | self._port = port 29 | self._server = None 30 | self._shutdown = False 31 | 32 | # -------------------------------------------------------------------------- 33 | def execute(self): 34 | 35 | self._server = AppServer( 36 | self.port, 37 | shutdown_callback=self._shutdown_server, 38 | ) 39 | self._server.start() 40 | 41 | while not self._shutdown: 42 | time.sleep(1) 43 | 44 | sys.exit(0) 45 | 46 | # -------------------------------------------------------------------------- 47 | def undo(self): 48 | pass 49 | 50 | # -------------------------------------------------------------------------- 51 | @property 52 | def port(self): 53 | return self._port 54 | 55 | # -------------------------------------------------------------------------- 56 | @property 57 | def server(self): 58 | return self._server 59 | 60 | # -------------------------------------------------------------------------- 61 | def _shutdown_server(self): 62 | 63 | self._shutdown = True 64 | 65 | -------------------------------------------------------------------------------- /dpa/ui/notify/fail.py: -------------------------------------------------------------------------------- 1 | 2 | from PySide import QtCore, QtGui 3 | 4 | from dpa.action import ActionError 5 | from dpa.action.registry import ActionRegistry 6 | from dpa.ptask.area import PTaskArea 7 | from dpa.ui.app.session import SessionActionDialog 8 | from dpa.ui.icon.factory import IconFactory 9 | 10 | # ----------------------------------------------------------------------------- 11 | 12 | FAIL_ICON_URI = "icon:///images/icons/warning_32x32.png" 13 | FAIL_OPTIONS_CONFIG = "config/ui/actions/fail.cfg" 14 | 15 | # ----------------------------------------------------------------------------- 16 | class FailDialog(SessionActionDialog): 17 | 18 | # ------------------------------------------------------------------------- 19 | def __init__(self): 20 | 21 | ptask_area = PTaskArea.current() 22 | options_config = ptask_area.config(FAIL_OPTIONS_CONFIG, 23 | composite_ancestors=True) 24 | 25 | icon_path = IconFactory().disk_path(FAIL_ICON_URI) 26 | 27 | super(FailDialog, self).__init__( 28 | title='Failure Report', 29 | options_config=options_config, 30 | icon_path=icon_path, 31 | action_button_text='Submit', 32 | modal=False, 33 | ) 34 | 35 | # ------------------------------------------------------------------------- 36 | def accept(self): 37 | 38 | # handles closing the dialog 39 | super(FailDialog, self).accept() 40 | 41 | try: 42 | fail_action_cls = ActionRegistry().get_action('fail') 43 | fail_action = fail_action_cls(**self.options.value) 44 | fail_action() 45 | except ActionError as e: 46 | error_dialog = QtGui.QErrorMessage(self.parent()) 47 | error_dialog.setWindowTitle('DPA Fail Failure') 48 | error_dialog.showMessage( 49 | "There was an error submitting the fail message:

" + \ 50 | str(e) 51 | ) 52 | 53 | -------------------------------------------------------------------------------- /dpa/maya/entity/geom.py: -------------------------------------------------------------------------------- 1 | 2 | import os.path 3 | import re 4 | 5 | from dpa.app.entity import EntityRegistry, EntityError 6 | from dpa.maya.entity.base import SetBasedEntity 7 | 8 | # options: 9 | # export types: obj 10 | 11 | # ----------------------------------------------------------------------------- 12 | class GeomEntity(SetBasedEntity): 13 | 14 | category = "geom" 15 | 16 | # ------------------------------------------------------------------------- 17 | def export(self, product_desc=None, version_note=None, obj_export=False, 18 | obj_options=None): 19 | """Export this entity to a product.""" 20 | 21 | product_reprs = [] 22 | 23 | if obj_export: 24 | product_reprs.extend( 25 | self._obj_export(obj_options, product_desc, version_note) 26 | ) 27 | 28 | return product_reprs 29 | 30 | # ------------------------------------------------------------------------- 31 | def _obj_export(self, options, product_desc, version_note): 32 | 33 | self.session.require_plugin('objExport') 34 | 35 | file_ext = 'obj' 36 | 37 | product_repr = self._create_product(product_desc, version_note, 38 | file_ext) 39 | product_repr_dir = product_repr.directory 40 | 41 | export_objs = self.get_export_objects() 42 | export_path = os.path.join(product_repr_dir, self.display_name) 43 | 44 | with self.session.selected(export_objs): 45 | self.session.mel.eval('CreatePolyFromPreview;') 46 | self.session.cmds.file(export_path, force=True, type='OBJexport', exportSelected=True, 47 | options='groups=0;ptgroups=0;materials=0;smoothing=1;normals=0') 48 | self.session.cmds.undo() 49 | 50 | product_repr.area.set_permissions(0660) 51 | 52 | return [product_repr] 53 | 54 | # ----------------------------------------------------------------------------- 55 | EntityRegistry().register('maya', GeomEntity) 56 | 57 | -------------------------------------------------------------------------------- /dpa/ui/notify/ooto.py: -------------------------------------------------------------------------------- 1 | 2 | from PySide import QtCore, QtGui 3 | 4 | from dpa.action import ActionError 5 | from dpa.action.registry import ActionRegistry 6 | from dpa.ptask.area import PTaskArea 7 | from dpa.ui.app.session import SessionActionDialog 8 | from dpa.ui.icon.factory import IconFactory 9 | 10 | # ----------------------------------------------------------------------------- 11 | 12 | OOTO_ICON_URI = "icon:///images/icons/ooto_32x32.png" 13 | OOTO_OPTIONS_CONFIG = "config/ui/actions/ooto.cfg" 14 | 15 | # ----------------------------------------------------------------------------- 16 | class OotoDialog(SessionActionDialog): 17 | 18 | # ------------------------------------------------------------------------- 19 | def __init__(self): 20 | 21 | ptask_area = PTaskArea.current() 22 | options_config = ptask_area.config(OOTO_OPTIONS_CONFIG, 23 | composite_ancestors=True) 24 | 25 | icon_path = IconFactory().disk_path(OOTO_ICON_URI) 26 | 27 | super(OotoDialog, self).__init__( 28 | title='Out Of The Office (OOTO)', 29 | options_config=options_config, 30 | icon_path=icon_path, 31 | action_button_text='Submit', 32 | modal=False, 33 | ) 34 | 35 | # ------------------------------------------------------------------------- 36 | def accept(self): 37 | 38 | # handles closing the dialog 39 | super(OotoDialog, self).accept() 40 | 41 | try: 42 | ooto_action_cls = ActionRegistry().get_action('ooto') 43 | ooto_action = ooto_action_cls(**self.options.value) 44 | ooto_action() 45 | except ActionError as e: 46 | error_dialog = QtGui.QErrorMessage(self.parent()) 47 | error_dialog.setWindowTitle('DPA OOTO Failure') 48 | error_dialog.showMessage( 49 | "There was an error submitting the ooto message:

" + \ 50 | str(e) 51 | ) 52 | 53 | -------------------------------------------------------------------------------- /dpa/ptask/action/source.py: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------------------------------------------- 3 | # Imports: 4 | # ---------------------------------------------------------------------------- 5 | 6 | from dpa.action import Action, ActionError 7 | from dpa.ptask.action.sync import _PTaskSyncAction 8 | from dpa.location import current_location_code 9 | from dpa.shell.output import Style 10 | 11 | # ---------------------------------------------------------------------------- 12 | # Classes: 13 | # ---------------------------------------------------------------------------- 14 | class PTaskSourceAction(_PTaskSyncAction): 15 | """Source the contents of one ptask into another.""" 16 | 17 | # ------------------------------------------------------------------------ 18 | def execute(self): 19 | 20 | try: 21 | super(PTaskSourceAction, self).execute() 22 | except ActionError as e: 23 | raise ActionError("Unable to source ptask: " + str(e)) 24 | else: 25 | print "\nSuccessfully sourced: ", 26 | if self.source_version: 27 | print Style.bright + str(self.source_version.spec) + \ 28 | Style.reset + "\n" 29 | else: 30 | print Style.bright + str(self.source.spec) + " [latest]" + \ 31 | Style.reset + "\n" 32 | 33 | # ------------------------------------------------------------------------ 34 | def validate(self): 35 | 36 | super(PTaskSourceAction, self).validate() 37 | 38 | # ---- make sure the destination location is the current location. 39 | 40 | cur_loc_code = current_location_code() 41 | 42 | if self.destination_version: 43 | dest_loc_code = self.destination_version.location_code 44 | else: 45 | dest_loc_code = self.destination_latest_version.location_code 46 | 47 | if cur_loc_code != dest_loc_code: 48 | raise ActionError("Destination location must be this location.") 49 | 50 | -------------------------------------------------------------------------------- /dpa/data/config/maya/shelves.cfg: -------------------------------------------------------------------------------- 1 | 2 | # shelf name 3 | Pipeline: 4 | 5 | import: 6 | command: | 7 | from dpa.ui.product.subscription._import import SubscriptionImportWizard 8 | sub_wiz = SubscriptionImportWizard() 9 | sub_wiz.show() 10 | label: IMPORT 11 | annotation: Create references from subscriptions 12 | image: "icon:///images/icons/import_32x32.png" 13 | 14 | version: 15 | command: | 16 | from dpa.ui.ptask.version import PTaskVersionDialog 17 | PTaskVersionDialog().show() 18 | 19 | label: Version 20 | annotation: Version the current ptask. 21 | image: "icon:///images/icons/version_32x32.png" 22 | 23 | 24 | export: 25 | command: | 26 | from dpa.ui.app.entity.export import EntityExportWizard 27 | EntityExportWizard().show() 28 | 29 | label: Export 30 | annotation: Export entities from the current session as products. 31 | image: "icon:///images/icons/export_32x32.png" 32 | 33 | ooto: 34 | command: | 35 | from dpa.ui.notify.ooto import OotoDialog 36 | OotoDialog().show() 37 | 38 | label: OOTO 39 | annotation: Send an OOTO notification 40 | image: "icon:///images/icons/ooto_32x32.png" 41 | 42 | fail: 43 | command: | 44 | from dpa.ui.notify.fail import FailDialog 45 | FailDialog().show() 46 | 47 | label: FAIL 48 | annotation: Send a FAIL notification 49 | image: "icon:///images/icons/warning_32x32.png" 50 | 51 | 52 | playblast: 53 | command: | 54 | from dpa.ui.maya.playblast import playblast_dialog 55 | playblast_dialog() 56 | 57 | label: PLAYBLAST 58 | annotation: Generate a playblast 59 | image: "icon:///images/icons/playblast_32x32.png" 60 | 61 | dark_knight: 62 | command: | 63 | from dpa.ui.dk.maya import MayaDarkKnightDialog 64 | MayaDarkKnightDialog().exec_() 65 | 66 | label: DARK KNIGHT 67 | annotation: Opens the dark knight render submission dialog. 68 | image: "icon:///images/icons/dk_32x32.png" 69 | -------------------------------------------------------------------------------- /dpa/imgres/test/test_imgres.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Module: dpa.imgres.tests.test_imgres 3 | # Author: Chuqiao Wang (chuqiaw) 4 | # ----------------------------------------------------------------------------- 5 | """Unit tests for dpa imgres. 6 | 7 | Still testing. 8 | 9 | """ 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Imports: 13 | # ----------------------------------------------------------------------------- 14 | 15 | import unittest 16 | 17 | from dpa.imgres import ImgRes 18 | # from dpa.imgres import ImgResError 19 | 20 | # ----------------------------------------------------------------------------- 21 | # Suite for all test cases defined: 22 | # ----------------------------------------------------------------------------- 23 | def suite(): 24 | """Returns a test suite for all frange tests.""" 25 | 26 | return unittest.TestSuite([ 27 | ImgResInstanceTestCase, 28 | ]) 29 | 30 | # ----------------------------------------------------------------------------- 31 | # Test Cases: 32 | # ----------------------------------------------------------------------------- 33 | class ImgResInstanceTestCase(unittest.TestCase): 34 | """ImgRes class method tests.""" 35 | 36 | # ------------------------------------------------------------------------- 37 | # setup/teardown: 38 | # ------------------------------------------------------------------------- 39 | def setUp(self): 40 | """Get a new ImgRes object for each test.""" 41 | self.ir = ImgRes(width=1920, height=1080) 42 | 43 | # ------------------------------------------------------------------------- 44 | def tearDown(self): 45 | """Clean up the ImgRes instance after each test method.""" 46 | del self.ir 47 | 48 | # ------------------------------------------------------------------------- 49 | # Tests: 50 | # ------------------------------------------------------------------------- 51 | def test_property_width(self): 52 | """ImgRes 'width' property is int and read only""" 53 | 54 | width = self.ir.width 55 | self.assertEqual(start, 1920) 56 | 57 | if __name__ == '__main__': 58 | unittest.main() 59 | 60 | -------------------------------------------------------------------------------- /dpa/app/server.py: -------------------------------------------------------------------------------- 1 | 2 | import socket 3 | 4 | import rpyc 5 | from rpyc.core.service import SlaveService 6 | from rpyc.utils.server import ThreadedServer 7 | from rpyc.utils.registry import REGISTRY_PORT, UDPRegistryClient 8 | 9 | # ----------------------------------------------------------------------------- 10 | class AppService(SlaveService): 11 | 12 | # ------------------------------------------------------------------------- 13 | def exposed_shutdown(self): 14 | 15 | if self.server: 16 | self.server.close() 17 | 18 | if self.shutdown_callback: 19 | self.shutdown_callback() 20 | 21 | # ------------------------------------------------------------------------- 22 | @property 23 | def shutdown_callback(self, callback): 24 | 25 | if not hasattr(self, '_shutdown_callback'): 26 | self._shutdown_callback = None 27 | 28 | return self._shutdown_callback 29 | 30 | # ------------------------------------------------------------------------- 31 | @shutdown_callback.setter 32 | def server(self, callback): 33 | self._shutdown_callback = callback 34 | 35 | # ------------------------------------------------------------------------- 36 | @property 37 | def server(self): 38 | 39 | if not hasattr(self, '_server'): 40 | self._server = None 41 | 42 | return self._server 43 | 44 | # ------------------------------------------------------------------------- 45 | @server.setter 46 | def server(self, server): 47 | self._server = server 48 | 49 | # ----------------------------------------------------------------------------- 50 | class AppServer(ThreadedServer): 51 | 52 | # ------------------------------------------------------------------------- 53 | def __init__(self, port=0, shutdown_callback=None): 54 | 55 | super(AppServer, self).__init__( 56 | AppService, 57 | port=port, 58 | registrar=UDPRegistryClient( 59 | ip="255.255.255.255", 60 | port=REGISTRY_PORT 61 | ), 62 | auto_register=False, 63 | ) 64 | 65 | # give the service a handle to the server for closing 66 | self.service.server = self 67 | self.service.shutdown_callback = shutdown_callback 68 | 69 | -------------------------------------------------------------------------------- /dpa/ffmpeg/action.py: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- 2 | 3 | import argparse 4 | import os 5 | import platform 6 | import shlex 7 | from subprocess import Popen 8 | 9 | from dpa.action import Action, ActionError 10 | from dpa.config import Config 11 | from dpa.env import EnvVar 12 | from dpa.ptask import PTaskArea 13 | 14 | # ---------------------------------------------------------------------------- 15 | 16 | FFMPEG_ACTION_CONFIG = "config/actions/ffmpeg.cfg" 17 | 18 | # ---------------------------------------------------------------------------- 19 | class FfmpegAction(Action): 20 | 21 | name = 'ffmpeg' 22 | target_type = 'file' 23 | description = 'Create a movie from a sequence of images.' 24 | 25 | # ------------------------------------------------------------------------ 26 | @classmethod 27 | def setup_cl_args(self, parser): 28 | 29 | # the file or software 30 | parser.add_argument( 31 | "basename", 32 | help="Base name of the sequence", 33 | ) 34 | 35 | parser.add_argument( 36 | "-g", "--gamma", 37 | default=1.0, 38 | help="Gamma to apply to sequence.", 39 | metavar="type" 40 | ) 41 | 42 | # ------------------------------------------------------------------------ 43 | def __init__(self, basename, gamma): 44 | super(FfmpegAction, self).__init__(basename, gamma) 45 | 46 | self._basename = basename 47 | self._gamma = gamma 48 | 49 | # ------------------------------------------------------------------------ 50 | def execute(self): 51 | print "making movie" 52 | sys_command = "ffmpeg -v 0 -y -r 24 -i %s.%%04d.exr -vf eq=gamma=%s -vcodec mjpeg -b:v 50M -r 24 %s.mov" % (self._basename, self._gamma, self._basename) 53 | os.system(sys_command) 54 | 55 | # ------------------------------------------------------------------------ 56 | def undo(self): 57 | pass 58 | 59 | # ------------------------------------------------------------------------ 60 | @property 61 | def basename(self): 62 | return self._basename 63 | 64 | # ------------------------------------------------------------------------ 65 | @property 66 | def gamma(self): 67 | return self._gamma 68 | 69 | -------------------------------------------------------------------------------- /dpa/data/config/houdini/shelves.cfg: -------------------------------------------------------------------------------- 1 | 2 | # toolbar name 3 | Pipeline: 4 | 5 | import: 6 | command: | 7 | from dpa.ui.product.subscription._import import SubscriptionImportWizard 8 | dialog = SubscriptionImportWizard() 9 | dialog.show() 10 | pyqt_houdini.exec_(app, dialog) 11 | 12 | label: Import 13 | annotation: Create references from subscriptions 14 | image: "icon:///images/icons/import_32x32.png" 15 | 16 | version: 17 | command: | 18 | from dpa.ui.ptask.version import PTaskVersionDialog 19 | dialog = PTaskVersionDialog() 20 | dialog.show() 21 | pyqt_houdini.exec_(app, dialog) 22 | 23 | label: Version 24 | annotation: Version the current ptask. 25 | image: "icon:///images/icons/version_32x32.png" 26 | 27 | 28 | export: 29 | command: | 30 | from dpa.ui.app.entity.export import EntityExportWizard 31 | dialog = EntityExportWizard() 32 | dialog.show() 33 | pyqt_houdini.exec_(app, dialog) 34 | 35 | label: Export 36 | annotation: Export entities from the current session as products. 37 | image: "icon:///images/icons/export_32x32.png" 38 | 39 | ooto: 40 | command: | 41 | from dpa.ui.notify.ooto import OotoDialog 42 | dialog = OotoDialog() 43 | dialog.show() 44 | pyqt_houdini.exec_(app, dialog) 45 | 46 | 47 | label: Ooto 48 | annotation: Send an OOTO notification 49 | image: "icon:///images/icons/ooto_32x32.png" 50 | 51 | fail: 52 | command: | 53 | from dpa.ui.notify.fail import FailDialog 54 | dialog = FailDialog() 55 | dialog.show() 56 | pyqt_houdini.exec_(app, dialog) 57 | 58 | label: FAIL 59 | annotation: Send a FAIL notification 60 | image: "icon:///images/icons/warning_32x32.png" 61 | 62 | dark_knight: 63 | command: | 64 | from dpa.ui.dk.houdini import HoudiniDarkKnightDialog 65 | dialog = HoudiniDarkKnightDialog() 66 | dialog.exec_() 67 | pyqt_houdini.exec_(app, dialog) 68 | 69 | label: Dark Knight 70 | annotation: Opens the dark knight render submission dialog. 71 | image: "icon:///images/icons/dk_32x32.png" 72 | 73 | -------------------------------------------------------------------------------- /dpa/nuke/utils.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | 4 | import nuke 5 | 6 | from dpa.action.registry import ActionRegistry 7 | from dpa.ptask import PTask, PTaskError 8 | from dpa.ptask.area import PTaskArea 9 | 10 | # ----------------------------------------------------------------------------- 11 | def create_product_before_render(node=None): 12 | 13 | if not node: 14 | node = nuke.thisNode() 15 | 16 | if not node.knob('product_name') or not node.knob('product_desc'): 17 | raise Exception("The supplied node is not a WriteProduct node.") 18 | 19 | print "Creating product for write node... " + str(node) 20 | 21 | ptask_area = PTaskArea.current() 22 | ptask = PTask.get(ptask_area.spec) 23 | 24 | if ptask_area.version: 25 | ptask_version = ptask.version(ptask_area.version) 26 | else: 27 | ptask_version = ptask.latest_version 28 | 29 | category = 'imgseq' 30 | 31 | file_type = node['file_type'].value() 32 | if not file_type: 33 | file_type = 'exr' 34 | 35 | product_name = node['product_name'].value() 36 | product_desc = node['product_desc'].value() 37 | product_ver_note = node['product_ver_note'].value() 38 | 39 | if not product_desc: 40 | raise Exception("Please enter a product description.") 41 | 42 | width = nuke.value(node.name() + '.width') 43 | height = nuke.value(node.name() + '.height') 44 | resolution = width + 'x' + height 45 | 46 | create_action_cls = ActionRegistry().get_action('create', 'product') 47 | if not create_action_cls: 48 | raise Exception("Unable to find product creation action.") 49 | 50 | create_action = create_action_cls( 51 | product=product_name, 52 | ptask=ptask.spec, 53 | version=ptask_version.number, 54 | category=category, 55 | description=product_desc, 56 | file_type=file_type, 57 | resolution=resolution, 58 | note=product_ver_note, 59 | ) 60 | 61 | try: 62 | create_action() 63 | except ActionError as e: 64 | raise Exception("Unable to create product: " + str(e)) 65 | 66 | out_path = os.path.join(create_action.product_repr.area.path, 67 | product_name + '.####.' + file_type) 68 | 69 | node['file'].setValue(out_path) 70 | 71 | return create_action.product_repr 72 | 73 | # ----------------------------------------------------------------------------- 74 | def set_permissions_after_frame(): 75 | 76 | node = nuke.thisNode() 77 | frame_path = nuke.filename(node, nuke.REPLACE) 78 | os.chmod(frame_path, 0660) 79 | 80 | -------------------------------------------------------------------------------- /dpa/data/config/mari/maps/export.cfg: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | options: 4 | 5 | tex_convert: 6 | 7 | type: bool 8 | default: True 9 | help: Convert to .tex files. 10 | label: Convert maps to .tex 11 | 12 | tx_convert: 13 | 14 | type: bool 15 | default: True 16 | help: Convert to .tx files. 17 | label: Convert maps to .tx 18 | 19 | queue_group: 20 | 21 | type: group 22 | help: Queue related options 23 | label: Queue options 24 | open: False 25 | options: 26 | tex_queue: 27 | 28 | type: bool 29 | default: True 30 | help: Execute .tex file conversion in the queue. 31 | label: Queue up .tex conversion 32 | 33 | queue_name: 34 | 35 | type: list 36 | default: 'velveeta' 37 | help: The queue where the tex conversion will run 38 | label: Queue 39 | required: True 40 | choices: ['cheddar', 'muenster', 'brie', 'hold', 'nuke', 'velveeta', 'cheezwhiz'] 41 | multiple: False 42 | 43 | ptex_options: 44 | 45 | type: group 46 | help: Options for Ptex Export 47 | label: Ptex options 48 | open: False 49 | options: 50 | ptex_inc_geo: 51 | 52 | type: bool 53 | default: False 54 | help: Include geometry in export 55 | label: Include geometry 56 | 57 | ptex_inc_adj: 58 | 59 | type: bool 60 | default: True 61 | help: Include adjacency in export 62 | label: Include adjacency 63 | 64 | ptex_gen_mipmaps: 65 | 66 | type: bool 67 | default: True 68 | help: Generate mipmaps on export 69 | label: Generate mipmaps 70 | 71 | ptex_inc_user_attr: 72 | 73 | type: bool 74 | default: True 75 | help: Include user attributes in export 76 | label: Include user attributes 77 | 78 | ptex_remap_quads: 79 | 80 | type: bool 81 | default: False 82 | help: Remap quads on export 83 | label: Remap quads 84 | 85 | -------------------------------------------------------------------------------- /dpa/nuke/session.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | 4 | from PySide import QtCore, QtGui 5 | 6 | from dpa.app.session import Session, SessionRegistry, SessionError 7 | 8 | # ----------------------------------------------------------------------------- 9 | 10 | try: 11 | import nuke 12 | except ImportError: 13 | NUKE_IMPORTED = False 14 | NUKE_HAS_UI = False 15 | else: 16 | NUKE_IMPORTED = True 17 | NUKE_HAS_UI = nuke.GUI 18 | 19 | # ----------------------------------------------------------------------------- 20 | class NukeSession(Session): 21 | 22 | app_name = 'nuke' 23 | 24 | # ------------------------------------------------------------------------- 25 | @classmethod 26 | def current(cls): 27 | if not NUKE_IMPORTED: 28 | return None 29 | return cls() 30 | 31 | # ------------------------------------------------------------------------- 32 | def __init__(self, file_path=None): 33 | 34 | super(NukeSession, self).__init__() 35 | 36 | self._nuke = self.init_module('nuke') 37 | 38 | if file_path: 39 | self.open_file(file_path) 40 | 41 | # ------------------------------------------------------------------------- 42 | def close(self): 43 | self.nuke.scriptExit() 44 | 45 | # ------------------------------------------------------------------------- 46 | def open_file(self, file_path): 47 | 48 | if not os.path.exists(file_path): 49 | raise SessionError( 50 | "Can not open '{f}'. File does not exist.".format(f=file_path)) 51 | 52 | self.nuke.scriptOpen(file_path) 53 | 54 | # ------------------------------------------------------------------------- 55 | def save(self, file_path=None): 56 | 57 | self.nuke.scriptSave(file_path) 58 | 59 | # ------------------------------------------------------------------------- 60 | @property 61 | def nuke(self): 62 | return self._nuke 63 | 64 | # ------------------------------------------------------------------------- 65 | @property 66 | def in_session(self): 67 | return NUKE_IMPORTED 68 | 69 | # ------------------------------------------------------------------------- 70 | @property 71 | def main_window(self): 72 | 73 | if not NUKE_HAS_UI: 74 | return None 75 | 76 | return QtGui.QApplication.activeWindow() 77 | 78 | # ------------------------------------------------------------------------- 79 | @property 80 | def name(self): 81 | return self.__class__.app_name 82 | 83 | # ----------------------------------------------------------------------------- 84 | SessionRegistry().register(NukeSession) 85 | 86 | -------------------------------------------------------------------------------- /dpa/ui/maya/importref.py: -------------------------------------------------------------------------------- 1 | from dpa.ptask.area import PTaskArea 2 | from dpa.ptask import PTask 3 | from dpa.product.representation import ProductRepresentation 4 | from dpa.maya.session import MayaSession 5 | 6 | 7 | class ImportRef(): 8 | 9 | choices = {} 10 | 11 | # ------------------------------------------------------------------------- 12 | def __init__(self): 13 | self.ses = MayaSession().current() 14 | self.choices[""]="" 15 | 16 | self.ses.cmds.window(menuBar=True, title='Create Reference') 17 | self.ses.cmds.columnLayout() 18 | self.ses.cmds.optionMenu( 'sub', label='Sub', cc=self.setDir) 19 | self.ses.cmds.menuItem( label="") 20 | self.ses.cmds.textFieldGrp('dir', label='Dir', text ="", editable=False) 21 | self.ses.cmds.textFieldGrp('count', label='Count', text=1) 22 | self.ses.cmds.checkBox( 'group', label='Place Under Group?', value=0) 23 | self.ses.cmds.rowLayout(nc=1) 24 | self.ses.cmds.button(label="Import", width= 250, c=self.imp) 25 | 26 | self.populateSubs() 27 | 28 | def show(self): 29 | self.ses.cmds.showWindow() 30 | 31 | def imp( self, *args ): 32 | choose = self.ses.cmds.optionMenu('sub', query=True, value=True) 33 | path = self.ses.cmds.textFieldGrp('dir', query=True, text=True) 34 | count = int(self.ses.cmds.textFieldGrp('count', query=True, text=True)) 35 | grp = int(self.ses.cmds.checkBox('group', query=True, value=True)) 36 | if( path != "" ): 37 | for x in range(0, count): 38 | self.ses.cmds.file(path, r=1, gr=grp, gn="%s%s"%(choose,x), mergeNamespacesOnClash=1, namespace=":") 39 | else: 40 | print "Nothing selected" 41 | 42 | def setDir( self, *args ): 43 | self.ses.cmds.textFieldGrp('dir', edit=True, text='%s'%self.choices[args[0]]) 44 | 45 | def populateSubs(self): 46 | 47 | ptask = self.ses.ptask 48 | 49 | for sub in ptask.latest_version.subscriptions: 50 | product = sub.product_version.product 51 | product_ver = sub.product_version 52 | 53 | product_rep_list = ProductRepresentation.list(product_version=product_ver.spec,type='ma', resolution='none') 54 | 55 | if( len(product_rep_list) == 1 ): 56 | product_rep = product_rep_list[0] 57 | 58 | choose = sub.product_version.product.name 59 | path = '%s/%s.%s'%(product_rep.directory, product.name, product_rep.type) 60 | 61 | self.choices[choose]=path 62 | self.ses.cmds.menuItem( label="%s"%choose ) 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Python setuptools installer module""" 2 | 3 | # ----------------------------------------------------------------------------- 4 | 5 | from codecs import open 6 | from os import pardir, path 7 | from setuptools import setup, find_packages 8 | 9 | # ----------------------------------------------------------------------------- 10 | 11 | AUTHOR = "Clemson Digital Production Arts Program", 12 | 13 | AUTHOR_EMAIL = "jtomlin@clemson.edu" 14 | 15 | CLASSIFIERS = [ 16 | "Development Status :: 2 - Pre-Alpha", 17 | "Intended Audience :: Education", 18 | "Intended Audience :: Developers", 19 | "Intended Audience :: End Users/Desktop", 20 | "License :: OSI Approved :: MIT License", 21 | "Natural Language :: English", 22 | "Operating System :: POSIX", 23 | "Programming Language :: Python :: 2.7", 24 | "Topic :: Education", 25 | "Topic :: Software Development :: Libraries :: Application Frameworks", 26 | "Topic :: Software Development :: Libraries :: Python Modules", 27 | ] 28 | 29 | DESCRIPTION = "DPA pipeline front end API" 30 | 31 | INSTALL_REQUIRES = [ 32 | "colorama", 33 | "ordereddict", 34 | "parsedatetime", 35 | "python-dateutil", 36 | "PyYAML", 37 | "requests", 38 | "rpyc", 39 | "Sphinx", 40 | "sphinx-rtd-theme", 41 | ] 42 | 43 | KEYWORDS = "production pipeline framework", 44 | 45 | LICENSE = 'MIT' 46 | 47 | NAME = 'dpa-pipe' 48 | 49 | PACKAGE_EXCLUDES = [ 50 | 'dpa_site', 51 | ] 52 | 53 | SCRIPTS = [ 54 | 'bin/dpa', 55 | 'bin/dpa_houdini', 56 | 'bin/dpa_uncompress', 57 | 'bin/dpa_ribrender', 58 | ] 59 | 60 | URL = "" # XXX once uploaded to git or bitbucket, set this 61 | 62 | # ----------------------------------------------------------------------------- 63 | 64 | # path to this file's directory 65 | PROJECT_ROOT = path.normpath(path.join(path.abspath(__file__), pardir)) 66 | 67 | # get a list of python packages to install 68 | PACKAGES = find_packages(exclude=PACKAGE_EXCLUDES) 69 | 70 | # get the long description 71 | with open(path.join(PROJECT_ROOT, 'README.rst'), encoding='utf-8') as f: 72 | LONG_DESCRIPTION = f.read() 73 | 74 | # fetch __version__ from the python package 75 | exec(open(path.join(PROJECT_ROOT, 'dpa', '__init__.py')).read()) 76 | VERSION = __version__ 77 | 78 | # ----------------------------------------------------------------------------- 79 | 80 | setup( 81 | author=AUTHOR, 82 | author_email=AUTHOR_EMAIL, 83 | classifiers=CLASSIFIERS, 84 | description=DESCRIPTION, 85 | install_requires=INSTALL_REQUIRES, 86 | include_package_data=True, 87 | keywords=KEYWORDS, 88 | license=LICENSE, 89 | long_description=LONG_DESCRIPTION, 90 | name=NAME, 91 | packages=PACKAGES, 92 | scripts=SCRIPTS, 93 | url=URL, 94 | version=VERSION, 95 | ) 96 | 97 | -------------------------------------------------------------------------------- /dpa/houdini/pyqt_houdini.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module helps you use PyQt in Houdini's GUI by integrating PyQt's event 3 | loop into Houdini's. Replace calls to QApplication.exec_() in your 4 | code with calls to pyqt_houdini.exec_(app). 5 | """ 6 | 7 | from PySide import QtCore 8 | from PySide import QtGui 9 | import hou 10 | 11 | class IntegratedEventLoop(object): 12 | """This class behaves like QEventLoop except it allows PyQt to run inside 13 | Houdini's event loop on the main thread. You probably just want to 14 | call exec_() below instead of using this class directly. 15 | """ 16 | def __init__(self, application, dialogs): 17 | # We need the application to send posted events. We hold a reference 18 | # to any dialogs to ensure that they don't get garbage collected 19 | # (and thus close in the process). The reference count for this object 20 | # will go to zero when it removes itself from Houdini's event loop. 21 | self.application = application 22 | self.dialogs = dialogs 23 | self.event_loop = QtCore.QEventLoop() 24 | 25 | def exec_(self): 26 | hou.ui.addEventLoopCallback(self.processEvents) 27 | 28 | def processEvents(self): 29 | # There is no easy way to know when the event loop is done. We can't 30 | # use QEventLoop.isRunning() because it always returns False since 31 | # we're not inside QEventLoop.exec_(). We can't rely on a 32 | # lastWindowClosed signal because the window is usually made invisible 33 | # instead of closed. Instead, we need to explicitly check if any top 34 | # level widgets are still visible. 35 | if not anyQtWindowsAreOpen(): 36 | hou.ui.removeEventLoopCallback(self.processEvents) 37 | 38 | self.event_loop.processEvents() 39 | self.application.sendPostedEvents(None, 0) 40 | 41 | def anyQtWindowsAreOpen(): 42 | return any(w.isVisible() for w in QtGui.QApplication.topLevelWidgets()) 43 | 44 | def exec_(application, *args): 45 | """You cannot call QApplication.exec_, or Houdini will freeze while PyQt 46 | waits for and processes events. Instead, call this function to allow 47 | Houdini's and PyQt's event loops to coexist. Pass in any dialogs as 48 | extra arguments, if you want to ensure that something holds a reference 49 | to them while the event loop runs. 50 | 51 | This function returns right away. 52 | """ 53 | IntegratedEventLoop(application, args).exec_() 54 | 55 | def execSynchronously(application, *args): 56 | """This function is like exec_, except it will not return until all PyQt 57 | windows have closed. Houdini will remain responsive while the PyQt window 58 | is open. 59 | """ 60 | exec_(application, *args) 61 | hou.ui.waitUntil(lambda: not anyQtWindowsAreOpen()) 62 | 63 | -------------------------------------------------------------------------------- /dpa/data/plugins/mari/init.py: -------------------------------------------------------------------------------- 1 | # Mari startup code. If MARI_SCRIPT_PATH points to this file's parent directory, 2 | # then this file will run automatically. This code will act as a broker for 3 | # importing mari plugins found within the current pipeline context. 4 | 5 | # ----------------------------------------------------------------------------- 6 | 7 | import imp 8 | import os 9 | import shlex 10 | import subprocess 11 | import sys 12 | import traceback 13 | 14 | # ----------------------------------------------------------------------------- 15 | def _populate_sys_path(): 16 | 17 | # hack to get regular python session's paths. assumes mari python version 18 | # matches system python version. 19 | 20 | # this is required to prevent the external python process from barfing 21 | try: 22 | del os.environ['PYTHONHOME'] 23 | except KeyError: 24 | pass 25 | 26 | # get the paths from the system python 27 | cmd = "python -c 'import sys;print sys.path'" 28 | paths_str = subprocess.Popen(shlex.split(cmd), 29 | stdout=subprocess.PIPE).communicate()[0] 30 | paths = eval(paths_str) 31 | 32 | # if path is not in mari's sys path, append it. 33 | for path in paths: 34 | if path and path not in sys.path: 35 | sys.path.append(path) 36 | 37 | # ----------------------------------------------------------------------------- 38 | def _import_mari_plugins(): 39 | 40 | from dpa.ptask.area import PTaskArea 41 | 42 | # first, get the context 43 | ptask_area = PTaskArea.current() 44 | 45 | # get a list of mari plugin directories 46 | plugin_dirs = ptask_area.ancestor_paths( 47 | 'plugins/mari', include_install=False) 48 | 49 | for plugin_dir in reversed(plugin_dirs): 50 | 51 | if not os.path.isdir(plugin_dir): 52 | continue 53 | 54 | file_names = os.listdir(plugin_dir) 55 | 56 | for file_name in file_names: 57 | 58 | # only python files 59 | if not file_name.endswith(".py"): 60 | continue 61 | 62 | full_path = os.path.join(plugin_dir, file_name) 63 | 64 | module_name = 'mari_plugin_' + file_name.replace(".", "_") 65 | 66 | try: 67 | module = imp.load_source(module_name, full_path) 68 | except Exception as e: 69 | print "Unable to load mari plugin: " + full_path 70 | traceback.print_exc() 71 | 72 | # ----------------------------------------------------------------------------- 73 | 74 | # get the python path setup 75 | _populate_sys_path() 76 | 77 | # register MariSession with SessionFactory 78 | print "Loading MariSession" 79 | import dpa.mari.session 80 | 81 | # register Mari Entityies 82 | print "Loading Mari Entities" 83 | import dpa.mari.entity.maps 84 | import dpa.mari.entity.geom 85 | 86 | # do the loading 87 | print "Loading DPA mari plugins..." 88 | _import_mari_plugins() 89 | 90 | -------------------------------------------------------------------------------- /dpa/data/plugins/nuke/init.py: -------------------------------------------------------------------------------- 1 | # Nuke startup code. If NUKE_PATH points to this file's parent directory, 2 | # then this file will run automatically. This code will act as a broker for 3 | # importing nuke plugins found within the current pipeline context. 4 | 5 | # ----------------------------------------------------------------------------- 6 | 7 | import imp 8 | import os 9 | import shlex 10 | import subprocess 11 | import sys 12 | import traceback 13 | 14 | import nuke 15 | 16 | # ----------------------------------------------------------------------------- 17 | def _populate_sys_path(): 18 | 19 | # hack to get regular python session's paths. assumes nuke python version 20 | # matches system python version. 21 | 22 | # this is required to prevent the external python process from barfing 23 | try: 24 | del os.environ['PYTHONHOME'] 25 | except KeyError: 26 | pass 27 | 28 | # get the paths from the system python 29 | cmd = "python -c 'import sys;print sys.path'" 30 | paths_str = subprocess.Popen(shlex.split(cmd), 31 | stdout=subprocess.PIPE).communicate()[0] 32 | paths = eval(paths_str) 33 | 34 | # if path is not in nuke's sys path, append it. 35 | for path in paths: 36 | if path and path not in sys.path: 37 | sys.path.append(path) 38 | 39 | # ----------------------------------------------------------------------------- 40 | def _import_nuke_plugins(): 41 | 42 | from dpa.ptask.area import PTaskArea 43 | 44 | # first, get the context 45 | ptask_area = PTaskArea.current() 46 | 47 | # get a list of nuke plugin directories 48 | plugin_dirs = ptask_area.ancestor_paths( 49 | 'plugins/nuke', include_install=False) 50 | 51 | for plugin_dir in reversed(plugin_dirs): 52 | 53 | if not os.path.isdir(plugin_dir): 54 | continue 55 | 56 | file_names = os.listdir(plugin_dir) 57 | 58 | for file_name in file_names: 59 | 60 | # only python files 61 | if not file_name.endswith(".py"): 62 | continue 63 | 64 | full_path = os.path.join(plugin_dir, file_name) 65 | 66 | module_name = 'nuke_plugin_' + file_name.replace(".", "_") 67 | 68 | try: 69 | module = imp.load_source(module_name, full_path) 70 | except Exception as e: 71 | print "Unable to load nuke plugin: " + full_path 72 | traceback.print_exc() 73 | 74 | # ----------------------------------------------------------------------------- 75 | 76 | # get the python path setup 77 | _populate_sys_path() 78 | 79 | # register NukeSession with SessionFactory 80 | print "Loading NukeSession" 81 | import dpa.nuke.session 82 | 83 | # register Nuke Entityies 84 | #print "Loading Nuke Entities" 85 | 86 | # do the loading 87 | print "Loading DPA nuke plugins..." 88 | _import_nuke_plugins() 89 | 90 | print "Loading nuke utils..." 91 | import dpa.nuke.utils 92 | 93 | -------------------------------------------------------------------------------- /dpa/queue/__init__.py: -------------------------------------------------------------------------------- 1 | """The code in here is specific to clemson dpa. 2 | 3 | Reimplement these based on how your facility submits tasks to your queue and 4 | monkey path. Given more time, I'd like to think of a better way to do this. 5 | 6 | """ 7 | 8 | # ----------------------------------------------------------------------------- 9 | 10 | import datetime 11 | import os 12 | 13 | from dpa.ptask.area import PTaskArea 14 | from dpa.user import current_username 15 | 16 | QUEUE = 'cheesyq' 17 | 18 | # ----------------------------------------------------------------------------- 19 | def get_unique_id(area_spec="", id_extra=None, dt=None): 20 | 21 | if not dt: 22 | dt = datetime.datetime.now() 23 | 24 | if id_extra: 25 | id_extra = "_" + id_extra 26 | else: 27 | id_extra = "" 28 | 29 | return "{u}_{t}_{s}{e}".format( 30 | u=current_username(), 31 | t=dt.strftime("%Y_%m_%d_%H_%M_%S"), 32 | s=area_spec.replace('=', '_'), 33 | e=id_extra, 34 | ) 35 | 36 | # ----------------------------------------------------------------------------- 37 | def queue_submit_cmd(command, queue_name, output_file=None, id_extra=None, 38 | dt=None): 39 | """Create and submit a shell script with the given command.""" 40 | 41 | ptask_area = PTaskArea.current() 42 | ptask_area.provision(QUEUE) 43 | script_dir = ptask_area.dir(dir_name=QUEUE) 44 | 45 | unique_id = get_unique_id(ptask_area.spec, id_extra=id_extra, dt=dt) 46 | 47 | script_name = unique_id + '.sh' 48 | log_name = unique_id + '.log' 49 | 50 | script_path = os.path.join(script_dir, script_name) 51 | log_path = os.path.join(script_dir, log_name) 52 | 53 | with open(script_path, "w") as script_file: 54 | script_file.write("#!/bin/bash\n") 55 | script_file.write(command + "\n") 56 | script_file.write("chmod 660 " + output_file + "\n") 57 | 58 | os.chmod(script_path, 0770) 59 | 60 | create_queue_task(queue_name, script_path, unique_id, 61 | output_file=output_file, submit=True, log_path=log_path) 62 | 63 | # ----------------------------------------------------------------------------- 64 | def create_queue_task(queue_name, script_path, unique_id, output_file=None, 65 | submit=True, log_path=None): 66 | 67 | # ---- submit to the queue 68 | 69 | from cheesyq import DPACheesyQ, DPADataLibrary, DPACheesyQTasks 70 | 71 | data_lib = DPADataLibrary.DjangoLibrary(None) 72 | 73 | render_task = DPACheesyQ.RenderTask() 74 | render_task.taskid = unique_id 75 | 76 | if log_path: 77 | render_task.logFileName = log_path 78 | 79 | if output_file: 80 | render_task.outputFileName = output_file 81 | render_task.outputLocation = os.path.dirname(output_file) 82 | 83 | data_lib.set(render_task.taskid, render_task) 84 | render_task.addTask(script_path) 85 | 86 | if submit: 87 | os.system("cqresubmittask {qn} {tid}".format( 88 | qn=queue_name, 89 | tid=render_task.taskid 90 | )) 91 | 92 | print "Submitted task: " + str(render_task.taskid) 93 | 94 | -------------------------------------------------------------------------------- /dpa/ptask/history.py: -------------------------------------------------------------------------------- 1 | 2 | # ----------------------------------------------------------------------------- 3 | # Imports: 4 | # ----------------------------------------------------------------------------- 5 | 6 | import os 7 | 8 | from dpa.env.vars import DpaVars 9 | 10 | # ----------------------------------------------------------------------------- 11 | # Classes: 12 | # ----------------------------------------------------------------------------- 13 | class PTaskHistory(object): 14 | 15 | # ------------------------------------------------------------------------- 16 | # Special methods: 17 | # ------------------------------------------------------------------------- 18 | def __init__(self, history_file=None, history_size=None): 19 | 20 | if history_file is None: 21 | history_file = DpaVars.ptask_history_file().get() 22 | 23 | if history_size is None: 24 | history_size = DpaVars.ptask_history_size().get() 25 | 26 | self.history_file = os.path.expanduser(history_file) 27 | self.history_size = history_size 28 | 29 | # ------------------------------------------------------------------------- 30 | # Instance methods: 31 | # ------------------------------------------------------------------------- 32 | def add(self, ptask_spec): 33 | """Adds a new ptask_spec to the history file.""" 34 | 35 | # create the file if it doesn't exist 36 | if not os.path.exists(self.history_file): 37 | open(self.history_file, "a").close() 38 | 39 | with open(self.history_file, 'r+') as history: 40 | specs = history.read().strip() 41 | specs = [l for l in specs.split(os.linesep) if l] 42 | specs.append(ptask_spec) 43 | specs = specs[-1 * self.history_size:] 44 | history.seek(0) 45 | history.write(os.linesep.join(specs)) 46 | history.truncate() 47 | 48 | # ------------------------------------------------------------------------- 49 | def get(self): 50 | """:returns: :py:obj:`list` of ptask specs from the history file.""" 51 | 52 | try: 53 | with open(self.history_file, 'r') as history: 54 | specs = history.read().strip().split("\n") 55 | except IOError: 56 | return [] 57 | 58 | return specs 59 | 60 | # ------------------------------------------------------------------------- 61 | # Instance properties: 62 | # ------------------------------------------------------------------------- 63 | @property 64 | def latest(self): 65 | """:returns: a spec for the latest ptask spec set in the history.""" 66 | 67 | try: 68 | return self.get()[-1] 69 | except IndexError: 70 | return None 71 | 72 | # ------------------------------------------------------------------------- 73 | @property 74 | def previous(self): 75 | """:returns: a spec for the next to last ptask spec set in history.""" 76 | 77 | try: 78 | return self.get()[-2] 79 | except IndexError: 80 | return None 81 | 82 | -------------------------------------------------------------------------------- /dpa/notify/action/ooto.py: -------------------------------------------------------------------------------- 1 | 2 | from .base import BaseNotifyAction 3 | from dpa.action import ActionError 4 | from dpa.config import Config 5 | from dpa.ptask.area import PTaskArea 6 | from dpa.shell.output import Output, Style 7 | from dpa.user import User, UserError 8 | 9 | # ----------------------------------------------------------------------------- 10 | 11 | OOTO_CONFIG_PATH = "config/notify/ooto.cfg" 12 | 13 | # ----------------------------------------------------------------------------- 14 | class OotoNotifyAction(BaseNotifyAction): 15 | """Out of the office notification action.""" 16 | 17 | name = "ooto" 18 | 19 | # ------------------------------------------------------------------------- 20 | def prompt(self): 21 | 22 | if self._message is None: 23 | self._message = Output.prompt_text_block( 24 | Style.bright + \ 25 | "\nEnter OOTO message" + \ 26 | Style.normal, 27 | blank=False, 28 | help_str="OOTO message can't be blank.", 29 | ) 30 | 31 | # ------------------------------------------------------------------------- 32 | def validate(self): 33 | 34 | if not self._message: 35 | raise ActionError("Message can not be empty.") 36 | 37 | try: 38 | self._sender = User.current() 39 | except UserError: 40 | raise ActionError("Could not identify current user.") 41 | 42 | # get ooto notification recipients from the configs 43 | ptask_area = PTaskArea.current() 44 | ooto_config = ptask_area.config( 45 | OOTO_CONFIG_PATH, 46 | composite_ancestors=True, 47 | composite_method="append", 48 | ) 49 | 50 | ooto_notify = ooto_config.get('notify', []) 51 | self._to.extend(ooto_notify) 52 | 53 | # for all usernames specified, make sure they're a valid user, 54 | # get their email addresses. 55 | recipients = set() 56 | for recipient in self._to: 57 | 58 | # assume already a valid email address 59 | if "@" in recipient: 60 | recipients.add(recipient) 61 | else: 62 | try: 63 | recipient = User.get(recipient) 64 | except UserError: 65 | raise ActionError( 66 | "Could not identify user: " + str(recipient) 67 | ) 68 | else: 69 | recipients.add(recipient.email) 70 | 71 | if not recipients: 72 | recipients = [self._sender.email] 73 | 74 | self._to = recipients 75 | 76 | # tag the message with a signature, including the current ptask area 77 | # if there is one. 78 | if ptask_area: 79 | self._message += "\n\nCurrent ptask: {p}".format(p=ptask_area.spec) 80 | self._message += "\n\n- {s}".format(s=self._sender.full_name) 81 | 82 | self._subject = "OOTO: {fn} ({un})".format( 83 | fn=self.sender.full_name, 84 | un=self.sender.username, 85 | ) 86 | 87 | -------------------------------------------------------------------------------- /dpa/ui/app/session.py: -------------------------------------------------------------------------------- 1 | 2 | from PySide import QtCore, QtGui 3 | 4 | from dpa.app.session import Session, SessionRegistry, SessionError 5 | from dpa.ui.action.options import ActionOptionWidget 6 | 7 | # ----------------------------------------------------------------------------- 8 | class SessionDialog(QtGui.QDialog): 9 | 10 | # ------------------------------------------------------------------------- 11 | def __init__(self, parent=None): 12 | 13 | self._session = SessionRegistry().current() 14 | if not self._session: 15 | raise SessionError("Unable to determine current app session.") 16 | 17 | if not parent: 18 | parent = self._session.main_window 19 | 20 | super(SessionDialog, self).__init__(parent=parent) 21 | 22 | # ------------------------------------------------------------------------- 23 | @property 24 | def session(self): 25 | return self._session 26 | 27 | # ----------------------------------------------------------------------------- 28 | class SessionActionDialog(SessionDialog): 29 | 30 | # ------------------------------------------------------------------------- 31 | def __init__(self, title, options_config, icon_path=None, 32 | action_button_text=None, modal=False): 33 | 34 | super(SessionActionDialog, self).__init__() 35 | 36 | self.setModal(modal) 37 | self.setWindowTitle(title) 38 | 39 | icon_lbl = QtGui.QLabel() 40 | icon_lbl.setPixmap(QtGui.QPixmap(icon_path)) 41 | icon_lbl.setAlignment(QtCore.Qt.AlignRight) 42 | 43 | title = QtGui.QLabel(title) 44 | title.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) 45 | font = title.font() 46 | font.setPointSize(18) 47 | title.setFont(font) 48 | 49 | header_layout = QtGui.QHBoxLayout() 50 | header_layout.addWidget(icon_lbl) 51 | header_layout.addWidget(title) 52 | header_layout.setStretchFactor(title, 90) 53 | 54 | self._options = ActionOptionWidget(options_config) 55 | 56 | self._btn_box = QtGui.QDialogButtonBox() 57 | self._btn_box.addButton(QtGui.QDialogButtonBox.Cancel) 58 | self._action_btn = self._btn_box.addButton(QtGui.QDialogButtonBox.Ok) 59 | 60 | if action_button_text: 61 | self._action_btn.setText(action_button_text) 62 | 63 | layout = QtGui.QVBoxLayout(self) 64 | layout.addLayout(header_layout) 65 | layout.addWidget(self._options) 66 | layout.addWidget(self._btn_box) 67 | 68 | self._options.value_changed.connect(self.check_value) 69 | 70 | self._btn_box.accepted.connect(self.accept) 71 | self._btn_box.rejected.connect(self.reject) 72 | 73 | self.check_value() 74 | 75 | # ------------------------------------------------------------------------- 76 | @property 77 | def options(self): 78 | return self._options 79 | 80 | # ------------------------------------------------------------------------- 81 | def check_value(self): 82 | 83 | if self._options.value_ok: 84 | self._action_btn.setEnabled(True) 85 | else: 86 | self._action_btn.setEnabled(False) 87 | 88 | -------------------------------------------------------------------------------- /dpa/notify/action/base.py: -------------------------------------------------------------------------------- 1 | 2 | from abc import ABCMeta, abstractmethod 3 | import smtplib 4 | from email.mime.text import MIMEText 5 | 6 | from dpa.action import Action, ActionError 7 | from dpa.config import Config 8 | from dpa.notify import Notification 9 | 10 | # ----------------------------------------------------------------------------- 11 | class BaseNotifyAction(Action): 12 | """Bse notification action.""" 13 | 14 | __metaclas__ = ABCMeta 15 | 16 | name = None 17 | 18 | # ------------------------------------------------------------------------- 19 | @classmethod 20 | def setup_cl_args(cls, parser): 21 | 22 | # ptask spec (name, parent) - can be relative to current ptask 23 | parser.add_argument( 24 | "-m", "--message", 25 | default=None, 26 | help="Body of the message to send.", 27 | ) 28 | 29 | parser.add_argument( 30 | "-s", "--subject", 31 | default=None, 32 | help="Subject of the message to send.", 33 | ) 34 | 35 | parser.add_argument( 36 | "-t", "--to", 37 | nargs="+", 38 | default=[], 39 | help="Space separated list of recipient usernames and/or emails", 40 | ) 41 | 42 | # ------------------------------------------------------------------------- 43 | def __init__(self, message, subject=None, to=None): 44 | 45 | super(BaseNotifyAction, self).__init__(message, subject=subject, to=to) 46 | 47 | self._message = message 48 | self._subject = subject 49 | self._to = to if to else [] 50 | 51 | # ------------------------------------------------------------------------- 52 | def execute(self): 53 | 54 | sender = self.sender.email 55 | 56 | recipients = self.to 57 | recipients.add(sender) 58 | 59 | notification = Notification( 60 | self.subject, self.message, list(recipients), sender) 61 | notification.send_email() 62 | 63 | if self.interactive: 64 | print "\nNotification sent!\n" 65 | 66 | # ------------------------------------------------------------------------- 67 | @abstractmethod 68 | def prompt(self): 69 | pass 70 | 71 | # ------------------------------------------------------------------------- 72 | def undo(self): 73 | pass 74 | 75 | # ------------------------------------------------------------------------- 76 | @abstractmethod 77 | def validate(self): 78 | pass 79 | 80 | # ------------------------------------------------------------------------- 81 | @property 82 | def sender(self): 83 | return self._sender 84 | 85 | # ------------------------------------------------------------------------- 86 | @property 87 | def subject(self): 88 | return self._subject 89 | 90 | # ------------------------------------------------------------------------- 91 | @property 92 | def message(self): 93 | return self._message 94 | 95 | # ------------------------------------------------------------------------- 96 | @property 97 | def to(self): 98 | return self._to 99 | 100 | -------------------------------------------------------------------------------- /dpa/user/action/info.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Imports: 3 | # ----------------------------------------------------------------------------- 4 | 5 | from dpa.action import Action 6 | from dpa.shell.output import Output, Style 7 | from dpa.user import User, UserError, current_username 8 | 9 | # ----------------------------------------------------------------------------- 10 | # Classes: 11 | # ----------------------------------------------------------------------------- 12 | class UserPrintInfoAction(Action): 13 | """ Print information about a user.""" 14 | 15 | name = "info" 16 | target_type = "user" 17 | 18 | # ------------------------------------------------------------------------- 19 | # Class methods: 20 | # ------------------------------------------------------------------------- 21 | @classmethod 22 | def setup_cl_args(cls, parser): 23 | 24 | parser.add_argument( 25 | "username", 26 | nargs="?", 27 | default=current_username(), 28 | help="Print info for the supplied username." 29 | ) 30 | 31 | # ------------------------------------------------------------------------- 32 | # Special methods: 33 | # ------------------------------------------------------------------------- 34 | def __init__(self, username): 35 | 36 | super(UserPrintInfoAction, self).__init__(username) 37 | self._username = username 38 | 39 | # ------------------------------------------------------------------------- 40 | # Methods: 41 | # ------------------------------------------------------------------------- 42 | def execute(self): 43 | 44 | try: 45 | user = User.get(self.username) 46 | except UserError: 47 | self.logger.error( 48 | 'Could not determine user from: "{u}"'.format(u=self.username) 49 | ) 50 | raise 51 | 52 | # output headers. reused while defining the look of the output 53 | username = 'Username' 54 | last_name = 'Last' 55 | first_name = 'First' 56 | email = 'Email' 57 | active = 'Active' 58 | 59 | # define the look of the output 60 | output = Output() 61 | # defining the data headers 62 | output.header_names = [username, email] 63 | output.add_item( 64 | { 65 | username: user.username, 66 | email: user.email, 67 | }, 68 | color_all=Style.bright, 69 | ) 70 | 71 | # build the title 72 | title = " {u.first_name} {u.last_name}".format(u=user) 73 | if not user.is_active: 74 | title += " [INACTIVE]" 75 | title += " " 76 | output.title = title 77 | 78 | # dump the output as a list of key/value pairs 79 | output.dump() 80 | 81 | # ------------------------------------------------------------------------- 82 | def undo(self): 83 | pass 84 | 85 | # ------------------------------------------------------------------------- 86 | # Properties: 87 | # ------------------------------------------------------------------------- 88 | @property 89 | def username(self): 90 | return self._username 91 | 92 | -------------------------------------------------------------------------------- /dpa/ui/app/entity/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from PySide import QtCore, QtGui 3 | 4 | # ----------------------------------------------------------------------------- 5 | class EntityTreeWidget(QtGui.QTreeWidget): 6 | 7 | # ------------------------------------------------------------------------- 8 | def __init__(self, entities=None, parent=None): 9 | 10 | self._category_items = dict() 11 | self._entity_items = [] 12 | 13 | super(EntityTreeWidget, self).__init__(parent=parent) 14 | 15 | self.setAllColumnsShowFocus(True) 16 | #self.setAlternatingRowColors(True) 17 | self.setAnimated(True) 18 | self.setHeaderLabels(['Entity']) 19 | self.setRootIsDecorated(False) 20 | self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) 21 | 22 | if entities: 23 | self.add_entities(entities) 24 | 25 | # ------------------------------------------------------------------------- 26 | def add_category(self, category): 27 | 28 | category_item = QtGui.QTreeWidgetItem([category]) 29 | category_item.setFlags(QtCore.Qt.ItemIsEnabled) 30 | self.addTopLevelItem(category_item) 31 | category_item.setExpanded(True) 32 | self._category_items[category] = category_item 33 | 34 | # ------------------------------------------------------------------------- 35 | def add_entities(self, entities): 36 | 37 | for entity in entities: 38 | self.add_entity(entity) 39 | 40 | # ------------------------------------------------------------------------- 41 | def add_entity(self, entity): 42 | 43 | if not entity.category in self._category_items.keys(): 44 | self.add_category(entity.category) 45 | 46 | category_item = self._category_items[entity.category] 47 | 48 | entity_item = EntityTreeWidgetItem(entity) 49 | category_item.addChild(entity_item) 50 | 51 | self.entity_items.append(entity_item) 52 | 53 | # ------------------------------------------------------------------------- 54 | def select_all_entities(self): 55 | 56 | for entity_item in self.entity_items: 57 | entity_item.setSelected(True) 58 | 59 | # ------------------------------------------------------------------------- 60 | def selected_entities(self): 61 | 62 | entities = [] 63 | for entity_item in self.entity_items: 64 | if entity_item.isSelected(): 65 | entities.append(entity_item.entity) 66 | 67 | return entities 68 | 69 | # ------------------------------------------------------------------------- 70 | @property 71 | def entity_items(self): 72 | return self._entity_items 73 | 74 | # ----------------------------------------------------------------------------- 75 | class EntityTreeWidgetItem(QtGui.QTreeWidgetItem): 76 | 77 | # ------------------------------------------------------------------------- 78 | def __init__(self, entity): 79 | 80 | self._entity = entity 81 | 82 | super(EntityTreeWidgetItem, self).__init__([entity.display_name]) 83 | self.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled) 84 | 85 | # ------------------------------------------------------------------------- 86 | @property 87 | def entity(self): 88 | return self._entity 89 | 90 | -------------------------------------------------------------------------------- /dpa/notify/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import smtplib 3 | from email.mime.text import MIMEText 4 | from dpa.user import User, UserError 5 | 6 | # ----------------------------------------------------------------------------- 7 | class Notification(object): 8 | 9 | # ------------------------------------------------------------------------- 10 | def __init__(self, subject, message, recipients, sender=None): 11 | 12 | self._subject = subject 13 | self._message = message 14 | self._recipients = list(set(recipients)) 15 | self._sender = sender 16 | 17 | # ------------------------------------------------------------------------- 18 | def send_email(self): 19 | 20 | msg = MIMEText(self.message) 21 | msg['Subject'] = self.subject 22 | 23 | if self.sender: 24 | msg['From'] = self.sender 25 | 26 | recipients = ", ".join(self.recipients) 27 | msg['To'] = recipients 28 | 29 | s = smtplib.SMTP('localhost') 30 | try: 31 | s.sendmail(self.sender, self.recipients, msg.as_string()) 32 | except Exception as e: 33 | raise NotificationError("Failed to send notification: " + str(e)) 34 | 35 | s.quit() 36 | 37 | # ------------------------------------------------------------------------- 38 | @property 39 | def message(self): 40 | return self._message 41 | 42 | # ------------------------------------------------------------------------- 43 | @message.setter 44 | def message(self, msg): 45 | self._message = msg 46 | 47 | # ------------------------------------------------------------------------- 48 | @property 49 | def recipients(self): 50 | return self._recipients 51 | 52 | # ------------------------------------------------------------------------- 53 | @recipients.setter 54 | def recipients(self, email_addrs): 55 | self._recipients = email_addrs 56 | 57 | # ------------------------------------------------------------------------- 58 | @property 59 | def sender(self): 60 | return self._sender 61 | 62 | # ------------------------------------------------------------------------- 63 | @sender.setter 64 | def sender(self, addr): 65 | self._sender = addr 66 | 67 | # ------------------------------------------------------------------------- 68 | @property 69 | def subject(self): 70 | return self._subject 71 | 72 | # ------------------------------------------------------------------------- 73 | @subject.setter 74 | def subject(self, sub): 75 | self._subject = sub 76 | 77 | # ----------------------------------------------------------------------------- 78 | def emails_from_unames(unames): 79 | 80 | emails = set() 81 | 82 | for uname in unames: 83 | 84 | # assume already a valid email address 85 | if "@" in uname: 86 | emails.add(uname) 87 | else: 88 | try: 89 | user = User.get(uname) 90 | except UserError: 91 | raise NotificationError("Could not identify user: " + str(uname)) 92 | else: 93 | emails.add(user.email) 94 | 95 | return list(emails) 96 | 97 | # ----------------------------------------------------------------------------- 98 | class NotificationError(Exception): 99 | pass 100 | 101 | -------------------------------------------------------------------------------- /dpa/maya/entity/camera.py: -------------------------------------------------------------------------------- 1 | 2 | import os.path 3 | import re 4 | 5 | from dpa.app.entity import EntityRegistry, EntityError 6 | from dpa.maya.entity.base import SetBasedWorkfileEntity 7 | 8 | # ----------------------------------------------------------------------------- 9 | class CameraEntity(SetBasedWorkfileEntity): 10 | 11 | category = "camera" 12 | 13 | # ------------------------------------------------------------------------- 14 | @classmethod 15 | def import_product_representation(cls, session, representation, *args, 16 | **kwargs): 17 | 18 | if representation.type == 'ma': 19 | super(CameraEntity, cls).import_product_representation( 20 | session, representation, *args, **kwargs) 21 | else: 22 | if representation.type != 'fbx': 23 | raise EntityError( 24 | "Unknown type for {cat} import: {typ}".format( 25 | cls=cls.category, typ=representation.type)) 26 | 27 | cls._fbx_import(session, representation, *args, **kwargs) 28 | 29 | # ------------------------------------------------------------------------- 30 | def export(self, product_desc=None, version_note=None, fbx_export=False, 31 | fbx_options=None, ma_export=False, ma_options=None): 32 | 33 | product_reprs = [] 34 | 35 | if fbx_export: 36 | product_reprs.extend( 37 | self._fbx_export(fbx_options, product_desc, version_note) 38 | ) 39 | 40 | if ma_export: 41 | product_reprs.extend( 42 | self._ma_export(ma_options, product_desc, version_note) 43 | ) 44 | 45 | return product_reprs 46 | 47 | # ------------------------------------------------------------------------- 48 | def _fbx_export(self, options, product_desc, version_note): 49 | 50 | self.session.require_plugin('fbxmaya') 51 | 52 | file_type = 'fbx' 53 | 54 | product_repr = self._create_product(product_desc, version_note, 55 | file_type) 56 | product_repr_dir = product_repr.directory 57 | 58 | export_objs = self.get_export_objects() 59 | 60 | export_path = os.path.join(product_repr_dir, self.display_name) 61 | 62 | with self.session.selected(export_objs): 63 | self.session.mel.eval( 64 | 'FBXExport -f "{path}" -s'.format(path=export_path)) 65 | 66 | product_repr.area.set_permissions(0660) 67 | 68 | return [product_repr] 69 | 70 | # ------------------------------------------------------------------------- 71 | def _ma_export(self, options, product_desc, version_note): 72 | 73 | file_type = 'ma' 74 | 75 | product_repr = self._create_product(product_desc, version_note, 76 | file_type) 77 | product_repr_dir = product_repr.directory 78 | product_repr_file = os.path.join( 79 | product_repr_dir, self.display_name + "." + file_type) 80 | 81 | export_objs = self.get_export_objects() 82 | 83 | with self.session.selected(export_objs): 84 | self.session.cmds.file( 85 | product_repr_file, 86 | type='mayaAscii', 87 | exportSelected=True, 88 | force=True, 89 | preserveReferences=False, 90 | ) 91 | 92 | product_repr.area.set_permissions(0660) 93 | 94 | return [product_repr] 95 | 96 | # ----------------------------------------------------------------------------- 97 | EntityRegistry().register('maya', CameraEntity) 98 | 99 | -------------------------------------------------------------------------------- /dpa/notify/action/fail.py: -------------------------------------------------------------------------------- 1 | 2 | from .base import BaseNotifyAction 3 | from dpa.action import ActionError 4 | from dpa.config import Config 5 | from dpa.ptask import PTaskArea 6 | from dpa.shell.output import Output, Style 7 | from dpa.user import User, UserError 8 | 9 | # ----------------------------------------------------------------------------- 10 | 11 | FAIL_CONFIG_PATH = "config/notify/fail.cfg" 12 | 13 | # ----------------------------------------------------------------------------- 14 | class FailNotifyAction(BaseNotifyAction): 15 | """Fail notification action.""" 16 | 17 | name = "fail" 18 | 19 | # ------------------------------------------------------------------------- 20 | def prompt(self): 21 | 22 | if self._subject is None: 23 | self._subject = Output.prompt( 24 | Style.bright + \ 25 | "\nEnter a one line description of the problem" + \ 26 | Style.normal, 27 | blank=False, 28 | help_str="Subject line can't be blank.", 29 | separator = ":\n" 30 | ) 31 | 32 | if self._message is None: 33 | self._message = Output.prompt_text_block( 34 | Style.bright + \ 35 | "\nEnter a detailed description of the problem.\n" + \ 36 | Style.normal + \ 37 | " Include steps to replicate, errors, etc below.\n" + \ 38 | " You can copy/paste below as well.\n", 39 | blank=False, 40 | help_str="Message can't be blank.", 41 | ) 42 | 43 | # ------------------------------------------------------------------------- 44 | def validate(self): 45 | 46 | if not self._subject: 47 | raise ActionError("Subject can't be empty.") 48 | 49 | self._subject = "FAIL: " + self._subject 50 | 51 | if not self._message: 52 | raise ActionError("Message can't be empty.") 53 | 54 | try: 55 | self._sender = User.current() 56 | except UserError: 57 | raise ActionError("Could not identify current user.") 58 | 59 | # get fail notification recipients from the configs 60 | ptask_area = PTaskArea.current() 61 | fail_config = ptask_area.config( 62 | FAIL_CONFIG_PATH, 63 | composite_ancestors=True, 64 | composite_method="append", 65 | ) 66 | 67 | fail_notify = fail_config.get('notify', []) 68 | self._to.extend(fail_notify) 69 | 70 | # for all usernames specified, make sure they're a valid user, 71 | # get their email addresses. 72 | recipients = set() 73 | for recipient in self._to: 74 | 75 | # assume already a valid email address 76 | if "@" in recipient: 77 | recipients.add(recipient) 78 | else: 79 | try: 80 | recipient = User.get(recipient) 81 | except UserError: 82 | raise ActionError( 83 | "Could not identify user: " + str(recipient) 84 | ) 85 | else: 86 | recipients.add(recipient.email) 87 | 88 | if not recipients: 89 | recipients = [self._sender.email] 90 | 91 | self._to = recipients 92 | 93 | # tag the message with a signature, including the current ptask area 94 | # if there is one. 95 | if ptask_area: 96 | self._message += "\n\nCurrent ptask: {p}".format(p=ptask_area.spec) 97 | self._message += "\n\n- {s}".format(s=self._sender.full_name) 98 | 99 | -------------------------------------------------------------------------------- /dpa/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """Pipeline customizations on top of the built-in argparse API.""" 2 | 3 | # ----------------------------------------------------------------------------- 4 | # Module: dpa.argparse 5 | # Contact: Josh Tomlinson (jtomlin) 6 | # ----------------------------------------------------------------------------- 7 | 8 | # ----------------------------------------------------------------------------- 9 | # Imports: 10 | # ----------------------------------------------------------------------------- 11 | 12 | import argparse 13 | import datetime 14 | import sys 15 | 16 | from parsedatetime.parsedatetime import Calendar 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Classes: 20 | # ----------------------------------------------------------------------------- 21 | class ParseDateTimeArg(argparse.Action): 22 | """argparse.Action subclass. parses natural language cl datetime strings. 23 | 24 | Use this class as an argument to the 'action' argument when calling 25 | add_argument on an argparse parser. When the command line arguments are 26 | parsed, the resulting namespace will have a datetime.datetime object 27 | assigned to the argument's destination. 28 | 29 | If a datetime could not be parsed from the string, a ValueError will be 30 | raised. 31 | 32 | Examples of parsable human readable datetime strings: 33 | 34 | "now", "yesterday", "2 weeks from now", "3 days ago", etc. 35 | 36 | Note: When the datetime string is more than one word, you should include 37 | the argument in quotes on the command line. 38 | 39 | """ 40 | 41 | # ------------------------------------------------------------------------- 42 | def __call__(self, parser, namespace, datetime_str, option_string=None): 43 | parsed_datetime = date_time_from_str(datetime_str) 44 | setattr(namespace, self.dest, parsed_datetime) 45 | 46 | # ----------------------------------------------------------------------------- 47 | class ParseDateArg(argparse.Action): 48 | """Similar to ParseDateTimeArg. 49 | 50 | Parses and returns a datetime.date object. 51 | 52 | """ 53 | 54 | # ------------------------------------------------------------------------- 55 | def __call__(self, parser, namespace, datetime_str, option_string=None): 56 | parsed_datetime = date_time_from_str(datetime_str) 57 | setattr(namespace, self.dest, parsed_datetime.date()) 58 | 59 | # ----------------------------------------------------------------------------- 60 | # Public functions: 61 | # ----------------------------------------------------------------------------- 62 | def date_time_from_str(datetime_str): 63 | # from http://stackoverflow.com/a/5903760, updated with more reasonable 64 | # variable names. 65 | 66 | assert datetime, "Unable to parse empty date/time string." 67 | 68 | parsed_result, date_type = Calendar().parse(datetime_str) 69 | 70 | parsed_datetime = None 71 | 72 | # what was returned (based on parsedatetime docs) 73 | # 0 = failed to parse 74 | # 1 = date (with current time, as a struct_time) 75 | # 2 = time (with current date, as a struct_time) 76 | # 3 = datetime 77 | 78 | if date_type == 3: 79 | 80 | # parsed_result is a datetime 81 | parsed_datetime = parsed_result 82 | 83 | elif date_type in (1, 2): 84 | 85 | # parsed_result is struct_time 86 | parsed_datetime = datetime.datetime(*parsed_result[:6]) 87 | 88 | else: 89 | 90 | # Failed to parse 91 | raise ValueError("Could not parse date/time string: " + datetime_str) 92 | 93 | return parsed_datetime 94 | 95 | -------------------------------------------------------------------------------- /dpa/maya/shelf.py: -------------------------------------------------------------------------------- 1 | 2 | from maya import cmds, mel 3 | 4 | from dpa.ui.icon.factory import IconFactory 5 | 6 | # ----------------------------------------------------------------------------- 7 | class MayaShelf(object): 8 | 9 | # ------------------------------------------------------------------------- 10 | @staticmethod 11 | def top_level_layout(): 12 | return mel.eval("$tmp=$gShelfTopLevel") 13 | 14 | # ------------------------------------------------------------------------- 15 | @classmethod 16 | def get(cls, name, layout=None): 17 | 18 | if not layout: 19 | layout = cls.top_level_layout() 20 | 21 | cmds.setParent(layout) 22 | shelf = cmds.shelfLayout(name, q=True, exists=True) 23 | 24 | if shelf: 25 | return cls(name, layout) 26 | else: 27 | raise NameError("Unable to find shelf: " + name) 28 | 29 | # ------------------------------------------------------------------------- 30 | def __init__(self, name, layout=None): 31 | self._name = name 32 | 33 | if not layout: 34 | layout = self.__class__.top_level_layout() 35 | self._layout = layout 36 | 37 | # ------------------------------------------------------------------------- 38 | def add_button(self, **kwargs): 39 | 40 | import sys 41 | 42 | # intercept/adjust some of the arguments 43 | for (key, val) in kwargs.iteritems(): 44 | 45 | # get full image path 46 | if key.startswith("image") and IconFactory.is_icon_path(val): 47 | kwargs[key] = self.icon_factory.disk_path(val) 48 | 49 | cmds.setParent("|".join([self.layout, self.name])) 50 | cmds.shelfButton(**kwargs) 51 | 52 | # ------------------------------------------------------------------------- 53 | def create(self): 54 | cmds.setParent(self.layout) 55 | cmds.shelfLayout(self.name) 56 | self._shelf_error_fix() 57 | 58 | # ------------------------------------------------------------------------- 59 | def delete(self): 60 | cmds.deleteUI("|".join([self.layout, self.name])) 61 | self._shelf_error_fix() 62 | 63 | # ------------------------------------------------------------------------- 64 | @property 65 | def exists(self): 66 | cmds.setParent(self.layout) 67 | return cmds.shelfLayout(self.name, q=True, exists=True) 68 | 69 | # ------------------------------------------------------------------------- 70 | @property 71 | def icon_factory(self): 72 | 73 | if not hasattr(self, '_icon_factory'): 74 | self._icon_factory = IconFactory() 75 | 76 | return self._icon_factory 77 | 78 | # ------------------------------------------------------------------------- 79 | @property 80 | def layout(self): 81 | return self._layout 82 | 83 | # ------------------------------------------------------------------------- 84 | @property 85 | def name(self): 86 | return self._name 87 | 88 | # ------------------------------------------------------------------------- 89 | def _shelf_error_fix(self): 90 | 91 | # FIXES error in shelf.mel. reassigns optionVars for this shelf 92 | shelves = cmds.shelfTabLayout( 93 | self.layout, query=True, tabLabelIndex=True) 94 | for index, shelf in enumerate(shelves): 95 | if shelf == self.name: 96 | cmds.optionVar( 97 | stringValue=("shelfName{i}".format(i=index+1), str(shelf)) 98 | ) 99 | 100 | -------------------------------------------------------------------------------- /dpa/ptask/action/complete.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Imports: 3 | # ----------------------------------------------------------------------------- 4 | 5 | from dpa.action import Action 6 | from dpa.ptask.area import PTaskArea, PTaskAreaError 7 | from dpa.ptask.spec import PTaskSpec 8 | 9 | # ----------------------------------------------------------------------------- 10 | # Classes: 11 | # ----------------------------------------------------------------------------- 12 | class PTaskCompleteSpecAction(Action): 13 | """Prints completion options for a given partial ptask spec.""" 14 | 15 | name = "complete" 16 | target_type = "ptask" 17 | logging = False 18 | 19 | # ------------------------------------------------------------------------- 20 | # Class methods: 21 | # ------------------------------------------------------------------------- 22 | @classmethod 23 | def setup_cl_args(cls, parser): 24 | 25 | parser.add_argument( 26 | "spec", 27 | nargs="?", 28 | default="", 29 | help="The partial ptask spec to complete." 30 | ) 31 | 32 | # ------------------------------------------------------------------------- 33 | # Special methods: 34 | # ------------------------------------------------------------------------- 35 | def __init__(self, spec="."): 36 | super(PTaskCompleteSpecAction, self).__init__(spec) 37 | self._spec = spec 38 | 39 | # ------------------------------------------------------------------------- 40 | # Methods: 41 | # ------------------------------------------------------------------------- 42 | def execute(self): 43 | 44 | match_specs = self._complete( 45 | self.spec, 46 | relative_to=PTaskArea.current().spec, 47 | ) 48 | 49 | if match_specs: 50 | print " ".join([s for s in match_specs]) 51 | 52 | # ------------------------------------------------------------------------- 53 | def undo(self): 54 | pass 55 | 56 | # ------------------------------------------------------------------------- 57 | # Properties: 58 | # ------------------------------------------------------------------------- 59 | @property 60 | def spec(self): 61 | return self._spec 62 | 63 | # ------------------------------------------------------------------------- 64 | def _complete(self, spec, relative_to=None): 65 | 66 | in_spec = self.spec.strip().strip(PTaskSpec.SEPARATOR) 67 | full_spec = PTaskSpec.get(in_spec, relative_to=relative_to) 68 | ptask_area = None 69 | 70 | # is the supplied spec a ptask area? if so, print sub directories 71 | # if not, get the parent spec and print its sub directories 72 | try: 73 | ptask_area = PTaskArea(full_spec) 74 | except PTaskAreaError: 75 | in_spec = PTaskSpec.parent(in_spec) 76 | full_spec = PTaskSpec.parent(full_spec) 77 | try: 78 | ptask_area = PTaskArea(full_spec) 79 | except PTaskAreaError: 80 | pass 81 | 82 | if not ptask_area: 83 | return 84 | 85 | # append the child name to the input spec and print them out for completion 86 | match_specs = [] 87 | for area_dir in ptask_area.dirs(children=True, product_dirs=True): 88 | if in_spec: 89 | match_specs.append(in_spec + PTaskSpec.SEPARATOR + area_dir) 90 | else: 91 | match_specs.append(area_dir) 92 | 93 | return [m + PTaskSpec.SEPARATOR for m in match_specs] 94 | 95 | -------------------------------------------------------------------------------- /dpa/mari/shelf.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import mari 4 | 5 | from dpa.ui.icon.factory import IconFactory 6 | from PySide import QtGui, QtCore 7 | 8 | # ----------------------------------------------------------------------------- 9 | class MariShelf(object): 10 | 11 | # ------------------------------------------------------------------------- 12 | def __init__(self, name, layout=None, widget=None, palette=None): 13 | self._name = name 14 | 15 | if not layout: 16 | layout = QtGui.QHBoxLayout() 17 | self._layout = layout 18 | 19 | if not widget: 20 | widget = QtGui.QWidget() 21 | widget.setLayout(self.layout) 22 | self._widget = widget 23 | 24 | if not palette: 25 | palette = mari.palettes.create(name, widget) 26 | self._palette = palette 27 | 28 | self._palette.show() 29 | 30 | # ------------------------------------------------------------------------- 31 | def add_button(self, **kwargs): 32 | 33 | # so not sure if this is going to work, yay programming! 34 | # intercept/adjust some of the arguments 35 | 36 | cmd = kwargs.get('command', 'print "No action defined"') 37 | label = kwargs.get('label', 'Unknown') 38 | annotation = kwargs.get('annotation', '') 39 | image = QtGui.QPixmap() 40 | 41 | for (key, val) in kwargs.iteritems(): 42 | if key.startswith("image") and IconFactory.is_icon_path(val): 43 | image = QtGui.QIcon(self.icon_factory.disk_path(val)) 44 | 45 | action = QtGui.QAction(self.widget) 46 | action.setIcon(image) 47 | action.setToolTip(annotation) 48 | action.triggered.connect(lambda: self._exec_cmd(cmd)) 49 | 50 | button = QtGui.QToolButton() 51 | button.setAutoRaise(True) 52 | button.setDefaultAction(action) 53 | self.layout.addWidget(button) 54 | 55 | # ------------------------------------------------------------------------- 56 | def create(self): 57 | self._palette = mari.palettes.create(self.name, self.widget) 58 | self._palette.show() 59 | 60 | # ------------------------------------------------------------------------- 61 | def delete(self): 62 | mari.palettes.remove(self.name) 63 | 64 | # ------------------------------------------------------------------------- 65 | @property 66 | def exists(self): 67 | return mari.palettes.find(self.name) 68 | 69 | # ------------------------------------------------------------------------- 70 | @property 71 | def icon_factory(self): 72 | 73 | if not hasattr(self, '_icon_factory'): 74 | self._icon_factory = IconFactory() 75 | 76 | return self._icon_factory 77 | 78 | # ------------------------------------------------------------------------- 79 | @property 80 | def palette(self): 81 | return self._palette 82 | 83 | # ------------------------------------------------------------------------- 84 | @property 85 | def layout(self): 86 | return self._layout 87 | 88 | # ------------------------------------------------------------------------- 89 | @property 90 | def name(self): 91 | return self._name 92 | 93 | # ------------------------------------------------------------------------- 94 | @property 95 | def widget(self): 96 | return self._widget 97 | 98 | # ------------------------------------------------------------------------- 99 | def _exec_cmd(self, cmd): 100 | # to work with the way the shelves.cfg is setup 101 | if cmd: 102 | exec(cmd) 103 | -------------------------------------------------------------------------------- /dpa/houdini/session.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import time 4 | 5 | # attempt to import hou module. if it fails, not in a houdini session. 6 | try: 7 | import hou 8 | except ImportError: 9 | HOU_IMPORTED = False 10 | else: 11 | HOU_IMPORTED = True 12 | 13 | # ----------------------------------------------------------------------------- 14 | # attempt to import hou ui module. if it fails, not in the UI 15 | try: 16 | from PySide import QtCore, QtGui 17 | except: 18 | HOU_UI_IMPORTED = False 19 | else: 20 | HOU_UI_IMPORTED = True 21 | 22 | from dpa.app.session import RemoteMixin, Session, SessionRegistry, SessionError 23 | 24 | # ----------------------------------------------------------------------------- 25 | class HoudiniSession(RemoteMixin, Session): 26 | 27 | app_name = 'houdini' 28 | 29 | # XXX should come from config 30 | SERVER_EXECUTABLE = "/home/jtomlin/dev/dpa-pipe/bin/dpa_houdini_server" 31 | 32 | # ------------------------------------------------------------------------- 33 | @classmethod 34 | def current(cls): 35 | if not HOU_IMPORTED: 36 | return None 37 | return cls() 38 | 39 | # ------------------------------------------------------------------------- 40 | def __init__(self, filepath=None, remote=False): 41 | 42 | super(HoudiniSession, self).__init__(remote=remote) 43 | 44 | self._hou = self.init_module('hou') 45 | 46 | if filepath: 47 | self.open_file(filepath) 48 | 49 | # ------------------------------------------------------------------------- 50 | def close(self): 51 | if self.remote_connection: 52 | self.shutdown() 53 | else: 54 | self.hou.hipFile.clear() 55 | 56 | # ------------------------------------------------------------------------- 57 | def open_file(self, filepath): 58 | 59 | if not os.path.exists(filepath): 60 | raise SessionError( 61 | "Can not open '{f}'. File does not exist.".format(f=filepath)) 62 | 63 | try: 64 | self.hou.hipFile.load(filepath) 65 | except RuntimeError as e: 66 | raise SessionError(str(e)) 67 | 68 | # ------------------------------------------------------------------------- 69 | def save(self, filepath=None, overwrite=False): 70 | 71 | if filepath and os.path.exists(filepath) and not overwrite: 72 | raise SessionError( 73 | "Can not save '{f}'. File exists.".format(f=filepath)) 74 | 75 | self.hou.hipFile.save(file_name=filepath) 76 | 77 | # ------------------------------------------------------------------------- 78 | @property 79 | def hou(self): 80 | return self._hou 81 | 82 | # ------------------------------------------------------------------------- 83 | @property 84 | def in_session(self): 85 | """Returns True if inside a current app session.""" 86 | return HOU_IMPORTED or self.remote_connection 87 | 88 | # ------------------------------------------------------------------------- 89 | @property 90 | def main_window(self): 91 | 92 | if not HOU_UI_IMPORTED: 93 | return None 94 | 95 | return QtGui.QApplication.activeWindow() 96 | 97 | # ------------------------------------------------------------------------- 98 | @property 99 | def name(self): 100 | """Returns the name of the application.""" 101 | return "houdini" 102 | 103 | # ------------------------------------------------------------------------- 104 | @property 105 | def server_executable(self): 106 | return self.__class__.SERVER_EXECUTABLE 107 | 108 | 109 | # ----------------------------------------------------------------------------- 110 | SessionRegistry().register(HoudiniSession) 111 | -------------------------------------------------------------------------------- /dpa/data/config/ui/actions/examples.cfg: -------------------------------------------------------------------------------- 1 | 2 | options: 3 | 4 | bool1: 5 | type: bool 6 | default: True 7 | help: This is a boolean option 8 | icon: "icon:///images/icons/version_32x32.png" 9 | label: "Boolean Option" 10 | required: True 11 | 12 | float1: 13 | type: float 14 | default: 3.7 15 | help: This is a float option 16 | icon: "icon:///images/icons/version_32x32.png" 17 | label: "Float Option" 18 | required: True 19 | min: -3.4 20 | max: 7.8 21 | step: .01 22 | 23 | int1: 24 | type: int 25 | default: 0 26 | help: This is an int option 27 | icon: "icon:///images/icons/version_32x32.png" 28 | label: "Int Option" 29 | required: False 30 | min: -50 31 | max: 50 32 | step: 2 33 | 34 | list1: 35 | type: list 36 | default: pig 37 | help: This is a list option 38 | icon: "icon:///images/icons/version_32x32.png" 39 | label: "List Option" 40 | required: True 41 | choices: [cat, dog, pig, horse, cow, rooster, farmer] 42 | multiple: False 43 | 44 | str1: 45 | type: str 46 | default: 'rubber baby buggy bumpers' 47 | help: This is a str option 48 | icon: "icon:///images/icons/version_32x32.png" 49 | label: "String Option" 50 | required: False 51 | 52 | text1: 53 | type: text 54 | default: "To be, or not to be. That is the question. Whether 'tis nobler..." 55 | help: This is a text option 56 | icon: "icon:///images/icons/version_32x32.png" 57 | label: "Text Option" 58 | required: True 59 | 60 | group1: 61 | type: group 62 | help: This is a group option 63 | icon: "icon:///images/icons/version_32x32.png" 64 | label: "Group1 Option" 65 | options: 66 | bool1: 67 | type: bool 68 | default: True 69 | help: This is a boolean option 70 | icon: "icon:///images/icons/version_32x32.png" 71 | label: "Boolean Option" 72 | required: True 73 | 74 | float1: 75 | type: float 76 | default: 3.7 77 | help: This is a float option 78 | icon: "icon:///images/icons/version_32x32.png" 79 | label: "Float Option" 80 | required: True 81 | min: -3.4 82 | max: 7.8 83 | step: .01 84 | 85 | group2: 86 | type: group 87 | help: This is a group option 88 | icon: "icon:///images/icons/version_32x32.png" 89 | label: "Group2 Options" 90 | open: False 91 | options: 92 | str1: 93 | type: str 94 | default: 'rubber baby buggy bumpers' 95 | help: This is a str option 96 | icon: "icon:///images/icons/version_32x32.png" 97 | label: "String Option" 98 | required: False 99 | 100 | group3: 101 | type: group 102 | help: This is a group option 103 | icon: "icon:///images/icons/version_32x32.png" 104 | label: "Group3 Options" 105 | open: False 106 | options: 107 | text1: 108 | type: text 109 | default: "To be, or not to be. That is the question. Whether 'tis nobler..." 110 | help: This is a text option 111 | icon: "icon:///images/icons/version_32x32.png" 112 | label: "Text Option" 113 | required: True 114 | 115 | 116 | -------------------------------------------------------------------------------- /bin/dpa_uncompress: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #dpauncompress 4 | # Kacey Coley 5 | # October 31, 2013 6 | # Tool for people who are too lazy to use the flags to uncompress various 7 | # archive files. Currently supports zip, tar, tar.gz and tgz 8 | # 9 | # Syntax: dpauncompress file.ext [uncompress directory] 10 | 11 | # ----------------------------------------------------------------------------- 12 | 13 | import os 14 | import os.path 15 | import sys 16 | import shutil 17 | import subprocess 18 | 19 | # ----------------------------------------------------------------------------- 20 | def extractArchive(archiveBase, extension, archiveDir): 21 | if extension == ".zip": 22 | try: 23 | print "Un-zipping " + archiveBase + extension + " to " 24 | print archiveDir 25 | subprocess.call(["unzip", "-q", "-o", "-d", archiveDir, archiveBase + extension]) 26 | except: 27 | print "Could not unzip file " + archiveBase + extension 28 | elif extension == ".tar": 29 | try: 30 | print "Un-taring..." + archiveBase + extension + " to " 31 | print archiveDir 32 | if not os.path.exists(archiveDir): 33 | os.makedirs(archiveDir) 34 | subprocess.call(["tar", "xvf", archiveBase + extension, "-C", archiveDir ]) 35 | except: 36 | print "Could not untar file " + archiveBase + extension 37 | elif extension == ".tar.gz" or extension == ".tgz": 38 | try: 39 | print "Un-targzing..." + archiveBase + extension + " to " 40 | print archiveDir 41 | if not os.path.exists(archiveDir): 42 | os.makedirs(archiveDir) 43 | subprocess.call(["tar", "-zxvf",archiveBase + extension, "-C", archiveDir]) 44 | except: 45 | print "Could not untar.gz file " + archiveBase + extension 46 | elif extension == ".rar": 47 | try: 48 | print "Un-rarring " + archiveBase + extension + " to " 49 | print archiveDir 50 | if not os.path.exists(archiveDir): 51 | os.makedirs(archiveDir) 52 | subprocess.call(["unrar", "x", archiveBase + extension, "-e", archiveDir]) 53 | except: 54 | print "Could not unrar file " + archiveBase + extension 55 | elif extension == ".7z": 56 | try: 57 | print "Un-7z-ing " + archiveBase + extension + " to " 58 | print archiveDir 59 | if not os.path.exists(archiveDir): 60 | os.makedirs(archiveDir) 61 | subprocess.call(["7za", "x", archiveBase + extension, "-o"+ archiveDir]) 62 | except: 63 | print "Could not un-7z file " + archiveBase + extension 64 | else: 65 | print "Cannot unarchive " + archiveBase + extension 66 | 67 | # ----------------------------------------------------------------------------- 68 | def main(): 69 | if len(sys.argv) < 4: 70 | archiveBase = "" 71 | archiveExtension = "" 72 | archiveDir = "" 73 | archiveName = sys.argv[1] 74 | 75 | try: 76 | archiveBase,archiveExtension = os.path.splitext(archiveName) 77 | if archiveExtension in ['.gz']: 78 | archiveBase,archiveExtension2 = os.path.splitext(archiveBase) 79 | archiveExtension = archiveExtension2 + archiveExtension 80 | if len(sys.argv) == 3: 81 | archiveDir = os.path.abspath(sys.argv[2]) 82 | archiveDir += "/" + os.path.basename(archiveBase) 83 | else: 84 | archiveDir = "." 85 | except: 86 | print archiveDir 87 | print "Improper archive file" 88 | return 89 | extractArchive(archiveBase, archiveExtension, archiveDir) 90 | 91 | # ----------------------------------------------------------------------------- 92 | if __name__ == "__main__": 93 | main() 94 | 95 | -------------------------------------------------------------------------------- /dpa/restful/__init__.py: -------------------------------------------------------------------------------- 1 | """A framework for restful APIs.""" 2 | # ----------------------------------------------------------------------------- 3 | # Module: dpa.restful 4 | # Author: Josh Tomlinson (jtomlin) 5 | # ----------------------------------------------------------------------------- 6 | 7 | # ----------------------------------------------------------------------------- 8 | # Imports: 9 | # ----------------------------------------------------------------------------- 10 | 11 | import copy 12 | 13 | from .client import RestfulClientError 14 | from .mixins import ListMixin, GetMixin 15 | 16 | # ----------------------------------------------------------------------------- 17 | # Public Classes 18 | # ----------------------------------------------------------------------------- 19 | class RestfulObject(object): 20 | 21 | exception_class = None 22 | 23 | # ------------------------------------------------------------------------- 24 | # Special methods: 25 | # ------------------------------------------------------------------------- 26 | def __init__(self, data): 27 | self._data = _RestfulData(data) 28 | 29 | # ------------------------------------------------------------------------- 30 | def __getattr__(self, attr): 31 | 32 | # look up the attribute in the _data 33 | try: 34 | return self._data.get(attr) 35 | except _RestfulDataError: 36 | raise AttributeError( 37 | '{cls} instance has no attribute "{attr}"'.format( 38 | cls=self.__class__.__name__, 39 | attr=attr, 40 | ) 41 | ) 42 | 43 | # ----------------------------------------------------------------------------- 44 | class RestfulObjectError(RestfulClientError): 45 | pass 46 | 47 | RestfulObject.exception_class = RestfulObjectError 48 | 49 | # ----------------------------------------------------------------------------- 50 | class ReadOnlyRestfulObject(ListMixin, GetMixin, RestfulObject): 51 | pass 52 | 53 | # ----------------------------------------------------------------------------- 54 | # Private Classes: 55 | # ----------------------------------------------------------------------------- 56 | class _RestfulData(object): 57 | 58 | # ------------------------------------------------------------------------- 59 | # Special methods: 60 | # ------------------------------------------------------------------------- 61 | def __init__(self, data_dict): 62 | """Constructor.""" 63 | 64 | super(_RestfulData, self).__init__() 65 | self._data = data_dict 66 | 67 | # ------------------------------------------------------------------------- 68 | # Instance methods: 69 | # ------------------------------------------------------------------------- 70 | def get(self, attr): 71 | 72 | try: 73 | return self._data[attr] 74 | except KeyError: 75 | raise _RestfulDataError( 76 | "No attribute '{a}' in data object.".format(a=attr)) 77 | 78 | # ------------------------------------------------------------------------- 79 | def set(self, attr, value): 80 | 81 | if not attr in self._data.keys(): 82 | raise _RestfulDataError( 83 | "No attribute '{a}' in data object.".format(a=attr)) 84 | 85 | self._data[attr] = value 86 | 87 | # ------------------------------------------------------------------------- 88 | # Properties 89 | # ------------------------------------------------------------------------- 90 | @property 91 | def data_dict(self): 92 | return self._data 93 | 94 | # ------------------------------------------------------------------------- 95 | @data_dict.setter 96 | def data_dict(self, data): 97 | self._data = data 98 | 99 | # ----------------------------------------------------------------------------- 100 | # Public exception classes: 101 | # ----------------------------------------------------------------------------- 102 | class _RestfulDataError(Exception): 103 | pass 104 | 105 | -------------------------------------------------------------------------------- /dpa/maya/entity/workfile.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import re 4 | 5 | from dpa.app.entity import EntityRegistry, EntityError 6 | from dpa.maya.entity.base import SetBasedWorkfileEntity 7 | 8 | # ----------------------------------------------------------------------------- 9 | class WorkfileEntity(SetBasedWorkfileEntity): 10 | 11 | category = "workfile" 12 | 13 | # ------------------------------------------------------------------------- 14 | @staticmethod 15 | def _name_from_context(session): 16 | 17 | ptask = session.ptask 18 | type_lookup = ptask.types 19 | 20 | # hardcoding knowledge of the pipeline hierarchy here which is, in 21 | # general, a bad idea. However, it makes things much cleaner/easier to 22 | # read as far as the output products, so we'll go with it for now. 23 | if 'build' in type_lookup and 'stage' in type_lookup: 24 | base_name = type_lookup['build'] + "_" + type_lookup['stage'] 25 | elif 'shot' in type_lookup and 'stage' in type_lookup: 26 | base_name = type_lookup['shot'] + "_" + type_lookup['stage'] 27 | 28 | # fall back to the file name 29 | else: 30 | file_path = session.cmds.file(q=True, sceneName=True) 31 | (base_name, file_ext) = os.path.splitext( 32 | os.path.split(file_path)[-1]) 33 | 34 | return base_name 35 | 36 | # ------------------------------------------------------------------------- 37 | @classmethod 38 | def get(cls, name, session, instance=None): 39 | """Retrieve an entity instance from the supplied session.""" 40 | 41 | base_name = cls._name_from_context(session) 42 | 43 | # name has to match the base name of the file for the default 44 | # 'workfile' entity 45 | if name == base_name and not instance: 46 | return cls(name, session) 47 | 48 | # doesn't match. look for a matching export group 49 | return super(WorkfileEntity, cls).get(name, session, instance=instance) 50 | 51 | # ------------------------------------------------------------------------- 52 | @classmethod 53 | def list(cls, session): 54 | """Retrieve all entities of this category from the supplied session.""" 55 | 56 | entities = super(WorkfileEntity, cls).list(session) 57 | 58 | # get the default workfile entity 59 | base_name = cls._name_from_context(session) 60 | entities.append(cls.get(base_name, session)) 61 | 62 | return entities 63 | 64 | # ------------------------------------------------------------------------- 65 | def export(self, product_desc=None, version_note=None, bake_references=True): 66 | """Export this entity to a product.""" 67 | 68 | file_type = 'ma' 69 | 70 | product_repr = self._create_product(product_desc, version_note, 71 | file_type) 72 | 73 | product_repr_dir = product_repr.directory 74 | 75 | product_repr_file = os.path.join( 76 | product_repr_dir, self.display_name + "." + file_type) 77 | 78 | if self.display_name == self.__class__._name_from_context(self.session): 79 | self.session.cmds.file( 80 | product_repr_file, 81 | type='mayaAscii', 82 | exportAll=True, 83 | force=True, 84 | preserveReferences=(not bake_references), 85 | ) 86 | else: 87 | export_objs = self.get_export_objects() 88 | with self.session.selected(export_objs): 89 | self.session.cmds.file( 90 | product_repr_file, 91 | type='mayaAscii', 92 | exportSelected=True, 93 | force=True, 94 | preserveReferences=(not bake_references), 95 | ) 96 | 97 | product_repr.area.set_permissions(0660) 98 | 99 | return [product_repr] 100 | 101 | # ----------------------------------------------------------------------------- 102 | EntityRegistry().register('maya', WorkfileEntity) 103 | 104 | -------------------------------------------------------------------------------- /bin/dpa_ribrender: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Author: Gina Guerrero 4 | 5 | import argparse 6 | import os 7 | import shlex 8 | import sys 9 | 10 | import prman 11 | 12 | # ----------------------------------------------------------------------------- 13 | class RibFilter(prman.Rif): 14 | """Custom rib generation filter.""" 15 | 16 | # ------------------------------------------------------------------------- 17 | def __init__(self, ri, output_dir, file_base, project_dir): 18 | 19 | super(RibFilter, self).__init__(ri) 20 | self._output_dir = output_dir 21 | self._file_base = file_base 22 | self._project_dir = project_dir 23 | 24 | # ------------------------------------------------------------------------- 25 | def Display(self, name, driver, channels, params): 26 | """Set the output file location""" 27 | 28 | if driver == 'openexr': 29 | 30 | file_name = os.path.split(name)[-1] 31 | 32 | if self._file_base: 33 | file_parts = file_name.split(".") 34 | # This really needed to be re-thought... 35 | # Currently, it doesn't take multiple outputs into consideration 36 | # So I'm altering it for a quick fix for now, because 37 | # it's causing problems for roomba atm. 38 | # file_parts[0] = self._file_base 39 | name_parts = file_parts[0].split("_") 40 | name_parts[0] = self._file_base 41 | file_parts[0] = "_".join(name_parts) 42 | file_name = ".".join(file_parts) 43 | 44 | # the output directory with the default file name 45 | name = os.path.join(self._output_dir, file_name) 46 | 47 | self.m_ri.Display(name, driver, channels, params) 48 | 49 | # ----------------------------------------------------------------------------- 50 | def process_rib(rib_file, output_dir, file_base, project_dir=None, prman_flags=None): 51 | 52 | # initialize the renderman interface 53 | ri = prman.Ri() 54 | 55 | if prman_flags: 56 | prman.Init(shlex.split(prman_flags)) 57 | 58 | prman.RifInit([RibFilter(ri, output_dir, file_base, project_dir)]) 59 | 60 | ri.Begin(ri.RENDER) 61 | # Im so sorry guys 62 | ri.ErrorHandler(ri.PRINT) 63 | prman.ParseFile(rib_file) 64 | ri.End() 65 | 66 | prman.RifInit([]) 67 | 68 | # ----------------------------------------------------------------------------- 69 | def main(): 70 | 71 | parser = argparse.ArgumentParser( 72 | description="Rib generation from the command line") 73 | 74 | # rib file 75 | parser.add_argument( 76 | "-r", "--rib", 77 | metavar="", 78 | type=str, 79 | help="Location to output the generated rib file. (required)", 80 | required=True, 81 | ) 82 | 83 | # image output directory 84 | parser.add_argument( 85 | "-o", "--output_dir", 86 | metavar="", 87 | type=str, 88 | help="Where the images will end up when rendering the rib. (required)", 89 | required=True, 90 | ) 91 | 92 | # image output directory 93 | parser.add_argument( 94 | "-f", "--file_base", 95 | metavar="", 96 | type=str, 97 | help="The base name of the output file.", 98 | required=True, 99 | ) 100 | 101 | # project dir 102 | parser.add_argument( 103 | "-p", "--project", 104 | metavar="", 105 | type=str, 106 | help="Location to output the generated rib file. (required)", 107 | ) 108 | 109 | # prman flags 110 | parser.add_argument( 111 | "--prman", 112 | metavar='""', 113 | type=str, 114 | help="Flags to forward to prman rib generation.", 115 | default=None, 116 | ) 117 | 118 | args = parser.parse_args() 119 | process_rib(args.rib, args.output_dir, args.file_base, args.project, args.prman) 120 | 121 | # ----------------------------------------------------------------------------- 122 | if __name__ == "__main__": 123 | main() 124 | 125 | -------------------------------------------------------------------------------- /dpa/ui/mari/importprod.py: -------------------------------------------------------------------------------- 1 | 2 | import os.path 3 | from collections import defaultdict 4 | 5 | from PySide import QtCore, QtGui 6 | 7 | from dpa.action import ActionError 8 | from dpa.app.entity import EntityRegistry 9 | from dpa.app.session import SessionRegistry 10 | from dpa.ptask.area import PTaskArea 11 | from dpa.ui.app.session import SessionActionDialog 12 | from dpa.ui.icon.factory import IconFactory 13 | 14 | # ----------------------------------------------------------------------------- 15 | 16 | IMPORT_ICON_URI = "icon:///images/icons/import_32x32.png" 17 | IMPORT_OPTIONS_CONFIG = "config/mari/geom/import.cfg" 18 | 19 | # ----------------------------------------------------------------------------- 20 | class ImportDialog(SessionActionDialog): 21 | 22 | # ------------------------------------------------------------------------- 23 | def __init__(self): 24 | 25 | ptask_area = PTaskArea.current() 26 | options_config = ptask_area.config(IMPORT_OPTIONS_CONFIG, 27 | composite_ancestors=True) 28 | 29 | self.get_files() 30 | 31 | options_config['options']['products'].set('choices', self.sublist.keys()) 32 | options_config['options']['products'].set('default', self.sublist.keys()[0]) 33 | 34 | icon_path = IconFactory().disk_path(IMPORT_ICON_URI) 35 | 36 | super(ImportDialog, self).__init__( 37 | title='Import Product', 38 | options_config=options_config, 39 | icon_path=icon_path, 40 | action_button_text='Import', 41 | modal=False, 42 | ) 43 | 44 | # ------------------------------------------------------------------------- 45 | def accept(self): 46 | 47 | # handles closing the dialog 48 | super(ImportDialog, self).accept() 49 | 50 | try: 51 | entity_classes = EntityRegistry().get_entity_classes( 52 | self.session.app_name) 53 | 54 | entity_class = None 55 | for ec in entity_classes: 56 | if ec.category == 'geom': 57 | entity_class = ec 58 | has_import_product = True 59 | else: 60 | has_import_product = False 61 | 62 | if has_import_product: 63 | val = self.options.value.products[0] 64 | ec(self.sublist[val][0], self.session).import_product( 65 | self.sublist[val][1],self.sublist[val][0]) 66 | 67 | except ActionError as e: 68 | error_dialog = QtGui.QErrorMessage(self.parent()) 69 | error_dialog.setWindowTitle('Import Product Failure') 70 | error_dialog.showMessage( 71 | "There was an error trying to import the product." 72 | ) 73 | 74 | # ------------------------------------------------------------------------- 75 | def get_files(self): 76 | sublist = defaultdict(dict) 77 | 78 | # for now, get only products available from existing subs with 79 | # same category 80 | for sub in SessionRegistry().current().ptask_version.subscriptions: 81 | prod_ver = sub.product_version 82 | prod = prod_ver.product 83 | 84 | # this should change if geomentity ever becomes NOT just OBJ 85 | for sub_rep in prod_ver.representations: 86 | if prod.category == 'geom': 87 | full_path = os.path.join(sub.import_path(), 88 | sub_rep.type, sub_rep.resolution) 89 | # actual files wont always be named the same as the 90 | # product (eg, maps, so be wary of this) 91 | full_path += '/' + prod.name + '.' + sub_rep.type 92 | 93 | # still don't know if using the spec is the best option 94 | sublist[prod.spec] = [full_path, prod.name] 95 | 96 | self._sublist = sublist 97 | 98 | # ------------------------------------------------------------------------- 99 | @property 100 | def sublist(self): 101 | if not hasattr(self, '_sublist'): 102 | return defaultdict(dict) 103 | 104 | return self._sublist -------------------------------------------------------------------------------- /dpa/location/action/info.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Imports: 3 | # ----------------------------------------------------------------------------- 4 | 5 | from dpa.action import Action, ActionError 6 | from dpa.shell.output import Output, Style 7 | from dpa.location import Location, LocationError, current_location_code 8 | 9 | # ----------------------------------------------------------------------------- 10 | # Classes: 11 | # ----------------------------------------------------------------------------- 12 | class LocationInfoAction(Action): 13 | """ Print information about a location.""" 14 | 15 | name = "info" 16 | target_type = "location" 17 | 18 | # ------------------------------------------------------------------------- 19 | # Class methods: 20 | # ------------------------------------------------------------------------- 21 | @classmethod 22 | def setup_cl_args(cls, parser): 23 | 24 | parser.add_argument( 25 | "code", 26 | nargs="?", 27 | default=current_location_code(), 28 | help="Print info for the supplied location code." 29 | ) 30 | 31 | # ------------------------------------------------------------------------- 32 | # Special methods: 33 | # ------------------------------------------------------------------------- 34 | def __init__(self, code): 35 | super(LocationInfoAction, self).__init__(code) 36 | self._code = code 37 | 38 | # ------------------------------------------------------------------------- 39 | # Instance methods: 40 | # ------------------------------------------------------------------------- 41 | def execute(self): 42 | 43 | # output headers. reused while defining the look of the output 44 | description = 'Description' 45 | timezone = 'Timezone' 46 | lat_long = 'Lat/Long' 47 | active = 'Active' 48 | code = 'Code' 49 | host = 'Host' 50 | filesystem_root = 'Filesystem root' 51 | 52 | # define the look of the output 53 | output = Output() 54 | # defining the data headers 55 | output.header_names = [ 56 | code, 57 | description, 58 | host, 59 | filesystem_root, 60 | timezone, 61 | lat_long, 62 | ] 63 | output.add_item( 64 | { 65 | code: self.location.code, 66 | description: self.location.description, 67 | host: self.location.host, 68 | filesystem_root: self.location.filesystem_root, 69 | timezone: self.location.timezone, 70 | lat_long: "{l.latitude}, {l.longitude}".format(l=self.location), 71 | }, 72 | color_all=Style.bright, 73 | ) 74 | 75 | # build the title 76 | title = " {l.name} ".format(l=self.location) 77 | if not self.location.active: 78 | title += " [INACTIVE]" 79 | title += " " 80 | output.title = title 81 | 82 | # dump the output as a list of key/value pairs 83 | output.dump() 84 | 85 | # ------------------------------------------------------------------------- 86 | def undo(self): 87 | pass 88 | 89 | # ------------------------------------------------------------------------- 90 | def validate(self): 91 | 92 | try: 93 | location = Location.get(self.code) 94 | except LocationError as e: 95 | raise ActionError( 96 | 'Could not determine location from code: "{c}"'.\ 97 | format(c=self.code) 98 | ) 99 | 100 | self._location = location 101 | 102 | # ------------------------------------------------------------------------- 103 | # Properties 104 | # ------------------------------------------------------------------------- 105 | @property 106 | def code(self): 107 | return self._code 108 | 109 | # ------------------------------------------------------------------------- 110 | @property 111 | def location(self): 112 | return self._location 113 | 114 | -------------------------------------------------------------------------------- /dpa/product/representation/status.py: -------------------------------------------------------------------------------- 1 | """Classes and functions related to DPA pipeline product representation statuses.""" 2 | 3 | # ----------------------------------------------------------------------------- 4 | # Imports: 5 | # ----------------------------------------------------------------------------- 6 | 7 | from dpa.location import Location 8 | from dpa.ptask.spec import PTaskSpec 9 | from dpa.restful import RestfulObject, RestfulObjectError 10 | from dpa.restful.mixins import CreateMixin, GetMixin, ListMixin, UpdateMixin 11 | from dpa.user import User 12 | 13 | # ----------------------------------------------------------------------------- 14 | # Public Classes: 15 | # ----------------------------------------------------------------------------- 16 | class ProductRepresentationStatus(CreateMixin, GetMixin, ListMixin, 17 | RestfulObject): 18 | """Product Representation API. 19 | 20 | .product_representation 21 | .product_representation_spec 22 | .location 23 | .location_code 24 | .status 25 | .spec 26 | 27 | """ 28 | 29 | # ------------------------------------------------------------------------- 30 | # Class attributes: 31 | # ------------------------------------------------------------------------- 32 | 33 | data_type = 'product-representation-statuses' 34 | 35 | # ------------------------------------------------------------------------- 36 | # Class Methods 37 | # ------------------------------------------------------------------------- 38 | @classmethod 39 | def create(cls, product_representation, location, status): 40 | 41 | # XXX validation 42 | 43 | data = { 44 | "product_representation": product_representation, 45 | "location": location, 46 | "status": status, 47 | } 48 | 49 | return super(ProductRepresentationStatus, cls).create(data) 50 | 51 | # ------------------------------------------------------------------------- 52 | @classmethod 53 | def get(cls, product_representation_spec, location_spec, relative_to=None): 54 | 55 | # XXX PTaskSpec >> ContextSpec 56 | # XXX PTaskArea >> ContextArea 57 | # XXX PTaskEnv >> ContextEnv 58 | if not isinstance(product_representation_spec, PTaskSpec): 59 | product_representation_spec = PTaskSpec.get( 60 | product_representation_spec, relative_to=relative_to) 61 | 62 | spec = ",".join([product_representation_spec, location_spec]) 63 | 64 | return super(ProductRepresentationStatus, cls).get(spec) 65 | 66 | # ------------------------------------------------------------------------- 67 | # Special methods: 68 | # ------------------------------------------------------------------------- 69 | def __eq__(self, other): 70 | return self.spec == other.spec 71 | 72 | # ------------------------------------------------------------------------- 73 | def __ne__(self, other): 74 | return self.spec != other.spec 75 | 76 | # ------------------------------------------------------------------------- 77 | def __repr__(self): 78 | """:returns: Unique string represntation of the product.""" 79 | return self.__class__.__name__ + "('" + self.spec + "')" 80 | 81 | # ------------------------------------------------------------------------- 82 | @property 83 | def product_representation(self): 84 | from dpa.product.representation import ProductRepresentation 85 | return ProductRepresentation.get(self.product_representation_spec) 86 | 87 | # ------------------------------------------------------------------------- 88 | @property 89 | def product_representation_spec(self): 90 | return self._data.get('product_representation') 91 | 92 | # ------------------------------------------------------------------------- 93 | @property 94 | def location(self): 95 | return Location.get(self.location_code) 96 | 97 | # ------------------------------------------------------------------------- 98 | @property 99 | def location_code(self): 100 | return self._data.get('location') 101 | 102 | # ----------------------------------------------------------------------------- 103 | class ProductRepresentationStatusError(RestfulObjectError): 104 | pass 105 | 106 | ProductRepresentationStatus.exception_class = ProductRepresentationStatusError 107 | -------------------------------------------------------------------------------- /dpa/maya/entity/geomcache.py: -------------------------------------------------------------------------------- 1 | 2 | import os.path 3 | import re 4 | 5 | from dpa.app.entity import EntityRegistry, EntityError 6 | from dpa.maya.entity.base import SetBasedEntity 7 | 8 | # ----------------------------------------------------------------------------- 9 | class GeomcacheEntity(SetBasedEntity): 10 | 11 | category = "geomcache" 12 | 13 | # ------------------------------------------------------------------------- 14 | @classmethod 15 | def import_product_representation(cls, session, representation, *args, 16 | **kwargs): 17 | 18 | if representation.type == 'fbx': 19 | cls._fbx_import(session, representation, *args, **kwargs) 20 | elif representation.type == 'abc': 21 | cls._abc_import(session, representation, *args, **kwargs) 22 | else: 23 | raise EntityError( 24 | "Unknown type for {cat} import: {typ}".format( 25 | cat=cls.category, typ=representation.type)) 26 | 27 | # ------------------------------------------------------------------------- 28 | def export(self, product_desc=None, version_note=None, fbx_export=False, 29 | abc_export=False, **kwargs): 30 | """Export this entity to a product.""" 31 | 32 | abc_options = kwargs.pop('abc_options', {}) 33 | fbx_options = kwargs.pop('fbx_options', {}) 34 | 35 | product_reprs = [] 36 | 37 | if fbx_export: 38 | product_reprs.extend( 39 | self._fbx_export(fbx_options, product_desc, version_note) 40 | ) 41 | 42 | if abc_export: 43 | product_reprs.extend( 44 | self._abc_export(abc_options, product_desc, version_note) 45 | ) 46 | 47 | return product_reprs 48 | 49 | # ------------------------------------------------------------------------- 50 | def _abc_export(self, options, product_desc, version_note): 51 | 52 | self.session.require_plugin('AbcExport') 53 | 54 | file_ext = 'abc' 55 | 56 | product_repr = self._create_product(product_desc, version_note, file_ext) 57 | product_repr_dir = product_repr.directory 58 | 59 | export_path = os.path.join(product_repr_dir, 60 | self.display_name + "." + file_ext) 61 | 62 | export_objs = self.get_export_objects() 63 | 64 | export_roots = "" 65 | for export_obj in export_objs: 66 | dag_path = self.session.cmds.ls(export_obj, l=True)[0] 67 | export_roots += "-root " + dag_path + " " 68 | 69 | uv_write = options.pop('uv_write', True) 70 | if uv_write: 71 | uv_write_flag = "-uvWrite" 72 | else: 73 | uv_write_flag = "" 74 | 75 | world_space = options.pop('world_space', True) 76 | if world_space: 77 | world_space_flag = "-worldSpace" 78 | else: 79 | world_space_flag = "" 80 | 81 | frame_start = self.session.cmds.playbackOptions(query=True, minTime=True) 82 | frame_end = self.session.cmds.playbackOptions(query=True, maxTime=True) 83 | 84 | cmd = 'AbcExport -j "{roots} {uw} {ws} -fr {fs} {fe} -file {path}"'.\ 85 | format(roots=export_roots, fs=frame_start, fe=frame_end, 86 | path=export_path, uw=uv_write_flag, ws=world_space_flag, 87 | ) 88 | 89 | self.session.mel.eval(cmd) 90 | 91 | product_repr.area.set_permissions(0660) 92 | 93 | return [product_repr] 94 | 95 | # ------------------------------------------------------------------------- 96 | def _fbx_export(self, options, product_desc, version_note): 97 | 98 | self.session.require_plugin('fbxmaya') 99 | 100 | file_ext = 'fbx' 101 | 102 | product_repr = self._create_product(product_desc, version_note, file_ext) 103 | product_repr_dir = product_repr.directory 104 | 105 | export_objs = self.get_export_objects() 106 | 107 | export_path = os.path.join(product_repr_dir, self.display_name) 108 | 109 | with self.session.selected(export_objs): 110 | self.session.mel.eval( 111 | 'FBXExport -f "{path}" -s'.format(path=export_path)) 112 | 113 | product_repr.area.set_permissions(0660) 114 | 115 | return [product_repr] 116 | 117 | # ----------------------------------------------------------------------------- 118 | EntityRegistry().register('maya', GeomcacheEntity) 119 | 120 | -------------------------------------------------------------------------------- /dpa/ui/ptask/version.py: -------------------------------------------------------------------------------- 1 | """Create a new version of the supplied ptask.""" 2 | 3 | from PySide import QtCore, QtGui 4 | 5 | from dpa.action import ActionError 6 | from dpa.action.registry import ActionRegistry 7 | from dpa.ptask import PTask, PTaskError 8 | from dpa.ptask.area import PTaskArea 9 | from dpa.ui.app.session import SessionActionDialog 10 | from dpa.ui.icon.factory import IconFactory 11 | 12 | # ----------------------------------------------------------------------------- 13 | 14 | VERSION_ICON_URI = "icon:///images/icons/version_32x32.png" 15 | VERSION_OPTIONS_CONFIG = "config/ui/actions/ptask/version.cfg" 16 | 17 | # ----------------------------------------------------------------------------- 18 | class PTaskVersionDialog(SessionActionDialog): 19 | 20 | # ------------------------------------------------------------------------- 21 | def __init__(self): 22 | 23 | self._ptask_area = PTaskArea.current() 24 | options_config = self._ptask_area.config(VERSION_OPTIONS_CONFIG, 25 | composite_ancestors=True) 26 | 27 | try: 28 | self._ptask = PTask.get(self._ptask_area.spec) 29 | except PTaskError as e: 30 | error_dialog = QtGui.QErrorMessage(self) 31 | error_dialog.setWindowTitle('Version Failure') 32 | error_dialog.showMessage("Unable to determine current ptask.") 33 | return 34 | 35 | icon_path = IconFactory().disk_path(VERSION_ICON_URI) 36 | 37 | super(PTaskVersionDialog, self).__init__( 38 | title='Version up', 39 | options_config=options_config, 40 | icon_path=icon_path, 41 | action_button_text='Submit', 42 | modal=False, 43 | ) 44 | 45 | # ------------------------------------------------------------------------- 46 | def accept(self): 47 | 48 | if not self._confirm(): 49 | return 50 | 51 | self.session.save() 52 | 53 | # handles closing the dialog 54 | super(PTaskVersionDialog, self).accept() 55 | 56 | # version up the work area 57 | try: 58 | version_action_cls = ActionRegistry().get_action('version', 'work') 59 | version_action = version_action_cls(self._ptask.spec, **self.options.value) 60 | version_action() 61 | except ActionError as e: 62 | error_dialog = QtGui.QErrorMessage(self.parent()) 63 | error_dialog.setWindowTitle('PTask Version Failure') 64 | error_dialog.showMessage( 65 | "There was an error versioning this ptask:

" + \ 66 | str(e) 67 | ) 68 | else: 69 | QtGui.QMessageBox.question(self, "Version Up Success", 70 | "Version up was successful.", 71 | buttons=QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok, 72 | defaultButton=QtGui.QMessageBox.NoButton, 73 | ) 74 | 75 | # ------------------------------------------------------------------------- 76 | def _confirm(self): 77 | 78 | confirm_message = """ 79 | Confirm version creation of version: {next_ver}:
80 | 81 | 82 | 83 | 84 | 85 | 86 | """.format( 87 | next_ver=self._ptask.next_version_number_padded, 88 | ptask=self._ptask.spec, 89 | ) 90 | 91 | for (key, val) in self.options.value.iteritems(): 92 | confirm_message += """ 93 | 94 | 95 | 96 | 97 | """.format( 98 | k=key.title(), 99 | v=val, 100 | ) 101 | 102 | confirm_message += """ 103 |
PTask :  {ptask}
{k} :  {v}


104 | Continuing will save your current session.

105 | Version up? 106 | """ 107 | 108 | # info dialog showing what will be done 109 | proceed = QtGui.QMessageBox.question( 110 | self, 111 | "Version Up Confirmation", 112 | confirm_message, 113 | buttons=QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Yes, 114 | defaultButton=QtGui.QMessageBox.NoButton, 115 | ) 116 | 117 | return proceed == QtGui.QMessageBox.Yes 118 | 119 | -------------------------------------------------------------------------------- /dpa/location/__init__.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Module: dpa.locations 3 | # Author: Josh Tomlinson (jtomlin) 4 | # ----------------------------------------------------------------------------- 5 | """Classes and methods specific to dpa pipeline physical locations. 6 | 7 | Classes 8 | ------- 9 | :py:obj:`Location` 10 | Read-only interface to dpa physical locations. 11 | 12 | Examples 13 | -------- 14 | 15 | Get an instance of a dpa location:: 16 | 17 | >>> from dpa.locations import Location 18 | >>> location = Location.get('CU_MCADAMS_DPA') 19 | >>> print locaction.name 20 | "Clemson DPA" 21 | 22 | Get all dpa locations:: 23 | 24 | >>> from dpa.locations import Location 25 | >>> locations = Location.list() 26 | >>> print str([loc.name for loc in locations]) 27 | ['Clemson DPA', 'Clemson Test DPA'] 28 | 29 | 30 | Get a filtered list of dpa locations by properties: 31 | 32 | >>> from dpa.locations import Location 33 | >>> locations = Location.list(timezone="US/Eastern") 34 | >>> print str([loc.name for loc in locations]) 35 | ['Clemson DPA'] 36 | 37 | Locations objects are read-only. If you feel you need to create a new or modify 38 | an existing location, please see the DPA pipeline support team. 39 | 40 | """ 41 | 42 | # ----------------------------------------------------------------------------- 43 | # Imports: 44 | # ----------------------------------------------------------------------------- 45 | 46 | from dpa.env.vars import DpaVars 47 | from dpa.restful import ReadOnlyRestfulObject, RestfulObjectError 48 | 49 | # ----------------------------------------------------------------------------- 50 | # Public Functions: 51 | # ----------------------------------------------------------------------------- 52 | def current_location_code(): 53 | """Retrieve the code of the current location. 54 | 55 | :rtype: str 56 | :return: The code of the current location. 57 | 58 | >>> from dpa.location import current_location_code 59 | >>> code = current_location_code() 60 | >>> print code 61 | 'CU_MCADAMS_DPA' 62 | 63 | """ 64 | 65 | return DpaVars.location_code("").get() 66 | 67 | # ----------------------------------------------------------------------------- 68 | # Public Classes: 69 | # ----------------------------------------------------------------------------- 70 | class Location(ReadOnlyRestfulObject): 71 | """Read-only interface to dpa physical locations. 72 | 73 | Instance ``properties``: 74 | 75 | :py:obj:`bool` ``active`` - ``True`` if locaiton is active, ``False`` 76 | otherwise 77 | 78 | :py:obj:`str` ``code`` - Unique identifier of the location. 79 | 80 | :py:obj:`str` ``description`` - Description of the location. 81 | 82 | :py:obj:`str` ``name`` - Full name of the location. 83 | 84 | :py:obj:`float` ``latitude`` - Latitude portion of the physical location. 85 | 86 | :py:obj:`float` ``longitude`` - Longitude portion of the physical location. 87 | 88 | :py:obj:`str` ``timezone`` - Name of the timezone for the location, ex: 89 | "US/Eastern" 90 | 91 | """ 92 | 93 | # ------------------------------------------------------------------------- 94 | # Class attributes: 95 | # ------------------------------------------------------------------------- 96 | 97 | data_type = 'locations' 98 | _current = None 99 | 100 | # ------------------------------------------------------------------------- 101 | # Class methods: 102 | # ------------------------------------------------------------------------- 103 | @classmethod 104 | def current(cls): 105 | 106 | if cls._current is None: 107 | cls._current = cls.get(current_location_code()) 108 | return cls._current 109 | 110 | # ------------------------------------------------------------------------- 111 | # Special methods: 112 | # ------------------------------------------------------------------------- 113 | def __eq__(self, other): 114 | return self.code == other.code 115 | 116 | # ------------------------------------------------------------------------- 117 | def __ne__(self, other): 118 | return self.code != other.code 119 | 120 | # ------------------------------------------------------------------------- 121 | def __repr__(self): 122 | 123 | return self.__class__.__name__ + "('" + self.code + "')" 124 | 125 | # ----------------------------------------------------------------------------- 126 | class LocationError(RestfulObjectError): 127 | pass 128 | 129 | Location.exception_class = LocationError 130 | 131 | -------------------------------------------------------------------------------- /bin/dpa: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ Command line interface to the DPA pipeline.""" 3 | 4 | # ------------------------------------------------------------------------------ 5 | # Imports: 6 | # ------------------------------------------------------------------------------ 7 | 8 | import argparse 9 | import sys 10 | 11 | # ---- import the appropriate actions 12 | 13 | from dpa.action import ActionError 14 | from dpa.action.registry import ActionRegistry 15 | from dpa.cli.action import CommandLineAction 16 | 17 | # ------------------------------------------------------------------------------ 18 | # Classes: 19 | # ------------------------------------------------------------------------------ 20 | class DPA(CommandLineAction): 21 | 22 | name = "dpa" 23 | 24 | # -------------------------------------------------------------------------- 25 | # Properties: 26 | # -------------------------------------------------------------------------- 27 | @classmethod 28 | def setup_cl_args(cls, parser): 29 | 30 | action_subparsers = parser.add_subparsers( 31 | title="Actions", 32 | ) 33 | 34 | # get all available actions 35 | # sort the actions by name + target type 36 | registry = ActionRegistry() 37 | all_actions = sorted(registry.get_registered_actions(), 38 | key=lambda a: a.name + a.target_type ) 39 | 40 | by_action = {} 41 | for action_class in all_actions: 42 | action_name = action_class.name 43 | action_list = by_action.setdefault(action_name, []) 44 | action_list.append(action_class) 45 | 46 | for action_name in sorted(by_action.keys()): 47 | 48 | action_parser = action_subparsers.add_parser(action_name, help="") 49 | target_subparsers = None 50 | 51 | for action_class in by_action[action_name]: 52 | 53 | target_type = action_class.target_type 54 | 55 | # no subparser 56 | if target_type.lower() == "none": 57 | if len(by_action[action_name]) > 1: 58 | raise ActionError( 59 | "Multiple actions with target of 'none'" 60 | ) 61 | 62 | target_parser = action_parser 63 | # subparser 64 | else: 65 | if not target_subparsers: 66 | target_subparsers = action_parser.add_subparsers( 67 | title="Targets", 68 | ) 69 | 70 | # add a parser for the target_type 71 | target_parser = target_subparsers.add_parser( 72 | target_type, 73 | help=action_class.get_description(), 74 | ) 75 | 76 | # extend the target_parser based on the action's args 77 | action_class.setup_cl_args(target_parser) 78 | 79 | # set the class for this target parser 80 | target_parser.set_defaults(action_class=action_class) 81 | 82 | # -------------------------------------------------------------------------- 83 | # Instance methods: 84 | # -------------------------------------------------------------------------- 85 | def __init__(self, *args, **kwargs): 86 | 87 | super(DPA, self).__init__(*args, **kwargs) 88 | 89 | action_class = self.kwargs.pop('action_class') 90 | self._action = action_class(*self.args, **self.kwargs) 91 | self._action.interactive = True 92 | 93 | # -------------------------------------------------------------------------- 94 | # Instance methods: 95 | # -------------------------------------------------------------------------- 96 | def execute(self): 97 | self.action() 98 | 99 | # -------------------------------------------------------------------------- 100 | def log_action(self): 101 | 102 | # only log if the action class to be executed has logging turned on 103 | if self.action.__class__.logging: 104 | super(DPA, self).log_action() 105 | 106 | # -------------------------------------------------------------------------- 107 | def undo(self): 108 | self.action.undo() 109 | 110 | # -------------------------------------------------------------------------- 111 | # Properties: 112 | # -------------------------------------------------------------------------- 113 | @property 114 | def action(self): 115 | return self._action 116 | 117 | # ------------------------------------------------------------------------------ 118 | if __name__ == "__main__": 119 | sys.exit(DPA.cli()) 120 | 121 | -------------------------------------------------------------------------------- /dpa/logging/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------------------------------------------- 3 | # Imports: 4 | # ---------------------------------------------------------------------------- 5 | 6 | import logging 7 | import os 8 | import platform 9 | import tempfile 10 | 11 | from dpa.env.vars import DpaVars 12 | from dpa.user import current_username 13 | 14 | # ---------------------------------------------------------------------------- 15 | # Private functions: 16 | # ---------------------------------------------------------------------------- 17 | def _create_log_file(path): 18 | with open(path, "w+") as f: 19 | f.write("") 20 | os.chmod(path, 0660) 21 | 22 | # ---------------------------------------------------------------------------- 23 | def _log_level_from_name(level_name): 24 | return getattr(logging, level_name.upper()) 25 | 26 | # ---------------------------------------------------------------------------- 27 | # Classes: 28 | # ---------------------------------------------------------------------------- 29 | class Logger(object): 30 | 31 | # ------------------------------------------------------------------------ 32 | # Public class attributes: 33 | # ------------------------------------------------------------------------ 34 | 35 | # the available levels for logging 36 | levels = ['NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] 37 | 38 | # log to the shared log directory, or a tempdirectory if undefined 39 | log_dir = DpaVars.share_logs(default=tempfile.gettempdir()).get() 40 | 41 | # the default logging level 42 | log_level = "WARNING" 43 | 44 | # ------------------------------------------------------------------------ 45 | # Private class attributes: 46 | # ------------------------------------------------------------------------ 47 | 48 | # ---- setup consistent stdout handling for all dpa code 49 | 50 | # formatting for all logging to stdout 51 | _stdout_formatter = logging.Formatter( 52 | fmt="%(name)s %(levelname)s: %(message)s" 53 | ) 54 | 55 | # handler for all stdout. Anything logged at a level matching or above 56 | # should go to stdout 57 | _stdout_handler = logging.StreamHandler() 58 | _stdout_handler.setFormatter(_stdout_formatter) 59 | _stdout_handler.setLevel(_log_level_from_name(log_level)) 60 | 61 | # format all log messages 62 | _logfile_formatter = logging.Formatter( 63 | fmt="[%(asctime)s] {un}@{ma} %(name)s %(levelname)s: %(message)s".\ 64 | format(ma=platform.node(), un=current_username()), 65 | datefmt="%Y/%m/%d-%H:%M:%S", 66 | ) 67 | 68 | # attach the handler to the root logger. 69 | _root_logger = logging.getLogger() 70 | _root_logger.addHandler(_stdout_handler) 71 | 72 | # keep track of processed logger names 73 | _logger_cache = dict() 74 | 75 | # ------------------------------------------------------------------------ 76 | # Class methods: 77 | # ------------------------------------------------------------------------ 78 | @classmethod 79 | def get(cls, name=None): 80 | 81 | if not name: 82 | name = "dpa" 83 | 84 | # make sure everything is namespaced with dpa. 85 | if not name.startswith("dpa"): 86 | name = ".".join(["dpa", name]) 87 | 88 | if not name in cls._logger_cache.keys(): 89 | 90 | # create the custom logger wrapper 91 | # log to a file based on the logger's name 92 | log_file = name + ".log" 93 | log_path = os.path.join(cls.log_dir, log_file) 94 | 95 | # ensure path exists with proper permissions first 96 | if not os.path.exists(log_path): 97 | _create_log_file(log_path) 98 | 99 | # create a handler to output to a log file, set the level to INFO 100 | logfile_handler = logging.FileHandler(filename=log_path) 101 | logfile_handler.setLevel(_log_level_from_name('INFO')) 102 | logfile_handler.setFormatter(cls._logfile_formatter) 103 | 104 | # get the named logger, set have it process all messages, and 105 | # have it send output to the file handler. note the level is 106 | # set on the handler. 107 | logger = logging.getLogger(name) 108 | logger.setLevel(_log_level_from_name('DEBUG')) 109 | logger.addHandler(logfile_handler) 110 | 111 | cls._logger_cache[name] = logger 112 | 113 | return cls._logger_cache[name] 114 | 115 | # ------------------------------------------------------------------------ 116 | @classmethod 117 | def set_level(cls, level): 118 | """Set the level for the root logger's stdout.""" 119 | cls.log_level = level 120 | cls._stdout_handler.setLevel(_log_level_from_name(level)) 121 | 122 | --------------------------------------------------------------------------------