Simple scenario using middleware class for mobile detection with app engine - python

I'm a newbie to app engine and python and am just trying to get a basic idea for how things work.
I have a simple app, with one mapped url (/). All the classes I'm trying to use are in the base directory of the app.
This is my main.py - all i want to do is use the middleware class to pass a variable to the template so I can render different parts of the page depending on device type.
import webapp2
import jinja2
import os
from useragents import search_strings
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('templates/index.html')
self.response.out.write(template.render())
app = webapp2.WSGIApplication([('/', MainPage)],
debug=True)
class Middleware(object):
#staticmethod
def process_request(request):
"""Adds a "mobile" attribute to the request which is True or False
depending on whether the request should be considered to come from a
small-screen device such as a phone or a PDA
//rest of class is [here][1]
"""

import webapp2
import jinja2
import os
from useragents import search_strings
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
class MainPage(webapp2.RequestHandler):
def get(self):
#i don't know if you want to overwrite self.request but here it is
self.request = Middleware.process_request(self.request)
template = jinja_environment.get_template('templates/index.html')
self.response.out.write(template.render())
app = webapp2.WSGIApplication([('/', MainPage)],
debug=True)
class Middleware(object):
#staticmethod
def process_request(request):
"""Adds a "mobile" attribute to the request which is True or False
depending on whether the request should be considered to come from a
small-screen device such as a phone or a PDA
//rest of class is [here][1]
"""

Related

Need to access self.request.GET from main.py in lib.py

I need to do calculations among other things within lib.py and I need to access the user input data within main.py for it, because it needs to be that way. How do I do this?
doing from main import MainHandler in lib.py and then calling it "works" in the sense that it doesn't give any code errors, but displays a blank page when done that way and I get a log error saying that I cannot import it.
main.py
import webapp2
from lib import FormData
from pages import FormPage
from pages import ResultsPage
class MainHandler(webapp2.RequestHandler):
def get(self):
f = FormPage()
s = ResultsPage()
fd1 = FormData()
if self.request.GET:
fd1.name = self.request.GET['name']
fd1.email = self.request.GET['email']
fd1.weight = self.request.GET['weight']
fd1.height = self.request.GET['height']
self.response.write(s.second_page(fd1.name, fd1.email, fd1.weight, fd1.height))
else:
self.response.write(f.form_page)
app = webapp2.WSGIApplication([
('/', MainHandler)
], debug=True)
lib.py
class FormData(object):
def __init__(self):
pass
I need to be able to access:
fd1.name = self.request.GET['name']
fd1.email = self.request.GET['email']
fd1.weight = self.request.GET['weight']
fd1.height = self.request.GET['height']
In lib.py
Your if/else statement is outside the get method, so it shouldn't even work properly.
I think the most clean way would be to pass the request data to the FormData class already in the initialiser. So the code would look like this:
main.py
import webapp2
from lib import FormData
from pages import FormPage
from pages import ResultsPage
class MainHandler(webapp2.RequestHandler):
def get(self):
f = FormPage()
s = ResultsPage()
fd1 = FormData(data=self.request.GET)
if fd1.name: # This is None, if form has not been submitted
self.response.write(s.second_page(fd1.name, fd1.email, fd1.weight, fd1.height))
else:
self.response.write(f.form_page)
app = webapp2.WSGIApplication([
('/', MainHandler)
], debug=True)
lib.py
class FormData(object):
def __init__(self, data):
self.name = data.get('name')
self.email = data.get('email')
self.weight = data.get('weight')
self.height = data.get('height')
As a side note, I don't really know this webapp2 framework, so there might be a better way to do this.

Why is testbed not flushing the datastore on TearDown() and why environ values aren't passed to the code I'm testing?

