route flask from scheduled function - python

I have a Flask app, which is running a web-app to control an alarm. I use apscheduler to trigger a function triggerAlarm() and within that I want to have the logic to actually trigger it, but also I want to have the web-page to display another alarm.html, no matter which site the user is currently on.
The idea is to show the user, that the alarm went off by opening another html page (possibly the main page, which has context dependent text and layout)! Also the page content is outdated after an alarm. The client must reload the page, if the after-alarm info should be displayed/updated.
This is the key problem: in my head it is necessary to redirect every client to the alarm page at this event. Maybe there is a better solution all along. (I don't want to use JavaScript, since I use 4 languages already besides the 3 I use regularly.)
The problem is, that the function itself has no "route" and is not part of the app (this is what I think happens). Also I cannot easily make it a route, since it is activated not by a html request, but by the scheduler.
How can I trigger a function and redirect to a specified page?
*I tried redirect() and RequestRedirect() w/ and w/o url_for() and return render_template('index.html', **templateData) ... but I cannot load the desired page.
The code looks something like this:
from apscheduler.schedulers.background import BackgroundScheduler
# Web Server Gateway WSGI
from flask import Flask, render_template, request, url_for
scheduler = BackgroundScheduler()
scheduler.start()
scheduler.add_job(triggerAlarm,'date',run_date=somedate)
#app.route("/")
def index():
# prepare templateData
return render_template('index.html', **templateData)
def triggerAlarm():
# this doesn't work, since it is called by the scheduler and has no route!
return redirect(url_for("/"))

If you have defined a method, "triggerAlarm()", and want to make it available to a route, then create your route like:
#app.route("/alarm-url")
def web_alarm():
triggerAlarm()
return redirect(url_for("/"))
This will cause the same thing that happens when the scheduler runs triggerAlarm() to happen when a user hits the route /alarm-url, and then return them home.
The takeaway is that you can define methods outside of flask route, in other modules, etc, and then call those methods in a flask route.
Update
If you have to keep triggerAlarm() separate from any routes you could do something like:
class StatusDenied(Exception):
pass
#app.errorhandler(StatusDenied)
def web_alarm(error):
return redirect(url_for("/"))
def triggerAlarm():
...
raise StatusDenied

Related

Is there a way to make flask asynchronous

Is there a way to make flask asynchronous? For example I want flask to pause for one second for a user and then display some text. Is there any way to do this?
Flask is based WSGI which is an API standard for connecting Python Web frameworks to Web servers.
And WSGI is a synchronous and blocking API.
If you are using flask render_template, like this :
#app.route('/home', methods=['GET'])
def hello(name=None):
return render_template('hello.html', name=name)
You can add a time.sleep() to make if wait before rendering the template.
But this is not a good practice
import time
#app.route('/home', methods=['GET'])
def hello(name=None):
time.sleep(30) #wait 30seconds
return render_template('hello.html', name=name)
But if you want to use it with a frontend in javascript, you should handle it on the client side.
And if you want to know about async tasks on flask you can take a look on Celery and this post.

Trading view alerts to trigger market order through python and Oanda's API

I'm trying to trigger a python module (market order for Oanda) using web hooks(from trading view).
Similar to this
1) https://www.youtube.com/watch?v=88kRDKvAWMY&feature=youtu.be
and this
2)https://github.com/Robswc/tradingview-webhooks-bot
But my broker is Oanda so I'm using python to place the trade. This link has more information.
https://github.com/hootnot/oanda-api-v20
The method is web hook->ngrok->python. When a web hook is sent, the ngrok (while script is also running) shows a 500 internal service error and that the server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
This is what my script says when its running (see picture);
First says some stuff related the market order then;
running script picture
One thing I noticed is that after Debug it doesn't say Running on... (so maybe my flask is not active?
Here is the python script;
from flask import Flask
import market_orders
# Create Flask object called app.
app = Flask(__name__)
# Create root to easily let us know its on/working.
#app.route('/')
def root():
return 'online'
#app.route('/webhook', methods=['POST'])
def webhook():
if request.method == 'POST':
# Parse the string data from tradingview into a python dict
print(market_orders.myfucn())
else:
print('do nothing')
if __name__ == '__main__':
app.run()
Let me know if there is any other information that would be helpful.
Thanks for your help.
I fixed it!!!! Google FTW
The first thing I learned was how to make my module a FLASK server. I followed these websites to figure this out;
This link helped me set up the flask file in a virtual environment. I also moved my Oanda modules to this new folder. And opened the ngrok app while in this folder via the command window. I also ran the module from within the command window using flask run.
https://topherpedersen.blog/2019/12/28/how-to-setup-a-new-flask-app-on-a-mac/
This link showed me how to set the FLASK_APP and the FLASK_ENV
Flask not displaying http address when I run it
Then I fixed the internal service error by adding return 'okay' after print(do nothing) in my script. This I learned from;
Flask Value error view function did not return a response

