From 3d15bd1ba8cb3da1b2af68735b118651664cb49a Mon Sep 17 00:00:00 2001 From: digambar Date: Tue, 9 Sep 2014 17:57:18 +0530 Subject: [PATCH] Created the pecan project for containers for API Co-Authored-By: Digambar Patil Co-Authored-By: Steven Dake Change-Id: I886a26ded99708f39bba22d0ebd08ce99cd26020 --- containers/MANIFEST.in | 1 + containers/api/__init__.py | 0 containers/api/app.py | 14 ++++++ containers/api/controllers/__init__.py | 0 containers/api/controllers/root.py | 22 ++++++++++ containers/api/model/__init__.py | 15 +++++++ containers/api/templates/error.html | 12 ++++++ containers/api/templates/index.html | 34 +++++++++++++++ containers/api/templates/layout.html | 22 ++++++++++ containers/api/tests/__init__.py | 22 ++++++++++ containers/api/tests/config.py | 25 +++++++++++ containers/api/tests/test_functional.py | 22 ++++++++++ containers/api/tests/test_units.py | 7 +++ containers/config.py | 54 ++++++++++++++++++++++++ containers/public/css/style.css | 43 +++++++++++++++++++ containers/public/images/logo.png | Bin 0 -> 20596 bytes containers/setup.cfg | 6 +++ containers/setup.py | 22 ++++++++++ 18 files changed, 321 insertions(+) create mode 100644 containers/MANIFEST.in create mode 100644 containers/api/__init__.py create mode 100644 containers/api/app.py create mode 100644 containers/api/controllers/__init__.py create mode 100644 containers/api/controllers/root.py create mode 100644 containers/api/model/__init__.py create mode 100644 containers/api/templates/error.html create mode 100644 containers/api/templates/index.html create mode 100644 containers/api/templates/layout.html create mode 100644 containers/api/tests/__init__.py create mode 100644 containers/api/tests/config.py create mode 100644 containers/api/tests/test_functional.py create mode 100644 containers/api/tests/test_units.py create mode 100644 containers/config.py create mode 100644 containers/public/css/style.css create mode 100644 containers/public/images/logo.png create mode 100644 containers/setup.cfg create mode 100644 containers/setup.py 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 0000000000000000000000000000000000000000..a8f403e4a4f3ce69a4577a46ae37f5633abb79fa GIT binary patch literal 20596 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 ZD6?|u)*zSG3-Sw<9?1AXo%Yig{{dC~0l)wN literal 0 HcmV?d00001 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']) +)