| #!/usr/bin/python |
| # Copyright 2012 Google Inc. All Rights Reserved. |
| # Author: mrdmnd@ (Matt Redmond) |
| # Based off of code in //depot/google3/experimental/mobile_gwp |
| """Code to transport profile data between a user's machine and the CWP servers. |
| Pages: |
| "/": the main page for the app, left blank so that users cannot access |
| the file upload but left in the code for debugging purposes |
| "/upload": Updates the datastore with a new file. the upload depends on |
| the format which is templated on the main page ("/") |
| input includes: |
| profile_data: the zipped file containing profile data |
| board: the architecture we ran on |
| chromeos_version: the chromeos_version |
| "/serve": Lists all of the files in the datastore. Each line is a new entry |
| in the datastore. The format is key~date, where key is the entry's |
| key in the datastore and date is the file upload time and date. |
| (Authentication Required) |
| "/serve/([^/]+)?": For downloading a file of profile data, ([^/]+)? means |
| any character sequence so to download the file go to |
| '/serve/$key' where $key is the datastore key of the file |
| you want to download. |
| (Authentication Required) |
| "/del/([^/]+)?": For deleting an entry in the datastore. To use go to |
| '/del/$key' where $key is the datastore key of the entry |
| you want to be deleted form the datastore. |
| (Authentication Required) |
| TODO: Add more extensive logging""" |
| |
| import cgi |
| import logging |
| import md5 |
| import urllib |
| |
| from google.appengine.api import users |
| from google.appengine.ext import db |
| from google.appengine.ext import webapp |
| from google.appengine.ext.webapp.util import run_wsgi_app |
| |
| logging.getLogger().setLevel(logging.DEBUG) |
| |
| |
| class FileEntry(db.Model): |
| profile_data = db.BlobProperty() # The profile data |
| date = db.DateTimeProperty(auto_now_add=True) # Date it was uploaded |
| data_md5 = db.ByteStringProperty() # md5 of the profile data |
| board = db.StringProperty() # board arch |
| chromeos_version = db.StringProperty() # ChromeOS version |
| |
| |
| class MainPage(webapp.RequestHandler): |
| """Main page only used as the form template, not actually displayed.""" |
| |
| def get(self, response=''): # pylint: disable-msg=C6409 |
| if response: |
| self.response.out.write('<html><body>') |
| self.response.out.write("""<br> |
| <form action="/upload" enctype="multipart/form-data" method="post"> |
| <div><label>Profile Data:</label></div> |
| <div><input type="file" name="profile_data"/></div> |
| <div><label>Board</label></div> |
| <div><input type="text" name="board"/></div> |
| <div><label>ChromeOS Version</label></div> |
| <div><input type="text" name="chromeos_version"></div> |
| <div><input type="submit" value="send" name="submit"></div> |
| </form> |
| </body> |
| </html>""") |
| |
| |
| class Upload(webapp.RequestHandler): |
| """Handler for uploading data to the datastore, accessible by anyone.""" |
| |
| def post(self): # pylint: disable-msg=C6409 |
| """Takes input based on the main page's form.""" |
| getfile = FileEntry() |
| f1 = self.request.get('profile_data') |
| getfile.profile_data = db.Blob(f1) |
| getfile.data_md5 = md5.new(f1).hexdigest() |
| getfile.board = self.request.get('board') |
| getfile.chromeos_version = self.request.get('chromeos_version') |
| getfile.put() |
| self.response.out.write(getfile.key()) |
| #self.redirect('/') |
| |
| |
| class ServeHandler(webapp.RequestHandler): |
| """Given the entry's key in the database, output the profile data file. Only |
| accessible from @google.com accounts.""" |
| |
| def get(self, resource): # pylint: disable-msg=C6409 |
| if Authenticate(self): |
| file_key = str(urllib.unquote(resource)) |
| request = db.get(file_key) |
| self.response.out.write(request.profile_data) |
| |
| |
| class ListAll(webapp.RequestHandler): |
| """Displays all files uploaded. Only accessible by @google.com accounts.""" |
| |
| def get(self): # pylint: disable-msg=C6409 |
| """Displays all information in FileEntry, ~ delimited.""" |
| if Authenticate(self): |
| query_str = 'SELECT * FROM FileEntry ORDER BY date ASC' |
| query = db.GqlQuery(query_str) |
| delimiter = '~' |
| |
| for item in query: |
| display_list = [item.key(), item.date, item.data_md5, item.board, |
| item.chromeos_version] |
| str_list = [cgi.escape(str(i)) for i in display_list] |
| self.response.out.write(delimiter.join(str_list) + '</br>') |
| |
| |
| class DelEntries(webapp.RequestHandler): |
| """Deletes entries. Only accessible from @google.com accounts.""" |
| |
| def get(self, resource): # pylint: disable-msg=C6409 |
| """A specific entry is deleted, when the key is given.""" |
| if Authenticate(self): |
| fkey = str(urllib.unquote(resource)) |
| request = db.get(fkey) |
| if request: |
| db.delete(fkey) |
| |
| |
| def Authenticate(webpage): |
| """Some urls are only accessible if logged in with a @google.com account.""" |
| user = users.get_current_user() |
| if user is None: |
| webpage.redirect(users.create_login_url(webpage.request.uri)) |
| elif user.email().endswith('@google.com'): |
| return True |
| else: |
| webpage.response.out.write('Not Authenticated') |
| return False |
| |
| |
| def main(): |
| application = webapp.WSGIApplication( |
| [ |
| ('/', MainPage), |
| ('/upload', Upload), |
| ('/serve/([^/]+)?', ServeHandler), |
| ('/serve', ListAll), |
| ('/del/([^/]+)?', DelEntries), |
| ], |
| debug=False) |
| run_wsgi_app(application) |
| |
| |
| if __name__ == '__main__': |
| main() |