modify flask url before routing

My Flask app has url routing defined as
self.add_url_rule('/api/1/accounts/<id_>', view_func=self.accounts, methods=['GET'])
Problem is one of the application making queries to this app adds additional / in url like /api/1//accounts/id. It's not in my control to correct the application which makes such queries so I cant change it.
To resolve this problem currently I have added multiple rules
self.add_url_rule('/api/1/accounts/<id_>', view_func=self.accounts, methods=['GET'])
self.add_url_rule('/api/1//accounts/<id_>', view_func=self.accounts, methods=['GET'])
There are number of such routes and it's ugly workaround. Is there a way in flask to modify URL before it hits the routing logic?
I'd normalise the path before it gets to Flask, either by having the HTTP server that hosts the WSGI container or a proxy server that sits before your stack do this, or by using WSGI middleware.
The latter is easily written:
import re
from functools import partial
class PathNormaliser(object):
_collapse_slashes = partial(re.compile(r'/+').sub, r'/')
def __init__(self, application):
self.application = application
def __call__(self, env, start_response):
env['PATH_INFO'] = self._collapse_slashes(env['PATH_INFO'])
return self.application(env, start_response)
You may want to log that you are applying this transformation, together with diagnostic information like the REMOTE_HOST and HTTP_USER_AGENT entries. Personally, I'd force that specific application to generate non-broken URLs as soon as possible.
Look at your WSGI server documentation to see how to add in extra WSGI middleware components.

How can I load a Python flask app into a gtk webview (Webkit)

