Utililities¶
Contents
Working with Assets¶
Tools for working with Asset Entries (Items)
Create or Update Asset¶
-
flow.operation.create_or_update_asset(id=None, metadata=None, acl=None, mediaacl=None, tags=None, materialtype=None, category=None, rightscode=None, client=None)¶ Creates or Updates an Item with a given
id. Metadata updates will be retried three times if there are conflicts.Parameters: - id (Optional[unicode]) – An asset id or “site identity”
- metadata (Optional[vizone.vdf.Payload]) – The metadata to update to. Can be a dict with a ‘form’ field too.
- acl (Optional[vizone.vizone.payload.user_group.Acl]) – Specify a ACL when creating the Asset
- mediaacl (Optional[vizone.vizone.payload.user_group.Acl]) – Specify a Media ACL when creating the Asset
- tags (Optional[dict]) – scheme => term dictionary for custom tags when creating the Asset
- materialtype (Optional[unicode]) – Set the Material Type to this when creating the Asset
- category (Optional[unicode]) – Set the Category to this when creating the Asset
- rightscode (Optional[unicode]) – Set the Rights Code to this when creating the Asset
- client (Optional[vizone.client.Instance]) – A Viz One client to use (None means the default)
Returns: The updated or created Asset Entry
Return type: vizone.payload.asset.Item
Modifying Group Permissions in an ACL¶
-
flow.acl.set_group_permissions(acl, group_name, read=True, write=True, admin=True)¶ Make sure a group is in the ACL and that is has the correct rights, If no rights are true, the group entry will be removed.
Parameters: - vizone.payload.user_group.aclentry.Acl (acl) – The ACL to operate on
- str (group_name) – The name of the group
- bool (admin) – Read right
- bool – Write right
- bool – Admin right
Returns: True if altered, False otherwise
Return type: bool
Working with Files¶
Tools useful for handling file import flows.
Import Unmanaged File¶
-
flow.operation.import_unmanaged_file(asset, uri_list, client=None)¶ Start an Unmanaged File import to a given Asset Entry
Parameters: - asset (vizone.payload.asset.Item) – The Asset Entry to import to
- uri_list (vizone.urilist.UriList) – A URI List containing the link to the media
- client (Optional[vizone.client.Instance]) – A Viz One client to use (None means the default)
Returns: True if successful, False on error
Return type: bool
Delete Unmanaged File¶
-
flow.operation.delete_unmanaged_file(unmanaged_file, client=None)¶ Delete an Unmanaged File from Viz One
Parameters: - unmanaged_file (vizone.payload.media.UnmanagedFile) – The Unmanaged File to delete
- client (Optional[vizone.client.Instance]) – A Viz One client to use (None means the default)
Building a Transfer Plugin¶
You can use Flow to build a Transfer Plugin quite easily.
Extending the TransferPlugin class¶
-
class
flow.transfer.TransferPlugin(instance_name=None)¶ Transfer Plugin base class.
TransferPlugin child classes automatically inherits Flow and NeedsClient.
Very basic example (for more advanced ones, look at the XmlExport built-ins):
from flow.transfer import TransferPlugin from vizone import logging class MyTransferPlugin(TransferPlugin): def start(self, plugin_data): self.use(plugin_data) self.update_progress(0) logging.info(u'Asset is %s', self.asset.title) logging.info(u'Source URL is %s', self.source) logging.info(u'Destination URL is %s', self.destination) self.update_progress(100) # or why not: self.fail("There were errors!")
-
SOURCE¶ alias of
STDIN
-
fail(message)¶ Failed the Transfer Step and exits the program with status 0. This allows for a cleaner reporting in Viz One.
Parameters: unicode|str (message) – An error message that will show up in Viz One.
-
start(data, **kwargs)¶ You’ll have to implement this method yourself.
-
update_progress(progress)¶ Update the progress of the associated Transfer Step.
Parameters: int (progress) – As a percentage from 0 to 100, e.g. 67
-
use(data, require_asset=True, require_source=True, require_destination=True)¶ Extract and verify the plugin data that comes from the Transfer Subsystem. We should have gotten two FTP addresses, plugin settings in the form of a VDF payload containing username and password, as well as a transfer step and request to control the workflow and report back progress.
You can use the
require_asset,require_sourceandrequire_metadataflags to control what to require in terms of content in the plugin data.
-
Setting up the Repository¶
Creating a Transfer Plugin package is easiest to do with pluginmgr on a Viz
One server. It will help you to create the files you need. Before you start,
create a new folder and add a file named plugin to it, with these contents:
type: runnable
package: myplugin
depends: python-one
methods: myplugin
mode: filecopy
title: My Plugin
author: Vizrt
version: 1.0
source-scheme: ftp
destination-scheme: ftp
destination-conflicts: none
killprocess: false
partial: false
partial-by-frame: false
You can now basically use pluginmgr make interactively until you get it right.
mkdir myplugin
cd myplugin
vim plugin
pluginmgr make
# vdf: vdf model at ~/myplugin/etc/xfer-plugin-myplugin.vdf
# required but missing at /opt/ardome/bin/pluginmgr line 847.
So, you need a VDF. This is for holding the settings of your plugin in a way
that is editable in Viz One’s Administration console. You can use pluginmgr
vdf to create one:
pluginmgr vdf plugin.user:Username:user …
plugin.password:Password:user …
> etc/xfer-plugin-myplugin.vdf
Now try pluginmgr make again:
pluginmgr make
# Could not find the specified bin/myplugin in source tree
So there is no plugin executable to run. For flow this will be a script, named
bin/myplugin (where myplugin would be what you specified as method
above) create a folder bin and put a file myplugin with this in it:
#!/bin/bash
/opt/python-one/bin/wrap_python -m flow …
/opt/ardome/apps/myplugin/myplugin.ini -g
Note that the path might need to be adjusted later, but this is a decent
convention. Now try pluginmgr make again:
pluginmgr make
# INFO:
# INFO: Edit files as necessary. Then run the following command to …
# build the apa-package:
# INFO:
# INFO: $ apa dist xfer-plugin-myplugin/1.0
# INFO:
# INFO: To install:
# INFO: # scamp install -i [version] xfer-plugin-myplugin--1.0.apa
# INFO: # scamp apply
# INFO: $ ardemctl restart xfer
# INFO:
Now pluginmgr is happy with the setup. Included here are also the
instructions on how to build an APA package out of this. It’s not time for this
quite yet though, so just make a note of the command for later use. We should
create the actual app as well:
mkdir -p apps/myplugin
vim apps/myplugin/myplugin.py
vim apps/myplugin/myplugin.ini
Good starting point for the myplugin.py file:
from flow.transfer import TransferPlugin
from vizone import logging
class MyPlugin(TransferPlugin):
def start(self, plugin_data):
self.use(plugin_data)
self.update_progress(0)
logging.info(u'Asset is %s', self.asset.title)
logging.info(u'Source URL is %s', self.source)
logging.info(u'Destination URL is %s', self.destination)
self.update_progress(100)
And for the myplugin.ini file:
[Flow]
app name = myplugin
class = myplugin.MyPlugin
[Source]
payload class = vizone.payload.transfer.PluginData
[Viz One]
use https = no
Now these files needs to be part of the APA package. To achieve this, edit the
file build/xfer-plugin-myplugin/FILES (the first line is new):
apps/myplugin/* -> apps/myplugin
@ chmod 755
bin/myplugin -> xferplugin/bin/myplugin
@ nochmod
@ nochmod
@ chmod 644
etc/xfer-plugin-myplugin.xml -> xferplugin/etc/xfer-plugin-myplugin.xml
@ nochmod
@ chmod 644
etc/xfer-plugin-myplugin.vdf -> xferplugin/etc/xfer-plugin-myplugin.vdf
@ nochmod
After changing this, do not run ``pluginmgr make`` again, as this will
overwrite the files in build/. You can actually delete the plugin file
now and edit etc/xfer-plugin-myplugin.xml if you want to change any plugin
settings.
Creating and Installing a Package¶
To build an APA package of your plugin, you can use the command given by
pluginmgr make previously. Remember: don’t run it again now!
apa dist xfer-plugin-myplugin/1.0
Note
The command apa might need to be called with explicit path, being
/opt/scamp/bin/apa.
This command should not give any output, but there should be an .apa file
in your working directory. Install this with scamp and restart the transfer
daemons:
sudo /opt/scamp/bin/scamp install xfer-plugin-myplugin--1.0.apa
sudo /opt/scamp/bin/scamp apply
ardemctl restart xfer
Setting up a Rewrite Rule so the Plugin Gets Used¶
That the plugin exists does not mean it’s automatically used. This example shows how to set up an export storage and use it to export to it. First create the export storage:
storagemgr add stg plugin-export description="Plugin Export"
storagemgr join export plugin-export
mkdir /home/ardome/plugin-export
sudo chown armedia:ardome /home/ardome/plugin-export
sudo ln -s /home/ardome/plugin-export /ardome/media/exp/plugin-export
storagemgr add mountpoint [INSERT SERVER HERE] plugin-export …
/ardome/media/exp/plugin-export
To make every transfer to this new export destination use the plugin you must
create a rewrite rule. Run confmgr edit transfer.rewrite, and add a new
one:
1:
criteria:
destination-storage:
- plugin-export
apply:
destination-step-method: myplugin
Miscellaneous¶
Various tools for making life easier.
Storing Data on the Server¶
-
class
flow.store.Store(appname, client)¶ A Store is a centralized store based on the Client Config API. Note that:
- The Client Config API operates per user.
- The Client Config API operates per application.
- Data can be any JSON serializable object, for instance nested python dicts, lists, strings, ints, floats and booleans.
Usage example:
from flow import Flow from flow.needs import NeedsStore, NeedsClient from flow.source import UnmanagedFilesListener class MyFlow(Flow, NeedsStore): """ MyFlow uses a Store. """ source = UnmanagedFilesListener def start(self, f, info=None, log_id=-1): # If your Flow class inherits NeedsStore, it will be equipped with # an ``self.store`` attribute which can be used to get, update and # delete stored data on the server. stored_info = self.store.get('key') if stored_info is None: # there was nothing there self.store.put('key', { 'any': 'json', 'serializable': ['structure', 'goes', 'here'] }) # You can also delete self.store.delete('key')
-
delete(key)¶ Delete the stored
valueunder akeyfor the object’s application.Parameters: key (unicode) – The key to delete
-
get(key)¶ Get the value of a certain key for the object’s application.
Parameters: key (unicode) – The key used for storage Returns: The storede value or NoneReturn type: dict
-
put(key, value)¶ Put a
valueunder akeyfor the object’s application.Parameters: - key (unicode) – The key used for storage
- value (dict) – The data to store, as a dict
Returns: The storede value is returned
Return type: dict
Parsing Data Fields with the MultiParser¶
-
class
flow.data.MultiParser(type='string', format=None, default_timezone='UTC', source=None)¶ A value parser that converts various data formats into Python and Viz One entities, including:
unicode(default)intfloatiso8601(timestamp)date(date with configurable format)time(time with configurable format)datetime(date + time with configurable format)dictionary(dictionary term with given source reference)
The intended use is for putting the resulting object as value into a VDF Payload.
Example:
from flow.data import MultiParser mp = MultiParser(type='date', format='%d/%m') result = mp.convert('4/12')
For a more thourough example using the MultiParser together with configuration, please check out
xmlimport.XmlImport.For more information about date and time parsing syntax, please refer to https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
Parameters: - type (str) –
string|integer|float|iso|date|time|datetime|dictionary - format (str) – format string for parseing
date,timeanddatetime - default_timezone (str) – default time zone only used fore
datetime - source (str) – url do
dictionary, should be an Atom-based feed
-
convert(raw_value, client)¶ Perform conversion configured when contructing the object.
Parameters: - raw_value (unicode) – The raw string to parse
- client (vizone.client.Instance) – The HTTP client to use when looking up dictionary terms.
Returns: object
Locking Based on a Key¶
-
class
flow.lock.Locked(key)¶ Thread-safe lock by key string.
Usage:
from flow.lock import Locked with Locked('mystring'): # .. do stuff
Only one process per key can be run simultaneously, other attempt will be held until the lock is released.
Retrying on Conflict¶
-
flow.operation.retry_on_conflict(max_retries=3)¶ Decorator that can wrap a function or method and retry it upon a conflict exception.
Example:
@retry_on_conflict(max_retries=3) def my_function(self, entry, conflict=False): # If there was a conflict, the entry needs to be refetched if conflict: entry.parse(self.client.GET(entry.self_link)) # Do the operations self.client.PUT(entry.edit_link, entry)
Note that:
- Any argument will be reused as it, with changes.
- You can raise a
Retryexception to retry for other reasons than a 409 Conflict.