I'm trying to setup unittests for my App Engine tutorial example.
I have defined the user in self.testbed.setup_env but the data is not visible in the tested code. Somehow, despite this data not being rendered by the print statement, it's being saved as a structured property in the datastore and is not deleted in the TearDown().
The code works when I use dev_appserver.py and enter the data into the form manually.
When I attempt to run the tests with nosetests --with-gae, the mock user data is not not available
Since I'm attempting to debug a test, I can't use a debugger and I'm relying on print statements.
import sys, os, subprocess, time, unittest, shlex
sys.path.append("/usr/local/google_appengine")
sys.path.append('/usr/local/google_appengine/lib/')
sys.path.append("/usr/local/google_appengine/lib/yaml/lib")
sys.path.append("/usr/local/google_appengine/lib/webapp2-2.5.2")
sys.path.append("/usr/local/google_appengine/lib/django-1.5")
sys.path.append("/usr/local/google_appengine/lib/cherrypy")
sys.path.append("/usr/local/google_appengine/lib/concurrent")
sys.path.append("/usr/local/google_appengine/lib/docker")
sys.path.append("/usr/local/google_appengine/lib/requests")
sys.path.append("/usr/local/google_appengine/lib/websocket")
sys.path.append("/usr/local/google_appengine/lib/fancy_urllib")
sys.path.append("/usr/local/google_appengine/lib/antlr3")
os.environ['APPLICATION_ID'] = 'myapp'
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from google.appengine.api import memcache, apiproxy_stub, apiproxy_stub_map
from google.appengine.ext import testbed
from google.appengine.datastore import datastore_stub_util
from google.appengine.tools.devappserver2 import devappserver2
from guestbook import Author, Greeting
from google.appengine.api import users
class NewVisitorTest(unittest.TestCase):
# enable the datastore stub
nosegae_datastore_v3 = True
nosegae_datastore_v3_kwargs = {
'datastore_file': '/tmp/nosegae.sqlite3',
'use_sqlite': True
}
def setUp(self):
# Start the dev server
cmd = "/usr/local/bin/dev_appserver.py /Users/Bryan/work/GoogleAppEngine/guestbook/app.yaml --port 8080 --storage_path /tmp/datastore --clear_datastore --skip_sdk_update_check"
self.dev_appserver = subprocess.Popen(shlex.split(cmd),
stdout=subprocess.PIPE)
time.sleep(2) # Important, let dev_appserver start up
self.testbed = testbed.Testbed()
self.testbed.setup_env(app_id='guestbook')
self.testbed.activate()
self.datastore_stub = apiproxy_stub_map.apiproxy.GetStub('datastore_v3')
# setup the dev_appserver
APP_CONFIGS = ['app.yaml']
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3)
#
def tearDown(self):
self.testbed.deactivate()
self.browser.quit()
self.dev_appserver.terminate()
def loginUser(self, email="elonMusk#example.com", id='123456', is_admin=False):
self.testbed.setup_env(
user_email=email,
user_id=id,
user_is_admin='1' if is_admin else '0',
overwrite=True
)
self.testbed.init_user_stub()
def test_guest_can_submit_new_greeting_and_author(self):
self.browser.get('http://localhost:8080')
self.loginUser()
self.browser.find_element_by_name('content').send_keys("Test Driven Development is awesome!!!")
self.browser.find_element_by_id('submit_guestbook').submit()
# Can I access them via querys on the database?
assert("Test Driven Development is awesome!!!" in self.browser.page_source)
self.browser.get('http://localhost:8000/datastore')
assert(Greeting.query(Greeting.author.email=='elonMusk#example.com').get())
self.browser.get('http://localhost:8000/datastore')
# I'm opening the datastore to see that the entity is saved.
# The entity is saved but the Author property is empty.
time.sleep(10) # this gives me time to view the datastore in browser.
self.assertEqual(1, Greeting.query().count())
Here is questbook.py:
import os
import urllib
from google.appengine.api import users
from google.appengine.ext import ndb
import jinja2
import webapp2
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'],
autoescape=True)
DEFAULT_GUESTBOOK_NAME = 'default_guestbook'
# We set a parent key on the 'Greetings' to ensure that they are all
# in the same entity group. Queries across the single entity group
# will be consistent. However, the write rate should be limited to
# ~1/second.
def guestbook_key(guestbook_name=DEFAULT_GUESTBOOK_NAME):
"""Constructs a Datastore key for a Guestbook entity.
We use guestbook_name as the key.
"""
return ndb.Key('Guestbook', guestbook_name)
class Author(ndb.Model):
"""Sub model for representing an author."""
identity = ndb.StringProperty(indexed=False)
email = ndb.StringProperty(indexed=True)
class Greeting(ndb.Model):
"""A main model for representing an individual Guestbook entry."""
author = ndb.StructuredProperty(Author)
content = ndb.StringProperty(indexed=False)
date = ndb.DateTimeProperty(auto_now_add=True)
class MainPage(webapp2.RequestHandler):
def get(self):
guestbook_name = self.request.get('guestbook_name',
DEFAULT_GUESTBOOK_NAME)
greetings_query = Greeting.query(
ancestor=guestbook_key(guestbook_name)).order(-Greeting.date)
greetings = greetings_query.fetch(10)
user = users.get_current_user()
if user:
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
else:
url = users.create_login_url(self.request.uri)
url_linktext = 'Login'
template_values = {
'user': user,
'greetings': greetings,
'guestbook_name': urllib.quote_plus(guestbook_name),
'url': url,
'url_linktext': url_linktext,
}
template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))
class Guestbook(webapp2.RequestHandler):
def post(self):
guestbook_name = self.request.get('guestbook_name',
DEFAULT_GUESTBOOK_NAME)
greeting = Greeting(parent=guestbook_key(guestbook_name))
if users.get_current_user():
greeting.author = Author(
identity=users.get_current_user().user_id(),
email=users.get_current_user().email())
greeting.content = self.request.get('content')
######################################
import os
print("XXXXXXXXXXXXXXXXXXXXX guestbook in post() USER_EMAIL: %s" % os.environ.get('USER_EMAIL'))
#print("xoxoxoxoxoxoxoxoxoxox guestbook greeting.author.email: %s" % greeting.author.email )
greeting_key = greeting.put()
query_params = {'guestbook_name': guestbook_name}
self.redirect('/?' + urllib.urlencode(query_params))
application = webapp2.WSGIApplication([
('/', MainPage),
('/sign', Guestbook),
], debug=False)
The datastore you can access from your test suite (the datastore_stub "mock" datastore) is not the same as the one the dev_appserver uses in your example. Hence changes to one of two datastores (such as adding an entity) will not be reflected in the other one, which is why your Greeting.query() returns nothing.
Instead of running a full blown, separate dev_appserver process, you can write your tests using the webtest framework (http://docs.pylonsproject.org/projects/webtest/en/latest/testapp.html) which will be simpler than having to manage a separate process; it will look this:
def setUp(self):
[...]
self.web_app = webtest.TestApp(guestbook.application)
[...]
def test_get(self):
response = self.web_app.get('/')

Stuck retrieving parameters in sessions - GAE webapp2

I'm trying to get sessions to work with Webapp2 with GAE, but I'm stuck retrieving the stored parameters. Maybe I'm overlooking the obvious.
A dressed down version of with main.py where I store a value in the webapp2 session:
import webapp2
from webapp2_extras import sessions
other imports...
class BaseHandler(webapp2.RequestHandler): # Copied from Google's doc
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
try:
# Dispatch the request.
webapp2.RequestHandler.dispatch(self)
finally:
# Save all sessions.
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
# Returns a session using the default cookie key.
return self.session_store.get_session()
class MainPage(webapp2.RequestHandler): # My main page proper
def get(self):
self.session['foo'] = 'bar' # Store somehing in the session
template_values = {
}
template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))
application = webapp2.WSGIApplication([
('/', MainPage, ),], debug = True)
My problem is how to access the value stored in the session from another module. In test.py I do the following:
from webapp2_extras import sessions
import main
other imports ...
class Test(main.BaseHandler):
def post(self):
foo1 = self.session.get('foo')
return foo1
class ProductsPage(webapp2.RequestHandler):
def get(self):
foo2 = Test.post()
...
But I get the following error:
TypeError: unbound method post() must be called with Test instance as first argument (got nothing instead)
I just can't find the proper way to use the Test class and retrieve the value stored in the session. I must be overlooking something when using the class, but I'm stuck and can't find the way out.
Maybe somebody looking from outside will spot it easily?
Looking further I found a post that helped me
GAE webapp2 session handling not working when putting basehandler to a different file
After some tinkering here's the recipe that worked for me to setup a session in webapp2 and share the session among different .py files. Hope it saves others some time.
1) Create basehandler.py, this will need to be imported by all .py files using the session. This is straight from Google's documentation:
import webapp2
from webapp2_extras import sessions
class BaseHandler(webapp2.RequestHandler):
def dispatch(self):
# Get a session store for this request
self.session_store = sessions.get_store(request = self.request)
try:
webapp2.RequestHandler.dispatch(self)
finally:
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
# Returns a session using the default cookie key
return self.session_store.get_session()
2) Create main.py importing BaseHandler. In my case I store a value in the session:
import os
import urllib
import webapp2
import jinja2
from basehandler import BaseHandler
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'])
class MainPage(BaseHandler):
def get(self):
self.session['foo'] = 'bar' # Store a value in the session
# you can store more than one :-)
# Define values that will be passed onto the template
template_values = {
'title': 'TITLE'
}
# search for an appropriate template and render it.
template = JINJA_ENVIRONMENT.get_template('./templates/index.html')
self.response.write(template.render(template_values))
config = {}
config['webapp2_extras.sessions'] = {
'secret_key': 'my-super-secret-key'
}
application = webapp2.WSGIApplication([
('/', MainPage)
], config = config, debug = True)
3) Create another.py taking care to also import BaseHandler and use the same secret key. Here you can retrieve the value stored by main.py or any other .py file sharing the same session:
import os
import urllib
import webapp2
import jinja2
from basehandler import BaseHandler
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'])
class AnotherPage(BaseHandler):
def get(self):
foo1 = self.session.get('foo')
# Now go and do something with foo1, even store a new value as in main.py
# ...
# ...
# Define values that will be passed onto the template
template_values = {
'title': 'TITLE'
}
# search for an appropriate template and render it.
template = JINJA_ENVIRONMENT.get_template('./templates/additional.html')
self.response.write(template.render(template_values))
config = {}
config['webapp2_extras.sessions'] = {
'secret_key': 'my-super-secret-key'
}
application = webapp2.WSGIApplication([
('/', AdditionalPage)
], config = config, debug = True)

