Can't update record in DynamoDB - python

Really new to python and coding in general - would really help if someone could point me in the right direction with some code.
So to start off with I am making a proof of concept for a car park running AWS Rekognition and I need some help with updating the database. As you can see with the code below it inputs the reg_plate, entry_time and exit_time into the database all okay. But, what I am trying to do is when Rekognition is invoked a second time with the same reg_plate it updates the exit_time only for the current record in the database.
import boto3
import time
def detect_text(photo, bucket):
client=boto3.client('rekognition')
response=client.detect_text(Image={'S3Object':{'Bucket':bucket,'Name':photo}})
textDetections=response['TextDetections']
for text in textDetections:
if text['Type'] == 'LINE':
return text['DetectedText']
return False
def main(event, context):
bucket=''
photo='regtest.jpg'
text_detected=detect_text(photo,bucket)
if (text_detected == False):
exit()
print("Text detected: " + str(text_detected))
entry_time = str(int(time.time()))
dynamodb = boto3.client('dynamodb')
table_name = 'Customer_Plate_DB'
item = {
'reg_plate': {
'S': str(text_detected)
},
'entry_time': {
'S': entry_time
},
'exit_time': {
'S': str(0)
}
}
dynamodb.put_item(TableName=table_name, Item=item)
Tried various if statements but with no luck as whenever I try it just keeps making new records in the database and the exit_time is never updated.

In DynamoDB a PutItem will overwrite/insert data, so its not what you need if you are wanting to update a single attribute. You will need to use UpdateItem:
response = dynamodb.update_item(
TableName=table_name,
Key={
'reg_plate': {'S': str(text_detected)},
'entry_time': {'S': entry_time}
},
UpdateExpression="SET #t = :t",
ExpressionAttributeValues={ ":t": str(0) },
ExpressionAttributeNames={"#t":"exit_time"},
ConditionExpression='attribute_exists(reg_plate)'
)
In your question you do not tell me what your partition and/or sort keys are, so here I assume you have a partition key only called pk, which you can change to suit your needs.
In the above example, for item where pk = '123' I set exit_time to a value only if the item already exists in your table (Condition Expression).

Related

return populated fields from a join table in SQLalchemy Flask

