Assistance using json_transform in pyportal project

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
macewmi
 
Posts: 69
Joined: Sat Feb 08, 2020 10:26 am

Assistance using json_transform in pyportal project

Post by macewmi »

I need some help using the adafruit_pyportal library.

I'm trying to adapt the Cleveland Museum of Art project (https://learn.adafruit.com/cleveland-mu ... rtal-frame) to the Art Institute of Chicago's public API.

In the CMA project, during a .fetch(), the pyportal is passed an URL. The pyportal uses the URL to get back a JSON response. The pyportal then uses a list of keys to navigate thru the JSON to extract a new URL that points to the jpg for the artwork. The pyportal uses the new jpg URL to download, resize and display the art.

The Art Institute of Chicago's API is a two step process. In ARTIC's API, the primary URL response does not have a singleton key:value pair that points to the jpg. Instead, the image_URL must be constructed from three separate values found in the JSON response:

The prefix: json_data["config"]["iiif_url"]
The artwork: json_data["imageID"]
And format: "/full/!320,240/0/default.jpg"

I don't know how to tell the pyportal how to construct the image URL. Looking through the documentation for the pyportal library, I see that you can provide "A function or a list of functions to call with the parsed JSON. Changes and additions are permitted for the dict object."

This suggests to me that I can pass in a python function to build the image URL using the information found in the dict (the response to the first URL), add the constructed jpg url back into the dict, and then let the pyportal find that value using the image_json_path argument that I pass in when pyportal was init'd. (The image_json_path being the key that was added back into the dict by the python function.)

Would that work? Can *anyone* point me to an example of such a helper function to manipulate the dict object?

Thanks.

User avatar
adafruit_support_mike
 
Posts: 67391
Joined: Thu Feb 11, 2010 2:51 pm

Re: Assistance using json_transform in pyportal project

Post by adafruit_support_mike »

Could you post some actual JSON for a work please?

Ultimately you'll need to write a function that reads values from the parsed JSON data structure and assembles the URL. The details of the function will be easier to describe with actual data.

User avatar
macewmi
 
Posts: 69
Joined: Sat Feb 08, 2020 10:26 am

Re: Assistance using json_transform in pyportal project

Post by macewmi »

Thank you. I'll gladly post specific JSON.

This URL,

Code: Select all

https://api.artic.edu/api/v1/artworks/search?q=degas&limit=1&fields=title,image_id,artist_title

