diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..5b63bda --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +recursive-include pyalchemybiz/public * +recursive-include pyalchemybiz/templates * diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..6718930 --- /dev/null +++ b/README.txt @@ -0,0 +1,19 @@ +This file is for you to describe the pyalchemybiz application. Typically +you would include information such as the information below: + +Installation and Setup +====================== + +Install ``pyalchemybiz`` using easy_install:: + + easy_install pyalchemybiz + +Make a config file as follows:: + + paster make-config pyalchemybiz config.ini + +Tweak the config file as appropriate and then setup the application:: + + paster setup-app config.ini + +Then you are ready to go. diff --git a/data/templates/base.mako.py b/data/templates/base.mako.py new file mode 100644 index 0000000..0349a7a --- /dev/null +++ b/data/templates/base.mako.py @@ -0,0 +1,45 @@ +from mako import runtime, filters, cache +UNDEFINED = runtime.UNDEFINED +__M_dict_builtin = dict +__M_locals_builtin = locals +_magic_number = 4 +_modified_time = 1223071301.6449161 +_template_filename=u'/home/jan/src/pyalchemybiz/pyalchemybiz/templates/base.mako' +_template_uri=u'/base.mako' +_template_cache=cache.Cache(__name__, _modified_time) +_source_encoding=None +_exports = [] + + +def render_body(context,**pageargs): + context.caller_stack._push_frame() + try: + __M_locals = __M_dict_builtin(pageargs=pageargs) + h = context.get('h', UNDEFINED) + next = context.get('next', UNDEFINED) + __M_writer = context.writer() + # SOURCE LINE 1 + __M_writer(u'\n\n \n PyAlchemyBiz\n ') + # SOURCE LINE 6 + __M_writer(unicode(h.stylesheet_link_tag('/pyalchemybiz.css'))) + __M_writer(u'\n ') + # SOURCE LINE 7 + __M_writer(unicode(h.javascript_include_tag( + '/javascripts/pyalchemybiz.js', builtins=True))) + # SOURCE LINE 8 + __M_writer(u'\n \n \n
\n ') + # SOURCE LINE 12 + __M_writer(unicode(next.body())) + __M_writer(u'') + # SOURCE LINE 13 + __M_writer(u' \n
\n \n\n') + return '' + finally: + context.caller_stack._pop_frame() + + diff --git a/data/templates/customer.mako.py b/data/templates/customer.mako.py new file mode 100644 index 0000000..a7cc270 --- /dev/null +++ b/data/templates/customer.mako.py @@ -0,0 +1,50 @@ +from mako import runtime, filters, cache +UNDEFINED = runtime.UNDEFINED +__M_dict_builtin = dict +__M_locals_builtin = locals +_magic_number = 4 +_modified_time = 1223072023.421654 +_template_filename='/home/jan/src/pyalchemybiz/pyalchemybiz/templates/customer.mako' +_template_uri='/customer.mako' +_template_cache=cache.Cache(__name__, _modified_time) +_source_encoding=None +_exports = [] + + +def _mako_get_namespace(context, name): + try: + return context.namespaces[(__name__, name)] + except KeyError: + _mako_generate_namespaces(context) + return context.namespaces[(__name__, name)] +def _mako_generate_namespaces(context): + pass +def _mako_inherit(template, context): + _mako_generate_namespaces(context) + return runtime._inherit_from(context, u'base.mako', _template_uri) +def render_body(context,**pageargs): + context.caller_stack._push_frame() + try: + __M_locals = __M_dict_builtin(pageargs=pageargs) + h = context.get('h', UNDEFINED) + c = context.get('c', UNDEFINED) + __M_writer = context.writer() + # SOURCE LINE 1 + __M_writer(u'\n\n

Hallo

