Add cache-control to my python app in Flask - python

I am doing a few tests about my website.
My app was developped using flask,
I want to add cach-control "max-age=3600" in my function below :
from flask import Flask, jsonify, abort
from waitress import serve
import pandas as pd
from time import time as t
from load_data.connexion_to_blob import connexion_to_blob
from load_data.load_recos import load_recos
# CONFIG FILE
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
# CHARGE DATA FROM BLOB
mode = 'blob'
blob_service_client = connexion_to_blob("AZURE_STORAGE_CONNECTION_STRING")
load_recos(blob_service_client, langue='fr', mode='blob')
# LOAD DATA IN APP
recos_fr_file = config['PATH']['recos_fr_file']
recos_fr = pd.read_pickle(recos_fr_file)
recos_dict = {'fr': recos_fr}
app = Flask(__name__)
#app.route('/<langue>/<program_id>/<top>')
def get_reco(langue, program_id, top=10):
t_all = t()
top = int(top)
if program_id not in list(recos_dict[langue].keys()):
abort(404, description="programId not found")
else:
recos_list = recos_dict[langue][program_id][:top]
json_output = {
"data": [
{"programId": programId}
for programId in recos_list
]
}
print("temps exec total: ", (t() - t_all))
print('---------------------------------')
return jsonify(json_output)
if __name__ == '__main__':
serve(app, host='X.X.X.X', port=XX, threads=8)
I already check some documentation,
My question is at what level should this be done?
Thank you.

We can add cach control using :
#app.after_request
def apply_caching(response):
response.headers['Cache-Control'] = 'public, max-age=3600,stale-while-revalidate=600, stale-if-error=259200'
return response

Related

Cannot get data from request.get_json(force=True)

import requests
import numpy as np
import json
from flask import Flask, request, jsonify
url = 'http://localhost:5000/api'
dat = np.genfromtxt('/home/panos/Manti_Milk/BigData/50_0_50_3.5_3-3.dat')
d1 = dat[:,0]
data = {"w0": d1[0], "w1": d1[1], "w2": d1[2], "w3": d1[3], "w4": d1[4],
"w5": d1[5], "w6": d1[6], "w7": d1[7], "w8": d1[8], "w9": d1[9], "w10": d1[10],
"w11": d1[11], "w12": d1[12], "w13": d1[13], "w14": d1[14], "w15": d1[15]}
jsondata = json.dumps(data, indent=4)
r = request.post(url, json = jsondata)
#app.route('/api',methods=['POST','GET'])
def predict():
jsondata = request.get_json(force=True)
dummy = json.loads(jsondata)
arr = np.fromiter(dummy.values(), dtype=float).reshape(16,1)
return {"data": arr}
if __name__ == '__main__':
app.run(port=5000, debug=True)
It returns Bad Request
Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)
When setting force=False, returns "Null"
Any Help?
I have read several questoins/answers where it should work. But this is not the case!
You do not need to decode json data after request.get_json(), it is already the Python dict. So the line dummy = json.loads(jsondata) is unnecessary.
#app.route('/api',methods=['POST','GET'])
def predict():
jsondata = request.get_json(force=True)
arr = np.fromiter(jsondata.values(), dtype=float)
EDIT:
First file - server.py:
import numpy as np
from flask import Flask, request
app = Flask(__name__)
#app.route('/api', methods=['POST', 'GET'])
def predict():
jsondata = request.get_json(force=True)
arr = np.fromiter(jsondata.values(), dtype=float).reshape(16, 1)
print(arr) # do whatever you want here
return "1"
app.run(debug=True)
Second file - client.py:
import requests
import numpy as np
dat = np.genfromtxt('data.dat')
d1 = dat[:, 0]
data = {"w0": d1[0], "w1": d1[1], "w2": d1[2], "w3": d1[3], "w4": d1[4],
"w5": d1[5], "w6": d1[6], "w7": d1[7], "w8": d1[8], "w9": d1[9], "w10": d1[10],
"w11": d1[11], "w12": d1[12], "w13": d1[13], "w14": d1[14], "w15": d1[15]}
requests.post("http://127.0.0.1:5000/api", json=data)
Then execute them separately (from different console tabs):
At first, start the server in [1] tab:
$ python server.py
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
And after that run request from client in another [2] tab:
$ python client.py
Then you will see desired output in server tab [1]:
[[2297.]
[1376.]
[1967.]
[2414.]
[2012.]
[2348.]
[2293.]
[1800.]
[2011.]
[2340.]
[1949.]
[2015.]
[2338.]
[1866.]
[1461.]
[2158.]]

