unique session id for different dash sessions - flask

I would like to create unique session ids for each time a user opens the dash app in browser.
I have been following the tutorial here:
https://dash.plot.ly/sharing-data-between-callbacks
This is my code:
import dash
import dash_html_components as html
import dash_core_components as dcc
import flask
import datetime
import uuid
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = flask.Flask(__name__)
dash_app = dash.Dash(__name__,server=app,url_base_pathname="/",external_stylesheets=external_stylesheets)
def serve_layout():
session_id = str(uuid.uuid4())
return html.Div([
html.Div(session_id, id='session-id', style={'display': 'none'}),
html.Div(dcc.Input(id="input_session_id",type="text",value=session_id))
])
dash_app.layout = serve_layout()
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=80)
It seems like the session ids are different if I use different computers but if I use the same computer, it will stay the same.
Is there a way to generate an unique session each time an user opens the url for the dash app?

Obviously problem was in this line: dash_app.layout = serve_layout()
You had to use it without parenthesis:
dash_app.layout = serve_layout
In fact you were assigning not a function, but a result of the function called once at the first page load.

Related

Blueprint error multiple dashapp into flask

I'd like to host multiple dashapp into a flak server. Each dashapp shall be accessible with a login and password.
Some users can access different dashapps.
I tried the dash_auth.BasicAuth. It works perfectly but only for one dashapp.
So I tried to authenticate with flask_httpauth. Here again, it works well for one dashboard, but not for 2 and more because of blueprints.
My flask_app.py:
import dash
from flask import Flask, render_template, redirect, Blueprint
import dash_bootstrap_components as dbc
from flask_httpauth import HTTPDigestAuth
from apps.dashboard import Dashboard
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello from Flask!'
#others routes
auth = HTTPDigestAuth()
users = {
"john": "hello",
"susan": "bye"
}
#auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
url1 = '/dahsboard1'
dash_app1 = dash.Dash(__name__, server = app, external_stylesheets=[dbc.themes.BOOTSTRAP])
dash_app1.config.suppress_callback_exceptions = True
dash_app1.layout = Dashboard(dash_app1, 'data1', 'Title1', url1).layout
#app.route(url1)
#app.route(url1 + '/')
#app.route('/dash1')
#auth.login_required
def render_dashboard1():
return dash_app1.index()
url2 = '/dashboard2'
dash_app2 = dash.Dash(name='app2', server = app, external_stylesheets=[dbc.themes.BOOTSTRAP])
dash_app2.config.suppress_callback_exceptions = True
dash_app2.layout = Dashboard(dash_app2, 'data2', 'Title2', url2).layout
#app.route(url2)
#app.route(url2 + '/')
#app.route('/dash2')
#auth.login_required
def render_dashboard2():
return dash_app2.index()
if __name__ == '__main__':
app.run(debug=True)
The error:
ValueError: The name '_dash_assets' is already registered for a different blueprint. Use 'name=' to provide a unique name.
I undestand that a blueprint is created at each dashapp creation. After the first call :
print(app.blueprints)
returns
{'_dash_assets': <Blueprint '_dash_assets'>}
How can I add different blueprint names for each dashapp created ? Or more generally, how can I manage authentification for several dashapps running on one flask server ?
EDTIT:
I can solve this problem using this argument at dashboard creation
url_base_pathname = '/fake-url/'
But it leads to another problem: I can't protect this route with
#app.route('/fake-url/')
#auth.login_required(role=['admin'])
def render_dashboard():
return dash_app.app.index()
So the question is: how can I protect the route used in the dash creation with the argument url_base_pathname ?
You may have already solved this by now but will leave the solution here for the community. First you will need to set url_base_pathname for example:
dash_app2 = dash.Dash(
name='app2',
server = app,
url_base_pathname='/your_url_of_choice/'
external_stylesheets=[dbc.themes.BOOTSTRAP])
This will resolve that error.

Flask session lost data

I am testing a simple app to store data in session. I created the check to avoid resetting, but still getting data lost. Here is my code of app.py:
from flask import Flask,render_template,request,session
from flask_session import Session
app = Flask(__name__)
app.config["SESSION_PERMANENT"] = True
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
#app.route("/",methods=["POST","GET"])
def index():
if session.get("notes") is None:
session["notes"] = []
if request.method == "POST":
note = request.form.get("note")
session["notes"].append(note)
return render_template("notes.html",notes=session["notes"])
================================================================
Any time I am posting a new info from form, it gets overwrites the old one. seems session got reset.
Any idea why it happened?
Thanks,
vardan
Please consider the following:
This is the most important point, for flask session to work, you need to configure a SECRET_KEY, like below:
app.config["SECRET_KEY"] = 'YourSecretKey#123'
I don't know, I just figured it out that you don't need the following line and it's corresponding import (it started working for me when I commented out this part, may be Flask-Session and Flask's inbuilt session are messing up with each other):
Session(app)
from flask_session import Session
Don't directly update the session, if it's an array, do like below:
note = request.form.get("note")
temp = session['notes']
temp.append(note)
session['notes'] = temp
I hope, it will work for you.