\n\n\n') + return '' + finally: + context.caller_stack._pop_frame() + + diff --git a/development.ini b/development.ini new file mode 100644 index 0000000..4e8ce6a --- /dev/null +++ b/development.ini @@ -0,0 +1,70 @@ +# +# pyalchemybiz - Pylons development environment configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +[DEFAULT] +debug = true +# Uncomment and replace with the address which should receive any error reports +#email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 5000 + +[app:main] +use = egg:pyalchemybiz +full_stack = true +cache_dir = %(here)s/data +beaker.session.key = pyalchemybiz +beaker.session.secret = somesecret + +# If you'd like to fine-tune the individual locations of the cache data dirs +# for the Cache data, or the Session saves, un-comment the desired settings +# here: +#beaker.cache.data_dir = %(here)s/data/cache +#beaker.session.data_dir = %(here)s/data/sessions + +# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* +# Debug mode will enable the interactive debugging tool, allowing ANYONE to +# execute malicious code after an exception is raised. +#set debug = false + +# Specify the database for SQLAlchemy to use. +# %(here)s may include a ':' character on Windows environments; this can +# invalidate the URI when specifying a SQLite db via path name +sqlalchemy.default.url = sqlite:///%(here)s/pyalchemybiz.db +sqlalchemy.default.echo = true + + +# Logging configuration +[loggers] +keys = root, pyalchemybiz + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_pyalchemybiz] +level = DEBUG +handlers = +qualname = pyalchemybiz + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/docs/index.txt b/docs/index.txt new file mode 100644 index 0000000..b1e5533 --- /dev/null +++ b/docs/index.txt @@ -0,0 +1,12 @@ +pyalchemybiz +++++++++++++ + +This is the main index page of your documentation. It should be written in +`reStructuredText format `_. + +You can generate your documentation in HTML format by running this command:: + + setup.py pudge + +For this to work you will need to download and install ``buildutils`` and +``pudge``. diff --git a/pyalchemybiz.egg-info/PKG-INFO b/pyalchemybiz.egg-info/PKG-INFO new file mode 100644 index 0000000..ad177e1 --- /dev/null +++ b/pyalchemybiz.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: pyalchemybiz +Version: 0.0.0dev +Summary: UNKNOWN +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/pyalchemybiz.egg-info/SOURCES.txt b/pyalchemybiz.egg-info/SOURCES.txt new file mode 100644 index 0000000..6be95e1 --- /dev/null +++ b/pyalchemybiz.egg-info/SOURCES.txt @@ -0,0 +1,29 @@ +MANIFEST.in +README.txt +setup.cfg +setup.py +pyalchemybiz/__init__.py +pyalchemybiz/websetup.py +pyalchemybiz.egg-info/PKG-INFO +pyalchemybiz.egg-info/SOURCES.txt +pyalchemybiz.egg-info/dependency_links.txt +pyalchemybiz.egg-info/entry_points.txt +pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl +pyalchemybiz.egg-info/requires.txt +pyalchemybiz.egg-info/top_level.txt +pyalchemybiz/config/__init__.py +pyalchemybiz/config/environment.py +pyalchemybiz/config/middleware.py +pyalchemybiz/config/routing.py +pyalchemybiz/controllers/__init__.py +pyalchemybiz/controllers/error.py +pyalchemybiz/controllers/template.py +pyalchemybiz/lib/__init__.py +pyalchemybiz/lib/app_globals.py +pyalchemybiz/lib/base.py +pyalchemybiz/lib/helpers.py +pyalchemybiz/model/__init__.py +pyalchemybiz/public/index.html +pyalchemybiz/tests/__init__.py +pyalchemybiz/tests/test_models.py +pyalchemybiz/tests/functional/__init__.py \ No newline at end of file diff --git a/pyalchemybiz.egg-info/dependency_links.txt b/pyalchemybiz.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyalchemybiz.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pyalchemybiz.egg-info/entry_points.txt b/pyalchemybiz.egg-info/entry_points.txt new file mode 100644 index 0000000..416807a --- /dev/null +++ b/pyalchemybiz.egg-info/entry_points.txt @@ -0,0 +1,7 @@ + + [paste.app_factory] + main = pyalchemybiz.config.middleware:make_app + + [paste.app_install] + main = pylons.util:PylonsInstaller + \ No newline at end of file diff --git a/pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl b/pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl new file mode 100644 index 0000000..e5458c4 --- /dev/null +++ b/pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl @@ -0,0 +1,64 @@ +# +# pyalchemybiz - Pylons configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +[DEFAULT] +debug = true +email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 5000 + +[app:main] +use = egg:pyalchemybiz +full_stack = true +cache_dir = %(here)s/data +beaker.session.key = pyalchemybiz +beaker.session.secret = ${app_instance_secret} +app_instance_uuid = ${app_instance_uuid} + +# If you'd like to fine-tune the individual locations of the cache data dirs +# for the Cache data, or the Session saves, un-comment the desired settings +# here: +#beaker.cache.data_dir = %(here)s/data/cache +#beaker.session.data_dir = %(here)s/data/sessions + +# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* +# Debug mode will enable the interactive debugging tool, allowing ANYONE to +# execute malicious code after an exception is raised. +set debug = false + +# Specify the database for SQLAlchemy to use. +# %(here)s may include a ':' character on Windows environments; this can +# invalidate the URI when specifying a SQLite db via path name +#sqlalchemy.default.url = sqlite:///%(here)s/pyalchemybiz.db +#sqlalchemy.default.echo = false + + +# Logging configuration +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s diff --git a/pyalchemybiz.egg-info/paster_plugins.txt b/pyalchemybiz.egg-info/paster_plugins.txt new file mode 100644 index 0000000..435fb65 --- /dev/null +++ b/pyalchemybiz.egg-info/paster_plugins.txt @@ -0,0 +1,2 @@ +Pylons +WebHelpers diff --git a/pyalchemybiz.egg-info/requires.txt b/pyalchemybiz.egg-info/requires.txt new file mode 100644 index 0000000..fe9f093 --- /dev/null +++ b/pyalchemybiz.egg-info/requires.txt @@ -0,0 +1 @@ +Pylons>=0.9.6.2 \ No newline at end of file diff --git a/pyalchemybiz.egg-info/top_level.txt b/pyalchemybiz.egg-info/top_level.txt new file mode 100644 index 0000000..2bc407b --- /dev/null +++ b/pyalchemybiz.egg-info/top_level.txt @@ -0,0 +1 @@ +pyalchemybiz diff --git a/pyalchemybiz/__init__.py b/pyalchemybiz/__init__.py index 8918ae1..e69de29 100644 --- a/pyalchemybiz/__init__.py +++ b/pyalchemybiz/__init__.py @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Package pyalchemybiz -# -# Author: Jan Dittberner -# Version: $Id$ -# -# This file is part of pyalchemybiz diff --git a/pyalchemybiz/config/__init__.py b/pyalchemybiz/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyalchemybiz/config/environment.py b/pyalchemybiz/config/environment.py new file mode 100644 index 0000000..9658bd6 --- /dev/null +++ b/pyalchemybiz/config/environment.py @@ -0,0 +1,38 @@ +"""Pylons environment configuration""" +import os + +from pylons import config +from sqlalchemy import engine_from_config +from pyalchemybiz.model import init_model + +import pyalchemybiz.lib.app_globals as app_globals +import pyalchemybiz.lib.helpers +from pyalchemybiz.config.routing import make_map + +def load_environment(global_conf, app_conf): + """Configure the Pylons environment via the ``pylons.config`` + object + """ + # Pylons paths + root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + paths = dict(root=root, + controllers=os.path.join(root, 'controllers'), + static_files=os.path.join(root, 'public'), + templates=[os.path.join(root, 'templates')]) + + # Initialize config with the basic options + config.init_app(global_conf, app_conf, package='pyalchemybiz', + template_engine='mako', paths=paths) + + config['routes.map'] = make_map() + config['pylons.g'] = app_globals.Globals() + config['pylons.h'] = pyalchemybiz.lib.helpers + + # Customize templating options via this variable + tmpl_options = config['buffet.template_options'] + + # CONFIGURATION OPTIONS HERE (note: all config options will override + # any Pylons config options) + engine = \ + engine_from_config(config, 'sqlalchemy.default.') + init_model(engine) diff --git a/pyalchemybiz/config/middleware.py b/pyalchemybiz/config/middleware.py new file mode 100644 index 0000000..84ed761 --- /dev/null +++ b/pyalchemybiz/config/middleware.py @@ -0,0 +1,57 @@ +"""Pylons middleware initialization""" +from paste.cascade import Cascade +from paste.registry import RegistryManager +from paste.urlparser import StaticURLParser +from paste.deploy.converters import asbool + +from pylons import config +from pylons.error import error_template +from pylons.middleware import error_mapper, ErrorDocuments, ErrorHandler, \ + StaticJavascripts +from pylons.wsgiapp import PylonsApp + +from pyalchemybiz.config.environment import load_environment + +def make_app(global_conf, full_stack=True, **app_conf): + """Create a Pylons WSGI application and return it + + ``global_conf`` + The inherited configuration for this application. Normally from + the [DEFAULT] section of the Paste ini file. + + ``full_stack`` + Whether or not this application provides a full WSGI stack (by + default, meaning it handles its own exceptions and errors). + Disable full_stack when this application is "managed" by + another WSGI middleware. + + ``app_conf`` + The application's local configuration. Normally specified in the + [app:] section of the Paste ini file (where + defaults to main). + """ + # Configure the Pylons environment + load_environment(global_conf, app_conf) + + # The Pylons WSGI app + app = PylonsApp() + + # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) + + if asbool(full_stack): + # Handle Python exceptions + app = ErrorHandler(app, global_conf, error_template=error_template, + **config['pylons.errorware']) + + # Display error documents for 401, 403, 404 status codes (and + # 500 when debug is disabled) + app = ErrorDocuments(app, global_conf, mapper=error_mapper, **app_conf) + + # Establish the Registry for this application + app = RegistryManager(app) + + # Static files + javascripts_app = StaticJavascripts() + static_app = StaticURLParser(config['pylons.paths']['static_files']) + app = Cascade([static_app, javascripts_app, app]) + return app diff --git a/pyalchemybiz/config/routing.py b/pyalchemybiz/config/routing.py new file mode 100644 index 0000000..af711a9 --- /dev/null +++ b/pyalchemybiz/config/routing.py @@ -0,0 +1,24 @@ +"""Routes configuration + +The more specific and detailed routes should be defined first so they +may take precedent over the more generic routes. For more information +refer to the routes manual at http://routes.groovie.org/docs/ +""" +from pylons import config +from routes import Mapper + +def make_map(): + """Create, configure and return the routes Mapper""" + map = Mapper(directory=config['pylons.paths']['controllers'], + always_scan=config['debug']) + + # The ErrorController route (handles 404/500 error pages); it should + # likely stay at the top, ensuring it can always be resolved + map.connect('error/:action/:id', controller='error') + + # CUSTOM ROUTES HERE + + map.connect(':controller/:action/:id') + map.connect('*url', controller='template', action='view') + + return map diff --git a/pyalchemybiz/controllers/__init__.py b/pyalchemybiz/controllers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyalchemybiz/controllers/customer.py b/pyalchemybiz/controllers/customer.py new file mode 100644 index 0000000..25550f7 --- /dev/null +++ b/pyalchemybiz/controllers/customer.py @@ -0,0 +1,20 @@ +import logging + +from pyalchemybiz.lib.base import * +from pyalchemybiz.model import customer, meta + +log = logging.getLogger(__name__) + +class CustomerController(BaseController): + + def index(self): + sess = meta.Session() + cust_q = sess.query(customer.Customer) + c.customers = cust_q.all() + return render('/customer.mako') + + def edit(self, id): + sess = meta.Session() + cust_q = sess.query(customer.Customer) + c.customer = cust_q.filter(customer.Customer.id==id).one() + return c.customer diff --git a/pyalchemybiz/controllers/error.py b/pyalchemybiz/controllers/error.py new file mode 100644 index 0000000..665ac31 --- /dev/null +++ b/pyalchemybiz/controllers/error.py @@ -0,0 +1,41 @@ +import cgi +import os.path + +from paste.urlparser import StaticURLParser +from pylons.middleware import error_document_template, media_path + +from pyalchemybiz.lib.base import * + +class ErrorController(BaseController): + """Generates error documents as and when they are required. + + The ErrorDocuments middleware forwards to ErrorController when error + related status codes are returned from the application. + + This behaviour can be altered by changing the parameters to the + ErrorDocuments middleware in your config/middleware.py file. + + """ + def document(self): + """Render the error document""" + page = error_document_template % \ + dict(prefix=request.environ.get('SCRIPT_NAME', ''), + code=cgi.escape(request.params.get('code', '')), + message=cgi.escape(request.params.get('message', ''))) + return page + + def img(self, id): + """Serve Pylons' stock images""" + return self._serve_file(os.path.join(media_path, 'img'), id) + + def style(self, id): + """Serve Pylons' stock stylesheets""" + return self._serve_file(os.path.join(media_path, 'style'), id) + + def _serve_file(self, root, path): + """Call Paste's FileApp (a WSGI application) to serve the file + at the specified path + """ + static = StaticURLParser(root) + request.environ['PATH_INFO'] = '/%s' % path + return static(request.environ, self.start_response) diff --git a/pyalchemybiz/controllers/template.py b/pyalchemybiz/controllers/template.py new file mode 100644 index 0000000..740491b --- /dev/null +++ b/pyalchemybiz/controllers/template.py @@ -0,0 +1,27 @@ +from pyalchemybiz.lib.base import * + +class TemplateController(BaseController): + + def view(self, url): + """By default, the final controller tried to fulfill the request + when no other routes match. It may be used to display a template + when all else fails, e.g.:: + + def view(self, url): + return render('/%s' % url) + + Or if you're using Mako and want to explicitly send a 404 (Not + Found) response code when the requested template doesn't exist:: + + import mako.exceptions + + def view(self, url): + try: + return render('/%s' % url) + except mako.exceptions.TopLevelLookupException: + abort(404) + + By default this controller aborts the request with a 404 (Not + Found) + """ + abort(404) diff --git a/pyalchemybiz/lib/__init__.py b/pyalchemybiz/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyalchemybiz/lib/app_globals.py b/pyalchemybiz/lib/app_globals.py new file mode 100644 index 0000000..7f7585f --- /dev/null +++ b/pyalchemybiz/lib/app_globals.py @@ -0,0 +1,14 @@ +"""The application's Globals object""" +from pylons import config + +class Globals(object): + """Globals acts as a container for objects available throughout the + life of the application + """ + + def __init__(self): + """One instance of Globals is created during application + initialization and is available during requests via the 'g' + variable + """ + pass diff --git a/pyalchemybiz/lib/base.py b/pyalchemybiz/lib/base.py new file mode 100644 index 0000000..1b923c0 --- /dev/null +++ b/pyalchemybiz/lib/base.py @@ -0,0 +1,36 @@ +"""The base Controller API + +Provides the BaseController class for subclassing, and other objects +utilized by Controllers. +""" +from pylons import c, cache, config, g, request, response, session +from pylons.controllers import WSGIController +from pylons.controllers.util import abort, etag_cache, redirect_to +from pylons.decorators import jsonify, validate +from pylons.i18n import _, ungettext, N_ +from pylons.templating import render + +import pyalchemybiz.lib.helpers as h +import pyalchemybiz.model as model + +from pyalchemybiz.model import meta +from sqlalchemy.orm import sessionmaker + +class BaseController(WSGIController): + + def __call__(self, environ, start_response): + """Invoke the Controller""" + # WSGIController.__call__ dispatches to the Controller method + # the request is routed to. This routing information is + # available in environ['pylons.routes_dict'] + conn = meta.engine.connect() + meta.Session.configure(bind=conn) + try: + return WSGIController.__call__(self, environ, start_response) + finally: + meta.Session.remove() + conn.close() + +# Include the '_' function in the public names +__all__ = [__name for __name in locals().keys() if not __name.startswith('_') \ + or __name == '_'] diff --git a/pyalchemybiz/lib/helpers.py b/pyalchemybiz/lib/helpers.py new file mode 100644 index 0000000..25a63d1 --- /dev/null +++ b/pyalchemybiz/lib/helpers.py @@ -0,0 +1,7 @@ +"""Helper functions + +Consists of functions to typically be used within templates, but also +available to Controllers. This module is available to both as 'h'. +""" +from webhelpers.rails.wrapped import * +from routes import url_for, redirect_to diff --git a/pyalchemybiz/model/__init__.py b/pyalchemybiz/model/__init__.py new file mode 100644 index 0000000..b4c8fe7 --- /dev/null +++ b/pyalchemybiz/model/__init__.py @@ -0,0 +1,17 @@ +import sqlalchemy as sa +from sqlalchemy import orm + +from pyalchemybiz.model import meta +from pyalchemybiz.model import customer + +def init_model(engine): + """Call me before using any of the tables or classes in the model.""" + + sm = orm.sessionmaker(autoflush=True, transactional=True, bind=engine) + + meta.engine = engine + meta.Session = orm.scoped_session(sm) + + customer.t_customer = sa.Table('customer', meta.metadata, + autoload=True, autoload_with=engine) + orm.mapper(customer.Customer, customer.t_customer) diff --git a/pyalchemybiz/model/customer.py b/pyalchemybiz/model/customer.py new file mode 100644 index 0000000..5fe8321 --- /dev/null +++ b/pyalchemybiz/model/customer.py @@ -0,0 +1,5 @@ +t_customer = None + +class Customer(object): + def __str__(self): + return "%s %s" % (self.firstname, self.lastname) diff --git a/pyalchemybiz/model/meta.py b/pyalchemybiz/model/meta.py new file mode 100644 index 0000000..578584a --- /dev/null +++ b/pyalchemybiz/model/meta.py @@ -0,0 +1,14 @@ +"""SQLAlchemy Metadata and Session object""" +from sqlalchemy import MetaData + +__all__ = ['engine', 'metadata', 'Session'] + +# SQLAlchemy database engine. Updated by model.init_model(). +engine = None + +# SQLAlchemy session manager. Updated by model.init_model(). +Session = None + +# Global metadata. If you have multiple databases with overlapping table +# names, you'll need a metadata for each database. +metadata = MetaData() diff --git a/pyalchemybiz/productdb.py b/pyalchemybiz/productdb.py deleted file mode 100644 index 50d5016..0000000 --- a/pyalchemybiz/productdb.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Package pyalchemybiz -# -# Author: Jan Dittberner -# Version: $Id$ -# -# This file is part of pyalchemybiz -from sqlalchemy import * - -def create_schema(metadata): - """Create the database schema for products.""" - product_table = Table( - 'product', metadata, - Column('product_id', Integer, primary_key=True), - Column('name', String(100), nullable=False), - Column('description', String(4096), nullable=False), - Column('product_type', Integer, nullable=False) - ) - product_table.create() diff --git a/pyalchemybiz/public/index.html b/pyalchemybiz/public/index.html new file mode 100644 index 0000000..98183c1 --- /dev/null +++ b/pyalchemybiz/public/index.html @@ -0,0 +1,108 @@ + + + + Pylons Default Page + + + + +