Issue with bad request syntax with flask

I am testing/attempting to learn flask, and flast_restful. This issue I get is:
code 400, message Bad request syntax ('name=testitem')
main.py:
from flask import Flask,request
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)
product_put_args = reqparse.RequestParser()
product_put_args.add_argument("name", type = str, help = "Name of the product")
product_put_args.add_argument("quantity", type = int, help = "Quantity of the item")
products = {}
class Product(Resource):
def get(self, barcode):
return products[barcode]
def put(self, barcode):
args = product_put_args.parse_args()
return {barcode: args}
api.add_resource(Product, "/product/<int:barcode>")
if(__name__) == "__main__":
app.run(debug = True)
and my
test.py
import requests
base = "http://127.0.0.1:5000/"
response = requests.put(base + "product/1", {"name": "testitem"})
print(response.json())
I have attempted to reform mat and change around both files to figure out what is sending the issue, I feel like it is something simple, but if you can help me, I bet this will help me and many others that are trying to start creating a rest API.
You need to add the location information to the RequestParser by default it tries to parse values from flask.Request.values, and flask.Request.json, but in your case, the values need to be parsed from a flask.request.form. Below code fixes your error
from flask import Flask,request
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)
product_put_args = reqparse.RequestParser()
product_put_args.add_argument("name", type = str, help = "Name of the product", location='form')
product_put_args.add_argument("quantity", type = int, help = "Quantity of the item", location='form')
products = {}
class Product(Resource):
def get(self, barcode):
return products[barcode]
def put(self, barcode):
args = product_put_args.parse_args()
products[barcode] = args['name']
return {barcode: args}
api.add_resource(Product, "/product/<int:barcode>")
if(__name__) == "__main__":
app.run(debug = True)

Flask-caching a function using blueprints

I am doing an app and I am using the Blueprints structure. My code is running okay, but I got this error while trying to implment flask-caching into one function.
The error returned is:
AttributeError: 'Blueprint' object has no attribute 'cache'
Does anybody has a light of solution in here to make the cache happens to this function?
Here is a piece of my code:
from flask import render_template, redirect, request, Blueprint
from cache import store_weather, number_of_views, cached_weather, cache
import json, requests
bp = Blueprint('bp', __name__, url_prefix="/weather")
main = Blueprint('main', __name__)
api_key = "42fbb2fcc79717f7601238775a679328"
#main.route('/')
def hello():
views = 5
max_views = number_of_views()
return render_template('index.html', cached_weather=cached_weather, max_views=max_views, views=views)
#bp.route('/', methods=['GET'])
def weather():
clean_list_cache()
if request.args.get('max').isdigit():
views = int(request.args.get('max'))
else:
views = 5
try:
city_name = request.args.get('city')
if city_name not in cached_weather:
uri = 'http://api.openweathermap.org/data/2.5/weather?q={city}&appid={key}'.format(city=city_name,key=api_key)
# On that point, we bring the data from the Open Weather API
rUrl = requests.get(uri)
# The temperature of the Open Weather API is in Kelvin, so, we have to subtract 273.15 to transform
# it into Celsius degrees
temperature = str(round((json.loads(rUrl.content)['main'])['temp']-273.15))+" °C"
name = json.loads(rUrl.content)['name']
description = json.loads(rUrl.content)['weather'][0]['description']
city_data = { "temp":temperature, "name":name, "desc":description }
store_weather(city_data)
max_views = number_of_views(views)
return render_template('weather.html', cached_weather = cached_weather, error_rec_city = False, max_views=max_views, views=views)
except KeyError:
max_views = number_of_views(views)
return render_template('weather.html', cached_weather=cached_weather, error_rec_city = True, max_views=max_views, views=views)
#bp.cache.cached(timeout=30, key_prefix='list_cache')
def clean_list_cache():
cached_weather.clear()
The error occurs, as you are trying to call cache on your blueprint: #bp.cache.cached. An example from the docs how to use cache is:
#app.route("/")
#cache.cached(timeout=50)
def index():
return render_template('index.html')
So you have to squeeze the cache decorator between your app decorator and the function

