Update json file by curl command - python

I'm trying to make an app which writes and make updates in a json file by accepting data from the curl utility. Right now my code looks like:
import os
import flask import Flask, url_for, json, request, jsonify
database = "./meeting_rooms.json"
#app.route('/book_room', methods = ['POST'])
def api_book_room():
if request.headers['Content-Type'] == 'application/json':
if os.stat(database).st_size == 0:
with open(database, 'w') as f:
data = request.get_json()
json.dump (data, f, indent=4)
return "OK\n", 200
else:
with open(database,'r+') as f:
data = json.load(f)
data.update(request.get_json())
json.dump(data, f, indent=4)
return "OK\n", 200
curl usage:
curl -H "Content-type: application/json" -X POST http://127.0.0.1:5000/book_room -d '{"4":{"room":"602","date":"2 days ago","booked_by":"HR"}} '
But all I got is
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)**. json file is empty.
What I'm doing wrong considering I have no real-world python experience?

A solution that catch error at json decode time. it check file existence and file typo.
import os
from flask import Flask, url_for, json, request, jsonify
from path import Path
app = Flask(__name__)
database = "./meeting_rooms.json"
#app.route('/book_room', methods = ['POST'])
def api_book_room():
if request.headers['Content-Type'] == 'application/json':
try:
tmp = json.load(open(database, 'r'))
except (ValueError, FileNotFoundError):
# FileNotFoundError: raised if file don't exist
# ValueError: raised if json is not able to decode
tmp = {}
for k, v in request.get_json().items():
tmp[k] = v
with open(database,'w') as f:
json.dump(tmp, f, indent=4)
return "OK\n", 200
return "FAIL\n", 200
app.run(host='0.0.0.0', port=8080, debug=True)
Edit: Update keys instead of append it.

Related

Python app using FLASK : The requested URL was not found on the server

I'm trying to test a simple TODO APP using a REST API with Flask. I'm using curl to send a POST request and test out my app. The goal is to be able to write a new note without needing a front-end web UI.
curl -X POST http://127.0.0.1:5000/item -d '{"item": "Setting up Flask"}' -H 'Content-Type: application/json'
But I'm getting the error
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
Here's my code
import helper
from flask import Flask, request, Response
import json
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World!'
#app.route('/item/new', methods = ['POST'])
def add_item():
req_data = request.get_json()
item = req_data['item']
#Add item to the list
res_data = helper.add_to_list(item)
if res_data is None:
response = Response("{'error': 'Item not added - '}" + item, status=400 , mimetype='application/json')
return response
response = Response(json.dumps(res_data), mimetype='application/json')
return response
and here's the helper file
import sqlite3
DB_PATH = './todo.db'
NOTSTARTED = 'Not Started'
INPROGRESS = 'In Progress'
COMPLETED = 'Completed'
def add_to_list(item):
try:
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('insert into items(item, status) values(?,?)', (item, NOTSTARTED))
conn.commit()
return {"item": item, "status": NOTSTARTED}
except Exception as e:
print('Error: ', e)
return None
Looks like your route is wrong. You are sending to /item when you define your route as /route/new. Try changing it to this:
curl -X POST http://127.0.0.1:5000/item/new -d '{"item": "Setting up Flask"}' -H 'Content-Type: application/json'

AWS Chalice, can't get image from POST request