Welcome to your Pylons Web Application

+ +

Weren't expecting to see this page?

+ +

The pyalchemybiz/public/ directory is searched for static files + before your controllers are run. Remove this file (pyalchemybiz/public/index.html) + and edit the routes in pyalchemybiz/config/routing.py to point the + root path to a 'hello' controller we'll create below: +

 map.connect('', controller='hello', action='index')
+

+ +

Getting Started

+

You're now ready to start creating your own web application. To create a 'hello' controller, + run the following command in your project's root directory: +

+pyalchemybiz$ paster controller hello
+
+ + This generates the following the following code in pyalchemybiz/controllers/hello.py: +
+import logging
+
+from pyalchemybiz.lib.base import *
+
+log = logging.getLogger(__name__)
+
+class HelloController(BaseController):
+
+    def index(self):
+        # Return a rendered template
+        #   return render('/some/template.mako)
+        # or, Return a response
+        return 'Hello World'
+
+

+

This controller simply prints out 'Hello World' to the browser. Pylons' default routes + automatically set up this controller to respond at the /hello URL. + With the additional route described above, this controller will also respond at the + root path. +

+ +

Using a template

+

To call a template and do something a little more complex, this following example + shows how to print out some request information from a + Mako template. +

+

Create a serverinfo.mako file in your project's pyalchemybiz/templates/ + directory with the following contents: +

