diff --git a/containers/MANIFEST.in b/containers/MANIFEST.in new file mode 100644 index 0000000000..c922f11ad7 --- /dev/null +++ b/containers/MANIFEST.in @@ -0,0 +1 @@ +recursive-include public * diff --git a/containers/api/__init__.py b/containers/api/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/containers/api/app.py b/containers/api/app.py new file mode 100644 index 0000000000..1c61c1ee05 --- /dev/null +++ b/containers/api/app.py @@ -0,0 +1,14 @@ +from pecan import make_app +from api import model + + +def setup_app(config): + + model.init_model() + app_conf = dict(config.app) + + return make_app( + app_conf.pop('root'), + logging=getattr(config, 'logging', {}), + **app_conf + ) diff --git a/containers/api/controllers/__init__.py b/containers/api/controllers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/containers/api/controllers/root.py b/containers/api/controllers/root.py new file mode 100644 index 0000000000..bc1e72bdf3 --- /dev/null +++ b/containers/api/controllers/root.py @@ -0,0 +1,22 @@ +from pecan import expose, redirect +from webob.exc import status_map + + +class RootController(object): + + @expose(generic=True, template='index.html') + def index(self): + return dict() + + @index.when(method='POST') + def index_post(self, q): + redirect('http://pecan.readthedocs.org/en/latest/search.html?q=%s' % q) + + @expose('error.html') + def error(self, status): + try: + status = int(status) + except ValueError: # pragma: no cover + status = 500 + message = getattr(status_map.get(status), 'explanation', '') + return dict(status=status, message=message) diff --git a/containers/api/model/__init__.py b/containers/api/model/__init__.py new file mode 100644 index 0000000000..d983f7bc51 --- /dev/null +++ b/containers/api/model/__init__.py @@ -0,0 +1,15 @@ +from pecan import conf # noqa + + +def init_model(): + """ + This is a stub method which is called at application startup time. + + If you need to bind to a parsed database configuration, set up tables or + ORM classes, or perform any database initialization, this is the + recommended place to do it. + + For more information working with databases, and some common recipes, + see http://pecan.readthedocs.org/en/latest/databases.html + """ + pass diff --git a/containers/api/templates/error.html b/containers/api/templates/error.html new file mode 100644 index 0000000000..f2d9796143 --- /dev/null +++ b/containers/api/templates/error.html @@ -0,0 +1,12 @@ +<%inherit file="layout.html" /> + +## provide definitions for blocks we want to redefine +<%def name="title()"> + Server Error ${status} + + +## now define the body of the template +
+

Server Error ${status}

+
+

${message}

diff --git a/containers/api/templates/index.html b/containers/api/templates/index.html new file mode 100644 index 0000000000..f17c3862ee --- /dev/null +++ b/containers/api/templates/index.html @@ -0,0 +1,34 @@ +<%inherit file="layout.html" /> + +## provide definitions for blocks we want to redefine +<%def name="title()"> + Welcome to Pecan! + + +## now define the body of the template +
+

+
+ +
+ +

This is a sample Pecan project.

+ +

+ Instructions for getting started can be found online at pecanpy.org +

+ +

+ ...or you can search the documentation here: +