I'm trying to invoke my sagemaker model using aws chalice, a lambda function, and an API Gateaway.
I'm attempting to send the image over POST request but I'm having problem receiving it on the lambda function.
My code looks like:
from chalice import Chalice
from chalice import BadRequestError
import base64
import os
import boto3
import ast
import json
app = Chalice(app_name='foo')
app.debug = True
#app.route('/', methods=['POST'], content_types=['application/json'])
def index():
body = ''
try:
body = app.current_request.json_body # <- I suspect this is the problem
return {'response': body}
except Exception as e:
return {'error': str(e)}
It's just returning
<Response [200]> {'error': 'BadRequestError: Error Parsing JSON'}
As I mentioned before, my end goal is to receive my image and make a sagemaker request with it. But I just can't seem to read the image.
My python test client looks like this:
import base64, requests, json
def test():
url = 'api_url_from_chalice'
body = ''
with open('b1.jpg', 'rb') as image:
f = image.read()
body = base64.b64encode(f)
payload = {'data': body}
headers = {'Content-Type': 'application/json'}
r = requests.post(url, data=payload, headers=headers)
print(r)
r = r.json()
# r = r['response']
print(r)
test()
Please help me, I spent way to much time trying to figure this out
That error message is because you're not sending a JSON body over to your Chalice app. One way to check this is by using the .raw_body property to confirm:
#app.route('/', methods=['POST'], content_types=['application/json'])
def index():
body = ''
try:
#body = app.current_request.json_body # <- I suspect this is the problem
return {'response': app.current_request.raw_body.decode()}
except Exception as e:
return {'error': str(e)}
You'll see that the body is form-encoded and not JSON.
$ python client.py
<Response [200]>
{'response': 'data=c2FkZmFzZGZhc2RmYQo%3D'}
To fix this, you can use the json parameter in the requests.post() call:
r = requests.post(url, json=payload, headers=headers)
We can then confirm we're getting a JSON body in your chalice app:
$ python client.py
<Response [200]>
{'response': '{"data": "c2FkZmFzZGZhc2RmYQo="}'}
So I was able to figure it out with the help of an aws engineer (i got lucky I suppose). I'm including the complete lambda function. Nothing changed on the client.
from chalice import Chalice
from chalice import BadRequestError
import base64
import os
import boto3
import ast
import json
import sys
from chalice import Chalice
if sys.version_info[0] == 3:
# Python 3 imports.
from urllib.parse import urlparse, parse_qs
else:
# Python 2 imports.
from urlparse import urlparse, parse_qs
app = Chalice(app_name='app_name')
app.debug = True
#app.route('/', methods=['POST'])
def index():
parsed = parse_qs(app.current_request.raw_body.decode())
body = parsed['data'][0]
print(type(body))
try:
body = base64.b64decode(body)
body = bytearray(body)
except e:
return {'error': str(e)}
endpoint = "object-detection-endpoint_name"
runtime = boto3.Session().client(service_name='sagemaker-runtime', region_name='us-east-2')
response = runtime.invoke_endpoint(EndpointName=endpoint, ContentType='image/jpeg', Body=body)
print(response)
results = response['Body'].read().decode("utf-8")
results = results['predictions']
results = json.loads(results)
results = results['predictions']
return {'result': results}

How to get Python dictionary via Flask request files attribute [duplicate]

This question already has answers here:
Read file data without saving it in Flask
(8 answers)
Opening a file that has been uploaded in Flask
(2 answers)
Closed 4 years ago.
With server.py running:
from flask import Flask, request, Response
app = Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got files: %s' % request.files)
return Response()
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
send a request using client.py:
import json, requests
dictionary_1 = {"file": {"url": "https://bootstrap.pypa.io/get-pip.py"}}
files = [('dictionary_1', ('get-pip.py', json.dumps(dictionary_1), 'application/json'))]
response = requests.post('http://127.0.0.1:5000/test', files=files)
Server logs that it received a request:
got files: ImmutableMultiDict([('dictionary_1', <FileStorage: u'get-pip.py' ('application/json')>)])
Apparently, the dictionary_1 was received as FileStorage object.
How to turn the received FileStorage into the Python dictionary?
edited later
The possible duplicate post does not clarify how to send and unpack the Python dictionary object sent via requests(files=list())
This is happening because you're posting files instead of data. This should work:
import flask
app = flask.Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got data: {}'.format(flask.request.json))
return Response()
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
and then send data to your app by
import requests
dictionary_1 = {"file": {"url": "https://bootstrap.pypa.io/get-pip.py"}}
response = requests.post('http://127.0.0.1:5000/test', json=dictionary_1)
In your example there's no need to post the file unless I'm misunderstanding something
Solution # 1:
from flask import Flask, request, Response
import StringIO, json
app = Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got files: %s' % request.files)
for key, file_storage in request.files.items():
string_io = StringIO.StringIO()
file_storage.save(string_io)
data = json.loads(string_io.getvalue())
print('data: %s type: %s' % (data, type(data)) )
return Response()
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
Solution # 2:
from flask import Flask, request, Response
import tempfile, json, os, time
app = Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got files: %s' % request.files)
for key, _file in request.files.items():
tmp_filepath = os.path.join(tempfile.mktemp(), str(time.time()))
if not os.path.exists(os.path.dirname(tmp_filepath)):
os.makedirs(os.path.dirname(tmp_filepath))
_file.save(tmp_filepath)
with open(tmp_filepath) as f:
json_data = json.loads(f.read())
print type(json_data), json_data
return Response(json_data)
if __name__ == '__main__':
app.run('0.0.0.0', 5000)