UserService is a join table connecting Users and Services tables. I have a query that returns all the tables that have a user_id = to the id passed in the route.
#bp.route('/user/<id>/services', methods=['GET'])
def get_services_from_user(id):
user = db_session.query(User).filter(id == User.id).first()
if not user:
return jsonify({'Message': f'User with id: {id} does not exist', 'Status': 404})
user_services = db_session.query(UserService).filter(user.id == UserService.user_id).all()
result = user_services_schema.dump(user_services)
for service in result:
user_service = db_session.query(Service).filter(service['service_id'] == Service.id).all()
result = services_schema.dump(user_service)
return jsonify(result)
result holds a list that looks as such:
[
{
"id": 1,
"service_id": 1,
"user_id": 1
},
{
"id": 2,
"service_id": 2,
"user_id": 1
}
]
how could I then continue this query or add another query to get all the actual populated services (Service class) instead of just the service_id and return all of them in a list? The for loop is my attempt at that but currently failing. I am only getting back a list with one populated service, and not the second.
You could try something like this:
userServies = db_session.query(Users, Services).filter(Users.id == Services.id).all()
userServices would be an iterable. You should use:
for value, index in userServices:
to iterate through it. (Could be index, value I'm not 100% sure of the order)
There is another way using .join() and adding the columns that you need with .add_columns().
There is also another way using
db_session.query(Users.id, Services.id, ... 'all the columns that you need' ...).filter(Users.id == Services.id).all()

"The document path provided in the update expression is invalid for update" when trying to update a map value

Here is my sample code
import boto3
import os
ENV = "dev"
DB = "http://awsservice.com"
REGION = "us-east-1"
TABLE = "traffic-count"
def main():
os.environ["AWS_PROFILE"] = ENV
client = boto3.resource("dynamodb", endpoint_url=DB, region_name=REGION)
kwargs = {'Key': {'id': 'D-D0000012345-P-1'},
'UpdateExpression': 'ADD #count.#car :delta \n SET #parentKey = :parent_key, #objectKey = :object_key',
'ExpressionAttributeValues': {':delta': 1, ':parent_key': 'District-D0000012345', ':object_key': 'Street-1'},
'ExpressionAttributeNames': {'#car': 'car', '#count': 'count', '#parentKey': 'parentKey', '#objectKey': 'objectKey'}}
client.Table(TABLE).update_item(**kwargs)
if __name__ == "__main__":
main()
What I want to achieve is this:
With a single API call (in this update_item), I want to be able to
If the item does not exit. create an item with a map count and initialise it with {'car': 1} and set the fields parent_key and object_key.
or
If the item already exists, update the field to {'car': 2} (if the original count is 1)
Previously, if I did not use a map, I can successfully update with this expression,
SET #count = if_not_exist(#count, :zero) + :delta,
#parentKey = :parent_key, #objectKey = :object_key
However I am getting this error:
botocore.exceptions.ClientError: An error occurred
(ValidationException) when calling the UpdateItem operation: The
document path provided in the update expression is invalid for update
Which document path is causing the problem? How can I fix it?
For those who landed on this page with similar error:
The document path provided in the update expression is invalid for update
The reason may be:
for the item on which the operation is being performed,
this attribute (count, for example) is not yet set.
Considering the sample code from question,
The exception could be coming from all those items where count is empty or not set. So the update query doesn't know on which map the new value(s) (car, for example) needs to be set or updated.
In the question, it was possible for the OP in the beginning because, the attribute is not a map and the process is simply setting the value to count as is. It's not trying to access a key of an unknown map to set the value.
This can be handled by catching the exception. For example:
from botocore.exceptions import ClientError
...
try:
response = table.update_item(
Key={
"pk": pk
},
UpdateExpression="set count.car = :c,
ExpressionAttributeValues={
':c': "some car"
},
ReturnValues="UPDATED_NEW"
)
except ClientError as e:
if e.response['Error']['Code'] == 'ValidationException':
response = table.update_item(
Key={
"pk": pk
},
UpdateExpression="set count = :count",
ExpressionAttributeValues={
':count': {
':c': "some car"
}
},
ReturnValues="UPDATED_NEW"
)

How to find on Daml-dazl app a transaction by key?

For example in a smart contract like this:
daml 1.2
module Example where
template Account with
owner : Party
number : Text
money : Int
where
signatory owner
key (owner, number) : (Party, Text)
maintainer key._1
It is possible to query a transaction based on a specific value of the template
import dazl
network = dazl.Network()
def main():
with dazl.simple_client('http://localhost:6865', 'Alice') as client:
# wait for the ACS to be fully read
client.ready()
allContracts = client.find(template = "Example.Account")
for contract in allContracts:
if contract.cdata ["money"] == 10000:
print("The query is" )
print(contract.cdata)
if __name__ == '__main__':
main()
However, how is possible to query based on key?
The only way to do this at the moment is to query on exactly the same condition you have in your key (which unfortunately duplicates code with the model):
all_contracts = clients.find("Example.Account", {"owner": "blah", "number": "blah"})
if all_contracts:
# something exists
...
else:
# nothing found
...
I added an issue to capture this feature request going forward: https://github.com/digital-asset/dazl-client/issues/85

Python: Cannot read returned values from functions

I am working on an Fall Detection System. I wrote the Arduino Code and connected to Firebase. So now I have two variables that get 1 or 0 status, and I created a mobile application to receive an automatic push notification whenever the system detects a fall through Firebase+Pusher. I wrote this Python code with PyCharm and I used the stream function to read live data from Firebase and send automatic notifications. The code was working for the variable "Fall_Detection_Status" and I was able to receive push notifications normally with every fall detection. But I tried to modify the code to read data from another variable "Fall_Detection_Status1" and I want my code now to send the notification if both variables are giving 1's. I came up with this code but it seems that the last if statement is not working because I am not able to receive notifications and also print(response['publishId']) at the end of the if statement is not showing any result.
So what is wrong?
import pyrebase
from pusher_push_notifications import PushNotifications
config = {
'apiKey': "***********************************",
'authDomain': "arfduinopushnotification.firebaseapp.com",
'databaseURL': "https://arduinopushnotification.firebaseio.com",
'projectId': "arduinopushnotification",
'storageBucket': "arduinopushnotification.appspot.com",
'messagingSenderId': "************"
}
firebase = pyrebase.initialize_app(config)
db = firebase.database()
pn_client = PushNotifications(
instance_id='*****************************',
secret_key='**************************',
)
value = 0
value1 = 0
def stream_handler(message):
global value
print(message)
if message['data'] is 1:
value = message['data']
return value
def stream_handler1(message):
global value1
print(message)
if message['data'] is 1:
value1 = message['data']
return value1
if value == 1 & value1 == 1:
response = pn_client.publish(
interests=['hello'],
publish_body={
'apns': {
'aps': {
'alert': 'Hello!',
},
},
'fcm': {
'notification': {
'title': 'Notification',
'body': 'Fall Detected !!',
},
},
},
)
print(response['publishId'])
my_stream = db.child("Fall_Detection_Status").stream(stream_handler)
my_stream1 = db.child("Fall_Detection_Status1").stream(stream_handler1)
You are using the wrong operator '&' to combine the results of the two tests. In Python, '&' is the bitwise and operator! I believe you want the logical version which is 'and'.
Secondly, assuming the stream_handler/1 calls are run by your last two statements, those two statements are AFTER the place where you test the values in the if statement. Move those line above the if block.

Dynamodb put_item() overwrites data

Device is my Partition key, table is for putting in multiple different users, under the same Device. However, if I run the following put_item() code, it will overwrite each user if they have the same Device key.
Example: If I put in Monitor, as my device variable, and gomez as my aliasInput variable it runs.
Then run it again as Monitor again as my device variable, but craig as my aliasInput it overwrites my gomez entry.
function to input data into my table :
import boto3
import json
import decimal
import time
import datetime
# Helper class to convert a DynamoDB item to JSON.
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
if o % 1 > 0:
return float(o)
else:
return int(o)
return super(DecimalEncoder, self).default(o)
dynamodb = boto3.resource('dynamodb', region_name='us-west-2', endpoint_url="http://localhost:8000")
table = dynamodb.Table('WishListTest')
device = input('What is the Item being requested?\n')
device = device.upper()
aliasInput = input('What is the Alias of the user?\n')
aliasInput = aliasInput.upper()
date = int((time.strftime("%d%m%Y")))
response = table.put_item(
Item={
'Device': device,
'RequestList': {
'Alias': aliasInput,
'Date': date
},
'AvailableQuanity': 0,
'ReserveQuanity': 0,
}
)
print("PutItem succeeded:")
print(json.dumps(response,
From the docs:
Put item
Creates a new item, or replaces an old item with a new item. If an item that has the same primary key as the new item already exists in the specified table, the new item completely replaces the existing item.
To prevent an overwrite, you need to add a conditional expression
specifying that the partition key does not yet exist.
Something like the following should work (sorry, I didn't quite get your key scheme, so you'll have to modify this).
table.put_item(
Item={'userId': 1, 'productId': 2},
ConditionExpression='userId <> :uid AND productId <> :pid',
ExpressionAttributeValues={':uid': 1, ':pid': 3}
)
You're looking for update_item(). You should use UpdateExpression because AttributeUpdates is deprecated, but this simple example should get you started:
response = table.update_item(
Key={
'Device': device,
},
AttributeUpdates={
'RequestList': {
'Alias': aliasInput,
'Date': date
},
'AvailableQuanity': 0,
'ReserveQuanity': 0,
},
)

Categories