diff options
| author | Andi Albrecht <albrecht.andi@gmail.com> | 2011-07-29 21:03:28 +0200 |
|---|---|---|
| committer | Andi Albrecht <albrecht.andi@gmail.com> | 2011-07-29 21:03:28 +0200 |
| commit | 92c142623d9b4ad806f51dd79e3842396ac464d8 (patch) | |
| tree | e56384484801edcd417901e485474914a98ea4a1 | |
| parent | c918cc9dc30b3a7684a96feae95fc5c05638360e (diff) | |
| download | sqlparse-92c142623d9b4ad806f51dd79e3842396ac464d8.tar.gz | |
Replace Django by Flask in App Engine frontend (fixes issue11).
--HG--
rename : extras/appengine/Makefile => extras/appengine-django/Makefile
rename : extras/appengine/README => extras/appengine-django/README
rename : extras/appengine/__init__.py => extras/appengine-django/__init__.py
rename : extras/appengine/app.yaml => extras/appengine-django/app.yaml
rename : extras/appengine/examples/customers.sql => extras/appengine-django/examples/customers.sql
rename : extras/appengine/examples/multiple_inserts.sql => extras/appengine-django/examples/multiple_inserts.sql
rename : extras/appengine/examples/pg_view.sql => extras/appengine-django/examples/pg_view.sql
rename : extras/appengine/examples/subquery.sql => extras/appengine-django/examples/subquery.sql
rename : extras/appengine/examples/subquery2.sql => extras/appengine-django/examples/subquery2.sql
rename : extras/appengine/index.yaml => extras/appengine-django/index.yaml
rename : extras/appengine/main.py => extras/appengine-django/main.py
rename : extras/appengine/settings.py => extras/appengine-django/settings.py
rename : extras/appengine/sqlformat/__init__.py => extras/appengine-django/sqlformat/__init__.py
rename : extras/appengine/sqlformat/urls.py => extras/appengine-django/sqlformat/urls.py
rename : extras/appengine/sqlformat/views.py => extras/appengine-django/sqlformat/views.py
rename : extras/appengine/static/bg_options.png => extras/appengine-django/static/bg_options.png
rename : extras/appengine/static/bgfieldset.png => extras/appengine-django/static/bgfieldset.png
rename : extras/appengine/static/bgfooter.png => extras/appengine-django/static/bgfooter.png
rename : extras/appengine/static/bgtop.png => extras/appengine-django/static/bgtop.png
rename : extras/appengine/static/blank.gif => extras/appengine-django/static/blank.gif
rename : extras/appengine/static/canvas.html => extras/appengine-django/static/canvas.html
rename : extras/appengine/static/hotkeys.js => extras/appengine-django/static/hotkeys.js
rename : extras/appengine/static/img_loading.gif => extras/appengine-django/static/img_loading.gif
rename : extras/appengine/static/jquery.textarearesizer.compressed.js => extras/appengine-django/static/jquery.textarearesizer.compressed.js
rename : extras/appengine/static/loading.gif => extras/appengine-django/static/loading.gif
rename : extras/appengine/static/lynx_screenshot.png => extras/appengine-django/static/lynx_screenshot.png
rename : extras/appengine/static/pygments.css => extras/appengine-django/static/pygments.css
rename : extras/appengine/static/resize-grip.png => extras/appengine-django/static/resize-grip.png
rename : extras/appengine/static/robots.txt => extras/appengine-django/static/robots.txt
rename : extras/appengine/static/rpc_relay.html => extras/appengine-django/static/rpc_relay.html
rename : extras/appengine/static/script.js => extras/appengine-django/static/script.js
rename : extras/appengine/static/sitemap.xml => extras/appengine-django/static/sitemap.xml
rename : extras/appengine/static/sqlformat_client_example.py => extras/appengine-django/static/sqlformat_client_example.py
rename : extras/appengine/static/styles.css => extras/appengine-django/static/styles.css
rename : extras/appengine/templates/404.html => extras/appengine-django/templates/404.html
rename : extras/appengine/templates/500.html => extras/appengine-django/templates/500.html
rename : extras/appengine/templates/about.html => extras/appengine-django/templates/about.html
rename : extras/appengine/templates/api.html => extras/appengine-django/templates/api.html
rename : extras/appengine/templates/index.html => extras/appengine-django/templates/index.html
rename : extras/appengine/templates/master.html => extras/appengine-django/templates/master.html
rename : extras/appengine/templates/python-client-example.html => extras/appengine-django/templates/python-client-example.html
rename : extras/appengine/templates/source.html => extras/appengine-django/templates/source.html
63 files changed, 1389 insertions, 169 deletions
@@ -5,7 +5,7 @@ dist MANIFEST .coverage extras/appengine/sqlparse -extras/appengine/pygments +extras/appengine/lib/ extras/py3k/sqlparse extras/py3k/tests extras/py3k/sqlparse.diff @@ -19,6 +19,9 @@ Bug Fixes * Add parsing of MS Access column names with braces (issue27, reported by frankz...@gmail.com). +Other + * Replace Django by Flask in App Engine frontend (issue11). + Release 0.1.2 (Nov 23, 2010) ---------------------------- diff --git a/extras/appengine/Makefile b/extras/appengine-django/Makefile index 31393c1..31393c1 100644 --- a/extras/appengine/Makefile +++ b/extras/appengine-django/Makefile diff --git a/extras/appengine-django/README b/extras/appengine-django/README new file mode 100644 index 0000000..4762faa --- /dev/null +++ b/extras/appengine-django/README @@ -0,0 +1,22 @@ +gae-sqlformat - An SQL formatting tool runnging on App Engine +============================================================= + + +To set up this application run + + make all + +This command fetches Django from Subversion and symlinks Pygments +and sqlparse. Note: You'll need Pygments installed somewhere in your +PYTHONPATH. + +For a manual setup have a look at the Makefile ;-) + + +To run the development server run + + make serve + + +Homepage: http://sqlformat.appspot.com + diff --git a/extras/appengine/__init__.py b/extras/appengine-django/__init__.py index e69de29..e69de29 100644 --- a/extras/appengine/__init__.py +++ b/extras/appengine-django/__init__.py diff --git a/extras/appengine-django/app.yaml b/extras/appengine-django/app.yaml new file mode 100644 index 0000000..14aab5e --- /dev/null +++ b/extras/appengine-django/app.yaml @@ -0,0 +1,30 @@ +application: sqlformat +version: 2 +runtime: python +api_version: 1 + +default_expiration: 7d # This is good for images, which never change + +handlers: + +- url: /(robots.txt|favicon.ico|sitemap.xml) + static_files: static/\1 + upload: static/(robots.txt|favicon.ico|sitemap.xml) + +- url: /google7a062e78b56854c0.html + static_files: static/robots.txt + upload: static/robots.txt + +- url: /static/(script.js|styles.css|upload.py) + static_files: static/\1 + upload: static/(script.js|styles.css|upload.py) + expiration: 1h # Shorter expiration, these change often + +- url: /static + static_dir: static + +- url: .* + script: main.py + +derived_file_type: +- python_precompiled
\ No newline at end of file diff --git a/extras/appengine-django/examples/customers.sql b/extras/appengine-django/examples/customers.sql new file mode 100644 index 0000000..8b73850 --- /dev/null +++ b/extras/appengine-django/examples/customers.sql @@ -0,0 +1 @@ +USE mydatabase;SELECT orders.customer, orders.day_of_order, orders.product, orders.quantity as number_ordered, inventory.quantity as number_instock, inventory.price FROM orders JOIN inventory ON orders.product = inventory.product;
\ No newline at end of file diff --git a/extras/appengine-django/examples/multiple_inserts.sql b/extras/appengine-django/examples/multiple_inserts.sql new file mode 100644 index 0000000..cf49d5d --- /dev/null +++ b/extras/appengine-django/examples/multiple_inserts.sql @@ -0,0 +1 @@ +insert into customer (id, name) values (1, 'John');insert into customer (id, name) values (2, 'Jack');insert into customer (id, name) values (3, 'Jane');insert into customer (id, name) values (4, 'Jim');insert into customer (id, name) values (5, 'Jerry');insert into customer (id, name) values (1, 'Joe');
\ No newline at end of file diff --git a/extras/appengine-django/examples/pg_view.sql b/extras/appengine-django/examples/pg_view.sql new file mode 100644 index 0000000..edf9f06 --- /dev/null +++ b/extras/appengine-django/examples/pg_view.sql @@ -0,0 +1 @@ +SELECT DISTINCT (current_database())::information_schema.sql_identifier AS view_catalog, (nv.nspname)::information_schema.sql_identifier AS view_schema, (v.relname)::information_schema.sql_identifier AS view_name, (current_database())::information_schema.sql_identifier AS table_catalog, (nt.nspname)::information_schema.sql_identifier AS table_schema, (t.relname)::information_schema.sql_identifier AS table_name FROM pg_namespace nv, pg_class v, pg_depend dv, pg_depend dt, pg_class t, pg_namespace nt WHERE ((((((((((((((nv.oid = v.relnamespace) AND (v.relkind = 'v'::"char")) AND (v.oid = dv.refobjid)) AND (dv.refclassid = ('pg_class'::regclass)::oid)) AND (dv.classid = ('pg_rewrite'::regclass)::oid)) AND (dv.deptype = 'i'::"char")) AND (dv.objid = dt.objid)) AND (dv.refobjid <> dt.refobjid)) AND (dt.classid = ('pg_rewrite'::regclass)::oid)) AND (dt.refclassid = ('pg_class'::regclass)::oid)) AND (dt.refobjid = t.oid)) AND (t.relnamespace = nt.oid)) AND (t.relkind = ANY (ARRAY['r'::"char", 'v'::"char"]))) AND pg_has_role(t.relowner, 'USAGE'::text)) ORDER BY (current_database())::information_schema.sql_identifier, (nv.nspname)::information_schema.sql_identifier, (v.relname)::information_schema.sql_identifier, (current_database())::information_schema.sql_identifier, (nt.nspname)::information_schema.sql_identifier, (t.relname)::information_schema.sql_identifier; diff --git a/extras/appengine-django/examples/subquery.sql b/extras/appengine-django/examples/subquery.sql new file mode 100644 index 0000000..dd4bbc1 --- /dev/null +++ b/extras/appengine-django/examples/subquery.sql @@ -0,0 +1 @@ +select sum(a1.Sales) from Store_Information a1 where a1.Store_name in (select store_name from Geography a2 where a2.store_name = a1.store_name);
\ No newline at end of file diff --git a/extras/appengine-django/examples/subquery2.sql b/extras/appengine-django/examples/subquery2.sql new file mode 100644 index 0000000..6c00a87 --- /dev/null +++ b/extras/appengine-django/examples/subquery2.sql @@ -0,0 +1 @@ +select user_id, count(*) as how_many from bboard where not exists (select 1 from bboard_authorized_maintainers bam where bam.user_id = bboard.user_id) and posting_time + 60 > sysdate group by user_id order by how_many desc;
\ No newline at end of file diff --git a/extras/appengine-django/index.yaml b/extras/appengine-django/index.yaml new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/extras/appengine-django/index.yaml diff --git a/extras/appengine-django/main.py b/extras/appengine-django/main.py new file mode 100644 index 0000000..5014cb9 --- /dev/null +++ b/extras/appengine-django/main.py @@ -0,0 +1,140 @@ +# Copyright 2008 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Main program for Rietveld. + +This is also a template for running a Django app under Google App +Engine, especially when using a newer version of Django than provided +in the App Engine standard library. + +The site-specific code is all in other files: urls.py, models.py, +views.py, settings.py. +""" + +# Standard Python imports. +import os +import sys +import logging +import traceback + + +# Log a message each time this module get loaded. +logging.info('Loading %s, app version = %s', + __name__, os.getenv('CURRENT_VERSION_ID')) + +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + +from google.appengine.dist import use_library +use_library('django', '1.2') + +# Fail early if we can't import Django. Log identifying information. +import django +logging.info('django.__file__ = %r, django.VERSION = %r', + django.__file__, django.VERSION) +assert django.VERSION[0] >= 1, "This Django version is too old" + +# AppEngine imports. +from google.appengine.ext.webapp import util +from google.appengine.api import mail + + +# Helper to enter the debugger. This passes in __stdin__ and +# __stdout__, because stdin and stdout are connected to the request +# and response streams. You must import this from __main__ to use it. +# (I tried to make it universally available via __builtin__, but that +# doesn't seem to work for some reason.) +def BREAKPOINT(): + import pdb + p = pdb.Pdb(None, sys.__stdin__, sys.__stdout__) + p.set_trace() + + +# Custom Django configuration. +from django.conf import settings +settings._target = None + +# Import various parts of Django. +import django.core.handlers.wsgi +import django.core.signals +import django.db +import django.dispatch.dispatcher +import django.forms + +# Work-around to avoid warning about django.newforms in djangoforms. +django.newforms = django.forms + + +def log_exception(*args, **kwds): + """Django signal handler to log an exception.""" + excinfo = sys.exc_info() + cls, err = excinfo[:2] + subject = 'Exception in request: %s: %s' % (cls.__name__, err) + logging.exception(subject) + try: + repr_request = repr(kwds.get('request', 'Request not available.')) + except: + repr_request = 'Request repr() not available.' + msg = ('Application: %s\nVersion: %s\n\n%s\n\n%s' + % (os.getenv('APPLICATION_ID'), os.getenv('CURRENT_VERSION_ID'), + '\n'.join(traceback.format_exception(*excinfo)), + repr_request)) + mail.send_mail_to_admins('albrecht.andi@googlemail.com', + '[%s] %s' % (os.getenv('APPLICATION_ID'), subject), + msg) + + +# Log all exceptions detected by Django. +django.core.signals.got_request_exception.connect(log_exception) + +# Unregister Django's default rollback event handler. +#django.core.signals.got_request_exception.disconnect( +# django.db._rollback_on_exception) + + +def real_main(): + """Main program.""" + # Create a Django application for WSGI. + application = django.core.handlers.wsgi.WSGIHandler() + # Run the WSGI CGI handler with that application. + util.run_wsgi_app(application) + + +def profile_main(): + """Main program for profiling.""" + import cProfile + import pstats + import StringIO + + prof = cProfile.Profile() + prof = prof.runctx('real_main()', globals(), locals()) + stream = StringIO.StringIO() + stats = pstats.Stats(prof, stream=stream) + # stats.strip_dirs() # Don't; too many modules are named __init__.py. + stats.sort_stats('time') # 'time', 'cumulative' or 'calls' + stats.print_stats() # Optional arg: how many to print + # The rest is optional. + # stats.print_callees() + # stats.print_callers() + print '\n<hr>' + print '<h1>Profile</h1>' + print '<pre>' + print stream.getvalue()[:1000000] + print '</pre>' + +# Set this to profile_main to enable profiling. +main = real_main + + +if __name__ == '__main__': + main() diff --git a/extras/appengine/settings.py b/extras/appengine-django/settings.py index 74af42c..74af42c 100644 --- a/extras/appengine/settings.py +++ b/extras/appengine-django/settings.py diff --git a/extras/appengine-django/sqlformat/__init__.py b/extras/appengine-django/sqlformat/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/extras/appengine-django/sqlformat/__init__.py diff --git a/extras/appengine/sqlformat/urls.py b/extras/appengine-django/sqlformat/urls.py index c83290e..c83290e 100644 --- a/extras/appengine/sqlformat/urls.py +++ b/extras/appengine-django/sqlformat/urls.py diff --git a/extras/appengine/sqlformat/views.py b/extras/appengine-django/sqlformat/views.py index 806496f..806496f 100644 --- a/extras/appengine/sqlformat/views.py +++ b/extras/appengine-django/sqlformat/views.py diff --git a/extras/appengine-django/static/bg_options.png b/extras/appengine-django/static/bg_options.png Binary files differnew file mode 100644 index 0000000..bc1a6ed --- /dev/null +++ b/extras/appengine-django/static/bg_options.png diff --git a/extras/appengine-django/static/bgfieldset.png b/extras/appengine-django/static/bgfieldset.png Binary files differnew file mode 100644 index 0000000..4d55f4a --- /dev/null +++ b/extras/appengine-django/static/bgfieldset.png diff --git a/extras/appengine-django/static/bgfooter.png b/extras/appengine-django/static/bgfooter.png Binary files differnew file mode 100644 index 0000000..9ce5bdd --- /dev/null +++ b/extras/appengine-django/static/bgfooter.png diff --git a/extras/appengine-django/static/bgtop.png b/extras/appengine-django/static/bgtop.png Binary files differnew file mode 100644 index 0000000..a0d4709 --- /dev/null +++ b/extras/appengine-django/static/bgtop.png diff --git a/extras/appengine/static/blank.gif b/extras/appengine-django/static/blank.gif Binary files differindex 3be2119..3be2119 100644 --- a/extras/appengine/static/blank.gif +++ b/extras/appengine-django/static/blank.gif diff --git a/extras/appengine/static/canvas.html b/extras/appengine-django/static/canvas.html index ab642d0..ab642d0 100644 --- a/extras/appengine/static/canvas.html +++ b/extras/appengine-django/static/canvas.html diff --git a/extras/appengine-django/static/hotkeys.js b/extras/appengine-django/static/hotkeys.js new file mode 100644 index 0000000..0e62a92 --- /dev/null +++ b/extras/appengine-django/static/hotkeys.js @@ -0,0 +1 @@ +(function(B){B.fn.__bind__=B.fn.bind;B.fn.__unbind__=B.fn.unbind;B.fn.__find__=B.fn.find;var A={version:"0.7.8",override:/keydown|keypress|keyup/g,triggersMap:{},specialKeys:{27:"esc",9:"tab",32:"space",13:"return",8:"backspace",145:"scroll",20:"capslock",144:"numlock",19:"pause",45:"insert",36:"home",46:"del",35:"end",33:"pageup",34:"pagedown",37:"left",38:"up",39:"right",40:"down",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12"},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":":","'":'"',",":"<",".":">","/":"?","\\":"|"},newTrigger:function(E,D,F){var C={};C[E]={};C[E][D]={cb:F,disableInInput:false};return C}};if(B.browser.mozilla){A.specialKeys=B.extend(A.specialKeys,{96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"})}B.fn.find=function(C){this.query=C;return B.fn.__find__.apply(this,arguments)};B.fn.unbind=function(H,E,G){if(B.isFunction(E)){G=E;E=null}if(E&&typeof E==="string"){var F=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();var D=H.split(" ");for(var C=0;C<D.length;C++){delete A.triggersMap[F][D[C]][E]}}return this.__unbind__(H,G)};B.fn.bind=function(J,F,K){var H=J.match(A.override);if(B.isFunction(F)||!H){return this.__bind__(J,F,K)}else{var N=null,I=B.trim(J.replace(A.override,""));if(I){N=this.__bind__(I,F,K)}if(typeof F==="string"){F={combi:F}}if(F.combi){for(var M=0;M<H.length;M++){var D=H[M];var G=F.combi.toLowerCase(),E=A.newTrigger(D,G,K),L=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();E[D][G].disableInInput=F.disableInInput;if(!A.triggersMap[L]){A.triggersMap[L]=E}else{if(!A.triggersMap[L][D]){A.triggersMap[L][D]=E[D]}}var C=A.triggersMap[L][D][G];if(!C){A.triggersMap[L][D][G]=[E[D][G]]}else{if(C.constructor!==Array){A.triggersMap[L][D][G]=[C]}else{A.triggersMap[L][D][G][C.length]=E[D][G]}}this.each(function(){var O=B(this);if(O.attr("hkId")&&O.attr("hkId")!==L){L=O.attr("hkId")+";"+L}O.attr("hkId",L)});N=this.__bind__(H.join(" "),F,A.handler)}}return N}};A.findElement=function(C){if(!B(C).attr("hkId")){if(B.browser.opera||B.browser.safari){while(!B(C).attr("hkId")&&C.parentNode){C=C.parentNode}}}return C};A.handler=function(E){var O=A.findElement(E.currentTarget),I=B(O),D=I.attr("hkId");if(D){D=D.split(";");var G=E.which,Q=E.type,P=A.specialKeys[G],N=!P&&String.fromCharCode(G).toLowerCase(),H=E.shiftKey,C=E.ctrlKey,M=E.altKey||E.originalEvent.altKey,F=null;for(var R=0;R<D.length;R++){if(A.triggersMap[D[R]][Q]){F=A.triggersMap[D[R]][Q];break}}if(F){var J;if(!H&&!C&&!M){J=F[P]||(N&&F[N])}else{var L="";if(M){L+="alt+"}if(C){L+="ctrl+"}if(H){L+="shift+"}J=F[L+P];if(!J){if(N){J=F[L+N]||F[L+A.shiftNums[N]]||(L==="shift+"&&F[A.shiftNums[N]])}}}if(J){var S=false;for(var R=0;R<J.length;R++){if(J[R].disableInInput){var K=B(E.target);if(I.is("input")||I.is("textarea")||K.is("input")||K.is("textarea")){return true}}S=S||J[R].cb.apply(this,[E])}return S}}}};window.hotkeys=A;return B})(jQuery);
\ No newline at end of file diff --git a/extras/appengine/static/img_loading.gif b/extras/appengine-django/static/img_loading.gif Binary files differindex 6465823..6465823 100644 --- a/extras/appengine/static/img_loading.gif +++ b/extras/appengine-django/static/img_loading.gif diff --git a/extras/appengine/static/jquery.textarearesizer.compressed.js b/extras/appengine-django/static/jquery.textarearesizer.compressed.js index 5464ae6..5464ae6 100644 --- a/extras/appengine/static/jquery.textarearesizer.compressed.js +++ b/extras/appengine-django/static/jquery.textarearesizer.compressed.js diff --git a/extras/appengine-django/static/loading.gif b/extras/appengine-django/static/loading.gif Binary files differnew file mode 100644 index 0000000..a879bed --- /dev/null +++ b/extras/appengine-django/static/loading.gif diff --git a/extras/appengine-django/static/lynx_screenshot.png b/extras/appengine-django/static/lynx_screenshot.png Binary files differnew file mode 100644 index 0000000..d1592ac --- /dev/null +++ b/extras/appengine-django/static/lynx_screenshot.png diff --git a/extras/appengine-django/static/pygments.css b/extras/appengine-django/static/pygments.css new file mode 100644 index 0000000..da02807 --- /dev/null +++ b/extras/appengine-django/static/pygments.css @@ -0,0 +1,59 @@ +.c { color: #408080; font-style: italic } /* Comment */ +.err { border: 1px solid #FF0000 } /* Error */ +.k { color: #008000; font-weight: bold } /* Keyword */ +.o { color: #666666 } /* Operator */ +.cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.cp { color: #BC7A00 } /* Comment.Preproc */ +.c1 { color: #408080; font-style: italic } /* Comment.Single */ +.cs { color: #408080; font-style: italic } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #FF0000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #808080 } /* Generic.Output */ +.gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0040D0 } /* Generic.Traceback */ +.kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.kp { color: #008000 } /* Keyword.Pseudo */ +.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #B00040 } /* Keyword.Type */ +.m { color: #666666 } /* Literal.Number */ +.s { color: #BA2121 } /* Literal.String */ +.na { color: #7D9029 } /* Name.Attribute */ +.nb { color: #008000 } /* Name.Builtin */ +.nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.no { color: #880000 } /* Name.Constant */ +.nd { color: #AA22FF } /* Name.Decorator */ +.ni { color: #999999; font-weight: bold } /* Name.Entity */ +.ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.nf { color: #0000FF } /* Name.Function */ +.nl { color: #A0A000 } /* Name.Label */ +.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.nt { color: #008000; font-weight: bold } /* Name.Tag */ +.nv { color: #19177C } /* Name.Variable */ +.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #666666 } /* Literal.Number.Float */ +.mh { color: #666666 } /* Literal.Number.Hex */ +.mi { color: #666666 } /* Literal.Number.Integer */ +.mo { color: #666666 } /* Literal.Number.Oct */ +.sb { color: #BA2121 } /* Literal.String.Backtick */ +.sc { color: #BA2121 } /* Literal.String.Char */ +.sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.s2 { color: #BA2121 } /* Literal.String.Double */ +.se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.sh { color: #BA2121 } /* Literal.String.Heredoc */ +.si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.sx { color: #008000 } /* Literal.String.Other */ +.sr { color: #BB6688 } /* Literal.String.Regex */ +.s1 { color: #BA2121 } /* Literal.String.Single */ +.ss { color: #19177C } /* Literal.String.Symbol */ +.bp { color: #008000 } /* Name.Builtin.Pseudo */ +.vc { color: #19177C } /* Name.Variable.Class */ +.vg { color: #19177C } /* Name.Variable.Global */ +.vi { color: #19177C } /* Name.Variable.Instance */ +.il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/extras/appengine/static/resize-grip.png b/extras/appengine-django/static/resize-grip.png Binary files differindex cae2a4e..cae2a4e 100644 --- a/extras/appengine/static/resize-grip.png +++ b/extras/appengine-django/static/resize-grip.png diff --git a/extras/appengine-django/static/robots.txt b/extras/appengine-django/static/robots.txt new file mode 100644 index 0000000..c033917 --- /dev/null +++ b/extras/appengine-django/static/robots.txt @@ -0,0 +1,8 @@ +# Directions for web crawlers. +# See http://www.robotstxt.org/wc/norobots.html. + +User-agent: HTTrack +User-agent: puf +User-agent: MSIECrawler +User-agent: Nutch +Disallow: / diff --git a/extras/appengine/static/rpc_relay.html b/extras/appengine-django/static/rpc_relay.html index c602043..c602043 100644 --- a/extras/appengine/static/rpc_relay.html +++ b/extras/appengine-django/static/rpc_relay.html diff --git a/extras/appengine-django/static/script.js b/extras/appengine-django/static/script.js new file mode 100644 index 0000000..71bbabb --- /dev/null +++ b/extras/appengine-django/static/script.js @@ -0,0 +1,103 @@ +var initialized = false; + +function update_output() { + data = {} + data.data = $('#id_data').val(); + data.format = 'json'; + if ( $('#id_remove_comments').attr('checked') ) { + data.remove_comments = 1 + } + if ( $('#id_highlight').attr('checked') ) { data.highlight = 1 } + data.keyword_case = $('#id_keyword_case').val(); + data.identifier_case = $('#id_identifier_case').val(); + data.n_indents = $('#id_n_indents').val(); + data.right_margin = $('#id_right_margin').val(); + data.output_format = $('#id_output_format').val(); + form = document.getElementById('form_options'); + $(form.elements).attr('disabled', 'disabled'); + $('#response').addClass('loading'); + $.post('/', data, + function(data) { + if ( data.output ) { + $('#response').html(data.output); + proc_time = 'Processed in '+data.proc_time+' seconds.'; + } else { + $('#response').html('An error occured: '+data.errors); + proc_time = ''; + } + $('#proc_time').html(proc_time); + $(form.elements).each( function(idx) { + obj = $(this); + if ( ! obj.is('.keep-disabled') ) { + obj.removeAttr('disabled'); + } + }); + $('#response').removeClass('loading'); + }, 'json'); + return false; +} + +function toggle_fieldset(event) { + id = $(this).attr('id'); + $('#'+id+'_content').slideDown(); + $('legend').each(function(idx) { + obj = $('#'+this.id+'_content'); + if ( this.id != id ) { + obj.slideUp(); + } + }); +} + + +function textarea_grab_focus(evt) { + evt.stopPropagation(); + evt.preventDefault(); + $('#id_data').focus(); + return false; +} + + +function show_help() { + $('#help').toggle(); + return false; +} + + +function hide_help() { + $('#help').hide(); + return false; +} + +function load_example() { + fname = $('#sel_example').val(); + data = {fname: fname}; + $.post('/load_example', data, + function(data) { + $('#id_data').val(data.answer); + }, 'json'); +} + + +function init() { + if (initialized) { return } + //$('legend').bind('click', toggle_fieldset); + // $('legend').each(function(idx) { + // obj = $('#'+this.id+'_content'); + // if ( this.id != 'general' ) { + // obj.hide(); + // } + // }); + $(document).bind('keydown', {combi:'Ctrl+f'}, + update_output); + $('#btn_format').val('Format SQL [Ctrl+F]'); + $(document).bind('keydown', {combi: 'h', disableInInput: true}, + show_help); + $(document).bind('keydown', 'Esc', hide_help); + $(document).bind('keydown', {combi: 't', disableInInput: true}, + textarea_grab_focus); + initialized = true; + /* jQuery textarea resizer plugin usage */ + $(document).ready(function() { + $('textarea.resizable:not(.processed)').TextAreaResizer(); + }); +}
\ No newline at end of file diff --git a/extras/appengine-django/static/sitemap.xml b/extras/appengine-django/static/sitemap.xml new file mode 100644 index 0000000..db3288e --- /dev/null +++ b/extras/appengine-django/static/sitemap.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<urlset + xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 + http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> +<!-- created with Free Online Sitemap Generator www.xml-sitemaps.com --> + +<url> + <loc>http://sqlformat.appspot.com/</loc> + <changefreq>monthly</changefreq> +</url> +<url> + <loc>http://sqlformat.appspot.com/about/</loc> + <changefreq>monthly</changefreq> +</url> +<url> + <loc>http://sqlformat.appspot.com/source/</loc> + <changefreq>monthly</changefreq> +</url> +<url> + <loc>http://sqlformat.appspot.com/api/</loc> + <changefreq>monthly</changefreq> +</url> +</urlset>
\ No newline at end of file diff --git a/extras/appengine-django/static/sqlformat_client_example.py b/extras/appengine-django/static/sqlformat_client_example.py new file mode 100644 index 0000000..eec17b9 --- /dev/null +++ b/extras/appengine-django/static/sqlformat_client_example.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +import urllib +import urllib2 + +REMOTE_API = 'http://sqlformat.appspot.com/format/' + +payload = ( + ('data', 'select * from foo join bar on val1 = val2 where id = 123;'), + ('format', 'text'), + ('keyword_case', 'upper'), + ('reindent', True), + ('n_indents', 2), + ) + +response = urllib2.urlopen(REMOTE_API, + urllib.urlencode(payload)) +print response.read() + diff --git a/extras/appengine-django/static/styles.css b/extras/appengine-django/static/styles.css new file mode 100644 index 0000000..41a540a --- /dev/null +++ b/extras/appengine-django/static/styles.css @@ -0,0 +1,245 @@ +body { + color: #000000; + background: #eeeeec; + font-family: "Free Sans", Arial, Verdana, sans; + font-size: 10pt; + margin: 0; + padding: 0; +} + +#header { + background: url(/static/bgtop.png) top left repeat-x; + border-bottom: 3px solid #2e3436; +} + +#header-inner, #main-inner, #footer-inner { + width: 70em; + margin-left: auto; + margin-right: auto; +} + + +#header-inner h1 { + margin: 0; + padding: 0; + margin-bottom: .2em; + font-weight: normal; + float: left; + font-size: 2em; + letter-spacing: .07em; +} + +#header-inner .q { + color: #f57900; + padding-right: 3px; +} + +#header-inner .q2 { + font-family: Georgia, "Times New Roman", serif; +} + +#header-inner h1 a { + text-decoration: none; + color: #eeeeec; +} + +#header-inner #slogan { + float: left; + color: #babdb6; + font-size: 1.4em; + margin-left: 1em; + letter-spacing: .18em; + padding-top: .2em; +} + + +#topmenu { + color: #729fcf; + clear: left; + padding-top: .5em; + padding-bottom: .5em; + font-size: 1.1em; +} + +#topmenu a { + color: #eeeeec; + text-decoration: none; +} + +#topmenu a:hover { + color: #ce5c00; +} + + +#main { + padding: 10px; + background: white; + line-height: 1.5em; + text-align: justify; +} + +#main form ul { + margin: 0; + padding: 0; + list-style-type: none; +} + +#main p, #main ol, #main .example, #main dl { + font-size: 12pt; + margin-left: 2em; +} + +#main dt { + font-weight: bold; +} + +#main li { + margin-bottom: .7em; +} + +#main a { + color: #f57900; +} + +#main h1, h2, h3, h4 { + color: #204a87; + font-weight: normal; + letter-spacing: .05em; +} + +#main pre, #main code.pre { + font-size: 10pt; + line-height: 1em; + padding: 4px; + background-color: #eeeeec; + border: 1px solid #babdb6; +} + +#input { + width: 50em; + float: right; + margin-left: 2em; +} + +#options { + width: 18em; + float: left; + color: #2e3436; + margin-top: .75em; + text-align: left; +} + +#options fieldset { + border: 1px solid #dddddd; + margin-bottom: .6em; + background: url(/static/bgfieldset.png) bottom left repeat-x; + -moz-border-radius: 3px; +} + + +#options input, select { + border: 1px solid #dddddd; +} + +#options .help { + font-size: .9em; + color: #888a85; + margin-bottom: .6em; +} + + +#footer { + background: url(/static/bgfooter.png) top left repeat-x; + padding: 10px; + min-height: 80px; + border-top: 4px solid #babdb6; +} + +#footer-inner { + width: 70em; + margin-left: auto; + margin-right: auto; + color: #888a85; +} + +#footer-inner a { + color: #888a85; +} + +#footer-inner a:hover { + color: #555753; +} + +.clearfix { + clear: both; +} + +.skip { + display: none; +} + +textarea { + border: 1px solid #cccccc; + border-bottom: none; + padding: 4px; + font-size: 12pt; + width: 100%; +} + +textarea:focus { + background-color: #eeeeec; +} + +div.grippie { + background: url(/static/resize-grip.png) bottom right no-repeat #eeeeec; + border-color: #cccccc; + border-style: solid; + border-width: 0pt 1px 1px; + cursor: se-resize; + height: 14px; + overflow: hidden; +} + +#help { + display: none; + position: fixed; + right: 10%; + left: 10%; + top: 0; + opacity: 0.85; + -moz-opacity: 0.85; + -khtml-opacity: 0.85; + filter: alpha(opacity=85); + -moz-border-radius: 0px 0px 10px 10px; + + background: #2e3436; + color: white; + font-weight: bold; + + padding: 1em; + z-index: 1; + overflow-x: hidden; + overflow-y: auto; +} + +#help .shortcut { + color: #f57900; + font-weight: bold; + width: 20px; + display: inline; +} + +.loading { + background: url(/static/loading.gif) top left no-repeat; +} + +.dev { + color: #cc0000; + font-size: .9em; + letter-spacing: 1; +} + +#proc_time { + color: #888a85; + font-size: .85em; +}
\ No newline at end of file diff --git a/extras/appengine/templates/404.html b/extras/appengine-django/templates/404.html index 1eb60e8..1eb60e8 100644 --- a/extras/appengine/templates/404.html +++ b/extras/appengine-django/templates/404.html diff --git a/extras/appengine/templates/500.html b/extras/appengine-django/templates/500.html index 8fa8f56..8fa8f56 100644 --- a/extras/appengine/templates/500.html +++ b/extras/appengine-django/templates/500.html diff --git a/extras/appengine/templates/about.html b/extras/appengine-django/templates/about.html index 2d4e03e..2d4e03e 100644 --- a/extras/appengine/templates/about.html +++ b/extras/appengine-django/templates/about.html diff --git a/extras/appengine/templates/api.html b/extras/appengine-django/templates/api.html index 79bf118..79bf118 100644 --- a/extras/appengine/templates/api.html +++ b/extras/appengine-django/templates/api.html diff --git a/extras/appengine/templates/index.html b/extras/appengine-django/templates/index.html index 3b4ea6f..3b4ea6f 100644 --- a/extras/appengine/templates/index.html +++ b/extras/appengine-django/templates/index.html diff --git a/extras/appengine/templates/master.html b/extras/appengine-django/templates/master.html index ea1c662..ea1c662 100644 --- a/extras/appengine/templates/master.html +++ b/extras/appengine-django/templates/master.html diff --git a/extras/appengine/templates/python-client-example.html b/extras/appengine-django/templates/python-client-example.html index 68bf820..68bf820 100644 --- a/extras/appengine/templates/python-client-example.html +++ b/extras/appengine-django/templates/python-client-example.html diff --git a/extras/appengine/templates/source.html b/extras/appengine-django/templates/source.html index a0ed89d..a0ed89d 100644 --- a/extras/appengine/templates/source.html +++ b/extras/appengine-django/templates/source.html diff --git a/extras/appengine/README b/extras/appengine/README index 4762faa..04d32b2 100644 --- a/extras/appengine/README +++ b/extras/appengine/README @@ -1,22 +1,3 @@ -gae-sqlformat - An SQL formatting tool runnging on App Engine -============================================================= - - -To set up this application run - - make all - -This command fetches Django from Subversion and symlinks Pygments -and sqlparse. Note: You'll need Pygments installed somewhere in your -PYTHONPATH. - -For a manual setup have a look at the Makefile ;-) - - -To run the development server run - - make serve - - -Homepage: http://sqlformat.appspot.com - +- Run "./bootstrap.py" to download all required Python modules. +- Run "dev_appserver.py ." for a local server. +- Have a look at config.py :) diff --git a/extras/appengine/app.yaml b/extras/appengine/app.yaml index 14aab5e..3afa891 100644 --- a/extras/appengine/app.yaml +++ b/extras/appengine/app.yaml @@ -1,5 +1,5 @@ application: sqlformat -version: 2 +version: dev runtime: python api_version: 1 @@ -23,8 +23,15 @@ handlers: - url: /static static_dir: static +- url: /_ereporter.* + script: $PYTHON_LIB/google/appengine/ext/ereporter/report_generator.py + login: admin + - url: .* script: main.py -derived_file_type: -- python_precompiled
\ No newline at end of file +builtins: +- appstats: on + +inbound_services: +- warmup diff --git a/extras/appengine/appengine_config.py b/extras/appengine/appengine_config.py new file mode 100644 index 0000000..8ed3ee5 --- /dev/null +++ b/extras/appengine/appengine_config.py @@ -0,0 +1,6 @@ + +def webapp_add_wsgi_middleware(app): + from google.appengine.ext.appstats import recording + app = recording.appstats_wsgi_middleware(app) + return app + diff --git a/extras/appengine/bootstrap.py b/extras/appengine/bootstrap.py new file mode 100755 index 0000000..debc2bf --- /dev/null +++ b/extras/appengine/bootstrap.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +"""Downloads required third-party modules.""" + +import os +import urllib2 +import gzip +import tarfile +import tempfile +import shutil +import sys +from StringIO import StringIO + +HERE = os.path.abspath(os.path.dirname(__file__)) +LIB_DIR = os.path.join(HERE, 'lib') + +PACKAGES = { + 'http://pypi.python.org/packages/source/F/Flask/Flask-0.7.2.tar.gz': + [('Flask-0.7.2/flask', 'flask')], + 'http://pypi.python.org/packages/source/W/Werkzeug/Werkzeug-0.6.2.tar.gz': + [('Werkzeug-0.6.2/werkzeug', 'werkzeug')], + 'http://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.5.5.tar.gz': + [('Jinja2-2.5.5/jinja2/', 'jinja2')], + 'http://pypi.python.org/packages/source/s/simplejson/simplejson-2.1.6.tar.gz': + [('simplejson-2.1.6/simplejson', 'simplejson')], + 'http://pypi.python.org/packages/source/P/Pygments/Pygments-1.4.tar.gz': + [('Pygments-1.4/pygments', 'pygments')], +} + + +def fetch_all(): + if not os.path.isdir(LIB_DIR): + os.makedirs(LIB_DIR) + for url, targets in PACKAGES.iteritems(): + if not _missing_targets(targets): + continue + sys.stdout.write(url) + sys.stdout.flush() + fetch(url, targets) + sys.stdout.write(' done\n') + sys.stdout.flush() + + +def fetch(url, targets): + blob = urllib2.urlopen(url).read() + gz = gzip.GzipFile(fileobj=StringIO(blob)) + tar = tarfile.TarFile(fileobj=gz) + tmpdir = tempfile.mkdtemp() + try: + tar.extractall(tmpdir) + for src, dest in targets: + dest = os.path.join(LIB_DIR, dest) + if os.path.isdir(dest): + shutil.rmtree(dest) + shutil.copytree(os.path.join(tmpdir, src), dest) + finally: + shutil.rmtree(tmpdir) + + +def _missing_targets(targets): + for _, dest in targets: + dest = os.path.join(LIB_DIR, dest) + if not os.path.isdir(dest): + return True + return False + + +def link_sqlparse(): + if os.path.islink('sqlparse'): + return + elif os.path.exists('sqlparse'): + shutil.rmtree('sqlparse') + if hasattr(os, 'symlink'): + os.symlink('../../sqlparse', 'sqlparse') + else: + shutil.copytree(os.path.join(HERE, '../../sqlparse'), + 'sqlparse') + + +if __name__ == '__main__': + fetch_all() + link_sqlparse() diff --git a/extras/appengine/config.py b/extras/appengine/config.py new file mode 100644 index 0000000..1599c00 --- /dev/null +++ b/extras/appengine/config.py @@ -0,0 +1,7 @@ +# SQLFormat configuration + +# Debug flag +DEBUG = True + +# Secret key, please change this +SECRET_KEY = 'notsosecret' diff --git a/extras/appengine/cron.yaml b/extras/appengine/cron.yaml new file mode 100644 index 0000000..a7fefce --- /dev/null +++ b/extras/appengine/cron.yaml @@ -0,0 +1,4 @@ +cron: +- description: Daily exception report + url: /_ereporter?sender=albrecht.andi@googlemail.com&versions=all&delete=false + schedule: every day 00:00
\ No newline at end of file diff --git a/extras/appengine/index.yaml b/extras/appengine/index.yaml index e69de29..7071349 100644 --- a/extras/appengine/index.yaml +++ b/extras/appengine/index.yaml @@ -0,0 +1,7 @@ +indexes: +- kind: __google_ExceptionRecord + properties: + - name: date + - name: major_version + - name: minor_version + direction: desc diff --git a/extras/appengine/main.py b/extras/appengine/main.py index 5014cb9..4cabba8 100644 --- a/extras/appengine/main.py +++ b/extras/appengine/main.py @@ -1,140 +1,41 @@ -# Copyright 2008 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Main program for Rietveld. - -This is also a template for running a Django app under Google App -Engine, especially when using a newer version of Django than provided -in the App Engine standard library. - -The site-specific code is all in other files: urls.py, models.py, -views.py, settings.py. -""" - -# Standard Python imports. +# SQLFormat's main script, dead simple :) + import os import sys + +from google.appengine.ext.webapp.util import run_wsgi_app + +LIB_DIR = os.path.join(os.path.dirname(__file__), 'lib') + +if LIB_DIR not in sys.path: + sys.path.insert(0, LIB_DIR) + +from sqlformat import app + +import config + import logging -import traceback - - -# Log a message each time this module get loaded. -logging.info('Loading %s, app version = %s', - __name__, os.getenv('CURRENT_VERSION_ID')) - -os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - -from google.appengine.dist import use_library -use_library('django', '1.2') - -# Fail early if we can't import Django. Log identifying information. -import django -logging.info('django.__file__ = %r, django.VERSION = %r', - django.__file__, django.VERSION) -assert django.VERSION[0] >= 1, "This Django version is too old" - -# AppEngine imports. -from google.appengine.ext.webapp import util -from google.appengine.api import mail - - -# Helper to enter the debugger. This passes in __stdin__ and -# __stdout__, because stdin and stdout are connected to the request -# and response streams. You must import this from __main__ to use it. -# (I tried to make it universally available via __builtin__, but that -# doesn't seem to work for some reason.) -def BREAKPOINT(): - import pdb - p = pdb.Pdb(None, sys.__stdin__, sys.__stdout__) - p.set_trace() - - -# Custom Django configuration. -from django.conf import settings -settings._target = None - -# Import various parts of Django. -import django.core.handlers.wsgi -import django.core.signals -import django.db -import django.dispatch.dispatcher -import django.forms - -# Work-around to avoid warning about django.newforms in djangoforms. -django.newforms = django.forms - - -def log_exception(*args, **kwds): - """Django signal handler to log an exception.""" - excinfo = sys.exc_info() - cls, err = excinfo[:2] - subject = 'Exception in request: %s: %s' % (cls.__name__, err) - logging.exception(subject) - try: - repr_request = repr(kwds.get('request', 'Request not available.')) - except: - repr_request = 'Request repr() not available.' - msg = ('Application: %s\nVersion: %s\n\n%s\n\n%s' - % (os.getenv('APPLICATION_ID'), os.getenv('CURRENT_VERSION_ID'), - '\n'.join(traceback.format_exception(*excinfo)), - repr_request)) - mail.send_mail_to_admins('albrecht.andi@googlemail.com', - '[%s] %s' % (os.getenv('APPLICATION_ID'), subject), - msg) - - -# Log all exceptions detected by Django. -django.core.signals.got_request_exception.connect(log_exception) - -# Unregister Django's default rollback event handler. -#django.core.signals.got_request_exception.disconnect( -# django.db._rollback_on_exception) - - -def real_main(): - """Main program.""" - # Create a Django application for WSGI. - application = django.core.handlers.wsgi.WSGIHandler() - # Run the WSGI CGI handler with that application. - util.run_wsgi_app(application) - - -def profile_main(): - """Main program for profiling.""" - import cProfile - import pstats - import StringIO - - prof = cProfile.Profile() - prof = prof.runctx('real_main()', globals(), locals()) - stream = StringIO.StringIO() - stats = pstats.Stats(prof, stream=stream) - # stats.strip_dirs() # Don't; too many modules are named __init__.py. - stats.sort_stats('time') # 'time', 'cumulative' or 'calls' - stats.print_stats() # Optional arg: how many to print - # The rest is optional. - # stats.print_callees() - # stats.print_callers() - print '\n<hr>' - print '<h1>Profile</h1>' - print '<pre>' - print stream.getvalue()[:1000000] - print '</pre>' - -# Set this to profile_main to enable profiling. -main = real_main - - -if __name__ == '__main__': - main() +from google.appengine.ext import ereporter + +ereporter.register_logger() + + +class EreporterMiddleware(object): + + def __init__(self, app): + self.app = app + + def __call__(self, environ, start_response): + try: + return self.app(environ, start_response) + except: + logging.exception('Exception in request:') + logging.debug(environ) + raise + + +app.config.from_object(config) + +app = EreporterMiddleware(app) + +run_wsgi_app(app) diff --git a/extras/appengine/sqlformat/__init__.py b/extras/appengine/sqlformat/__init__.py index e69de29..db9c132 100644 --- a/extras/appengine/sqlformat/__init__.py +++ b/extras/appengine/sqlformat/__init__.py @@ -0,0 +1,24 @@ +from flask import Flask, make_response + +from sqlformat.legacy import legacy + + +app = Flask('sqlformat') + + +@app.route('/ping') +def ping(): + return make_response('pong') + +@app.route('/_ah/warmup') +def warmup(): + return make_response('polishing chrome') + +@app.route('/fail') +def fail(): + # test URL for failure handling + raise AssertionError('You shouldn\'t be here!') + + +# Register legacy URLs last so that newer URLs replace them. +app.register_blueprint(legacy) diff --git a/extras/appengine/sqlformat/legacy.py b/extras/appengine/sqlformat/legacy.py new file mode 100644 index 0000000..bf137fb --- /dev/null +++ b/extras/appengine/sqlformat/legacy.py @@ -0,0 +1,147 @@ +"""Legacy URLs.""" + +import logging +import os +import time + +from google.appengine.api import memcache + +from flask import Blueprint, make_response, render_template, Response, request + +from pygments import highlight +from pygments.formatters import HtmlFormatter +from pygments.lexers import SqlLexer, PythonLexer, PhpLexer + +import simplejson as json + +import sqlparse + + +legacy = Blueprint('', 'legacy') + + +EXAMPLES_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../examples')) + + +@legacy.route('/', methods=['POST', 'GET']) +def index(): + data = {'examples': _get_examples()} + extra = {'highlight': True, 'comments': False, + 'keywords': 'upper', 'idcase': '', + 'n_indents': '2', + 'lang': 'sql'} + sql_orig = 'select * from foo join bar on val1 = val2 where id = 123;' + if request.method == 'POST': + oformat = request.form.get('format', 'html') + extra['highlight'] = 'highlight' in request.form + extra['comments'] = 'remove_comments' in request.form + extra['keywords'] = request.form.get('keyword_case', '') + extra['idcase'] = request.form.get('identifier_case', '') + extra['n_indents'] = request.form.get('n_indents', '2') + extra['lang'] = request.form.get('output_format', 'sql') + sql = _get_sql(request.form, request.files) + sql_orig = sql + start = time.time() + data['output'] = _format_sql(sql, request.form, format=oformat) + data['proc_time'] = '%.3f' % (time.time()-start) + if oformat == 'json': + data['errors'] = '' + return make_response(Response(json.dumps(data), + content_type='text/x-json')) + elif oformat == 'text': + return make_response(Response(data['output'], content_type='text/plain')) + data['sql_orig'] = sql_orig + data['extra'] = extra + return render_template('index.html', **data) + + +@legacy.route('/source/') +def source(): + return render_template('source.html') + + +@legacy.route('/about/') +def about(): + return render_template('about.html') + +@legacy.route('/api/') +def api(): + return render_template('api.html') + + +@legacy.route('/format/', methods=['GET', 'POST']) +def format_(): + if request.method == 'POST': + sql = _get_sql(request.form, request.files) + data = request.form + else: + sql = _get_sql(request.args) + data = request.args + formatted = _format_sql(sql, data, format='text') + return make_response(Response(formatted, content_type='text/plain')) + + +@legacy.route('/load_example', methods=['GET', 'POST']) +def load_example(): + fname = request.form.get('fname') + if fname is None: + answer = 'Uups, I\'ve got no filename...' + elif fname not in _get_examples(): + answer = 'Hmm, I think you don\'t want to do that.' + else: + answer = open(os.path.join(EXAMPLES_DIR, fname)).read() + data = json.dumps({'answer': answer}) + return make_response(Response(data, content_type='text/x-json')) + + +def _get_examples(): + examples = memcache.get('legacy_examples') + if examples is None: + examples = os.listdir(EXAMPLES_DIR) + memcache.set('legacy_examples', examples) + return examples + + +def _get_sql(data, files=None): + sql = None + if files is not None and 'datafile' in files: + sql = files['datafile'].read() + if not sql: + sql = data.get('data') + return sql or '' + + +def _format_sql(sql, data, format='html'): + popts = {} + if data.get('remove_comments'): + popts['strip_comments'] = True + if data.get('keyword_case', 'undefined') not in ('undefined', ''): + popts['keyword_case'] = data.get('keyword_case') + if data.get('identifier_case', 'undefined') not in ('undefined', ''): + popts['identifier_case'] = data.get('identifier_case') + if data.get('n_indents', None) is not None: + val = data.get('n_indents') + try: + popts['indent_width'] = max(1, min(1000, int(val))) + popts['reindent'] = True + except (ValueError, TypeError): + pass + if data.get('output_format', None) is not None: + popts['output_format'] = data.get('output_format') + logging.debug('Format: %s, POPTS: %r', format, popts) + logging.debug(sql) + sql = sqlparse.format(sql, **popts) + if format in ('html', 'json'): + if data.get('highlight', False): + if popts['output_format'] == 'python': + lexer = PythonLexer() + elif popts['output_format'] == 'php': + lexer = PhpLexer() + else: + lexer = SqlLexer() + sql = highlight(sql, lexer, HtmlFormatter()) + else: + sql = ('<textarea class="resizable" ' + 'style="height: 350px; margin-top: 1em;">%s</textarea>' + % sql) + return sql diff --git a/extras/appengine/sqlformat/templates/about.html b/extras/appengine/sqlformat/templates/about.html new file mode 100644 index 0000000..2d4e03e --- /dev/null +++ b/extras/appengine/sqlformat/templates/about.html @@ -0,0 +1,46 @@ +{% extends "master.html" %} + +{% block title %}About{% endblock %} + +{% block main %} +<h1>About this Application</h1> +<p> + This application is a online SQL formatting tool. +</p> +<p> + Basically it's a playground for a Python module to parse and format + SQL statements. Sometimes it's easier to combine the available + options and to see the resulting output using a web front-end than + on the command line ;-) +</p> +<p> + To get started, enter a SQL statement in the text box on the top, + choose some options and click on "Format SQL" (Ctrl+F) + to see the result. +</p> +<p> + <em>Note:</em> The SQL formatter and parser is in an early stage + of development. If you're looking for a mature tool, try one of + <a href="http://www.google.com/search?q=online+sql+formatter">these</a>. +</p> +<h2>Using it from the Command Line</h2> +<p> + There are three ways to use this SQL formatter from the command line: +</p> +<ol> + <li>Grab the <a href="/source/">sources</a> and use the module in your + Python scripts.</li> + <li> + Write a little script in your favorite language that sends a POST + request to this application.<br/> + Read the <a href="/api/">API Documentation</a> for more information. + </li> + <li>Use + <a href="/static/lynx_screenshot.png" + alt="Lynx Screenshot" target="_blank" + title="Screenshot: sqlformat.appspot.com on Lynx">Lynx + </a> + </li> +</ol> + +{% endblock %} diff --git a/extras/appengine/sqlformat/templates/api.html b/extras/appengine/sqlformat/templates/api.html new file mode 100644 index 0000000..79bf118 --- /dev/null +++ b/extras/appengine/sqlformat/templates/api.html @@ -0,0 +1,52 @@ +{% extends "master.html" %} + +{% block title %}API{% endblock %} + +{% block main %} +<h1>API Documentation</h1> + +<p> + Using the API for this application is pretty simple. Just send a + <code>POST</code> request to +</p> +<p> + <code>http://sqlformat.appspot.com/format/</code> +</p> + +<h2>Options</h2> +<p> + The <code>POST</code> request accepts various options to control + formatting. Only the <em>data</em> option is required. All others + are optional. +</p> + +<dl> + <dt>data</dt> + <dd>The SQL statement to format.</dd> + <dt>remove_comments</dt> + <dd>Set to 1 to remove comments.</dd> + <dt>keyword_case</dt> + <dd>How to convert keywords. Allowed values are 'lower', 'upper', + 'capitalize'.</dd> + <dt>identifier_case</dt> + <dd>How to convert identifiers. Allowed values are 'lower', 'upper', + 'capitalize'.</dd> + <dt>n_indents</dt> + <dd>An integer indicating the indendation depth.</dd> + <dt>right_margin</dt> + <dd>An integer indicating the maximum line length.</dd> + <dt>output_format</dt> + <dd>Transfer the statement into another programming language. + Allowed values are 'python', 'php'</dd> +</dl> + +<h2>Example</h2> +<p> + Here's a example in Python: +</p> +{% include "python-client-example.html" %} +<p> + <a href="/static/sqlformat_client_example.py">Download sqlformat_example_client.py</a> +</p> + +{% endblock %} diff --git a/extras/appengine/sqlformat/templates/index.html b/extras/appengine/sqlformat/templates/index.html new file mode 100644 index 0000000..22d6fdb --- /dev/null +++ b/extras/appengine/sqlformat/templates/index.html @@ -0,0 +1,120 @@ +{% extends "master.html" %} + +{% block main %} + +{% if output %} + <a href="#output" class="skip">Jump to formatted query</a> +{% endif %} + +<form method="post" action="" id="form_options" enctype="multipart/form-data"> + <div id="input"> + <div> + <strong>Type your SQL here:</strong><br /> + <textarea id="id_data" rows="10" cols="40" name="data" class="resizable">{{ sql_orig }}</textarea> + </div> + <div style="margin-top: .5em;"> + <strong>...or upload a file:</strong> + <input type="file" name="datafile" id="id_datafile" /> + </div> + <div id="examples" style="margin-top: .5em;"></div> + <div id="actions" style="margin-top: .5em;"> + <input type="submit" value="Format SQL" id="btn_format" /> + </div> + {% if output %}<a name="output"></a> + <div id="response">{{output|safe}}</div> + {% else %} + <div id="response"></div> + {% endif %} + <div id="proc_time"> + {% if proc_time %}Processed in {{proc_time}} seconds.{% endif %} + </div> + <div style="margin-top: 1em;"> + <script type="text/javascript"> + <!-- + google_ad_client = "pub-8870624642249726"; + /* 468x60, Erstellt 07.03.09 */ + google_ad_slot = "9840041509"; + google_ad_width = 468; + google_ad_height = 60; + //--> + </script> + <script type="text/javascript" + src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> + </script> + </div> + + </div> + <div id="options"> + <h1 class="skip">Options</h1> + <fieldset><legend id="general"><strong>General Options</strong></legend> + <div id="general_content" class="content"> + <input type="checkbox" id="id_remove_comments" name="remove_comments" value="1" {% if extra.comments %}checked="checked"{% endif %}/> + <label for="id_remove_comments">Remove comments</label> + <br /> + <input type="checkbox" id="id_highlight" name="highlight" value="1" {% if extra.highlight %}checked="checked"{% endif %} /> + <label for="id_highlight">Enable syntax highlighting</label> + </div> + </fieldset> + <fieldset><legend id="kwcase"> + <strong>Keywords & Identifiers</strong></legend> + <div> + Keywords: <select id="id_keyword_case" name="keyword_case"> + <option value="">Unchanged</option> + <option value="lower" {% if extra.keywords == 'lower' %}selected="selected"{% endif %}>Lower case</option> + <option value="upper" {% if extra.keywords == 'upper' %}selected="selected"{% endif %}>Upper case</option> + <option value="capitalize" {% if extra.keywords == 'capitalize' %}selected="selected"{% endif %}>Capitalize</option> + </select> + </div> + <div> + Identifiers: <select name="identifier_case" id="id_identifier_case"> + <option value="">Unchanged</option> + <option value="lower" {% if extra.idcase == 'lower' %}selected="selected"{% endif %}>Lower case</option> + <option value="upper" {% if extra.idcase == 'upper' %}selected="selected"{% endif %}>Upper case</option> + <option value="capitalize" {% if extra.idcase == 'capitalize' %}selected="selected"{% endif %}>Capitalize</option> + </select> + </div> + </fieldset> + <fieldset><legend id="indent"><strong>Indentation & Margins</strong> + </legend> + <div id="indent_content" class="content"> + <label for="id_n_indents">Indentation: </label> + <input name="n_indents" value="{{extra.n_indents}}" maxlength="2" type="text" id="id_n_indents" size="2" /> spaces + <div class="help">Empty field means leave indentation unchanged.</div> + </div> + </fieldset> + <fieldset><legend id="output"><strong>Output Format</strong></legend> + <label for="id_output_format">Language: </label> + <select name="output_format" id="id_output_format"> + <option value="sql" {% if extra.lang == 'sql' %}selected="selected"{% endif %}>SQL</option> + <option value="python" {% if extra.lang == 'python' %}selected="selected"{% endif %}>Python</option> + <option value="php" {% if extra.lang == 'php' %}selected="selected"{% endif %}>PHP</option> + </select> + </fieldset> + + <div class="dev">This software is in development.</div> + + <div> + <g:plusone size="medium"></g:plusone> + <a href="http://flattr.com/thing/350724/SQLFormat-Online-SQL-formatting-service" target="_blank"> + <img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /> + </a> + </div> + <div style="padding-top: 15px;"> + <a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" data-via="andialbrecht">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script> + </div> + </div> + <div class="clearfix"></div> +</form> + +<script language="javascript"> +html = '<strong>...or select an example:</strong> '; +html = html + '<select onchange="load_example();" id="sel_example">'; +html = html + '<option value="">-- Choose Example --</option>'; +{% for ex in examples %} + html = html + '<option value="{{ex}}">{{ex}}</option>'; +{% endfor %} +html = html + '</select>'; +$('#examples').html(html); +</script> +{% endblock %} + diff --git a/extras/appengine/sqlformat/templates/master.html b/extras/appengine/sqlformat/templates/master.html new file mode 100644 index 0000000..88a9d36 --- /dev/null +++ b/extras/appengine/sqlformat/templates/master.html @@ -0,0 +1,103 @@ +<html> + <head> + <title>{% block title %}SQLFormat - Online SQL Formatter{% endblock %}</title> + <meta name="keywords" content="SQL, format, parse, python, beautify, pretty, online, formatting, formatter" /> + <meta name="description" content="Easy to use web service to format SQL statements." /> + <link rel="stylesheet" href="/static/pygments.css" /> + <link rel="stylesheet" href="/static/styles.css" /> + <script src="http://www.google.com/jsapi"></script> + <script> + google.load("jquery", "1.2.6"); + </script> + <script src="/static/hotkeys.js"></script> + <script src="/static/script.js"></script> + </head> + <body> + + <div id="help"> + <p>Keyboard Shortcuts</p> + <p> + <span class="shortcut">H</span> - Show / hide this help window<br/> + <span class="shortcut">Ctrl+F</span> - Format SQL and display result<br/> + <span class="shortcut">O</span> - Show / hide options<br/> + <span class="shortcut">T</span> - Set focus on SQL input<br/> + </p> + </div> + + <div id="header"> + <div id="header-inner"> + {% if user %}<img src="{{userimg}}" border="0" align="right" style="padding-top:.4em;"/>{% endif %} + <h1> + <a href="/"> + <span class="q">S<span class="q2">Q</span>L</span>Format + </a> + </h1> + <div id="slogan">Online SQL formatting service</div> + <div id="topmenu"> + <a href="/">Home</a> + | + <a href="/about/">About</a> + | + <a href="/source/">Source Code</a> + | + <a href="/api/">API</a> +<!-- + | + {% if user %} + <a href="{{logout_url}}">Sign out</a> + {% else %} + <a href="{{login_url}}">Sign in</a> + <span style="color: #babdb6;">with your Google account + to save preferred settings.</span> + {% endif %} +--> + </div> + </div> + </div> + + <div id="main"> + <div id="main-inner"> + {% block main %}MAIN CONTENT GOES HERE{% endblock %} + </div> + </div> + + <div id="footer"> + <div id="footer-inner"> + <div style="float: left; font-size: .85em;"> + <div>© 2011 Andi Albrecht + <code><albrecht dot andi gmail></code> + </div> + <div> + <a href="/">Home</a> + | + <a href="/about/">About</a> + | + <a href="/source/">Source Code</a> + | + <a href="/api/">API</a> + | + <a href="http://andialbrecht.wordpress.com/">Blog</a> + </div> + </div> + <div style="float: right;"> + <img src="http://code.google.com/appengine/images/appengine-silver-120x30.gif" + alt="Powered by Google App Engine" /> + </div> + <div class="clearfix"></div> + </div> + </div> + + <script type="text/javascript"> + var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); + document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); + </script> + <script type="text/javascript"> + try { + var pageTracker = _gat._getTracker("UA-3535525-2"); + pageTracker._trackPageview(); + } catch(err) {}</script> + <script>init();</script> + <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> + + </body> +</html> diff --git a/extras/appengine/sqlformat/templates/python-client-example.html b/extras/appengine/sqlformat/templates/python-client-example.html new file mode 100644 index 0000000..68bf820 --- /dev/null +++ b/extras/appengine/sqlformat/templates/python-client-example.html @@ -0,0 +1,17 @@ +<div class="highlight example"><pre><span class="c">#!/usr/bin/env python</span> + +<span class="k">import</span> <span class="nn">urllib</span> +<span class="k">import</span> <span class="nn">urllib2</span> + +<span class="n">payload</span> <span class="o">=</span> <span class="p">(</span> + <span class="p">(</span><span class="s">'data'</span><span class="p">,</span> <span class="s">'select * from foo join bar on val1 = val2 where id = 123;'</span><span class="p">),</span> + <span class="p">(</span><span class="s">'format'</span><span class="p">,</span> <span class="s">'text'</span><span class="p">),</span> + <span class="p">(</span><span class="s">'keyword_case'</span><span class="p">,</span> <span class="s">'upper'</span><span class="p">),</span> + <span class="p">(</span><span class="s">'reindent'</span><span class="p">,</span> <span class="bp">True</span><span class="p">),</span> + <span class="p">(</span><span class="s">'n_indents'</span><span class="p">,</span> <span class="mf">2</span><span class="p">),</span> + <span class="p">)</span> + +<span class="n">response</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="s">'http://sqlformat.appspot.com/format/'</span><span class="p">,</span> + <span class="n">urllib</span><span class="o">.</span><span class="n">urlencode</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span> +<span class="k">print</span> <span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> +</pre></div> diff --git a/extras/appengine/sqlformat/templates/source.html b/extras/appengine/sqlformat/templates/source.html new file mode 100644 index 0000000..a0ed89d --- /dev/null +++ b/extras/appengine/sqlformat/templates/source.html @@ -0,0 +1,60 @@ +{% extends "master.html" %} + +{% block title %}Source code{% endblock %} + +{% block main %} +<div id="response"> + <h1>Source Code</h1> + + <h2>Python Module</h2> + <p> + The sources for the SQL parser and formatter module are + hosted on Google Code. + To clone the repository run: + <p> + <code class="pre"> hg clone http://python-sqlparse.googlecode.com/hg/ python-sqlparse</code> + </p> + <p> + <a href="http://python-sqlparse.googlecode.com">Visit the project page</a> + | + <a href="http://code.google.com/p/python-sqlparse/source/browse/">Browse the sources online</a> + | + <a href="http://python-sqlparse.googlecode.com/svn/docs/api/index.html"> API Documentation</a> + </p> + <p> + Some relevant parts of the Python module contain code from the + <a href="http://pygments.org/">pygments</a> syntax highlighter. + The underlying Python module uses a non-validating SQL parser. + This approach makes it possible to parse even syntactically incorrect + SQL statements. + </p> + + <p> + Currently the parser module is used by + <a href="http://crunchyfrog.googlecode.com/">CrunchyFrog</a> - a + database front-end for Gnome. + </p> + + <p> + The <code>sqlparse</code> module is released under the terms of the + <a href="http://www.opensource.org/licenses/bsd-license.php">New BSD License</a>. + </p> + + <h2>App Engine Application</h2> + <p> + The source code for this App Engine application is available in the + <code>examples</code> directory of the Python module + (but it's really nothing special ;-). + </p> + + <h2>Contributing</h2> + <p> + Please file bug reports and feature requests on the project site at + <a href="http://code.google.com/p/python-sqlparse/issues/entry">http://code.google.com/p/python-sqlparse/issues/entry</a> + or if you have code to contribute upload it to + <a href="http://codereview.appspot.com">http://codereview.appspot.com</a> + and add albrecht.andi@googlemail.com as reviewer. + </p> + +</div> +{% endblock %} diff --git a/extras/appengine/static/favicon.ico b/extras/appengine/static/favicon.ico Binary files differnew file mode 100644 index 0000000..1372520 --- /dev/null +++ b/extras/appengine/static/favicon.ico diff --git a/extras/appengine/static/script.js b/extras/appengine/static/script.js index 71bbabb..8bdf271 100644 --- a/extras/appengine/static/script.js +++ b/extras/appengine/static/script.js @@ -11,7 +11,6 @@ function update_output() { data.keyword_case = $('#id_keyword_case').val(); data.identifier_case = $('#id_identifier_case').val(); data.n_indents = $('#id_n_indents').val(); - data.right_margin = $('#id_right_margin').val(); data.output_format = $('#id_output_format').val(); form = document.getElementById('form_options'); $(form.elements).attr('disabled', 'disabled'); @@ -72,7 +71,7 @@ function load_example() { fname = $('#sel_example').val(); data = {fname: fname}; $.post('/load_example', data, - function(data) { + function(data) { $('#id_data').val(data.answer); }, 'json'); } @@ -96,8 +95,4 @@ function init() { $(document).bind('keydown', {combi: 't', disableInInput: true}, textarea_grab_focus); initialized = true; - /* jQuery textarea resizer plugin usage */ - $(document).ready(function() { - $('textarea.resizable:not(.processed)').TextAreaResizer(); - }); }
\ No newline at end of file diff --git a/extras/appengine/static/sqlformat_client_example.py b/extras/appengine/static/sqlformat_client_example.py index eec17b9..8b2a9e9 100644 --- a/extras/appengine/static/sqlformat_client_example.py +++ b/extras/appengine/static/sqlformat_client_example.py @@ -13,6 +13,7 @@ payload = ( ('n_indents', 2), ) + response = urllib2.urlopen(REMOTE_API, urllib.urlencode(payload)) print response.read() |
