How do you get a response from a Flask app with curl? - api

I'm building a Flask test predictor using AllenNLP.
I'm passing 'passage' and 'question' from a .json file to the predictor.
However, when I pass the json file using curl, it doesn't return a response. Is there a special return in Flask to get it?
Code looks like:
from allennlp.predictors.predictor import Predictor as AllenNLPPredictor
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route("/", methods=['GET','POST'])
def hello():
return "<h1>Test app!</h1>"
class PythonPredictor:
def __init__(self, config):
self.predictor = AllenNLPPredictor.from_path(
"https://storage.googleapis.com/allennlp-public-models/bidaf-elmo-model-2018.11.30-charpad.tar.gz"
)
def predict(self, payload):
if request.method == "POST":
prediction = self.predictor.predict(
passage=payload["passage"], question=payload["question"]
)
return prediction["best_span_str"]
Curl command looks like:
curl http://127.0.0.1:5000 -X POST -H "Content-Type: application/json" -d #sample.json

Unless I've misunderstood (I'm guessing you're asking how to obtain the JSON submission in your route, and return the result) it sounds like you need to do something like:
p = PythonPredictor()
#app.route("/", methods=['POST'])
def hello():
data = request.get_json()
result = p.predict(data)
return result
This effectively runs the data in your sample.json through your PythonPredictor.predict method, and returns that prediction to the client.
Notice this code creates the instance p outside the route function, so that the NLP model is only loaded when your flask app starts (not on every request). However it looks like this may re-download that file, unless AllenNLPPredictor.from_path does some caching, so it would probably be advisable to manually download that file to your own storage first, and load from there in the PythonPredictor.__init__ function.
Let me know if any of this needs clarification, or I've missunderstood.

Related

Cross Origin Problem with Flask Api (Access-Control-Allow-Origin)

Hello all good people.
I have tested everything that I can find on internet and nothing is working to fix this problem. I'm really hoping that someone here can help me solve this.
When i try to do "patch" request from backend to my flask API I get this error (GET, DELETE & PUT are working fine):
Access to fetch at 'https://MYAPI-NOTREALURL.com' from origin
'https://MYBACKEND-NOTREALURL.com' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: The
'Access-Control-Allow-Origin' header contains multiple values
'https://MYBACKEND-NOTREALURL.com, *', but only one is allowed. Have
the server send the header with a valid value, or, if an opaque
response serves your needs, set the request's mode to 'no-cors' to
fetch the resource with CORS disabled.
This is how my code for API is written:
from flask_cors import CORS, cross_origin
from flask import render_template, redirect, flash, request, url_for, jsonify, session, make_response
from flask_restful import Api, Resource, reqparse
import requests
app = Flask(__name__)
cors = CORS(app, resources={r"/*": {"origins": "*"}})
api = Api(app)
class ordersByID(Resource):
def get(self,ID_ORDER):
****
return jsonify(data)
def patch(self,ID_ORDER):
req321 = request.form
result = updateOrder(req321,ID_ORDER)
return result
def delete(self,ID_ORDER):
****
return result
def put(self,ID_ORDER):
****
return result
api.add_resource(ordersByID, "/orders/id/<string:ID_ORDER>")
if __name__ == '__main__':
app.run(debug=True)
I have tested everything that I can find on internet and nothing is working when trying to do patch request. I'm doing patch request with fetch from popup window.
<form action="{{ **https://MYAPI-NOTREALURL.com** }}" id="popupForm" method="patch" onsubmit="formFetch(event,this,'patch')">
You can check javascript code under.
function formFetch(e,form,method) {
result = fetch(form.action, {method:method, body: new FormData(form)})
.then(response => response.json())
.then(data => document.getElementById('submitedFormConfirmationText').innerHTML = data['DB_Result']
);
e.preventDefault();
document.getElementById('submitedFormConfirmation').style.display = 'inline';
};
I really hope that someone can help me solve this problem without needing to redo whole code?
I managed to solve this.
For some strange reason "patch" with small letters was working on local but when deployed it did not work.
Changing method from "patch" to "PATCH" solved this problem.

Send file generated by pdfkit from Flask