+
+<h2>
+Server info for ${request.host}
+</h2>
+
+<p>
+The URL you called: ${h.url_for()}
+</p>
+
+<p>
+The name you set: ${c.name}
+</p>
+
+<p>The WSGI environ:<br />
+<pre>${c.pretty_environ}</pre>
+</p>
+
+ +Then add the following to your 'hello' controller class: +
+    def serverinfo(self):
+        import cgi
+        import pprint
+        c.pretty_environ = cgi.escape(pprint.pformat(request.environ))
+        c.name = 'The Black Knight'
+        return render('/serverinfo.mako')
+
+ +You can now view the page at: /hello/serverinfo +

+ + diff --git a/pyalchemybiz/templates/base.mako b/pyalchemybiz/templates/base.mako new file mode 100644 index 0000000..ed0c538 --- /dev/null +++ b/pyalchemybiz/templates/base.mako @@ -0,0 +1,20 @@ + + + + PyAlchemyBiz + ${h.stylesheet_link_tag('/pyalchemybiz.css')} + ${h.javascript_include_tag( + '/javascripts/pyalchemybiz.js', builtins=True)} + + +
+ ${next.body()}\ + +
+ + diff --git a/pyalchemybiz/templates/customer.mako b/pyalchemybiz/templates/customer.mako new file mode 100644 index 0000000..8aefd56 --- /dev/null +++ b/pyalchemybiz/templates/customer.mako @@ -0,0 +1,9 @@ +<%inherit file="base.mako" /> + +