I am currently working on an application that requires math expressions to be rendered (from latex) and needs to have some sort of native gui (even if it just uses gtk, then renders html in webkit).
I did some research and decided an easy way to do this would be to use webkit to load a web page and use a JavaScript library like MathJax to render the math.
Some other reasons why I chosen to do it this way over other solutions are I have had a fair amount of experience developing web apps in python (although a while ago), lack of experience with native guis and the portability it would provide.
For a web app framework I have chosen to use flask as it is one I am most familiar with.
The problem is this application needs to have it's own native GUI through preferably gtk (even if just renders html with webkit) and also preferably shouldn't have a http server that is attached to some socket.
So my question is, instead of running flask's server is there any way to do something like this:
import gtk
import webkit
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return "<h1>Hello World!</h1>"
if __name__ == '__main__':
window = gtk.Window()
webview = webkit.WebView()
webview.load_string(
app.load_from_uri('/'),
"text/html",
"utf-8",
'/'
)
window.add(webview)
window.show_all()
Where app.load_from_uri('/') is just used as an example of a way to load the webpage for a given uri of a Flask app. But as this is just an example, how could app.load_from_uri('/') be done in real code?
Also is there anyway to override when the user clicks a link so it does something like this:
def link_clicked(uri):
webview.load_string(
app.load_from_uri(uri),
"text/html",
"utf-8",
uri
)
Thanks any help is greatly appreciated!
I've ended up finding a solution to this myself (but open to better ones).
The first thing, loading a page, was pretty easy. Flask provides a way to test apps which mainly just sets up all the things for WSGI to be able to process a request. This is just what I needed so I used this like so:
from flask import Flask
class WebViewFlask(Flask):
"""
Adds the ability to load a uri without the
need of a HTTP server.
"""
def load_from_uri(self, uri):
"""
Loads a uri without a running HTTP server.
"""
with self.test_client() as c:
response = c.get(uri)
return response.data, response.mimetype
The second part, overriding "when the user clicks a link", is a bit more trickier.
import os
import webkit
class FlaskAppView(webkit.WebView):
"""
Loads pages for flask apps into a WebView.
"""
def __init__(self, flask_app, *args, **kwargs):
# Protocol for flask app, by default file:// is used
# so a protocol is defined here to prevent that.
self.PROTOCOL = 'flask://'
super(webkit.WebView, self).__init__(*args, **kwargs)
self._flask_app = flask_app
# Register new navigation handler.
self.connect(
"navigation-policy-decision-requested",
self._nav_request
)
# For navigation handler.
self.prev_uri = None
# Redefine open like this as when using super
# an error like this occurs:
# AttributeError: 'super' object has no attribute 'open'
self._open = self.open
self.open = self.open_
def _nav_request(self, view, frame, net_req, nav_act, pol_dec):
"""
WebView navigation handler for Flask apps.
"""
# Get the uri
uri = net_req.get_uri()
# In order for flask apps to use relative links
# the protocol is removed and it is made into an absolute
# path.
if uri.startswith(self.PROTOCOL):
# In this case it is not relative but
# it needs to have it's protocol removed
uri = uri[len(self.PROTOCOL):]
elif not self.prev_uri.endswith(uri):
# It is relative and self.prev_uri needs to
# be appended.
uri = os.path.normpath(os.path.join(self.prev_uri, uri))
# This is used to prevent an infinite recursive loop due
# to view.load_string running this function with the same
# input.
if uri == self.prev_uri:
return False
self.prev_uri = uri
# Create response from Flask app.
response = app.load_from_uri(uri) + ('utf-8', uri)
# Load response.
view.load_string(*response)
# Return False to prevent additional
# handlers from running.
return False
def open_(self, uri):
"""
Prepends protocol to uri for webkit.WebView.open.
"""
self._open(self.PROTOCOL + uri)
Basically a new navigation event handler is registered with some code to allow for successful recursion and support for relative paths.
Anyway, with that code above by just replacing Flask with WebViewFlask and WebView with FlaskAppView everything pretty much just works.
And the result:
Which is a flask app being loaded in a webkit.WebView without any sort of server. The best thing about it is by just switching app back to an instance of Flask instead of WebViewFlask It's a plain webapp again.

How to disable Django Internal function cache?

I'm making some Django projects using Redis as Backend Cache[1], but I can't be sure that the Redis Server will be On all the time, then I'm trying to use Redis "if" it's available otherwise use some other Backend like LocMem and so on.
The Redis Backend that I'm using[1] is full compatible so I can use Django Decorations.
I was think to create a function to be called like that:
from django.views.decorators.cache import cache_page
from utils import PingBackend
from time import time
#cache_page(60, cache=PingBackend(time()))
def index(request):
artigos = Artigo.objects.filter(ativo=1)
return render_to_response('index.html', {'artigos':artigos}, RequestContext(request))
The problem is that Django (Internals I guess) Caches the response of PingBackend() and call it just the first time, even if I drop the RedisServer Django tells that the ping process was successfully.
It occurs even with DEBUG=True and 'default' CacheBackend to dummy.
def PingBackend(time):
print time
response = None
try:
con = StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0)
# Execute some action
con.ping()
# If not give an exception, use redis
response = 'redis'
except:
response = 'default' #dummy
return last_response
I'm passing time() just to create some differentiation as a try solve the cache problm.
The big picture is that the function PingBackend() aren't executing for each request, just for the first the I can't monitor the Redis Server.
Thank you!
[1] - https://github.com/niwibe/django-redis
This is not about Django internals, this is about how decorators work. When you define your view like this:
#cache_page(60, cache=PingBackend(time()))
def index(request):
blah blah
it is exactly equivalent to this:
def index(request):
blah blah
index = cache_page(60, cache=PingBackend(time()))(index)
You are invoking cache_page only one, passing it a cache argument that you got by invoking PingBackend once. It isn't even executing just for the first request, it is executing once when the view function is defined.
You should write your own cache backend that uses Redis if it's available, or something else if it is not.

Categories

Resources