thirdparty/google_appengine/google/appengine/tools/dev_appserver_info.py
changeset 109 620f9b141567
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # Copyright 2007 Google Inc.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 #
       
     9 #     http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 #
       
    17 
       
    18 """CGI for displaying info about the currently running app in dev_appserver.
       
    19 
       
    20 This serves pages under /_ah/info/ that display information about the app
       
    21 currently running in the dev_appserver. It currently serves on these URLs:
       
    22 
       
    23   /_ah/info/queries:
       
    24     A list of datastore queries run so far, grouped by kind. Used to suggest
       
    25     composite indices that should be built.
       
    26 
       
    27   /_ah/info/index.yaml:
       
    28     Produces an index.yaml file that can be uploaded to the real app
       
    29     server by appcfg.py.  This information is derived from the query
       
    30     history above, by removing queries that don't need any indexes to
       
    31     be built and by combining queries that can use the same index.
       
    32 """
       
    33 
       
    34 
       
    35 
       
    36 import cgi
       
    37 import wsgiref.handlers
       
    38 
       
    39 from google.appengine.api import apiproxy_stub_map
       
    40 from google.appengine.datastore import datastore_pb
       
    41 from google.appengine.ext import webapp
       
    42 from google.appengine.tools import dev_appserver_index
       
    43 
       
    44 
       
    45 class QueriesHandler(webapp.RequestHandler):
       
    46   """A handler that displays a list of the datastore queries run so far.
       
    47   """
       
    48 
       
    49   HEADER = """<html>
       
    50 <head><title>Query History</title></head>
       
    51 
       
    52 <body>
       
    53 <h3>Query History</h3>
       
    54 
       
    55 <p>This is a list of datastore queries your app has run.  You have to
       
    56 make composite indices for these queries before deploying your app.
       
    57 This is normally done automatically by running dev_appserver, which
       
    58 will write the file index.yaml into your app's root directory, and
       
    59 then deploying your app with appcfg, which will upload that
       
    60 index.yaml.</p>
       
    61 
       
    62 <p>You can also view a 'clean' <a href="index.yaml">index.yaml</a>
       
    63 file and save that to your app's root directory.</p>
       
    64 
       
    65 <table>
       
    66 <tr><th>Times run</th><th>Query</th></tr>
       
    67 """
       
    68 
       
    69   ROW = """<tr><td>%(count)s</td><td>%(query)s</td></tr>"""
       
    70 
       
    71   FOOTER = """
       
    72 </table>
       
    73 </body>
       
    74 </html>"""
       
    75 
       
    76   def Render(self):
       
    77     """Renders and returns the query history page HTML.
       
    78 
       
    79     Returns:
       
    80       A string, formatted as an HTML page.
       
    81     """
       
    82     history = apiproxy_stub_map.apiproxy.GetStub('datastore_v3').QueryHistory()
       
    83     history_items = [(count, query) for query, count in history.items()]
       
    84     history_items.sort(reverse=True)
       
    85     rows = [self.ROW % {'query': _FormatQuery(query),
       
    86                         'count': count}
       
    87             for count, query in history_items]
       
    88     return self.HEADER + '\n'.join(rows) + self.FOOTER
       
    89 
       
    90   def get(self):
       
    91     """Handle a GET.  Just calls Render()."""
       
    92     self.response.out.write(self.Render())
       
    93 
       
    94 
       
    95 class IndexYamlHandler(webapp.RequestHandler):
       
    96   """A handler that renders an index.yaml file suitable for upload."""
       
    97 
       
    98   def Render(self):
       
    99     """Renders and returns the index.yaml file.
       
   100 
       
   101     Returns:
       
   102       A string, formatted as an index.yaml file.
       
   103     """
       
   104     datastore_stub = apiproxy_stub_map.apiproxy.GetStub('datastore_v3')
       
   105     query_history = datastore_stub.QueryHistory()
       
   106     body = dev_appserver_index.GenerateIndexFromHistory(query_history)
       
   107     return 'indexes:\n' + body
       
   108 
       
   109   def get(self):
       
   110     """Handle a GET.  Just calls Render()."""
       
   111     self.response.headers['Content-Type'] = 'text/plain'
       
   112     self.response.out.write(self.Render())
       
   113 
       
   114 
       
   115 def _FormatQuery(query):
       
   116   """Format a Query protobuf as (very simple) HTML.
       
   117 
       
   118   Args:
       
   119     query: A datastore_pb.Query instance.
       
   120 
       
   121   Returns:
       
   122     A string containing formatted HTML.  This is mostly the output of
       
   123     str(query) with '<' etc. escaped, and '<br>' inserted in front of
       
   124     Order and Filter parts.
       
   125   """
       
   126   res = cgi.escape(str(query))
       
   127   res = res.replace('Order', '<br>Order')
       
   128   res = res.replace('Filter', '<br>Filter')
       
   129   return res
       
   130 
       
   131 
       
   132 def _DirectionToString(direction):
       
   133   """Turn a direction enum into a string.
       
   134 
       
   135   Args:
       
   136     direction: ASCENDING or DESCENDING
       
   137 
       
   138   Returns:
       
   139     Either 'asc' or 'descending'.
       
   140   """
       
   141   if direction == datastore_pb.Query_Order.DESCENDING:
       
   142     return 'descending'
       
   143   else:
       
   144     return 'asc'
       
   145 
       
   146 
       
   147 URL_MAP = {
       
   148   '/_ah/info/queries': QueriesHandler,
       
   149   '/_ah/info/index.yaml': IndexYamlHandler,
       
   150 
       
   151 }
       
   152 
       
   153 
       
   154 def main():
       
   155   application = webapp.WSGIApplication(URL_MAP.items())
       
   156   wsgiref.handlers.CGIHandler().run(application)
       
   157 
       
   158 
       
   159 if __name__ == '__main__':
       
   160   main()