Not able to get JSON response due to UnicodeDecodeError?

I am not able to get JSON response when I try to run python core-backup.py file: In code FB_SHORT_ACCESS_TOKEN and FB_LONG_ACCESS_TOKEN are same.
core-backup.py :
import os
from os.path import join
import requests
def refresh_short_token():
"""
Refresh short access token
"""
request_url = FB_URL + 'oauth/access_token'
request_payload = {
'grant_type': 'fb_exchange_token',
'client_id': FB_APP_ID,
'client_secret': FB_APP_SECRET,
'fb_exchange_token': FB_SHORT_ACCESS_TOKEN
}
response = REQ_SESSION.get(request_url, params=request_payload).json()
# dotenvfile = find_dotenv()
# load_dotenv(dotenvfile)
# dotenv.set_key(dotenvfile, "FB_LONG_ACCESS_TOKEN", response['access_token'])
FB_LONG_ACCESS_TOKEN = response["access_token"]
# PAYLOAD['access_token'] = dotenv.get_key(dotenvfile, "FB_LONG_ACCESS_TOKEN")
PAYLOAD['access_token'] = FB_LONG_ACCESS_TOKEN
'''
TODO: refresh_long_token()
A function to refresh the long term access token
Current validity: 60 days
'''
def get_feed():
"""
Fetch feed
"""
request_url = FB_URL + LTTK_GROUP_ID + '/feed'
response = REQ_SESSION.get(request_url, params=PAYLOAD)
if response.status_code == 400:
refresh_short_token()
print(response.json())
return response.json()
def main():
"""
Fetch posts from a Facebook group and populate in database
"""
get_feed()
if __name__ == "__main__":
main()
I am getting UnicodeDecodeError in windows7 after running core-backup.py
file. How to fix this issue.
See screenshot for more clarity:
Entire code of file can be found out here:
https://gist.github.com/anonymous/2ab9e023d631a7cc4dad15237104ee34
It appears that your code page is set to cp437. Try setting python output to utf-8 by entering the following line in your terminal before running your python script.
set PYTHONIOENCODING=UTF-8
python core-backup.py
Try changing response encoding to UTF-8:
response.encoding = 'UTF-8'
print(response.json())

Python Flask: Send file and variable

I have two servers where one is trying to get a file from the other. I am using Flask get requests to send simple data back and forth (strings, lists, JSON objects, etc.).
I also know how to send just a file, but I need to send an error code with my data.
I'm using something along the following lines:
Server 1:
req = requests.post('https://www.otherserver.com/_download_file', data = {'filename':filename})
Server 2:
#app.route('/_download_file', methods = ['POST'])
def download_file():
filename = requests.form.get('filename')
file_data = codecs.open(filename, 'rb').read()
return file_data
Server 1:
with codecs.open('new_file.xyz', 'w') as f:
f.write(req.content)
...all of which works fine. However, I want to send an error code variable along with file_data so that Server 1 knows the status (and not the HTTP status, but an internal status code).
Any help is appreciated.
One solution that comes to my mind is to use a custom HTTP header.
Here is an example server and client implementation.
Of course, you are free to change the name and the value of the custom header as you need.
server
from flask import Flask, send_from_directory
app = Flask(__name__)
#app.route('/', methods=['POST'])
def index():
response = send_from_directory(directory='your-directory', filename='your-file-name')
response.headers['my-custom-header'] = 'my-custom-status-0'
return response
if __name__ == '__main__':
app.run(debug=True)
client
import requests
r = requests.post(url)
status = r.headers['my-custom-header']
# do what you want with status
UPDATE
Here is another version of the server based on your implementation
import codecs
from flask import Flask, request, make_response
app = Flask(__name__)
#app.route('/', methods=['POST'])
def index():
filename = request.form.get('filename')
file_data = codecs.open(filename, 'rb').read()
response = make_response()
response.headers['my-custom-header'] = 'my-custom-status-0'
response.data = file_data
return response
if __name__ == '__main__':
app.run(debug=True)

Categories

Resources