APScheduler duplicating jobs in Flask app hosted on Heroku - flask

I have an app structured as below which allows a user to schedule tasks to query FB API each morning, afternoon etc to pull page/post data (3am in this case).
The problem I am experiencing is that the scheduler is executing each job twice which is obviously undesirable. Oddly, the issue doesn’t seem to occur locally, only when in production mode and I am hosting this in Heroku with 1 web dyno and 1 worker dyno, each with only 1 process each. I am therefore leaning towards Heroku being the issue.
I have a page to list the current scheduled jobs and when I inspect it, upon refreshing the page the same job instance will flick between two different values (screenshot below). It is as if there are two BackgroundScheduler's instances running.
I suspected this was to do with the BackgroundScheduler being initiated twice (once in flasky.py and again in tasks.py) so I created a temporary solution to stop the double initialising of the BackgroundScheduler and I still experience the same issue and am now stuck. Any help would be much appreciated.
-->app
-->__init__.py
-->decorators.py
-->models.py
-->tasks.py
-->auth
-->__init__.py
-->errors.py
-->forms.py
-->views.py
-->main
-->__init__.py
-->errors.py
-->forms.py
-->views.py
-->static
-->templates
-->migrations
config.py
flasky.py
Procfile
requirements.txt
app/flasky.py
from flask import Flask,render_template, session, redirect, url_for, flash
import os
from app import create_app,db
from app.models import User,Role
from datetime import datetime
from flask_migrate import Migrate,upgrade
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
app/_ init _.py
from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from config import config
from flask_session import Session
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
import rq
from redis import Redis
from flask_login import LoginManager
from worker import conn
import os
bootstrap = Bootstrap()
moment = Moment()
db = SQLAlchemy()
scheduler = BackgroundScheduler()
migrate = Migrate()
session= Session()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
def create_app(config_name='default'):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
bootstrap.init_app(app)
moment.init_app(app)
migrate.init_app(app,db)
session.init_app(app)
login_manager.init_app(app)
if not scheduler.running:
scheduler.start()
jobstore_url = os.environ.get('DATABASE_URL')
scheduler.add_jobstore(SQLAlchemyJobStore(url=jobstore_url),'sqlalchemy')
from .main import main as main_blueprint
from .auth import auth as auth_blueprint
app.register_blueprint(main_blueprint)
app.register_blueprint(auth_blueprint,url_prefix='/auth')
app.task_queue = rq.Queue('flasky',connection=Redis.from_url(os.environ.get(REDIS_URL))
if app.config['SSL_REDIRECT']:
from flask_sslify import SSLify
sslify = SSLify(app)
return app
app/tasks.py
from . import create_app,db
from .models import User,Tokens,Files
from .decorators import token_getter
from flask_login import current_user
import requests
import datetime as dt
import urllib
import os
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
<FUNCTIONS HERE>
app/auth/views.py
from flask import render_template,url_for,redirect,request,flash,session,current_app,Response
from .. import db,scheduler
from . import auth
from ..models import User,Role,Tokens,Files
from flask_login import login_user, logout_user, login_required
from ..decorators import admin_required, token_setter, token_getter,permission_required
import requests
import urllib
from .forms import LoginForm, SubmitConnection, ScheduleJobForm
from app.tasks import refreshed_google_client,test_context
app/main/views.py
from flask import render_template, session, redirect, url_for, flash,current_app,request
from datetime import datetime
from . import main
from .. import db,scheduler
from ..models import User,Tokens
from .forms import NameForm,AnalyticsForm
from flask_login import login_required,current_user
from ..decorators import admin_required,permission_required
import requests
import rq
from redis import Redis
from app.tasks import refreshed_google_client,load_analytics
#main.route('/ig_sync',methods=['GET','POST'])
#login_required
#permission_required(4)
def ig_sync():
form = IGAnalyticsForm()
if request.method=='POST':
from app.tasks import load_ig_sync
if form.validate_on_submit():
if form.submit_analytics_schedule.data:
#GET VARIABLES FROM FORM
scheduler.add_job(func=load_ig_sync,args=[#VARIABLES HERE],trigger='cron',hour=3,id=f'SYNC_IG_{page_name}',jobstore='sqlalchemy')
return(redirect(url_for('main.job_schedule')))
return render_template('ig_sync.html',form=form)
app/Procfile
web: gunicorn flasky:app
worker: rq worker -u $REDIS_URL flasky

Related

ImportError: cannot import name 'app' from partially initialized module 'market' (most likely due to a circular import)-Flask

#market package
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from market import routes
app = Flask(__name__)
app.config\['SQLALCHEMY_DATABASE_URI'\] = 'sqlite:///market.db'
db = SQLAlchemy(app)
----------
from market import db #(models)
----------
from market import app #(routes)
from flask import render_template
from market.models import Item
----------#run
from market import app
if __name__ == '__main__':
app.run(debug=True)
Structuring the code into packages then importing modules but then getting the above error

Flask csrf: key is missing with Blueprint

Hello everyone I have this issue I am not able to correct:
KeyError: 'A secret key is required to use CSRF.'
I am now using Flask with Blueprint.
I am not using CSRF at all but I think the LoginForm is.
I structured my project with Blueprint.
Before that, everything was find.
Here is my init.py file:
from flask import Flask
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask.config import Config
from flask_wtf.csrf import CSRFProtect
db = SQLAlchemy()
migrate = Migrate(db)
bcrypt = Bcrypt()
csrf = CSRFProtect()
login_manager = LoginManager()
login_manager.login_view = "login"
login_manager.login_message_category = "info"
from Flask import models
from Flask.models import User
admin = Admin(name='Admin')
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
admin.init_app(app)
db.init_app(app)
csrf.init_app(app)
login_manager.init_app(app)
migrate.init_app(app)
bcrypt.init_app(app)
db.init_app(app)
from Flask.users.routes import users
app.register_blueprint(users)
return app
This is my config.py file:
import os
class Config:
SECRET_KEY = "ef2006629e09b70e55a6fb95c4e3a538"
SQLALCHEMY_DATABASE_URI = "sqlite:///site.db"
# WTF_CSRF_SECRET_KEY= "bjk567nvhbvj63vg363vghvghv3768vgfbkijvr784"
# CSRF_ENABLED = True
Thank you for your help !
You should create a SECRET_KEY=<Your secret key here> property in your configuration. It must be a difficult string.
I found the problem
I was not calling properly my config.py file
In my init.py file I change the line:
from flask.config import Config
with the line:
from Flask.config import Config
Flask is the name of my file, which is different from flask.
I should have found another name

Django scheduled tasks

i want to display the current exchnage rate of USD/Bitcoin price-pair on my website.
Therefor i set celery and a small periodic_task.
Im currently not really able to understand how i call this periodic_task task or display the json data it returns.
this is how my celeter setup look like:
__init_.py
from __future__ import absolute_import, unicode_literals
from .celery import app as celery_app
__all__ = ('celery_app',)
celery.py
from __future__ import absolute_import, unicode_literals
from celery import Celery
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
#app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
tasks.py
from celery import Celery
from celery.schedules import crontab
from celery.task import periodic_task
from celery.utils.log import get_task_logger
import requests
logger = get_task_logger(__name__)
app = Celery('tasks', broker='redis://127.0.0.1')
#app.task
def test():
return "Test Successful"
#periodic_task(run_every=(crontab(minute='*/15')), name="get_btc_exchange_rate", ignore_result=True)
def get_exchange_rate():
api_url = "https://api.coinmarketcap.com/v1/ticker/?limit=1"
try:
exchange_rate = requests.get(api_url).json()
logger.info("BTC Exchange rate updated.")
except Exception as e:
print(e)
exchange_rate = dict()
return exchange_rate
I'm currently stating celery with this script:
https://gist.github.com/psych0der/44a8994495abee1b4e832420c1c2974d
So my question is how can i trigger that periodic_task and display the return of the json data/field "price_usd"? in a template
Thanks in advance
You'll need to start a celerybeat instance. It will schedule and send off events that you can set on an interval.
http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html

Can't import models to celery tasks.py file

in my tasks.py file I want to import models from polls app, but I get django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet when starting the worker
tasks.py
from __future__ import absolute_import
import sys ,os
from polls.models import User
from .celery import app
#app.task
def add_user(user):
# for user in users:
print('urra')
#user = User(user.first_name, user.last_name, user.email)
# user.save()
celery.py:
from __future__ import absolute_import, unicode_literals
from celery import Celery
import os, sys
from task import celery_config
import dotenv
from os.path import dirname, join
app = Celery('task',
broker='amqp://root:lusine_admin#localhost/task',
backend='amqp://',
include=['task.tasks'])
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "task.settings")
app.config_from_object(celery_config)
# app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
if __name__ == '__main__':
app.start()
Actaually I got error polls module not found, but then from bash I added it to pythonpath and know I get this error.
Your error is with your config. If you want to connect celery with your django, you have to initialize the celery config from the django settings. In your celery.py replace this line:
app.config_from_object(celery_config)
with
app.config_from_object('django.conf:settings', namespace='CELERY')

How to separate flask routes to another modules

I have hundreds of routes in my flask main module,
I think it need to separate those hundred of routes from the main module.
How to do it ?
#!/usr/bin/env python3
# -*- coding: utf8 -*-
from flask import request, url_for
from flask import Flask, request, jsonify
from flask_request_params import bind_request_params
from flask import g
import datetime
import pandas as pd
import pymongo
from webargs import Arg
from webargs.flaskparser import use_args, use_kwargs
import yaml
import time, functools
from pdb import set_trace
from pandas_helper import PandasHelper
import errors
from app_helper import *
from release_schedule import ReleaseSchedule
from mongo import Mongo
#app.route('/next_release', methods=["GET"])
#return_json
def next_release():
schedules = ReleaseSchedule.next_release(DB)
return pd.DataFrame([sche for sche in schedules])
...
#app.route('/last_release', methods=["GET"])
This is what blueprints were made to do.
Another alternative is flask-classy (which is awesome). I'm going to talk about the blueprint approach since that's what I know better.
If I was in your position I would want to split my routes up based on common imports.
Without knowning your application I'm going to guess that a distribution like this
parse_user_data_views.py
from webargs import Arg
from webargs.flaskparser import use_args, use_kwargs
import yaml
push_to_db_views.py
from pandas_helper import PandasHelper
from mongo import Mongo
import pymongo
import pandas as pd
import datetime
release_views.py
from release_schedule import ReleaseSchedule
import pandas as pd
#app.route('/next_release', methods=["GET"])
#return_json
def next_release():
schedules = ReleaseSchedule.next_release(DB)
return pd.DataFrame([sche for sche in schedules])
is likely distribution. We can't answer this for you, only you can.
But this allows you to separate out your application in some pretty nice ways.
in __init__.py
from flask import Flask
from yourapplication.release_views import release_views
from yourapplication.push_to_db_views import push_to_db_views
from yourapplication.parse_user_data_views import parse_user_data_views
app = Flask(__name__)
app.register_blueprint(release_views)
app.register_blueprint(push_to_db_views)
app.register_blueprint(parse_user_data_views)
Create a new file called views.py and add all your routes there. Then import views.py in your __ init __.py .