Why my tornado application always re-authenticates?

I am building a web application based on Python tornado and I am struggling with the authentication process. Based on the demos provided with tornado I create an authentication through Google.
I authenticated and get redirected to my index.
I try to connect to my /profile site, but tornado redirects me to Google Authentication and to my index.
/profile requires that the user is authenticated. In my case, tornado doesn't seem to
accept it or I made a mistake in the Authentication classes.
Why I cannot access my /profile site? What I am doing wrong with the authentication?
Authentication related handlers
from site import models
import mongoengine
import tornado.auth
import tornado.escape
import tornado.ioloop
import tornado.web
from tornado import gen
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
user_json = self.get_secure_cookie("mysite")
if not user_json: return None
return tornado.escape.json_decode(user_json)
class AuthGoogleLoginHandler(BaseHandler, tornado.auth.GoogleMixin):
#gen.coroutine
def get(self):
if self.get_argument("openid.mode", None):
user = yield self.get_authenticated_user()
self.set_secure_cookie("mysite",
tornado.escape.json_encode(user))
email = user.get('email')
try:
print 'trying to find the user'
usr = models.User.objects.get(email=email)
except mongoengine.DoesNotExist as e:
# there is no user with the wished email address
# let's create a new one.
new_user = models.User()
new_user.email = user.get('email')
new_user.first_name = user.get('first_name')
new_user.last_name = user.get('last_name')
new_user.locale = user.get('locale')
new_user.save()
self.redirect('/')
return
self.authenticate_redirect()
class AuthLogoutHandler(BaseHandler):
'''
Log the current user out.
'''
def get(self):
self.clear_cookie("mysite")
self.redirect('/')
Other handlers and main
from mysite import models
from mysite import auth
from tornado.options import options, define, parse_command_line
import django.core.handlers.wsgi
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.escape
import tornado.wsgi
import os
define('port', type=int, default=1234)
class ProfileHandler(tornado.web.RequestHandler):
#tornado.web.authenticated
def get(self):
join = lambda x, y, separator: separator.join([x, y])
self.render('profile.html', user=user, join=join)
class ProfileEditHandler(tornado.web.RequestHandler):
#tornado.web.authenticated
def get(self):
self.render('profile-edit.html', user=user)
#tornado.web.authenticated
def post(self):
first_name = self.get_argument('first_name')
last_name = self.get_argument('last_name')
city = self.get_argument('city')
state = self.get_argument('state')
country = self.get_argument('country')
# write to MongoDB
self.redirect('/profile')
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render('welcome.html')
def main():
template_path=os.path.join(os.path.abspath(os.path.dirname(__file__)),"templates")
static_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static')
wsgi_app = tornado.wsgi.WSGIContainer(django.core.handlers.wsgi.WSGIHandler())
handlers = [('/profile', ProfileHandler),
('/profile-edit', ProfileEditHandler),
('/auth/login', auth.AuthGoogleLoginHandler),
('/auth/logout', auth.AuthLogoutHandler),
('/', IndexHandler),
('.*', tornado.web.FallbackHandler, dict(fallback=wsgi_app)),]
tornado_app = tornado.web.Application(handlers,
static_path=static_path,
template_path=template_path,
cookie_secret='some_secret',
login_url='/auth/login',
debug=True)
server = tornado.httpserver.HTTPServer(tornado_app)
server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
main()
Finally I found a solution. When the get method of my ProfileHandler was called the #tornado.web.authenticated decorator checks if a user is logged in.
But who finds out if there is a logged in user or not? In the basic tornado functionality this is not implemented. You have to create a BaseHandler and all other handlers which
need this authentication information should subclass the BaseHandler.
After the BaseHandler was defined and subclassed - authentication worked perfectly!
To sum it up. If you get stuck in a vicious cycle of log in requests:
1) Create a BaseHandler and override get_current_user.
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
user_json = self.get_secure_cookie("heroe")
if not user_json: return None
return tornado.escape.json_decode(user_json)
2) Subclass BaseHandler with your common handler:
class ProfileHandler(BaseHandler)
3) Add the tornado decorator #tornado.web.authenticated to ensure authentication:
class ProfileHandler(auth.BaseHandler):
#tornado.web.authenticated
def get(self):
self.render('profile.html')

Detect if URL contains parameters in GAE

I would like to parse the URL upon load to see if it has any parameters. I'm just trying to set up a basic test to see if that's possible. What is the correct regex to send a url like http://example.com/?hiyall to ParamHandler?
class ParamHandler(webapp2.RequestHandler):
def get(self):
self.response.out.write('parameters detected')
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.out.write('Hello World')
application = webapp2.WSGIApplication ([('/', MainHandler), ('/\?.*', ParamHandler)], debug=True)
Do something like this in get(self):
class ParamHandler(webapp2.RequestHandler):
def get(self):
hyl = self.request.get("hiyall")
self.response.out.write("hiyall was: " + hyl)
Customary official docs link: https://developers.google.com/appengine/docs/python/tools/webapp/requestclass#Request_get
If you're using webapp2, you cannot route requests based on parameters.
But you can create a condition based on query_string which can check if parameters exist. Like the following:
class MainHandler(webapp2.RequestHandler):
def get(self):
if self.request.query_string:
self.response.out.write('Has parameters')
else:
self.response.out.write('No parameters')
application = webapp2.WSGIApplication ([('/', MainHandler)], debug=True)

Categories

Resources