Start and Stop a periodically background Task with Django

I would like to make a bitcoin notification with Django. If managed to have a working Telegram bot that send the bitcoin stat when I ask him to do so. Now I would like him to send me a message if bitcoin reaches a specific value. There are some tutorials with running python script on server but not with Django. I read some answers and descriptions about django channels but couldn't adapt them to my project.
I would like to send, by telegram, a command about the amount and duration. Django would then start a process with these values and values of the channel I'm sending from in the background. If now, within the duration, the amount is reached, Django sends a message back to my channel. This should also be possible for more than one person.
Is these possible to do with Django out of the box, maybe with decorators, or do I need django-channels or something else?
Edit 2018-08-10:
Maybe my code explains a little bit better what I want to do.
import requests
import json
from datetime import datetime
from django.shortcuts import render
from django.http import HttpResponse
from django.conf import settings
from django.views.generic import TemplateView
from django.views.decorators.csrf
import csrf_exempt
class AboutView(TemplateView):
template_name = 'telapi/about.html'
bot_token = settings.BOT_TOKEN
def get_url(method):
return 'https://api.telegram.org/bot{}/{}'.format(bot_token, method)
def process_message(update):
data = {}
data['chat_id'] = update['message']['from']['id']
data['text'] = "I can hear you!"
r = requests.post(get_url('sendMessage'), data=data)
#csrf_exempt
def process_update(request, r_bot_token):
''' Method that is called from telegram-bot'''
if request.method == 'POST' and r_bot_token == bot_token:
update = json.loads(request.body.decode('utf-8'))
if 'message' in update:
if update['message']['text'] == 'give me news':
new_bitcoin_price(update)
else:
process_message(update)
return HttpResponse(status=200)
bitconin_api_uri = 'https://api.coinmarketcap.com/v2/ticker/1/?convert=EUR'
# response = requests.get(bitconin_api_uri)
def get_latest_bitcoin_price():
response = requests.get(bitconin_api_uri)
response_json = response.json()
euro_price = float(response_json['data']['quotes']['EUR']['price'])
timestamp = int(response_json['metadata']['timestamp'])
date = datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
return euro_price, date
def new_bitcoin_price(update):
data = {}
data['chat_id'] = update['message']['from']['id']
euro_price, date = get_latest_bitcoin_price()
data['text'] = "Aktuel ({}) beträgt der Preis {:.2f}€".format(
date, euro_price)
r = requests.post(get_url('sendMessage'), data=data)
Edit 2018-08-13:
I think the solution would be celery-beat and channels. Does anyone know a good tutorial?
One of my teammates uses django-celery-beat, that is available at https://github.com/celery/django-celery-beat to do this and he gave me some excellent feedback from it. You can schedule the celery tasks using the crontab syntax.
I had same issue, there are several typical approaches: Celery, Django-Channels, etc.
But you can avoid them all with simple approach: https://docs.djangoproject.com/en/2.1/howto/custom-management-commands/
I have used django commands in my project to run periodically tasks to rebuild users statistics:
Implement yourself application command, for example your application name is myapp and you have placed my_periodic_task.py in myapp/management/commands folder, so you can run your task once by typing python manage.py my_periodic_task
place beside manage.py file new file for example background.py with same code:
-
import os
from subprocess import call
BASE = os.path.dirname(__file__)
MANAGE_BASE = os.path.join(BASE, 'manage.py')
while True:
sleep(YOUR_TIMEOUT)
call(['python', MANAGE_BASE , 'my_periodic_task'])
Run your server for example: python background.py & python manage.py runserver 0.0.0.0:8000

xlwings + Django: how to not loose a connection

