seems like web.py sessions are in app rather than on client - python

So the code below is more or less taken from http://webpy.org/cookbook/session
If I run the app it works as it should i.e. counter increments by one upon each refresh, however if I access the app in an incognito window or other web browser, the counter does not reset. To me it seems like the session doesn't initialize with count: 0 as it should. What is it that causes the new session to take the values of session in other client?
import web
web.config.debug = False
urls = (
"/", "count",
"/reset", "reset"
)
app = web.application(urls, locals())
session = web.session.Session(app, web.session.DiskStore('sessions'),
{'count': 0})
session_data = session._initializer
class count:
def GET(self):
session_data['count'] += 1
return str(session_data['count'])
class reset:
def GET(self):
session.kill()
return ""
if __name__ == "__main__":
app.run()
Sessions should be stored on the client but when I execute this code it seems like it is on the server, which would imply that only one user can use the app and I have to rerun the app to reset the counter.
I haven't been able to solve this for almost a week now. Pleeease help.

The example has sessions being created from the initial session variable. For example, session.count += 1 would add 1 to the current session's count. In your code you change session_data for each user. The way the documentation demonstrates creating a session variable with an initializer is:
session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'count': 0})
So, instead of doing session_data['count'] += 1, the documentation recommends doing session['count'] += 1 or session.count += 1. You would also need to update the return line in your Index.
I tested and confirmed that this works for me.

Related

Multi-threading use of SQLAlchemy Sessions

I've been reading about the multi-threading use of the Sqlalchemy session, but still wonder if it is the best way to use one global session for multiple threads and allow only one thread to use it at once or to limit the number of sessions created and make a new session per thread and have like a limit on the number of sessions running together. In my project, I need to make several requests simultaneously, and while I wait for the response to those requests, the session is still active. Once the response is received, I add it to the PostgreSQL DB and close the session. Up to 6-7 requests per second are fine and 85 concurrent sessions seem to be enough, but when I increase it to 8-9 requests per second, due to larger response time, many sessions get stuck and don't close in time.
def plan_request(self, url, time_num):
logger.info(f'Sleeping for {time_num} seconds')
session_manager = GetNewLocalSession()
time.sleep(time_num)
# after delay new session is created using session manager I made
session_local = session_manager.get_new_local_session()
self.tool.test(session_local, item)
self.session_manager.remove_local_session(session_local)
session_local.close()
db.Session.remove()
class GetNewLocalSession():
def __init__(self):
self.sessions = []
def get_new_local_session(self):
print(f"Sessions used: {len(self.sessions)}")
if len(self.sessions) <= 85:
# creates new scoped_session object
session_local = Session()
self.sessions.append(session_local)
return session_local
else:
success = False
while not success:
if len(self.sessions) <= 85:
session_local = Session()
self.sessions.append(session_local)
success = True
else:
time.sleep(0.1)
return session_local
def remove_local_session(self, session_local):
if session_local in self.sessions:
self.sessions.remove(session_local)
else:
pass

How do I separate sessions between app users in Twilio using Flask sessions?

During testing of my app with ngrok, two of my users texted the Twilio number at the same time and they some how landed in the same session so any counters I was using got messed up for their own respective user experiences. How can I ensure that each user texting the number to use the app will have their own separate experience? Is the trick to somehow create a dynamic SECRET_KEY in Flask session?
Some code below
from flask import Flask, request, session
from twilio.twiml.messaging_response import MessagingResponse
SECRET_KEY = 'a secret key'
app = Flask(__name__)
app.config.from_object(__name__)
#app.route("/sms", methods=['GET','POST'])
def sms_logic():
try:
# Increment the counter
counter = session.get('counter',0)
# Initiate conversation + Save the new counter value in the session
body = request.values.get('Body', None)
counter += 1
session['counter'] = counter
# start our TwiML response
resp = MessagingResponse()
if counter == 1 and body.lower() == 'target_string':
resp.message('response 1')
elif counter == 1 and body.lower() != 'target_string':
resp.message('Unknown entry. Please try again.')
session.clear()
else:
resp.message('response 2 ')
session.clear()
#print(counter)
return(str(resp))
except Exception as e:
pass
if __name__ == "__main__":
app.run(debug=True)
I am clearing the session to reset the counter instead of waiting 4 hours for the session to expire especially if the user would like to go through the app again to make another entry.
Thanks in advance!

Change session variable value fails