returns this JSON (I've redacted some irrelevant key:value pairs.)

Code: Select all

{
  "preference": null,
  "pagination": {..."total_pages": 183,"current_page": 1},
  "data": [
    {
      "_score": 159.52505,
[u]      "artist_title": "Hilaire Germain Edgar Degas",
[/u][b]      "image_id": "cb34b0a8-bc51-d063-aab1-47c7debf3a7b",
[/b]      [u]"title": "Ballet at the Paris Opéra"[/u]
    }
  ],
  "info": {...},
[b]  "config": {
    "iiif_url": "https://www.artic.edu/iiif/2",
[/b]    "website_url": "http://www.artic.edu"
  }
}
keys:value pairs relevant to constructing the image_URL appear in bold. (Additional key:value pairs that I plan to extract and display as Labels on the pyportal appear underlined.)

For this specific query, the URL that points to the JPEG file would be

Code: Select all

https://www.artic.edu/iiif/2/cb34b0a8-bc51-d063-aab1-47c7debf3a7b/full/!320,240/0/default.jpg
Last edited by adafruit_support_mike on Mon Nov 21, 2022 11:38 pm, edited 1 time in total.
Reason: added CODE tags around the JSON

User avatar
adafruit_support_mike
 
Posts: 67391
Joined: Thu Feb 11, 2010 2:51 pm

Re: Assistance using json_transform in pyportal project

Post by adafruit_support_mike »

Thank you.

Here's some python that builds the full URL from the JSON above:

Code: Select all

import json

source = """{
  "preference": "null",
  "pagination": { "total_pages": 183,"current_page": 1 },
  "data": [
    {
      "_score": 159.52505,
      "artist_title": "Hilaire Germain Edgar Degas",
      "image_id": "cb34b0a8-bc51-d063-aab1-47c7debf3a7b",
        "title": "Ballet at the Paris Opéra"
    }
  ],
  "config": {
    "iiif_url": "https://www.artic.edu/iiif/2",
    "website_url": "http://www.artic.edu"
  }
}"""
	
hash = json.loads( source )

prefix = hash[ "config" ][ "iiif_url" ]
artwork = hash[ "data" ][ 0 ][ "image_id" ]
format = "full/!320,240/0/default.jpg"

full_url = prefix + "/" + artwork + "/" + format

print( full_url )
There are more clever ways to combine the pieces, but simple concatenation works.

User avatar
macewmi
 
Posts: 69
Joined: Sat Feb 08, 2020 10:26 am

Re: Assistance using json_transform in pyportal project

Post by macewmi »

Thank you for the code, but it doesn't address my question.

My question is how to get a PyPortal object to do that concatenation.

Here's some highly redacted pseudo-code that I've copied out of the Cleveland Museum Art Learning Guide that I referenced in my original post.

Code: Select all

from adafruit_pyportal import PyPortal

pyportal = PyPortal(default_bg=BACKGROUND_FILE,
                    image_json_path=["data", 0, "images", "web", "url"],
                    json_path=["data", 0, "title"])

APIURL = "https://openaccess-api.clevelandart.org/api/artworks?cc0=1&has_image=1&indent=2&limit=1&skip=534"

response = pyportal.fetch(APIURL)

while True:
    pass
This code will
  • Initialize a pyportal object
  • Initialize APIURL to get the data about artwork number 534
  • Fetch the data from the API
  • Display the data on the screen
During the fetch, the pyportal object is doing ALL the heavy lifting. The pyportal object
  1. makes the API request to the Museum
  2. receives a JSON response back
  3. extracts the image's full url from the JSON response using the keys in image_json_path
  4. downloads the image
  5. extracts the title of the art using the keys in json_path
  6. displays the image and title
The problem with adapting this code to the Art Institute of Chicago is in step 3. The image url can't be read directly from the Art Institute's JSON response. It has to be CONSTRUCTED by concatenating multiple key:values in the JSON and tacking on a string constant.

My question is whether the json_transform parameter of the PyPortal initialization can be used to construct the image url and if so, what would such a json_transform function look like?

I'd be satisfied with example code for ANY pyportal project that uses a json_transform that manipulates the JSON response.

Thanks.

User avatar
adafruit_support_mike
 
Posts: 67391
Joined: Thu Feb 11, 2010 2:51 pm

Re: Assistance using json_transform in pyportal project

Post by adafruit_support_mike »

The PyPortal runs CircuitPython, which includes the loads() function I used above.

json-transform is just another module that provides JSON encoding and decoding. Your only use for it would be to get the loads() function:

Code: Select all

from jsontransform import load, loadd, loads
(code from the json-transform documentation page: https://pypi.org/project/json-transform/)

User avatar
macewmi
 
Posts: 69
Joined: Sat Feb 08, 2020 10:26 am

Re: Assistance using json_transform in pyportal project

Post by macewmi »

Well, I did get one step closer to solving the problem with your last reply.

Here's an updated butchering of code in the Cleveland Museum of Art Learning Guide.
Notice that I've added two new functions, addKey() and accessKey(). Also, in the creation of the pyportal object, I've added a json_transform(addKey, accessKey) parameter.

Code: Select all

import board
from adafruit_pyportal import PyPortal
APIURL = "https://openaccess-api.clevelandart.org/api/artworks?cc0=1&has_image=1&indent=2&limit=1&skip=10"

def addKey(OBJECT):
    print("\nHello from inside addKey")
    print(type(OBJECT)) # a dict, apparently
    print(OBJECT["data"][0]["title"])
    OBJECT["data"][0]["programmer"] = "MACE"
    
def accessKey(OBJECT):
	print("\nHello from inside accessKey")
	print(OBJECT["data"][0]["programmer"])
	
BACKGROUND_FILE = "/background.bmp"
WIDTH = board.DISPLAY.width
HEIGHT = board.DISPLAY.height

pyportal = PyPortal(default_bg=BACKGROUND_FILE,
                    image_json_path=["data", 0, "images", "web", "url"],
                    image_dim_json_path=(["data", 0, "images", "web", "width"],
                                         ["data", 0, "images", "web", "height"]),
                    image_resize=(WIDTH, HEIGHT - 15),
                    image_position=(0, 0),
                    text_font="/fonts/OpenSans-9.bdf",
                    json_path=["data", 0, "title"],
                    text_position=(4, HEIGHT - 9),
                    text_color=0xFFFFFF,
                    json_transform=[addKey, accessKey],
                    )

print("retrieving url:", APIURL)
response = pyportal.fetch(APIURL)
print("Response is", response)

while True:
    pass
When I run the code, the pyportal object calls my two functions in sequence right before the fetch() finishes. So, I can write functions that access the JSON data that was downloaded from the library. The problem is that my functions run too late. The image has already been downloaded and displayed on the screen by the time my functions are called. So maybe the json_transform parameter isn't going to be the solution to my problem.

Do you have any suggestions on how I can get the pyportal to construct the image URL, as opposed to just reading the image URL from the JSON data?

Something like

Code: Select all

pyportal = PyPortal( image_url_path = ["config", "iiif_url"] +"/"+ ["imageID"] +"/full/!320,240/0/default.jpg",
...

User avatar
macewmi
 
Posts: 69
Joined: Sat Feb 08, 2020 10:26 am

Re: Assistance using json_transform in pyportal project

Post by macewmi »

I've abandoned my attempts to use the json_transform and success_callback parameters. They execute too late in the processing stream to do me any good.

Instead, I've downloaded the .py version of the adafruit_pyportal library, put it onto the pyportal and modified the .fetch() to do the concatenation immediately after the dict is created. The images are displaying, but it's a kludge.

This is my patch to __init__.py:

Code: Select all

PREFIX = json_out["config"]["iiif_url"] + "/"
IMAGE = json_out["data"][0]["image_id"]
OPTIONS = '/full/!320,240/0/default.jpg'
json_out["artic_image_URL"] = PREFIX + IMAGE + OPTIONS
and this is how I initialize the PyPortal object

Code: Select all

pyportal = PyPortal(default_bg='/artInstituteSplash.bmp',
                    image_json_path=["artic_image_URL"],
...
I've submitted a feature request on the Github for that library. Where my patch is, should be code to run a preprocessing_transform= function list.

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”