I am trying to deploy a spreadsheet model with a web page front end using Django. The web "app" flow is simple:
User enters data in a web form
Send form data to a Django backend view function "run_model(request)"
Parse request object to get user inputs and then populate named ranges in the excel model's input sheet using xlwings to interact with a spreadsheet model (sheet.range function is used)
Run "calculate()" on the spreadsheet
Read outputs from another tab in the spreadsheet using xlwings and named ranges (again, using sheet.range function).
The problem is that the connection to the Excel process keeps getting killed by Django (I believe it handles each request as a separate process), so I can get at most one request to work (by importing xlwings inside the view function) but when I send a second request, the connection is dead and it won't reactivate.
Basically, how can I keep the connection to the workbook alive between requests or at least re-open a connection for each request?
Ok, ended up implementing a simple "spreadsheet server" to address the issue with Django killing the connection.
First wrote code for the server (server.py), then some code to start it up from command line args (start_server.py), then had my view open a connection to this model when it needs to use it (in views.py).
So, I had to separate my (Excel + xlwings) and Django into independent processes to keep the interfaces clean and control how much access Django has to my speadsheet model. Works fine now.
start_server.py
"""
Starts spreadsheet server on specified port
Usage: python start_server.py port_number logging_filepath
port_number: sets server listening to localhost:<port_number>
logging_filepath: full path to logging file (all messages directed to this file)
"""
import argparse
import os
import subprocess
_file_path = os.path.dirname(os.path.abspath(__file__))
#command line interface
parser = argparse.ArgumentParser()
parser.add_argument('port_number',
help='sets server listening to localhost:<port_number>')
parser.add_argument('logging_filepath',help='full path to logging file (all messages directed to this file)')
args = parser.parse_args()
#set up logging
_logging_path = args.logging_filepath
print("logging output to " + _logging_path)
_log = open(_logging_path,'wb')
#set up and start server
_port = args.port_number
print('starting Excel server...')
subprocess.Popen(['python',_file_path +
'\\server.py',str(_port)],stdin=_log, stdout=_log, stderr=_log)
print("".join(['server listening on localhost:',str(_port)]))
server.py
"""
Imports package that starts Excel process (using xlwings), gets interface
to the object wrapper for the Excel model, and then serves requests to that model.
"""
import os
import sys
from multiprocessing.connection import Listener
_file_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(_file_path)
import excel_object_wrapper
_MODEL_FILENAME = 'excel_model.xls'
model_interface = excel_object_wrapper.get_model_interface(_file_path+"\\"+_MODEL_FILENAME)
model_connection = model_interface['run_location']
close_model = model_interface['close_model']
_port = sys.argv[1]
address = ('localhost', int(_port))
listener = Listener(address)
_alive = True
print('starting server on ' + str(address))
while _alive:
print("listening for connections")
conn = listener.accept()
print 'connection accepted from', listener.last_accepted
while True:
try:
input = conn.recv()
print(input)
if not input or input=='close':
print('closing connection to ' + str(conn))
conn.close()
break
if input == 'kill':
_alive = False
print('stopping server')
close_model()
conn.send('model closed')
conn.close()
listener.close()
break
except EOFError:
print('closing connection to ' + str(conn))
conn.close()
break
conn.send(model_connection(*input))
views.py (from within Django)
from __future__ import unicode_literals
import os
from multiprocessing.connection import Client
from django.shortcuts import render
from django.http import HttpResponse
def run(request):
model_connection = Client(('localhost',6000)) #we started excel server on localhost:6000 before starting Django
params = request.POST
param_a = float(params['a'])
param_b = float(params['b'])
model_connection.send((param_a ,param_b ))
results = model_connection.recv()
return render(request,'model_app/show_results.html',context={'results':results})

Beaker session in bottle

while using beaker session, i came across to use same session object along the whole application.
I came through this url: Bottle.py session with Beaker
But, still i am getting 'KeyError' when i am trying to access the save session value in one function by another function.
my rest.py file looks like:
import bottle
from bottle import route,default_app
from beaker.middleware import SessionMiddleware
app = bottle.default_app()
#bottle.hook('before_request')
def setup_request():
request.session = request.environ['beaker.session']
#app.route('/login')
def login():
request.session['uname'] = 'user'
#app.route('/logout')
def logout():
print request.session['uname']
# expecting to print user
session_opts = {
'session.type': 'file',
'session.data_dir': '/tmp/',
'session.cookie_expires': True,
}
app = SessionMiddleware(bottle.default_app(),session_opts)
I have mentioned the SessionMiddleware at the end as im getting errors with the help of this link https://groups.google.com/forum/#!topic/bottlepy/m0akSbWRpZg
But when i am accessing request.session in the logout function i am getting
'KeyError': Uname not found
can any one give clear example of how to adjust the code inorder to maintain same session in whole application.