I'm generating a PDF file using pdfkit. As I do it in memory, I use the following code:
result = pdfkit.from_string(html, False)
result is bytes type, then I want Flask to send it to the client to be downloaded:
response = make_response(result)
response.headers.set('Content-Type', 'application/pdf')
response.headers.set(
'Content-Disposition', 'attachment', filename= 'report.pdf')
return response
I take it on the client side (JavaScript, React) and try to save:
FileDownload(response.data, 'myfile.pdf')
But the file is always empty with weird title somewhere inside. I think the problem is with encoding but I can't figure out what exactly to do.
This worked for me and produced a valid pdf.
import pdfkit
app = Flask(__name__)
#app.route("/")
def index():
pdf = pdfkit.from_string('Hello!', False)
response = make_response(pdf)
response.headers.set('Content-Type', 'application/pdf')
response.headers.set('Content-Disposition', 'inline', filename='report.pdf')
return response
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8080, debug=True)

create simple api with python to catch post request

I have developed a python plugin which is capable of sending log file in json format
in mm code i have used requests.post(url, data={})
what will be the api structure that catch this data and will be available for
send anywhere with GET request
If you are fairly new to web programming I would suggest using a lightweight framework like Flask. With it you can define custom paths that your server accepts requests on as follows:
from flask import Flask
from flask import request, jsonify
app = Flask(__name__)
log_file = None
#app.route("/api/logfile", methods=['GET', 'POST'])
def post_logfile():
if request.method == 'GET':
if log_file is not None:
return "Log file not instantiated yet", 404
else:
return jsonify(log_file)
elif request.method == 'POST':
log_file = request.form
if log_file is not None:
# log_file variable will have all the information
# from the JSON log file
return "Ok"
else:
return "No data provided", 400
if __name__ == "__main__":
app.run(port=9000)
As you can see, we have a global variable log_file which will be used to store the JSON logfile data, and a function that accepts both POST and GET requests and acts accordingly. If a GET request is sent, it checks if log_file variable is assigned. If so, it returns the log file as a JSON file else it return a 404 error. If a POST request is sent it checks if it has the log file and stores in the log_file variable, making it useful for all subsequent GET requests.
The URL used are:
localhost:9000/api/logfile
And you only need to change the method of the request(e.g. POST or GET)

HttpResponseRedirect' object has no attribute 'client'