EDIT: actually, I can see the value of the session variable changed, but in the next call to the function the value is set back to 0
I am a Flask beginner and I have problems in changing the value of a session variable. Here is an excerpt of my code:
EDIT after first round of comments
0) I set the SECRET_KEY variable in my config.py.
1) when a user logs in I set a session variable:
#app.route('/login', methods=['GET', 'POST'])
def login():
session['info_released'] = 0
app.logger.debug('info_released session value: {}'.format(session['info_released'])
...
Checking the log, the value of the session variable is correctly set to 0.
2) I have a counter variable passed via request.json that is incremented from time to time. Between one counter increment and the following one I check the following condition several times (via an ajax call):
#app.route('/get_actual_demand', methods=['GET', 'POST'])
def get_actual_demand():
app.logger.info('> SESSION: {}'.format(session['info_released']))
if request.json['counter'] == 10 and session['info_released'] == 0:
#code
session['info_released'] = 1
app.logger.info('> SESSION VAR. AFTER CHANGE: {}'.format(session['info_released']))
return jsonify(released=1)
else:
return jsonify(released=0)
That is, when counter == 10 I check the condition many times but I want to run the #code only once (the first time the counter == 10 and the session variable is 0).
EDIT: Checking the log, the session['info_released'] is changed to 1 when counter == 10, but in the next call the value is set back to 0: indeed, the #code is run many times until the counter gets incremented.
I can't understand what I am doing wrong. I may also have the program flow better organized, but I don't think it matters with the problem I am having.
EDIT: it seems that everything I do on the session variables inside the get_actual_demand() view function are only "local". I changed the code as follows, removing the session['info_released'] = 0 from the login() function:
#app.route('/get_actual_demand', methods=['GET', 'POST'])
def get_actual_demand():
# the variable session['info_released'] is no more
# defined in the login() function
if request.json['counter'] == 10:
try:
# The first time I get here, raise a KeyError exception
if session['info_released'] == 1:
return jsonify(released=0)
except KeyError:
# Use `session['info_released']` as a flag,
# defined here
session['info_released'] = 1
return jsonify(released=1)
else:
return jsonify(released=0)
Checking the log (removed from the code) I can see the session variable defined when it first hits the exception, but then hits again the exception as if the session variable is still not present.
I think there is something missing in my configuration, but I can't find hints in the documentation. My config.py is as follows:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
DEBUG = False
TESTING = False
CSRF_ENABLED = True
WTF_CSRF_ENABLED = True
SECRET_KEY = 'oqEr[]*woi+145##11!&$fsa%(Mn21eq'
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
class DevelopmentConfig(Config):
DEVELOPMENT = True
DEBUG = True
SQLALCHEMY_DATABASE_URI ='postgresql+psycopg2://libra:password#localhost/mydb'
I had the same problem and I solved it using Flask-Session extension which adds support for Server-side Session to your application.
Here is the URL:
http://pythonhosted.org/Flask-Session/
I added the next lines to my app:
from flask import Flask, session
from flask.ext.session import Session
app = Flask(__name__)
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
and that's all. No need secret keys.
Hope this helps.

Can I batch incrementing values in order to reduce database writes?

My web app uses Django on Heroku.
The app has two components: a banner serving API and a reporting API.
Banners served by the app contain an ajax call to the app's reporting API. This call is made once per impression. The purpose of this is to record how many impressions each banner gets each day.
Reporting API abridged source:
def report_v1(request):
'''
/report/v1/?cid=
- cid= the ID of the banner
'''
creative = get_creative(creative_id = request.GET.get('cid'))
if not creative:
return HttpResponseNotFound("404 creative with that ID not found")
day = timezone.now()
fact, created = CreativeFact.objects.get_or_create(
day = day,
app = app,
creative = creative,
defaults = {
'impression_count': 0,
}
)
fact.impression_count += 1
response = HttpResponse("200 OK")
response["Access-Control-Allow-Origin"] = "*"
return response
My problem: Is there any way to avoid writing to the database on every single impression? I'm currently tracking hundreds of thousands of impressions this way, and expect to track millions soon, and I think this is inefficient.
You could cache the counts and then write them to the model in increments of 100 or whatever the optimal value is:
At the top of report_v1:
if getattr(report_v1, 'cache', None) is None:
report_v1.cache = {}
Then, after you verify that the cid is valid (after the line with the 404 response):
if cid in report_v1.cache:
report_v1.cache[cid] += 1
else:
report_v1.cache[cid] = 1
Then, you'd want to increment the value on the model only at certain intervals:
if not report_v1.cache[cid] % 100:
#increment impression_count
#wipe cache
The downside to this approach is that the cache is in memory, so it's lost if the app crashes.

How to use beaker with GAE

Hi I'm going to use an own session object and I'm trying to apply beaker with python. Can you tell me how to use it with google app engine? I've got the following code and then I'm unsure how to proceed:
session_opts = {
'session.cookie_expires': True,
'session.type': 'ext:google',
'session.key': 'mykey.beaker.session.id',
}
def main():
logging.getLogger().setLevel(logging.DEBUG)
application = webapp.WSGIApplication([(...
... handlers ],debug=True)
application = SessionMiddleware(application, session_opts)
util.run_wsgi_app(application)
As the documentation says:
Once the SessionMiddleware is in
place, a session object will be made
available as beaker.session in the
WSGI environ.
In Google App Engine you can access the beaker session dictonary object from a WebHandler with:
session = self.request.environ['beaker.session']
the session is a Python dictionary where you can basically put data with:
session['somekey'] = 'foo'
or get data using:
my_var = session['somekey']
A simple Counter example would be something like this:
class MainPage(webapp.RequestHandler):
def get(self):
session = self.request.environ['beaker.session']
if 'counter' in session:
counter = session['counter'] + 1
session['counter'] = counter
else:
session['counter'] = 1
self.response.out.write('counter: %d' % counter)

Categories

Resources