I want to log, who is logging in the app that uses FLASK.
I tried using
#app.before_request but the problem is I need to access the username which is through flask globals and I get none if I use this decorator.
Also, using global variables like below also doesn`t work. How do I get a global variable in request context?
import logging
import time
from flask import request, flash
from flask import g
from forms import QueryForm, RequestForm, Approve_Reject_Btn_Form
from query.sqlQuery import SQLQuery
from database.oracle import Database
import datetime
from requests import Session
logger = logging.getLogger(__name__)
login_count = 0
'''
Homepage route - Displays all the tables in the homepage
'''
#app.route('/')
#app.route('/index')
def index():
try:
if not g.identity.is_authenticated():
return render_template('homepage.html')
else:
try:
global login_count
if login_count == 0:
username = g.identity.name
user_ip = request.headers.get('IP_header')
current_time = time.strftime('%c')
db = Database(db_config.username, db_config.password)
query = "INSERT INTO UserIP (username, login_time, ip_address) values ('%s', systimestamp, '%s')" % (username, user_ip)
dml_query(query)
logger.debug('User : %s, ip : %s, noted at time : %s, login_count : %s', username , user_ip, current_time, login_count)
login_count = 1
As far as your question "How to execute a block of code only once in flask?" goes, something like this should work fine:
app = Flask(__name__)
#app.before_first_request
def do_something_only_once():
app.logger.setLevel(logging.INFO)
app.logger.info("Initialized Flask logger handler")
The second part of your question is how to setup global variable, like the login counter in your example. For something like this I would recommend that you use an external cache. See example below with the werkzeug SimpleCache class. In production you should replace SimpleCache with redis or mongodb.
from werkzeug.contrib.cache import SimpleCache
class Cache(object):
cache = SimpleCache(threshold = 1000, default_timeout = 3600)
#classmethod
def get(cls, key = None):
return cls.cache.get(key)
#classmethod
def delete(cls, key = None):
return cls.cache.delete(key)
#classmethod
def set(cls, key = None, value = None, timeout = 0):
if timeout:
return cls.cache.set(key, value, timeout = timeout)
else:
return cls.cache.set(key, value)
#classmethod
def clear(cls):
return cls.cache.clear()
You can use Cache class like this.
from mycache import Cache
#app.route('/')
#app.route('/index')
def index():
if not g.identity.is_authenticated():
return render_template('homepage.html')
else:
login_count = Cache.get("login_count")
if login_count == 0:
# ...
# Your code
login_count += 1
Cache.set("login_count", login_count)
EDIT 1: Added after_request example
Per Flask documentation, after_request decorator is used to run registered function after each request. It can be used to invalidate cache, alter response object, or pretty much anything that would require a specific response modification.
#app.after_request
def after_request_callback(response):
# Do something with the response (invalidate cache, alter response object, etc)
return response
Related
I have two python files ,
factor_main.py
import app
from app import *
class Webhookhandler:
def __init__(self):
self.app = app
self.item = {}
#staticmethod
def localize(item):
return item
class Validate:
def __init__(self):
pass
#staticmethod
def localize(item):
print(item)
def main(method):
localizers = {
"Webhookhandler": Webhookhandler,
"Validate": Validate
}
return localizers[method]
if __name__ == "__main__":
app.run(debug=True)
w = main("Webhookhandler")
print(w.item)
v = main("Validate")
app.py
from flask import Flask, request
from factory_main import *
import asyncio
app = Flask(__name__)
#app.route('/data', methods=["POST"])
def parse_request():
item = ""
Webhookhandler.localize(item)
webhook_data = request.get_json()
alert_severity = ""
alert_name = webhook_data['name']
for i in webhook_data['fields']:
if i['key'] == 'severity':
alert_severity = i['value']
item = {"alertName": alert_name, "alertSeverity": alert_severity}
Webhookhandler.localize(item)
return "SUCCESS!"
So what I'm trying to achieve here is , I'm trying to send a POST request from POSTMAN with a payload , read it from the Webhookhandler and only extract the required part and send it to the validate class to validate it in terms of length etc , also i'm trying to do all this with respect to factory approach. The problem here is ,i think i need to use async and await to wait until the POST payload (/data endpoint is hit) so that my item and other values will be populated since all these values are not accessible and mostly returning NULL but do not know where. I have just used asyncio.sleep() but I guess it doesn't serve the purpose here. Is my approach wrong or is there any better approach to achieve this ?
I have attached 2 separate codes one is for the flask app server.py and the second is for loading pickles files: util.py.
util.py has 3 functions,
First hello() returning message successfully
and the second get_location_names() not returning the location, it is supposed to return the location
This is my server.py which is a flask app.
from flask import Flask, request, jsonify
app = Flask(__name__)
import util
#app.route('/')
def hello():
response = jsonify({
'mesg' : util.hello()
})
response.headers.add('Access-Control-Allow-Origin','*')
return response
#app.route('/get_location_names')
def get_location_names():
response = jsonify({
'locations' : util.get_location_names()
})
response.headers.add('Access-Control-Allow-Origin','*')
return response
#app.route('/predict_home_price',methods=['POST'])
def predict_home_price():
total_sqft = float(request.form['total_sqft'])
location = request.form['location']
bed = int(request.form['bed'])
bath = int(request.form['bath'])
response = jsonify({
'estimated_price' : utils.get_estimated_price(location, total_sqft, bed, bath)
})
response.headers.add('Access-Control-Allow-Origin','*')
return response
if __name__ == '__main__':
print('Starting Python Flask Server')
app.run()
And here is my utils for loading pickles files
import json
import pickle
import numpy as np
__location = None
__data_columns = None
__model = None
def hello():
return "Hello World! This is Python Flask Server...........!"
def get_estimated_price(location,sqft,bed,bath):
try:
loc_index = __data_columns.index(location.lower())
except:
loc_index = -1
x = np.zeros(len(__data_columns))
x[0] = sqft
x[1] = bath
x[2] = bed
if loc_index >= 0:
x[loc_index] = 1
return round(__model.predict([x])[0],2)
def get_location_names():
global __location
return __location
def load_saved_artifacts():
print('Loading Saved Artifacts....Start')
global __data_columns
global __location
with open('./artifacts/columns.json','r') as col:
__data_columns = json.load(col)['my_data_columns']
__location = __data_columns[3:]
global __model
with open('./artifacts/House_Prediction_Price.pickle','rb') as my_model:
__model = pickle.load(my_model)
print('Loading saved artifacts is done...!')
if __name__ == '__main__':
load_saved_artifacts()
print(get_location_names())
print(get_estimated_price('1st block jayanagar',1000,3,3))
print(get_estimated_price('1st block jayanagar',1000,2,2))
print(get_estimated_price('kodihalli',1000,2,2))
print(get_estimated_price('Ejipura',1000,2,2))
The code is importing the module utils.py from another module server.py, then the __name__ variable will be set to that module’s name, and hence your load_saved_artifacts() in if block won't execute.
Try adding load_saved_artifacts() outside of if block, and check for the _location in get_location_names()
In your get_location_names() check the value for __location
#.... <utils.py> other lines
def get_location_names():
global __location
if __location is None:
load_saved_artifacts() # Doing this will make first request dependent on the execution time of this function call
return __location
load_saved_artifacts() # you can call your function here so that it will execute when your module is imported
if __name__ == "__main__":
#....
#other lines
I'm using Flask-HTTPAuth for authentication, Flask-login for login and want to add unit tests for endpoint decorated with #auth.login_required(scheme="Bearer"):
endpoints.py
class UserData(Resource):
#auth.login_required
def get(self, user_id):
user = db_models.User.query.get(user_id)
return result
authent.py
#auth.verify_token
def verify_token(token):
data = token_serializer.loads(token)
return data.get("username", False)
But got stuck with mocking (it doesn't work):
test.py
#mock.patch("myapp.auth")
#mock.patch("myapp.db_models.User")
def test_get_user(self, mock_user, mock_login):
with app.test_client() as client:
mock_login.verify_token.return_value = TEST_USER["username"]
mock_login.login_required.return_value = True
mock_user.query.get.return_value = TEST_USER
response = client.get("/user/100000")
self.assertIsNotNone(response)
self.assertIsInstance(response.get_json(), dict)
And the second approach:
test.py
#mock.patch("myapp.auth.login_required", return_value = True)
#mock.patch("myapp.db_models.User")
def test_get_user(self, mock_user, mock_login):
with app.test_client() as client:
mock_user.query.get.return_value = TEST_USER
response = client.get("/user/100000")
self.assertIsNotNone(response)
self.assertIsInstance(response.get_json(), dict)
Could you please help me to figure out how to do it in the right way?
Thank you for your help!
I'm using bottle's #auth_basic decorator to build my login module. I want to add a feature that if a user inputs the wrong password they will be restricted from trying again for 5 secs. How can this be implemented with bottle's #auth_basic?
Obviously I don't have the actual login piece. But here is a way leveraging beaker and a decorator to track login attempts.
import gevent
from gevent import monkey, signal
monkey.patch_all()
from gevent.pywsgi import WSGIServer
from mainapp import mainappRoute
import bottle
from bottle import request, get, post, template
from beaker.middleware import SessionMiddleware
from whitenoise import WhiteNoise
staticfolder = 'static'
beakerconfig = {
'session.type': 'memory',
'session.auto': True,
'session.cookie_path': '/',
'session.key': 'site_id',
'session.secret' : 'lsfkjsdlfhofuhrlifuheroifh',
'session.httponly' : True
}
class user(object):
def __init__(self):
self.session = request.environ['beaker.session']
self.login_attempts = 0
def set(self, **kwargs):
for k,v in kwargs.items():
self.session[k] = v
self.__dict__.update(self.session)
def attempt(self):
self.session['login_attempts'] = self.session.get('login_attempts', 0) + 1
if self.session['login_attempts'] == 3:
#do something like redirect
pass
def check_login(fn):
def check_uid(**kwargs):
u = user()
u.attempt()
return fn(**kwargs)
return check_uid
def shutdown():
print('Shutting down ...')
server.stop(timeout=60)
exit(signal.SIGTERM)
#get('/login')
def login():
return template('login.html')
#post('/login')
#check_login
def process_login():
u = user()
#let javascript handle the timeout
return template('index.html', attempts=u.login_attempts)
botapp = bottle.app()
for Route in (mainappRoute,):
botapp.merge(Route)
botapp = SessionMiddleware(botapp, beakerconfig)
botapp = WhiteNoise(botapp)
botapp.add_files(staticfolder, prefix='static/')
server = WSGIServer(("0.0.0.0", int(80)), botapp)
gevent.signal(signal.SIGTERM, shutdown)
gevent.signal(signal.SIGINT, shutdown) # CTRL C
server.serve_forever()
I'm trying to update a base class with a session token and user id for long polling.
Every time I call my function I create a new instance which calls a login function, that I don't want to happen.
I only want to call the login() method when the value is None
How do I return the instance of apiclient after the session token is set to use with the function for get_balance??
client.py
from __future__ import absolute_import, unicode_literals
import requests
import os
from matchbook import endpoints
class BaseClient(object):
def __init__(self, username, password=None, locale=None):
self.username = username
self.password = password
self.locale = locale
self.url_beta = 'https://beta.matchbook.com'
self.urn_main = '/bpapi/rest/'
self.session = requests.Session()
self.session_token = None
self.user_id = None
def set_session_token(self, session_token, user_id):
self.session_token = session_token
self.user_id = user_id
class APIClient(BaseClient):
def __init__(self, username, password=None):
super(APIClient, self).__init__(username, password)
self.login = endpoints.Login(self)
self.account = endpoints.Account(self)
def __repr__(self):
return '<APIClient [%s]>' % self.username
def __str__(self):
return 'APIClient'
get_bal.py
from client import APIClient
from celery import shared_task
def get_client():
apiclient = APIClient(username, password)
if apiclient.session_token is None:
apiclient.login()
session_token = apiclient.session_token
user_id = apiclient.user_id
apiclient.set_session_token(session_token,user_id)
else:
print('session token assigned',apiclient.session_token, apiclient.user_id)
return apiclient
#shared_task
def get_balance():
apiclient = get_client() *to call instance after login is set*
r = apiclient.account.get_account()
print(r)
You are creating a new instance of APIClient each time you call get_client(), which is what happens each time get_balance() get called.
You need to maintain an instance of the APIClient outside of the function scope for you to carry over your program and update your get_balance() to not call get_client() each time:
def get_balance(apiclient):
r = apiclient.account.get_account()
print(r)
def main():
apiclient = get_client()
get_balance(apiclient) # pass instance of APIClient in as an argument
Another note in your get_client() function, since both of the if conditions are the opposite of each other, just wrap them in an if... else block:
def get_client():
apiclient = APIClient(username, password)
if apiclient.session_token is None:
apiclient.login()
session_token = apiclient.session_token
user_id = apiclient.user_id
apiclient.set_session_token(session_token,user_id)
else:
print('session token assigned',apiclient.session_token, apiclient.user_id)
return apiclient
All that said, a much more OOP way would be to bake the get_balance() into the APIClient as an instance method so you don't even need to worry about the instance:
class APIClient(BaseClient):
...
def get_balance(self):
print(self.account.get_account())
# Then just call the get_balance() anywhere:
apiclient = get_client()
apiclient.get_balance()