Post Slack message on publish¶
Next, we implement a custom finaliser within Maya - a small function that posts a Slack message containing the version ident, the comment and a thumbnail (can be replaced with the full Quicktime reviewable movie if desired), with each publish made.
Functions that run after load and import are called “finalizers” and they are fed all the data from the previous steps and stages.
Save copy of thumbnail image¶
As a preparation, we need to have the thumbnail publisher to save a copy to be used with the Slack post:
PIPELINE/ftrack-connect-pipeline-definitions/resource/plugins/maya/python/publisher/exporters/maya_thumbnail_publisher_exporter.py
1import os
2import shutil
3
4..
5
6def run(self, context_data=None, data=None, options=None):
7 ..
8 # Make a copy of the thumbnail to be used with Slack post
9 path_slack_thumbnail = os.path.join(os.path.dirname(path), 'slack-{}'.format(os.path.basename(path)))
10 shutil.copy(path, path_slack_thumbnail)
11
12 return [path]
Finaliser¶
PIPELINE/ftrack-connect-pipeline-definitions/resource/plugins/common/python/publisher/finalisers/common_slack_post_publisher_finalizer.py
1# :coding: utf-8
2# :copyright: Copyright (c) 2014-2022 ftrack
3import os
4
5from slack import WebClient
6
7import ftrack_api
8
9from ftrack_connect_pipeline import plugin
10
11
12class CommonSlackPublisherFinalizerPlugin(plugin.PublisherPostFinalizerPlugin):
13
14 plugin_name = 'common_slack_publisher_finalizer'
15
16 SLACK_CHANNEL = 'test'
17
18 def run(self, context_data=None, data=None, options=None):
19
20 # Harvest publish data
21 reviewable_path = asset_version_id = component_names = None
22 for component_data in data:
23 if component_data['name'] == 'thumbnail':
24 for output in component_data['result']:
25 if output['name'] == 'exporter':
26 reviewable_path = output['result'][0]['result'][0]
27 elif component_data['type'] == 'finalizer':
28 for step in component_data['result']:
29 if step['name'] == 'finalizer':
30 asset_version_id = step['result'][0]['result'][
31 'asset_version_id'
32 ]
33 component_names = step['result'][0]['result'][
34 'component_names'
35 ]
36 break
37
38 # Fetch version
39 version = self.session.query(
40 'AssetVersion where id={}'.format(asset_version_id)
41 ).one()
42
43 # Fetch path to thumbnail
44 if reviewable_path:
45 # Assume it is on the form /tmp/tmp7vlg8kv5.jpg.0000.jpg, locate our copy
46 reviewable_path = os.path.join(
47 os.path.dirname(reviewable_path),
48 'slack-{}'.format(os.path.basename(reviewable_path)),
49 )
50
51 client = WebClient("<slack-api-key>")
52
53 ident = '|'.join(
54 [cl['name'] for cl in version['asset']['parent']['link']]
55 + [version['asset']['name'], 'v%03d' % (version['version'])]
56 )
57
58 if reviewable_path:
59 self.logger.info(
60 'Posting Slack message "{}" to channel {}, attaching reviewable "{}"'.format(
61 ident, self.SLACK_CHANNEL, reviewable_path
62 )
63 )
64 try:
65 response = client.files_upload(
66 channels=self.SLACK_CHANNEL,
67 file=reviewable_path,
68 title=ident,
69 initial_comment=version['comment'],
70 )
71 finally:
72 os.remove(reviewable_path) # Not needed anymore
73 else:
74 # Just post a message
75 self.logger.info(
76 'Posting Slack message "{}" to channel {}, without reviewable'.format(
77 ident, self.SLACK_CHANNEL
78 )
79 )
80 client.chat_postMessage(channel=self.SLACK_CHANNEL, text=ident)
81 if response.get('ok') is False:
82 raise Exception(
83 'Slack file upload failed! Details: {}'.format(response)
84 )
85
86 return {}
87
88
89def register(api_object, **kw):
90 if not isinstance(api_object, ftrack_api.Session):
91 # Exit to avoid registering this plugin again.
92 return
93 plugin = CommonSlackPublisherFinalizerPlugin(api_object)
94 plugin.register()
Breakdown of plugin:
With the data argument, the finaliser gets passed on the result from the entire publish process. From this data we harvest the temporary path to thumbnail and asset version id.
We transcode the path so we locate the thumbnail copy.
A Slack client API session is created
An human readable asset version identifier is compiled
If a thumbnail were found, it is uploaded to Slack. A standard chat message is posted otherwise.
Add Slack finaliser to publishers¶
Finally we augment the publishers that we wish to use.
PIPELINE/ftrack-connect-pipeline-definition/resource/definitions/publisher/maya/geometry-maya-publish.json
1{
2 "type": "publisher",
3 "name": "Geometry Publisher",
4 "contexts": [],
5 "components": [],
6 "finalizers": [
7 {
8 "name": "main",
9 "stages": [
10 {
11 "name": "pre_finalizer",
12 "visible": false,
13 "plugins":[
14 {
15 "name": "Pre publish to ftrack server",
16 "plugin": "common_passthrough_publisher_pre_finalizer"
17 }
18 ]
19 },
20 {
21 "name": "finalizer",
22 "visible": false,
23 "plugins":[
24 {
25 "name": "Publish to ftrack server",
26 "plugin": "common_passthrough_publisher_finalizer"
27 }
28 ]
29 },
30 {
31 "name": "post_finalizer",
32 "visible": true,
33 "plugins":[
34 {
35 "name": "Post slack message",
36 "plugin": "common_slack_publisher_finalizer"
37 }
38 ]
39 }
40 ]
41 }
42 ]
43}
Repeat this for all publishers that should have the finaliser.
Add Slack library¶
To be able to use the Slack Python API, we need to add it to our Framework build. We do that by adding the dependency to setup.py:
ftrack-connect-pipeline-definition/setup.py
1..
2
3# Configuration.
4setup(
5 name='ftrack-connect-pipeline-definition',
6 description='Collection of definitions of package and packages.',
7 long_description=open(README_PATH).read(),
8 keywords='ftrack',
9 url='https://bitbucket.org/ftrack/ftrack-connect-pipeline-definition',
10 author='ftrack',
11 author_email='support@ftrack.com',
12 license='Apache License (2.0)',
13 packages=find_packages(SOURCE_PATH),
14 package_dir={'': 'source'},
15 python_requires='<3.10',
16 use_scm_version={
17 'write_to': 'source/ftrack_connect_pipeline_definition/_version.py',
18 'write_to_template': version_template,
19 'version_scheme': 'post-release',
20 },
21 setup_requires=[
22 'sphinx >= 1.8.5, < 4',
23 'sphinx_rtd_theme >= 0.1.6, < 2',
24 'lowdown >= 0.1.0, < 2',
25 'setuptools>=44.0.0',
26 'setuptools_scm',
27 'slackclient'
28 ],
29 install_requires=[
30 'slackclient'
31 ],
32 tests_require=['pytest >= 2.3.5, < 3'],
33 cmdclass={'test': PyTest, 'build_plugin': BuildPlugin},
34 zip_safe=False,
35)
Important
A better approach is to add the dependency to the ftrack-connect-pipeline
module where the other pipeline dependencies are defined and built.