+ +
+
+ + +
+ Enter search terms or a module, class or function name. +
+ +
diff --git a/containers/api/templates/layout.html b/containers/api/templates/layout.html new file mode 100644 index 0000000000..409085919f --- /dev/null +++ b/containers/api/templates/layout.html @@ -0,0 +1,22 @@ + + + ${self.title()} + ${self.style()} + ${self.javascript()} + + + ${self.body()} + + + +<%def name="title()"> + Default Title + + +<%def name="style()"> + + + +<%def name="javascript()"> + + diff --git a/containers/api/tests/__init__.py b/containers/api/tests/__init__.py new file mode 100644 index 0000000000..78ea5274f8 --- /dev/null +++ b/containers/api/tests/__init__.py @@ -0,0 +1,22 @@ +import os +from unittest import TestCase +from pecan import set_config +from pecan.testing import load_test_app + +__all__ = ['FunctionalTest'] + + +class FunctionalTest(TestCase): + """ + Used for functional tests where you need to test your + literal application and its integration with the framework. + """ + + def setUp(self): + self.app = load_test_app(os.path.join( + os.path.dirname(__file__), + 'config.py' + )) + + def tearDown(self): + set_config({}, overwrite=True) diff --git a/containers/api/tests/config.py b/containers/api/tests/config.py new file mode 100644 index 0000000000..be4b70c8c6 --- /dev/null +++ b/containers/api/tests/config.py @@ -0,0 +1,25 @@ +# Server Specific Configurations +server = { + 'port': '8080', + 'host': '0.0.0.0' +} + +# Pecan Application Configurations +app = { + 'root': 'api.controllers.root.RootController', + 'modules': ['api'], + 'static_root': '%(confdir)s/../../public', + 'template_path': '%(confdir)s/../templates', + 'debug': True, + 'errors': { + '404': '/error/404', + '__force_dict__': True + } +} + +# Custom Configurations must be in Python dictionary format:: +# +# foo = {'bar':'baz'} +# +# All configurations are accessible at:: +# pecan.conf diff --git a/containers/api/tests/test_functional.py b/containers/api/tests/test_functional.py new file mode 100644 index 0000000000..32e9f41410 --- /dev/null +++ b/containers/api/tests/test_functional.py @@ -0,0 +1,22 @@ +from unittest import TestCase +from webtest import TestApp +from api.tests import FunctionalTest + + +class TestRootController(FunctionalTest): + + def test_get(self): + response = self.app.get('/') + assert response.status_int == 200 + + def test_search(self): + response = self.app.post('/', params={'q': 'RestController'}) + assert response.status_int == 302 + assert response.headers['Location'] == ( + 'http://pecan.readthedocs.org/en/latest/search.html' + '?q=RestController' + ) + + def test_get_not_found(self): + response = self.app.get('/a/bogus/url', expect_errors=True) + assert response.status_int == 404 diff --git a/containers/api/tests/test_units.py b/containers/api/tests/test_units.py new file mode 100644 index 0000000000..573fb682f8 --- /dev/null +++ b/containers/api/tests/test_units.py @@ -0,0 +1,7 @@ +from unittest import TestCase + + +class TestUnits(TestCase): + + def test_units(self): + assert 5 * 5 == 25 diff --git a/containers/config.py b/containers/config.py new file mode 100644 index 0000000000..a289dae2a5 --- /dev/null +++ b/containers/config.py @@ -0,0 +1,54 @@ +# Server Specific Configurations +server = { + 'port': '8080', + 'host': '0.0.0.0' +} + +# Pecan Application Configurations +app = { + 'root': 'api.controllers.root.RootController', + 'modules': ['api'], + 'static_root': '%(confdir)s/public', + 'template_path': '%(confdir)s/api/templates', + 'debug': True, + 'errors': { + 404: '/error/404', + '__force_dict__': True + } +} + +logging = { + 'root': {'level': 'INFO', 'handlers': ['console']}, + 'loggers': { + 'api': {'level': 'DEBUG', 'handlers': ['console']}, + 'pecan.commands.serve': {'level': 'DEBUG', 'handlers': ['console']}, + 'py.warnings': {'handlers': ['console']}, + '__force_dict__': True + }, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'color' + } + }, + 'formatters': { + 'simple': { + 'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]' + '[%(threadName)s] %(message)s') + }, + 'color': { + '()': 'pecan.log.ColorFormatter', + 'format': ('%(asctime)s [%(padded_color_levelname)s] [%(name)s]' + '[%(threadName)s] %(message)s'), + '__force_dict__': True + } + } +} + +# Custom Configurations must be in Python dictionary format:: +# +# foo = {'bar':'baz'} +# +# All configurations are accessible at:: +# pecan.conf diff --git a/containers/public/css/style.css b/containers/public/css/style.css new file mode 100644 index 0000000000..55c9db54a0 --- /dev/null +++ b/containers/public/css/style.css @@ -0,0 +1,43 @@ +body { + background: #311F00; + color: white; + font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif; + padding: 1em 2em; +} + +a { + color: #FAFF78; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +div#content { + width: 800px; + margin: 0 auto; +} + +form { + margin: 0; + padding: 0; + border: 0; +} + +fieldset { + border: 0; +} + +input.error { + background: #FAFF78; +} + +header { + text-align: center; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Futura-CondensedExtraBold', 'Futura', 'Helvetica', sans-serif; + text-transform: uppercase; +} diff --git a/containers/public/images/logo.png b/containers/public/images/logo.png new file mode 100644 index 0000000000..a8f403e4a4 Binary files /dev/null and b/containers/public/images/logo.png differ diff --git a/containers/setup.cfg b/containers/setup.cfg new file mode 100644 index 0000000000..ccfb5b2c5b --- /dev/null +++ b/containers/setup.cfg @@ -0,0 +1,6 @@ +[nosetests] +match=^test +where=api +nocapture=1 +cover-package=api +cover-erase=1 diff --git a/containers/setup.py b/containers/setup.py new file mode 100644 index 0000000000..75f9e5cdc8 --- /dev/null +++ b/containers/setup.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +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='api', + version='0.1', + description='', + author='', + author_email='', + install_requires=[ + "pecan", + ], + test_suite='api', + zip_safe=False, + include_package_data=True, + packages=find_packages(exclude=['ez_setup']) +)