How can I access a nested JSON attribute in python? - python

How do I access the description field in my request payload?
REQUEST PAYLOAD:
data = { "other_details" :{
"my_story":true,
"preferences":false,
"profile_photo":false,
},
"experience_detail":[
{
"description":"Location: Indan\nCurrent job
responsibilities:\n\n• To manage, interpret,
and integrate inspection and function test data associated
with pressure relief
devices\n• Collection and Managing the database of
inspection/test records\n",
"employer_name":"Wood Group",
"end_date": "2020-04-28",
"id": "30dbe99d0c55435ea3d5119ece0ac873",
"is_current_employer": true,
"job_title": "Integrity Engineer",
"start_date": "2013-11-25"
},
{
"description":"Location: Indian\nCurrent job
responsibilities:\n\n• To manage, interpret,
and integrate inspection and function test data
associated with pressure relief devices\n•
Collection and Managing the database of
inspection/test records\n",
"employer_name":"QUESS CORP.",
"end_date": "2020-04-28",
"id": "30dbe99d0c55435ea3d5119ece0ac873",
"is_current_employer": true,
"job_title": "Integrity Engineer",
"start_date": "2013-11-25"
}
]
}
I tried a few ways, but they didn't work.
Attempt 1:
def index():
data = json.loads(request.data)
clean_description = (data['other_details']
['experience_detail']['description'])
Output:
clean_description = (data['other_details']['experience_detail']['description'])
**TypeError**: list indices must be integers or slices, not str
Attempt 2:
def index():
data = json.loads(request.data)
clean_description = (data['other_details']['experience_detail'])
Output:
[
{
"description":"Location: Australia\nCurrent job responsibilities:\n\n• To manage, interpret, and integrate inspection and function test data associated with pressure relief devices\n• Collection and Managing the database of inspection/test records\n• Pressure Relief Device (PRD) Inspection Report review/approval and recommendation\n• Compliance check on inspection reports in accordance with the scope and approve and/or send for re-review\n• Database management using Meridium, SAP and other Document Control Databases",
"employer_name":"Wood Group",
"end_date":"2020-04-28",
"id":"30dbe99d0c55435ea3d5119ece0ac873",
"is_current_employer":true,
"job_title":"Integrity Engineer",
"start_date":"2013-11-25"
},
{
"description":"Location: Australia\n\nResponsibilities:\n• Experience in pressure relief device sizing and selection\n• Exposure to Upstream Facilities Engineering activities\n• Experience with SharePoint for document management\n• Experience with Meridium",
"employer_name":"Jacobs Engineering",
"end_date":"2013-10-25",
"id":"7d8afe3cadaa4aee842bb4d8b000f5c7",
"is_current_employer":false,
"job_title":"Integrity Engineer",
"start_date":"2011-11-25"
}
]
It is displaying all the fields but I just need the description field.
Image of payload:

Related

How to search for flights using the Amadeus API and Python, by considering the originRadius and destinationRadius parameters?

I am trying to get Amadeus API flight data by considering the originRadius and destinationRadius parameters. Can someone help me with that? How can I search for flights by considering these two parameters?
Currently, I have implemented following code:
def check_flights(
self,
originLocationCode,
destinationLocationCode,
departureDate,
returnDate,
adults,
currencyCode
):
''' Return a list of FlightData objects based on the API search results. '''
amadeus = Client(client_id=API_KEY, client_secret=API_SECRET)
try:
response = amadeus.get(
API_URL,
originLocationCode=originLocationCode,
destinationLocationCode=destinationLocationCode,
departureDate=departureDate,
returnDate=returnDate,
adults=adults,
currencyCode=currencyCode
)
data = response.data
self.save_data_to_file(data=response.body)
except ResponseError as error:
# TO DO: If error occurs, render error in available_flights
return error
For that you will have to use the POST method of the Flight Offers Search API. I leave an example below that takes into consideration the originRadius. This parameter includes other possible locations around the point, located less than this distance in kilometers away with a max of 300km and it can not be combined with dateWindow or timeWindow.
POST https://test.api.amadeus.com/v2/shopping/flight-offers
{
"originDestinations": [
{
"id": "1",
"originLocationCode": "MAD",
"destinationLocationCode": "ATH",
"originRadius": "299",
"departureDateTimeRange": {
"date": "2023-03-03"
}
}
],
"travelers": [
{
"id": "1",
"travelerType": "ADULT"
}
],
"sources": [
"GDS"
]
}
The logic is the same for the destinationRadius.
For more details check the Amadeus for Developers API reference.

