Skip to content
This repository has been archived by the owner on Mar 7, 2021. It is now read-only.

KPBS/py3-pmp-wrapper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

py3-pmp-wrapper

Archived 2021-03-06

An application for interacting with the Public Media Platform (PMP) API. This application has been written for Python 3.3 and has been tested with Python3.3 and Python3.4. The full documentation for this application is located here.

To find out more about the Public Media Platform API, consult the documentation.

Installation

This package may be installed using pip:

pip install py3-pmp-wrapper

Quickstart

First, create a pmp_client.Client object and pass it the entry-point to the application:

>>> from pmp_api import Client
>>> client = Client("https://api.pmp.io")

Generate Access Token

After that, you will need to authenticate your client. To authenticate, pass in your client-id and client-secret to the gain_access method. You should already have a client-id and client-secret, but if not see below.

>>> client.gain_access(CLIENT-ID, CLIENT-SECRET) # This is NOT a username/password combination

Making Requests

Now, we're ready to make requests of the PMP API:

>>> home_doc = client.home() # Get homedoc
>>> random_request = client.get("https://Some/arbitrary/endpoint?params=someparam")
>>> random_request
<Navigable Doc: https://Some/arbitrary/endpoint?params=someparam>
>>> client.document # Most recent result is saved here
<NavigableDoc: https://Some/arbitrary/endpoint?params=someparam>

The client will automatically sign all requests and it should renew your access token if it expires.

Navigating

Using the fetched document's navigation object, the Client can follow navigation, if it's present:

>>> client.next() # If the document defines a 'next' navigation element, we can follow it
<NavigableDoc: https://api-pilot.pmp.io/docs?guid=SOME_GUID&offset=10>
>>> client.prev() # Same as above, returns None if nothing there...
>>>
>>> client.last()  # requests 'last' page of results as given by document
>>> client.first() # requests 'first' page of results as given by document

Keep in mind: each request here fetches a new document and sets the client.document attribute to the current document.

We can also go back or forward, like a browser, re-requesting previous documents:

>>> client.document
<NavigableDoc: https://api-pilot.pmp.io/docs?params=someparam>
>>> client.back()  # This issues a new request
<NavigableDoc: https://api-pilot.pmp.io/docs?guid=SOME_GUID>
>>> client.forward() 
<NavigableDoc: https://api-pilot.pmp.io/docs?params=someparam>

Using URNs

Most of the useful navigation is done via urn, the primary method for accessing content, and the Client object provides a query method for use with a urn. For example, let's look at urn:collectiondoc:query:docs, which contains information for querying documents.

>>> document = client.query('urn:collectiondoc:query:docs', params={"tag": "samplecontent", "profile": "story"})
<NavigableDoc: https://api-pilot.pmp.io/docs?guid=SOME_GUID>
>>> document.items
{ ITEMS ... }

NavigableDoc objects

To really get interesting information back, we need to have some way of managing it. For this reason, a Client object returns NavigableDoc elements. These have a number of methods and properties, which should make it easier to extract information from the document.

>>> document = client.query('urn:collectiondoc:query:docs', params={"tag": "samplecontent", "profile": "story"})
<NavigableDoc: https://api-pilot.pmp.io/docs?guid=SOME_GUID> # returns NavigableDoc
>>> document.links
{'item': [{'href': 'https://api-pilot.pmp.io/docs/SOMEGUID ...
>>> document.items
[{'attributes': {'valid': {'to': '3014-07-29T18:08:11+00:00', 'from': ...
>>> document.querylinks
[{'rels': ['urn:collectiondoc:query:users'], 'href-template': ...

Querying

In order to get interesting results back, we generally want to issue queries, but it can be tough to remember how to make queries. The NavigableDoc has a template method that will reveal what params are available for a query and you can use the Client to create a query with these params:

>>> document.template('urn:collectiondoc:query:docs')
'https://api-pilot.pmp.io/docs{?guid,limit,offset,tag,collection,text,searchsort,has,author,distributor,distributorgroup,startdate,enddate,profile,language}'
>>> client.query('urn:collectiondoc:query:docs', params={'has': 'audio', 'language': 'en'})
<NavigableDoc: https://api-pilot.pmp.io/docs/SOMEQUERY>

The document also has a query method which will create well-formed queries for you, but it will raise a BadQuery exception if you pass it a set of invalid parameters, telling you which of those parameters are invalid:

>>> document.query('urn:collectiondoc:query:docs',params={'has': 'audio', 'language': 'en'})
'https://api-pilot.pmp.io/docs?language=en&has=audio'
>>> document.query('urn:collectiondoc:query:docs',params={'Not-a-param': 'Error', 'profile': 'story'})
Traceback (most recent call last):
...
  raise BadQuery(errmsg.format(badstuff))
pmp_api.core.exceptions.BadQuery: Query param does not exist: {'Not-a-param'}

(For values accepted by query keys, consult the PMP documentation

Options and Query-Types

We can also find all options associated with the urn:

>>> document.options('urn:collectiondoc:query:docs')
{'rels': ['urn:collectiondoc:query:docs'], 'href-template': ...

What if we want to know which urns are listed at a particular endpoint? We must ask the document for its query_types:

>>> for item in document.query_types():
...     print(item)
('Query for users', ['urn:collectiondoc:query:users'])
('Query for schemas', ['urn:collectiondoc:query:schemas'])
('Access documents', ['urn:collectiondoc:hreftpl:docs'])
('Query for documents', ['urn:collectiondoc:query:docs'])
etc.

All Results

Finally, you can always retrieve all of the results inside a document by acessing its collectiondoc attribute. This will return a dictionary of all values contained in the document:

>>> document.collectiondoc
{ALL-The_Data ...}

You can get or set values directly this way, or you can use the edit method, which requires knowing exactly where the data is located (and if you are not careful, it's easy to make a document invalid in this way by clobbering a list with a string for instance).

To use the edit method, pass in a tuple or list of keys/indices to where the data is located:

>>> document.edit(['attributes', 'guid'], "NEW GUID")
{'guid': "NEW GUID", etc.}
>>> document.edit(['links', 'navigation', 0, 'rels'], "NEW REL")

And you can get a json dump of the data inside a NavigableDoc (for submitting it back to PMP, for instance) by calling its serialize method:

>>> document.serialize()
'{ALL-The_Data ...}'

Credentials

The PMP API uses OAUTH2, which means that you need a client-id and client-secret in order to receive an access token and to use the application.

In order to request access, you must already have a username/password. Only the maintainers of PMP can issue a username/password. This application has been written and maintained by a third-party and neither this application nor the maintainers can issue username/password combinations.

Generating Credentials

To generate new client_id/client_secret in order to access the PMP API, you can either issue a curl request or you can use the pmp_api.core.access module, which will create read-only credentials.

Via Curl
curl --user pmpuser:pmpsecret \
     -X POST "https://publish-pilot.pmp.io/auth/credentials" \
     -d "scope=read" \
     -d "token_expires_in=1380000" \
     -d "label=somethingCool" \
     -L \
     -m 5 \
Issuing Credentials Using This Application

If you do not have a client_id/client_secret for your application, you can create a PmPAccess object and have it generate credentials for you using your username and password:

>>> from pmp_api.core.access import PmpAccess
>>> pmp_access = PmpAccess(username, password)
>>> pmp_access.generate_new_credentials(PMP_API_AUTHENTICATION_ENDPOINT, LABEL)
(CLIENT_ID, CLIENT_SECRET)