Hallo

+ +
    +% for customer in c.customers: +
  • ${customer.firstname} ${customer.lastname} [${h.link_to('edit', h.url_for(id=customer.id, action="edit"))}]
  • +% endfor +
diff --git a/pyalchemybiz/tests/__init__.py b/pyalchemybiz/tests/__init__.py new file mode 100644 index 0000000..91ff2e7 --- /dev/null +++ b/pyalchemybiz/tests/__init__.py @@ -0,0 +1,40 @@ +"""Pylons application test package + +When the test runner finds and executes tests within this directory, +this file will be loaded to setup the test environment. + +It registers the root directory of the project in sys.path and +pkg_resources, in case the project hasn't been installed with +setuptools. It also initializes the application via websetup (paster +setup-app) with the project's test.ini configuration file. +""" +import os +import sys +from unittest import TestCase + +import pkg_resources +import paste.fixture +import paste.script.appinstall +from paste.deploy import loadapp +from routes import url_for + +__all__ = ['url_for', 'TestController'] + +here_dir = os.path.dirname(os.path.abspath(__file__)) +conf_dir = os.path.dirname(os.path.dirname(here_dir)) + +sys.path.insert(0, conf_dir) +pkg_resources.working_set.add_entry(conf_dir) +pkg_resources.require('Paste') +pkg_resources.require('PasteScript') + +test_file = os.path.join(conf_dir, 'test.ini') +cmd = paste.script.appinstall.SetupCommand('setup-app') +cmd.run([test_file]) + +class TestController(TestCase): + + def __init__(self, *args, **kwargs): + wsgiapp = loadapp('config:test.ini', relative_to=conf_dir) + self.app = paste.fixture.TestApp(wsgiapp) + TestCase.__init__(self, *args, **kwargs) diff --git a/pyalchemybiz/tests/functional/__init__.py b/pyalchemybiz/tests/functional/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyalchemybiz/tests/functional/test_customer.py b/pyalchemybiz/tests/functional/test_customer.py new file mode 100644 index 0000000..5a96361 --- /dev/null +++ b/pyalchemybiz/tests/functional/test_customer.py @@ -0,0 +1,7 @@ +from pyalchemybiz.tests import * + +class TestCustomerController(TestController): + + def test_index(self): + response = self.app.get(url_for(controller='customer')) + # Test response... diff --git a/pyalchemybiz/tests/test_models.py b/pyalchemybiz/tests/test_models.py new file mode 100644 index 0000000..e69de29 diff --git a/pyalchemybiz/websetup.py b/pyalchemybiz/websetup.py new file mode 100644 index 0000000..5504807 --- /dev/null +++ b/pyalchemybiz/websetup.py @@ -0,0 +1,22 @@ +"""Setup the pyalchemybiz application""" +import logging + +from paste.deploy import appconfig +from pylons import config + +from pyalchemybiz.config.environment import load_environment + +log = logging.getLogger(__name__) + +def setup_config(command, filename, section, vars): + """Place any commands to setup pyalchemybiz here""" + conf = appconfig('config:' + filename) + load_environment(conf.global_conf, conf.local_conf) + + # Populate the DB on 'paster setup-app' + import pyalchemybiz.model as model + + log.info("Setting up database connectivity...") + log.info("Creating tables...") + model.meta.metadata.create_all(bind=model.meta.engine) + log.info("Successfully set up.") diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..60ab224 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,59 @@ +[egg_info] +tag_build = dev +tag_svn_revision = true + +[easy_install] +find_links = http://www.pylonshq.com/download/ + +[pudge] +theme = pythonpaste.org + +# Add extra doc files here with spaces between them +docs = docs/index.txt + +# Doc Settings +doc_base = docs/ +dest = docs/html + +# Add extra modules here separated with commas +modules = pyalchemybiz +title = Pyalchemybiz +organization = Pylons + +# Highlight code-block sections with Pygments +highlighter = pygments + +# Optionally add extra links +#organization_url = http://pylonshq.com/ +#trac_url = http://pylonshq.com/project +settings = no_about=true + +# Optionally add extra settings +# link1=/community/ Community +# link2=/download/ Download + +[publish] +doc-dir=docs/html +make-dirs=1 + +# Babel configuration +[compile_catalog] +domain = pyalchemybiz +directory = pyalchemybiz/i18n +statistics = true + +[extract_messages] +add_comments = TRANSLATORS: +output_file = pyalchemybiz/i18n/pyalchemybiz.pot +width = 80 + +[init_catalog] +domain = pyalchemybiz +input_file = pyalchemybiz/i18n/pyalchemybiz.pot +output_dir = pyalchemybiz/i18n + +[update_catalog] +domain = pyalchemybiz +input_file = pyalchemybiz/i18n/pyalchemybiz.pot +output_dir = pyalchemybiz/i18n +previous = true diff --git a/setup.py b/setup.py index 57f6141..c6d0429 100755 --- a/setup.py +++ b/setup.py @@ -1,19 +1,31 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Author: Jan Dittberner -# Version: $Id$ -# -# This file is part of pyalchemybiz -# -from setuptools import setup -setup(name='pyalchemybiz', - description='Python SQLAlchemy Business', - version='0.1dev', - author='Jan Dittberner', - author_email='jan@dittberner.info', - url='http://www.dittberner.info/projects/pyalchemybiz/', - packages=['pyalchemybiz'], - install_requires=['sqlalchemy >= 0.3.10'], - license='GPL' - ) +try: + from setuptools import setup, find_packages +except ImportError: + from ez_setup import use_setuptools + use_setuptools() + from setuptools import setup, find_packages + +setup( + name='pyalchemybiz', + version="0.1", + description='python based small business suite.', + author='Jan Dittberner', + author_email='jan@dittberner.info', + url='http://www.dittberner.info/projects/pyalchemybiz', + install_requires=["Pylons>=0.9.6.2", "SQLAlchemy>=0.4"], + packages=find_packages(exclude=['ez_setup']), + include_package_data=True, + test_suite='nose.collector', + package_data={'pyalchemybiz': ['i18n/*/LC_MESSAGES/*.mo']}, + #message_extractors = {'pyalchemybiz': [ + # ('**.py', 'python', None), + # ('templates/**.mako', 'mako', None), + # ('public/**', 'ignore', None)]}, + entry_points=""" + [paste.app_factory] + main = pyalchemybiz.config.middleware:make_app + + [paste.app_install] + main = pylons.util:PylonsInstaller + """, +) diff --git a/test.ini b/test.ini new file mode 100644 index 0000000..9d3ed61 --- /dev/null +++ b/test.ini @@ -0,0 +1,21 @@ +# +# pyalchemybiz - Pylons testing environment configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +[DEFAULT] +debug = true +# Uncomment and replace with the address which should receive any error reports +#email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 5000 + +[app:main] +use = config:development.ini + +# Add additional test specific configuration options as necessary.