Home | History | Annotate | Download | only in bartlett
      1 #!/usr/bin/python
      2 # Copyright 2012 Google Inc. All Rights Reserved.
      3 # Author: mrdmnd@ (Matt Redmond)
      4 # Based off of code in //depot/google3/experimental/mobile_gwp
      5 """Code to transport profile data between a user's machine and the CWP servers.
      6     Pages:
      7     "/": the main page for the app, left blank so that users cannot access
      8          the file upload but left in the code for debugging purposes
      9     "/upload": Updates the datastore with a new file. the upload depends on
     10                the format which is templated on the main page ("/")
     11                input includes:
     12                     profile_data: the zipped file containing profile data
     13                     board:  the architecture we ran on
     14                     chromeos_version: the chromeos_version
     15     "/serve": Lists all of the files in the datastore. Each line is a new entry
     16               in the datastore. The format is key~date, where key is the entry's
     17               key in the datastore and date is the file upload time and date.
     18               (Authentication Required)
     19     "/serve/([^/]+)?": For downloading a file of profile data, ([^/]+)? means
     20                        any character sequence so to download the file go to
     21                        '/serve/$key' where $key is the datastore key of the file
     22                        you want to download.
     23                        (Authentication Required)
     24     "/del/([^/]+)?": For deleting an entry in the datastore. To use go to
     25                      '/del/$key' where $key is the datastore key of the entry
     26                      you want to be deleted form the datastore.
     27                      (Authentication Required)
     28     TODO: Add more extensive logging"""
     29 
     30 import cgi
     31 import logging
     32 import md5
     33 import urllib
     34 
     35 from google.appengine.api import users
     36 from google.appengine.ext import db
     37 from google.appengine.ext import webapp
     38 from google.appengine.ext.webapp.util import run_wsgi_app
     39 
     40 logging.getLogger().setLevel(logging.DEBUG)
     41 
     42 
     43 class FileEntry(db.Model):
     44   profile_data = db.BlobProperty()  # The profile data
     45   date = db.DateTimeProperty(auto_now_add=True)  # Date it was uploaded
     46   data_md5 = db.ByteStringProperty()  # md5 of the profile data
     47   board = db.StringProperty()  # board arch
     48   chromeos_version = db.StringProperty()  # ChromeOS version
     49 
     50 
     51 class MainPage(webapp.RequestHandler):
     52   """Main page only used as the form template, not actually displayed."""
     53 
     54   def get(self, response=''):  # pylint: disable-msg=C6409
     55     if response:
     56       self.response.out.write('<html><body>')
     57       self.response.out.write("""<br>
     58         <form action="/upload" enctype="multipart/form-data" method="post">
     59           <div><label>Profile Data:</label></div>
     60           <div><input type="file" name="profile_data"/></div>
     61           <div><label>Board</label></div>
     62           <div><input type="text" name="board"/></div>
     63           <div><label>ChromeOS Version</label></div>
     64           <div><input type="text" name="chromeos_version"></div>
     65           <div><input type="submit" value="send" name="submit"></div>
     66         </form>
     67       </body>
     68       </html>""")
     69 
     70 
     71 class Upload(webapp.RequestHandler):
     72   """Handler for uploading data to the datastore, accessible by anyone."""
     73 
     74   def post(self):  # pylint: disable-msg=C6409
     75     """Takes input based on the main page's form."""
     76     getfile = FileEntry()
     77     f1 = self.request.get('profile_data')
     78     getfile.profile_data = db.Blob(f1)
     79     getfile.data_md5 = md5.new(f1).hexdigest()
     80     getfile.board = self.request.get('board')
     81     getfile.chromeos_version = self.request.get('chromeos_version')
     82     getfile.put()
     83     self.response.out.write(getfile.key())
     84     #self.redirect('/')
     85 
     86 
     87 class ServeHandler(webapp.RequestHandler):
     88   """Given the entry's key in the database, output the profile data file. Only
     89       accessible from @google.com accounts."""
     90 
     91   def get(self, resource):  # pylint: disable-msg=C6409
     92     if Authenticate(self):
     93       file_key = str(urllib.unquote(resource))
     94       request = db.get(file_key)
     95       self.response.out.write(request.profile_data)
     96 
     97 
     98 class ListAll(webapp.RequestHandler):
     99   """Displays all files uploaded. Only accessible by @google.com accounts."""
    100 
    101   def get(self):  # pylint: disable-msg=C6409
    102     """Displays all information in FileEntry, ~ delimited."""
    103     if Authenticate(self):
    104       query_str = 'SELECT * FROM FileEntry ORDER BY date ASC'
    105       query = db.GqlQuery(query_str)
    106       delimiter = '~'
    107 
    108       for item in query:
    109         display_list = [item.key(), item.date, item.data_md5, item.board,
    110                         item.chromeos_version]
    111         str_list = [cgi.escape(str(i)) for i in display_list]
    112         self.response.out.write(delimiter.join(str_list) + '</br>')
    113 
    114 
    115 class DelEntries(webapp.RequestHandler):
    116   """Deletes entries. Only accessible from @google.com accounts."""
    117 
    118   def get(self, resource):  # pylint: disable-msg=C6409
    119     """A specific entry is deleted, when the key is given."""
    120     if Authenticate(self):
    121       fkey = str(urllib.unquote(resource))
    122       request = db.get(fkey)
    123       if request:
    124         db.delete(fkey)
    125 
    126 
    127 def Authenticate(webpage):
    128   """Some urls are only accessible if logged in with a @google.com account."""
    129   user = users.get_current_user()
    130   if user is None:
    131     webpage.redirect(users.create_login_url(webpage.request.uri))
    132   elif user.email().endswith('@google.com'):
    133     return True
    134   else:
    135     webpage.response.out.write('Not Authenticated')
    136     return False
    137 
    138 
    139 def main():
    140   application = webapp.WSGIApplication(
    141       [
    142           ('/', MainPage),
    143           ('/upload', Upload),
    144           ('/serve/([^/]+)?', ServeHandler),
    145           ('/serve', ListAll),
    146           ('/del/([^/]+)?', DelEntries),
    147       ],
    148       debug=False)
    149   run_wsgi_app(application)
    150 
    151 
    152 if __name__ == '__main__':
    153   main()
    154