Django 1.9.6
I'd like to write some unit test for checking redirection.
Could you help me understand what am I doing wrongly here.
Thank you in advance.
The test:
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.http.request import HttpRequest
from django.contrib.auth.models import User
class GeneralTest(TestCase):
def test_anonymous_user_redirected_to_login_page(self):
user = User(username='anonymous', email='vvv#mail.ru', password='ttrrttrr')
user.is_active = False
request = HttpRequest()
request.user = user
hpv = HomePageView()
response = hpv.get(request)
self.assertRedirects(response, reverse("auth_login"))
The result:
ERROR: test_anonymous_user_redirected_to_login_page (general.tests.GeneralTest)
Traceback (most recent call last):
File "/home/michael/workspace/photoarchive/photoarchive/general/tests.py", line 44, in test_anonymous_user_redirected_to_login_page
self.assertRedirects(response, reverse("auth_login"))
File "/home/michael/workspace/venvs/photoarchive/lib/python3.5/site-packages/django/test/testcases.py", line 326, in assertRedirects
redirect_response = response.client.get(path, QueryDict(query),
AttributeError: 'HttpResponseRedirect' object has no attribute 'client'
Ran 3 tests in 0.953s
What pdb says:
-> self.assertRedirects(response, reverse("auth_login"))
(Pdb) response
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/">
You need to add a client to the response object. See the updated code below.
from django.test import TestCase, Client
from django.core.urlresolvers import reverse
from django.http.request import HttpRequest
from django.contrib.auth.models import User
class GeneralTest(TestCase):
def test_anonymous_user_redirected_to_login_page(self):
user = User(username='anonymous', email='vvv#mail.ru', password='ttrrttrr')
user.is_active = False
request = HttpRequest()
request.user = user
hpv = HomePageView()
response = hpv.get(request)
response.client = Client()
self.assertRedirects(response, reverse("auth_login"))
Looks like you are directly calling your view's get directly rather than using the built-in Client. When you use the test client, you get your client instance back in the response, presumably for cases such as this where you want to check/fetch a redirect.
One solution would be to use the client to fetch the response from your view. Another is to stick a client in the response as mentioned above.
A third option is tell assertRedirects not to fetch the redirect. There is no need for client if you don't ask the assertion to fetch the redirect. That's done by adding fetch_redirect_response=False to your assertion.

OAuth2Decorator: Using developer's token to run API calls for user

For the "normal" oauth2 dance, I get to specify the user and get a corresponding token.
This allows me to make API calls masquerading as that user, i.e. on his behalf.
It can also allow the user to make calls masquerading as me.
A use case is bigquery where I don't have to grant table access to the user and I can specify my own preferred level of control.
Using the simplified OAuth2Decorator, I don't seem to have this option.
Am I right to say that?
Or is there a work-around?
In general, what is the best practice? To use the proper oauth (comprising of Flow, Credentials and Storage)? Or to use OAuth2Decorator.
Thank you very much.
You can certainly use an OAuth2Decorator
Here is an example:
main.py
import bqclient
import httplib2
import os
from django.utils import simplejson as json
from google.appengine.api import memcache
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from oauth2client.appengine import oauth2decorator_from_clientsecrets
PROJECT_ID = "xxxxxxxxxxx"
DATASET = "your_dataset"
QUERY = "select columns from dataset.table"
CLIENT_SECRETS = os.path.join(os.path.dirname(__file__),'client_secrets.json')
http = httplib2.Http(memcache)
decorator = oauth2decorator_from_clientsecrets(CLIENT_SECRETS,
'https://www.googleapis.com/auth/bigquery')
bq = bqclient.BigQueryClient(http, decorator)
class MainHandler(webapp.RequestHandler):
#decorator.oauth_required
def get(self):
data = {'data': json.dumps(bq.Query(QUERY, PROJECT_ID))}
template = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(render(template, data))
application = webapp.WSGIApplication([('/', MainHandler),], debug=True)
def main():
run_wsgi_app(application)
if __name__ == '__main__':
main()
bqclient.py that gets imported in your main.py which handles BigQuery actions
from apiclient.discovery import build
class BigQueryClient(object):
def __init__(self, http, decorator):
"""Creates the BigQuery client connection"""
self.service = build('bigquery', 'v2', http=http)
self.decorator = decorator
def Query(self, query, project, timeout_ms=10):
query_config = {
'query': query,
'timeoutMs': timeout_ms
}
decorated = self.decorator.http()
queryReply = (self.service.jobs()
.query(projectId=project, body=query_config)
.execute(decorated))
jobReference=queryReply['jobReference']
while(not queryReply['jobComplete']):
queryReply = self.service.jobs().getQueryResults(
projectId=jobReference['projectId'],
jobId=jobReference['jobId'],
timeoutMs=timeout_ms).execute(decorated)
return queryReply
where all your authentication details are kept in a json file client_secrets.json
{
"web": {
"client_id": "xxxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxx",
"redirect_uris": ["http://localhost:8080/oauth2callback"],
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token"
}
}
finally, don't forget to add these lines to your app.yaml:
- url: /oauth2callback
script: oauth2client/appengine.py
Hope that helps.
I am not sure I completely understand the use case, but if you are creating an application for others to use without their having to authorize access based on their own credentials, I would recommend using App Engine service accounts.
An example of this type of auth flow is described in the App Engine service accounts + Prediction API article.
Also, see this part and this part of the App Engine Datastore to BigQuery codelab, which also uses this authorization method.
The code might look something like this:
import httplib2
# Available in the google-api-python-client lib
from apiclient.discovery import build
from oauth2client.appengine import AppAssertionCredentials
# BigQuery Scope
SCOPE = 'https://www.googleapis.com/auth/bigquery'
# Instantiate and authorize a BigQuery API client
credentials = AppAssertionCredentials(scope=SCOPE)
http = credentials.authorize(httplib2.Http())
bigquery_service = build("bigquery", "v2", http=http)
# Make some calls to the API
jobs = bigquery_service.jobs()
result = jobs.insert(projectId='some_project_id',body='etc, etc')