A client library for interacting with Bebanjo's Movida and Seqeuence APIs.
A client library interfacing to Bebanjo's Movida and Sequence APIs allowing integration developers to focus on the business logic of their application and not the low level detail.
The library provides support for authentication, exception handling, logging and utilities for common tasks. It can be used in an interactive session. By design it is very loosely coupled to Movida/Sequence schemas.
To demonstrate some features, here is an application to upload an image on all titles scheduled on platform Android GB
and target them for that platform and set them as the post art
for that title in the Movida UI.
from bebanjo import MovidaAPI
mapi = MovidaAPI(env='staging')
IMAGE_URL = 'tests/image/1yeVJox3rjo2jBKrrihIMj7uoS9.JPEG'
platforms = mapi.platforms.fetch()
target_platform = filter(lambda p: p.name == 'Android GB', platforms)[0]
for title in target_platform.titles.get_paginated():
image = title.images.create_image(IMAGE_URL, meta1={'IsPosterArt': True})
image.target_platforms.create(target_platform.url)
Although this would work as described, a well designed application would also include exception handling, checking if the image already exists and logging to file.
pip install --user bebanjo-api
Visit bebanjo-api in GitLab to clone and contribute to the project.
from bebanjo import MovidaAPI, install_auth_handlers
The authentication handler must be initialised with login data for the Bebanjo environments used. The library assumes there no distinction between Sequence and Movida credentials. The format of the credentials to be supplied to the handler initialisation is a nested dict as follows:
config = {
'staging': {
'name': 'robot_hi2meuk',
'password': 'mypassword'
}
}
The environment key(s) must be one of:
staging
,preproduction
orproduction
.
Setup the authentication handler before instantiating an API providing a config data structure as previously described:
install_auth_handlers(config)
A function is provided by the library to setup the auth config based on a local JSON file.
from bebanjo.utils import read_local_config
config = read_local_config(CONFIG_FILE)
The config file must be in JSON format. If a file path is not supplied, the default is ".bebanjo.json" in users home directory (Windows or Linux).
mapi = MovidaAPI(env='staging')
It is possible to specify a local Wiremock server to emulate Movida/Sequence for component testing purposes:
mock_url = 'http://localhost:8080/api'
mapi = MovidaAPI(url=mock_url)
API calls made and responses are logged using the python logging framework.
Logs are generated at debug level containing the XML payloads sent and received. Console output only includes logs of severity WARNINGS and above. This is a characteristic of the logging framework. The following code placed in the application will enable DEBUG to the console with in the format specified.
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
Imagine you are to navigate the Movida API and your only knowledge and starting point is the root: /api
. You would first have to do a GET call that resource to find it contains other collection resources (REST collection) like titles
and title_groups
and others. If you made a GET call on the titles
resource you will notice it has numerous metadata keys and values and links
to other collection resources including extended Metadata
. Likewise, you would have to do a GET call on those resources e.g. assets
to know what sort of resources it has.
The same applies to using this client library, often we have to do a fetch to access the properties beyond the API root.
This approach means the library is loosely coupled to Movida. If there are changes in Movida schema it is unlikely to require an update to this library. it also means you do not have to tell the library about your companies custom metadata; the library will discover it and make it available to your application.
There are exceptions to this "fetch first" principal. Because fetching the API is a common starting point and what it contains is very predictable, the library pre-populates the properties of the root API object, e.g. it already has a titles
property:
mapi = MovidaAPI(env='staging')
title = mapi.fetch(1001)
If you want to avoid unnecessary GET calls when you know the next item beyond the current URL path, you can patch it in to your object. A common use case is the schedule resource; when you fetch it, you find it only contains a link to the schedulings resource and nothing else. We can add the schedulings resource directly and save a GET call.
mapi = MovidaAPI(env='staging')
title = titles.fetch(1001)
schedulings = title.schedule.add_link('schedulings').fetch()
The get_paginated() method of a collection resource e.g. titles is a python generator. It will iterate through each title of the resource returning each title at a time. It will make the GET calls to Movida one page at a time as and when needed (by default with a page size of 50). Only the page of items is held in memory within the collection object at any time.
platform = mapi.platforms.fetch(101)
titles = platform.titles.get_paginated()
for title in titles:
print(title.name)
Images can be uploaded from a local file or from a remote server location, the method will be determined from the file path given to the create_image()
method. The return value is an image object representing the created image. Images can be created from a pre-fetched or post fetched object. The encoding type and file name are by default extracted from the given image file name but can be overridden by passing in a dict object. Other metadata can also be passed in. Movida will set values for size and MD5 checksum when the file is processed - this is done asynchronously for remotely sourced images and will be empty in the locally created object.
IMAGE_PATH_LOCAL = 'tests/image/1yeVJox3rjo2jBKrrihIMj7uoS9.JPEG'
mapi = MovidaAPI(env='staging')
title = titles.fetch(1001)
image = title.images.create_image(IMAGE_PATH_REMOTE)
image.target_platforms.add_platforms(['https://movida.bebanjo.net/api/platforms/51', 'api/platforms/52', 53])
A remote path is also supported.
IMAGE_PATH_REMOTE = 'https://mydomain.com/image/1yeVJox3rjo2jBKrrihIMj7uoS9.jpg'
For existing images there is a risk the addition of a target_platform will fail if it already exists on the image.
images = title.images.fetch()
for image in images:
if image.type == SELECTED_IMAGE_TYPE:
image = image.fetch()
image.target_platforms.add_platforms(TARGET_PLATFORMS) # will fail if any exists already
To avoid the possible failure do:
image = image.fetch()
image.target_platforms = image.target_platforms.fetch() # get existing target platforms
image.target_platforms.add_platforms(TARGET_PLATFORMS) # will not add platform if already exists
Note the long code line in the above:
image.target_platforms = image.target_platforms.fetch()
This is necessary when target_platforms is of class Fetcher; it cannot upgrade its own class to Collection. A helper is available to make the code a little cleaner.
from bebanjo.utils import replace_self_fetch
replace_self_fetch(image.target_platforms)
As long as you have valid credentials loaded that are valid for Movida and Sequence, then the library does is agnostic to the resource referenced in an object property. E.g. the jobs resource on a Movida scheduling refers to a resource in Sequence and accessing that resource will automatically trigger authentication with Sequence.
Inspect the properties of a title; first class metadata, extended metadata and links to related objects that can be fetched for further inspection. It prints directly to the console. It is intended for debugging and interactive sessions.
from bebanjo import inspect
inspect(title)
Output:
Instance of Title (//titles/1001)
> name: Winter Is Coming
> title: Winter Is Coming
> external_id: 63056
> title_type: 'episode
> episode_number: 1
> tags:
Metadata:
> short_description: The "one" where winter's comming
> air_date: 2011-04-24
> content_source: The Movie Database
> subgenres: ['Action', 'Fantasy']
> download_rights: True
Getters:
> assets
> availability_windows
> blackouts
> images
> licensor
> linear_schedulings
> metadata
> rights
> rules
> schedule
> title_groups
> trailers
title = mapi.titles.fetch(1001)
title = mapi.titles.fetch(external_id='HI2MEUK/377364', expand=['metadata', 'images'])
asset = mapi.assets.fetch(name='BBJ12345A')
No need to fetch the collection resource first as only it's URL is needed to create an object inside this resource.
mapi = MovidaAPI(env='staging')
meta = {'name': 'The Ring'}
relations = {'licensor': 'api/licensors/4'}
mapi.titles.create(meta, relations)
In the above example, if name were not specified, Movida would complain with a 422 response because
name
is mandatory for a title. Likewise if a metadata key or value is invalid, a 422 response would also result.
Access the values of metadata like they're python dictionary.
# first class metadata
name = title['name']
# extended metadata
description = title.metadata['description']
# first class metadata
title.update({'name': 'The Ring'}, {'licensor': 'api/licensors/1201'})
# extended metadata requires a seperate call to Movida
title.metadata.update({'name': 'abcdéf', 'description': 'Simon looses the ring.'})
id_from_platform = {}
for platform in mapi.platforms.fetch():
id_from_platform[platform.name] = platform.id