Real data elements from JSON

I am trying to extract data elements from json url link using python.Below is the code. It is working partially when I try to extract elements
response = urllib.request.urlopen(url)
data = json.loads(response.read())
print("planId",data[0]["planId"]) #Gives result as planId PWR93173MBE1
print("postcode",data[0]["postcode"]) # Gives result as postcode 2000
print("tariffType", data[0]["tariffType"]) This gives me error.
Also, if I want to extract other elements such as PlanType and other fields in Fees, how can I do it?
[
{
"planData":{
"planType":"M",
"tariffType":"SR",
"contract":[
{
"pricingModel":"SR",
"benefitPeriod":"Ongoing",
"coolingOffDays":10,
"additionalFeeInformation":"This offer provides access to wholesale prices, utilises your Powerbank to smooth wholesale market volatility and Powerwatch to warn of higher prices. For more information on this and any other standard fees, visit our website www.powerclub.com.au",
"fee":[
{
"description":"Annual Membership payable each year for each of your business premises taking supply.",
"amount":79,
"feeType":"MBSF",
"percent":0,
"feeTerm":"A"
},
{
"description":"Cost for providing a paper bill",
"amount":2.5,
"feeType":"PBF",
"percent":0,
"feeTerm":"F"
},
{
"description":"Disconnection fee",
"amount":59.08,
"feeType":"DiscoF",
"percent":0,
"feeTerm":"F"
},
{
"description":"Reconnection Fee",
"amount":59.08,
"feeType":"RecoF",
"percent":0,
"feeTerm":"F"
},
{
"description":"Meter Read - Requested by Customer",
"amount":12.55,
"feeType":"OF",
"percent":0,
"feeTerm":"F"
}
],
"planId":"PWR93173MBE1",
"planType":"E#B#PWR93173MBE1",
"postcode":2000
}
]
The tariffType property sits inside the planData property, so you need to do something like
print("tariffType", data[0]["planData"]["tariffType"])
You forgot to nest, correct should be:
print("tariffType", data[0]["planData"]["tariffType"])

How to push the data from dynamodb through stream

Below is the json file
[
{
"year": 2013,
"title": "Rush",
"actors": [
"Daniel Bruhl",
"Chris Hemsworth",
"Olivia Wilde"
]
},
{
"year": 2013,
"title": "Prisoners",
"actors": [
"Hugh Jackman",
"Jake Gyllenhaal",
"Viola Davis"
]
}
]
Below is the code to push to dynamodb. I have created testjsonbucket bucket name, moviedataten.json is the filename and saved above json.Create a dynamodb with Primary partition key as year (Number) and
Primary sort key as title (String).
import json
from decimal import Decimal
import json
import boto3
s3 = boto3.resource('s3')
obj = s3.Object('testjsonbucket', 'moviedataten.json')
body = obj.json
#def lambda_handler(event,context):
# print (body)
def load_movies(movies, dynamodb=None):
if not dynamodb:
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Movies')
for movie in movies:
year = int(movie['year'])
title = movie['title']
print("Adding movie:", year, title)
table.put_item(Item=movie)
def lambda_handler(event, context):
movie_list = json.loads(body, parse_float=Decimal)
load_movies(movie_list)
I want to push in to ElasticSearch from dynamodb.
I have created a Elastic Domain https://xx.x.x.com/testelas
I have gone through the link https://aws.amazon.com/blogs/compute/indexing-amazon-dynamodb-content-with-amazon-elasticsearch-service-using-aws-lambda/
I clicked Managestream also
My Requirement:
Any change in Dynamodb has to reflect in the Elasticsearch?
This lambda just writing the document to DynamoDb, and I will not recommend adding the code in this lambda to push the same object to Elastic search, as lambda function should perform a single task and pushing the same document to ELK should be managed as a DynamoDB stream.
What if ELK is down or not available how you will manage this in lambda?
What if you want to disable this in future? you will need to modify lambda instead of controlling this from AWS API or AWS console, all you need to just disable the stream when required no changes on above lambda side code
What if you want to move only modify or TTL item to elastic search?
So create Dyanodb Stream that pushes the document to another Lambda that is responsible to push the document to ELK, with this option you can also push old and new both items.
You can look into this article too that describe another approach data-streaming-from-dynamodb-to-elasticsearch
For above approach look into this GitHub project dynamodb-stream-elasticsearch.
const { pushStream } = require('dynamodb-stream-elasticsearch');
const { ES_ENDPOINT, INDEX, TYPE } = process.env;
function myHandler(event, context, callback) {
console.log('Received event:', JSON.stringify(event, null, 2));
pushStream({ event, endpoint: ES_ENDPOINT, index: INDEX, type: TYPE })
.then(() => {
callback(null, `Successfully processed ${event.Records.length} records.`);
})
.catch((e) => {
callback(`Error ${e}`, null);
});
}
exports.handler = myHandler;
DynamoDB has a built in feature (DynamoDB streams) that will handle the stream part of this question.
When you configure this you have the choice of the following configurations:
KEYS_ONLY — Only the key attributes of the modified item.
NEW_IMAGE — The entire item, as it appears after it was modified.
OLD_IMAGE — The entire item, as it appeared before it was modified.
NEW_AND_OLD_IMAGES — Both the new and the old images of the item.
This will produce an event that looks like the following
{
"Records":[
{
"eventID":"1",
"eventName":"INSERT",
"eventVersion":"1.0",
"eventSource":"aws:dynamodb",
"awsRegion":"us-east-1",
"dynamodb":{
"Keys":{
"Id":{
"N":"101"
}
},
"NewImage":{
"Message":{
"S":"New item!"
},
"Id":{
"N":"101"
}
},
"SequenceNumber":"111",
"SizeBytes":26,
"StreamViewType":"NEW_AND_OLD_IMAGES"
},
"eventSourceARN":"stream-ARN"
},
{
"eventID":"2",
"eventName":"MODIFY",
"eventVersion":"1.0",
"eventSource":"aws:dynamodb",
"awsRegion":"us-east-1",
"dynamodb":{
"Keys":{
"Id":{
"N":"101"
}
},
"NewImage":{
"Message":{
"S":"This item has changed"
},
"Id":{
"N":"101"
}
},
"OldImage":{
"Message":{
"S":"New item!"
},
"Id":{
"N":"101"
}
},
"SequenceNumber":"222",
"SizeBytes":59,
"StreamViewType":"NEW_AND_OLD_IMAGES"
},
"eventSourceARN":"stream-ARN"
},
{
"eventID":"3",
"eventName":"REMOVE",
"eventVersion":"1.0",
"eventSource":"aws:dynamodb",
"awsRegion":"us-east-1",
"dynamodb":{
"Keys":{
"Id":{
"N":"101"
}
},
"OldImage":{
"Message":{
"S":"This item has changed"
},
"Id":{
"N":"101"
}
},
"SequenceNumber":"333",
"SizeBytes":38,
"StreamViewType":"NEW_AND_OLD_IMAGES"
},
"eventSourceARN":"stream-ARN"
}
]
}
As you're already familiar with Lambda it makes sense to use a Lambda function to consume the records and then iterate through them to process them in the Elasticsearch format before adding them to your index.
When doing this make sure that you iterate through each record as there may be multiple depending on your configuration.
For more information on the steps required for the Lambda side of the function check out the Tutorial: Using AWS Lambda with Amazon DynamoDB streams page.

How to share data in `AWS Step Functions` without passing it between the steps

I use AWS Step Functions and have the following workflow
initStep - It's a lambda function handler, that gets some data and sends it to SQS for external service.
activity = os.getenv('ACTIVITY')
queue_name = os.getenv('QUEUE_NAME')
def lambda_handler(event, context):
event['my_activity'] = activity
data = json.dumps(event)
# Retrieving a queue by its name
sqs = boto3.resource('sqs')
queue = sqs.get_queue_by_name(QueueName=queue_name)
queue.send_message(MessageBody=data, MessageGroupId='messageGroup1' + str(datetime.time(datetime.now())))
return event
validationWaiting - It's an activity that waits for an answer from the external service that include the data.
complete - It's a lambda function handler, that uses the data from the initStep.
def lambda_handler(event, context):
email = event['email'] if 'email' in event else None
data = event['data'] if 'data' in event else None
client = boto3.client(service_name='ses')
to = email.split(', ')
message_conrainer = {'Subject': {'Data': 'Email from step functions'},
'Body': {'Html': {
'Charset': "UTF-8",
'Data': """<html><body>
<p>""" + data """</p>
</body> </html> """
}}}
destination = {'ToAddresses': to,
'CcAddresses': [],
'BccAddresses': []}
return client.send_email(Source=from_addresses,
Destination=destination,
Message=message_container)
It does work, but the problem is that I'm sending full data from the initStep to external service, just to pass it later to complete. Potentially more steps can be added.
I believe it would be better to share it as some sort of global data (of current step function), that way I could add or remove steps and data would still be available for all.
You can make use of InputPath and ResultPath. In initStep you would only send necessary data to external service (probably along with some unique identifier of Execution). In the ValidaitonWaiting step you can set following properties (in State Machine definition):
InputPath: What data will be provided to GetActivityTask. Probably you want to set it to something like $.execution_unique_id where execution_unique_id is field in your data that external service uses to identify Execution (to match it with specific request during initStep).
ResultPath: Where output of ValidationWaiting Activity will be saved in data. You can set it to $.validation_output and json result from external service will be present there.
This way you can send to external service only data that is actually needed by it and you won't lose access to any data that was previously (before ValidationWaiting step) in the input.
For example, you could have following definition of the State Machine:
{
"StartAt": "initStep",
"States": {
"initStep": {
"Type": "Pass",
"Result": {
"executionId": "some:special:id",
"data": {},
"someOtherData": {"value": "key"}
},
"Next": "ValidationWaiting"
},
"ValidationWaiting": {
"Type": "Pass",
"InputPath": "$.executionId",
"ResultPath": "$.validationOutput",
"Result": {
"validationMessages": ["a", "b"]
},
"Next": "Complete"
},
"Complete": {
"Type": "Pass",
"End": true
}
}
}
I've used Pass states for initStep and ValidationWaiting to simplify the example (I haven't run it, but it should work). Result field is specific to Pass task and it is equivalent to the result of your Lambda functions or Activity.
In this scenario Complete step would get following input:
{
"executionId": "some:special:id",
"data": {},
"someOtherData": {"value": key"},
"validationOutput": {
"validationMessages": ["a", "b"]
}
}
So the result of ValidationWaiting step has been saved into validationOutput field.
Based on the answer of Marcin Sucharski I've came up with my own solution.
I needed to use Type: Task since initStep is a lambda, which sends SQS.
I didn't needed InputPath in ValidationWaiting, but only ResultPath, which store the data received in activity.
I work with Serverless framework, here is my final solution:
StartAt: initStep
States:
initStep:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:init-step
Next: ValidationWaiting
ValidationWaiting:
Type: Task
ResultPath: $.validationOutput
Resource: arn:aws:states:#{AWS::Region}:#{AWS::AccountId}:activity:validationActivity
Next: Complete
Catch:
- ErrorEquals:
- States.ALL
ResultPath: $.validationOutput
Next: Complete
Complete:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:complete-step
End: true
Here a short and simple solution with InputPath and ResultPath. My Lambda Check_Ubuntu_Updates return a list of instance ready to be updated. This list of instances is received by the step Notify_Results, then it use this data. Remember that if you have several ResultPath in your Step Function and you need more than 1 input in a step you can use InputPath only with $.
{
"Comment": "A state machine that check some updates systems available.",
"StartAt": "Check_Ubuntu_Updates",
"States": {
"Check_Ubuntu_Updates": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:#############:function:Check_Ubuntu_Updates",
"ResultPath": "$.instances",
"Next": "Notify_Results"
},
"Notify_Results": {
"Type": "Task",
"InputPath": "$.instances",
"Resource": "arn:aws:lambda:us-east-1:#############:function:Notify_Results",
"End": true
}
}
}

Google fit data via google python api libraries

I'm using this python library from google but I can't figure out what to use for the 'body' argument. Is there an example body that I can draw from to create the dict that this tool will need?
Here is the code that I'm using:
flow = client.flow_from_clientsecrets(
workLaptop,
scope='https://www.googleapis.com/auth/fitness.activity.read',
redirect_uri='oauth:code:from:somehwere')
auth_uri = flow.step1_get_authorize_url()
webbrowser.open_new(auth_uri)
auth_code = "a;ldskjfa;lsdkfja;ldsfkja;lsdkfjaldgha;"
credentials = flow.step2_exchange(auth_code)
http_auth = credentials.authorize(httplib2.Http())
service = discovery.build('fitness', 'v1',http_auth)
fitData = service.users().dataset().aggregate(userId='me',body=body).execute()
It's all fine until the part where I need to define the body. Here is the body that I'm trying:
body = {
"aggregateBy": [
{
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps",
"dataTypeName": "com.google.step_count.delta"
},
],
"bucketByActivitySegment": {
"minDurationMillis": "A String", # Only activity segments of duration longer than this is used
},
"endTimeMillis": "1435269600000000000",
"bucketBySession": {
"minDurationMillis": "10", # Only sessions of duration longer than this is used
},
"bucketByActivityType": {
"minDurationMillis": "10", # Only activity segments of duration longer than this is used
},
"startTimeMillis": "1435183200000000000", # required time range
"bucketByTime": { # apparently oneof is not supported by reduced_nano_proto
"durationMillis": "10",
},
}
What is wrong with my body dict? Here is the error code:
https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate?alt=json returned "Internal Error">
Here is an example of the object in the API explorer:
Although I'm not 100% o fey with the Google API for the Google Fit, there definitely some issues with your JSON body request in the first instance.
For example:
body = {
"aggregateBy": [
{
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps",
"dataTypeName": "com.google.step_count.delta"
},
],
"bucketByActivitySegment": {
"minDurationMillis": "A String", # Only activity segments of duration longer than this is used
},
"endTimeMillis": "1435269600000000000",
"bucketBySession": {
"minDurationMillis": "10", # Only sessions of duration longer than this is used
},
"bucketByActivityType": {
"minDurationMillis": "10", # Only activity segments of duration longer than this is used
},
"startTimeMillis": "1435183200000000000", # required time range
"bucketByTime": { # apparently oneof is not supported by reduced_nano_proto
"durationMillis": "10",
},
}
Should actually be this;
body = {
"aggregateBy": [
{
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps",
"dataTypeName": "com.google.step_count.delta"
}
],
"bucketByActivitySegment": {
"minDurationMillis": "A String" # Only activity segments of duration longer than this is used
},
"endTimeMillis": "1435269600000000000",
"bucketBySession": {
"minDurationMillis": "10" # Only sessions of duration longer than this is used
},
"bucketByActivityType": {
"minDurationMillis": "10" # Only activity segments of duration longer than this is used
},
"startTimeMillis": "1435183200000000000", # required time range
"bucketByTime": { # apparently oneof is not supported by reduced_nano_proto
"durationMillis": "10"
}
}
JSON Based rest services are really unforgiving for the use of extra comma's where they should not be, it renders the string un-jsonable which will lead to a 500 failure. Give that a try in the first instance ;)
Not an expert myself, but I have been playing with the API for a number of days. Here's a sample from my OAuth playground.
Sample response
From what I understand, your "endTimeMillis": "1435269600000000000" is not properly defined as it's in nanoseconds. For it to be in milli, change it to "1435269600000"

Categories