Flask returning 404 for anything

This is my app.py
import logging.config
import os
from flask import Flask, Blueprint, url_for
from flask_migrate import Migrate
from flask_restplus import Api
from authentek import settings
from authentek.api.blog.endpoints.posts import ns as blog_posts_namespace
from authentek.api.blog.endpoints.categories import ns as blog_categories_namespace
from authentek.api.auth.endpoints.users import ns as users_namespace
from authentek.api.restplus import api
from authentek.database import db
app = Flask(__name__)
logging_conf_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '../logging.conf'))
logging.config.fileConfig(logging_conf_path)
log = logging.getLogger(__name__)
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
#app.route("/sitemap")
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
# links is now a list of url, endpoint tuples
def configure_app(flask_app):
flask_app.config['SERVER_NAME'] = settings.FLASK_SERVER_NAME
flask_app.config['SQLALCHEMY_DATABASE_URI'] = settings.SQLALCHEMY_DATABASE_URI
flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = settings.SQLALCHEMY_TRACK_MODIFICATIONS
flask_app.config['SWAGGER_UI_DOC_EXPANSION'] = settings.RESTPLUS_SWAGGER_UI_DOC_EXPANSION
flask_app.config['RESTPLUS_VALIDATE'] = settings.RESTPLUS_VALIDATE
flask_app.config['RESTPLUS_MASK_SWAGGER'] = settings.RESTPLUS_MASK_SWAGGER
flask_app.config['ERROR_404_HELP'] = settings.RESTPLUS_ERROR_404_HELP
def initialize_app(flask_app):
configure_app(flask_app)
blueprint = Blueprint('api', __name__, url_prefix='/api')
# api.init_app(blueprint)
migrate = Migrate(flask_app, db)
api.add_namespace(blog_posts_namespace)
api.add_namespace(blog_categories_namespace)
api.add_namespace(users_namespace)
if blueprint.name not in flask_app.blueprints.keys():
flask_app.register_blueprint(blueprint)
else:
flask_app.blueprints[blueprint.name] = blueprint
print(flask_app.blueprints)
db.init_app(flask_app)
api.init_app(flask_app)
def main():
from authentek.database.models import Post, Category, User, BlacklistToken # noqa
app.config.from_object(settings)
initialize_app(app)
log.info('>>>>> Starting development server at http://{}/api/ <<<<<'.format(app.config['SERVER_NAME']))
app.run(host='0.0.0.0', debug=settings.FLASK_DEBUG)
if __name__ == "__main__":
main()
forget about all the routes, at least /sitemap should work!

Testing routing Blueprint - Flask Restplus

I'm trying to test the example of ToDo in Flask-Restplus site, but it keeps on getting me 404...
Basically I have 3 files:
app.py
import sys
import os
import platform
import datetime
import logging
from logging import Formatter
from logging.handlers import RotatingFileHandler
from jinja2 import Environment, PackageLoader
from flask import Flask, url_for, render_template, abort, request, Blueprint
from flask.ext.restplus import Api, Resource, fields
from werkzeug.contrib.fixers import ProxyFix
api_v1 = Blueprint('api', __name__, url_prefix='/api/1')
ns = api.namespace('todos', description='TODO operations')
TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '?????'},
'todo3': {'task': 'profit!'},
}
todo = api.model('Todo', {
'task': fields.String(required=True, description='The task details')
})
listed_todo = api.model('ListedTodo', {
'id': fields.String(required=True, description='The todo ID'),
'todo': fields.Nested(todo, description='The Todo')
})
def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
api.abort(404, "Todo {} doesn't exist".format(todo_id))
parser = api.parser()
parser.add_argument('task', type=str, required=True, help='The task details', location='form')
#ns.route('/<string:todo_id>')
#api.doc(responses={404: 'Todo not found'}, params={'todo_id': 'The Todo ID'})
class Todo(Resource):
'''Show a single todo item and lets you delete them'''
#api.doc(description='todo_id should be in {0}'.format(', '.join(TODOS.keys())))
#api.marshal_with(todo)
def get(self, todo_id):
'''Fetch a given resource'''
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]
#api.doc(responses={204: 'Todo deleted'})
def delete(self, todo_id):
'''Delete a given resource'''
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return '', 204
#api.doc(parser=parser)
#api.marshal_with(todo)
def put(self, todo_id):
'''Update a given resource'''
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task
#ns.route('/')
class TodoList(Resource):
'''Shows a list of all todos, and lets you POST to add new tasks'''
#api.marshal_list_with(listed_todo)
def get(self):
'''List all todos'''
return [{'id': id, 'todo': todo} for id, todo in TODOS.items()]
#api.doc(parser=parser)
#api.marshal_with(todo, code=201)
def post(self):
'''Create a todo'''
args = parser.parse_args()
todo_id = 'todo%d' % (len(TODOS) + 1)
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201
app = Flask(__name__)
app.secret_key = "secretkey"
app.config.from_pyfile('settings.py')
if os.path.exists(os.path.join(app.root_path, 'local_settings.py')):
app.config.from_pyfile('local_settings.py')
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://%s#%s/%s' % (
app.config['DB_USER'], app.config['DB_HOST'], app.config['DB_NAME'])
main.py
import sys
from flask import Blueprint
from portal.app import app
from portal import libs
if __name__ == '__main__':
if len(sys.argv) == 2:
port = int(sys.argv[1])
else:
port = 5000
host = app.config.get('HOST', '127.0.0.1')
from portal.app import api_v1
app.register_blueprint(api_v1)
import os
app.root_path = os.getcwd()
print "Running in", app.root_path, " with DEBUG=", app.config.get('DEBUG', False)
app.run(host,
port,
app.config.get('DEBUG', False),
use_reloader=True
)
tests.py
class ApiTests(helpers.ViewBase):
############################
#### setup and teardown ####
############################
def setUp(self):
super(ApiTests, self).setUp()
app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False
app.config['DEBUG'] = False
self.assertEquals(app.debug, False)
# executed after to each test
def tearDown(self):
pass
###############
#### tests ####
###############
def test_can_obtain_todos(self):
response = self.client.get('/api/1/todos')
self.assertEqual(response.status_code, 200)
If I run the app I can access http://localhost:5000/api/1/todos without problems, but if I run the tests, I keep to getting 404 routing exception
Traceback (most recent call last):
File "/home/internetmosquito/python_envs/portal/local/lib/python2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "/home/internetmosquito/python_envs/portal/local/lib/python2.7/site-packages/flask/app.py", line 1617, in dispatch_request
self.raise_routing_exception(req)
File "/home/internetmosquito/python_envs/portal/local/lib/python2.7/site-packages/flask/app.py", line 1600, in raise_routing_exception
raise request.routing_exception
NotFound: 404: Not Found
> /home/internetmosquito/git/wizbots/portal/src/portal/test/test_api.py(47)test_can_obtain_todos()
Any idea what I'm missing here? thanks!
Just for the record, test was failing because I wasn't properly initializing the blueprint again in the test file...this is done in main.py to avoid circular dependencies issues I was having.
So simply re-creating the blueprint and assigning it to app in setUp in the test file does the trick, but this is not efficient and should be avoided, guess I should check why the circular dependencies is happening and do everythng in the app.py file instead...

Categories