From e036abd313668dcacf00e40140ddcdcd3d5cd753 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 1 Apr 2017 15:17:32 -0500 Subject: [PATCH] Initial commit as generated from: pcreate -t websauna_app hotcooler https://websauna.org/docs/tutorials/gettingstarted/tutorial_03.html --- .gitignore | 37 ++++++++++++++ CHANGES.rst | 4 ++ MANIFEST.in | 2 + README.rst | 57 +++++++++++++++++++++ alembic/env.py | 3 ++ alembic/script.py.mako | 28 +++++++++++ alembic/versions/README.txt | 3 ++ hotcooler/__init__.py | 57 +++++++++++++++++++++ hotcooler/admins.py | 1 + hotcooler/conf/base.ini | 39 +++++++++++++++ hotcooler/conf/development.ini | 15 ++++++ hotcooler/conf/production.ini | 16 ++++++ hotcooler/conf/staging.ini | 13 +++++ hotcooler/conf/test.ini | 14 ++++++ hotcooler/models.py | 1 + hotcooler/static/logo.png | Bin 0 -> 11271 bytes hotcooler/static/theme.css | 4 ++ hotcooler/templates/email/header.html | 23 +++++++++ hotcooler/templates/hotcooler/home.html | 24 +++++++++ hotcooler/templates/site/css.html | 20 ++++++++ hotcooler/templates/site/logo.html | 6 +++ hotcooler/tests/__init__.py | 0 hotcooler/tests/test_login.py | 47 ++++++++++++++++++ hotcooler/views.py | 9 ++++ setup.cfg | 26 ++++++++++ setup.py | 63 ++++++++++++++++++++++++ 26 files changed, 512 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGES.rst create mode 100644 MANIFEST.in create mode 100644 README.rst create mode 100644 alembic/env.py create mode 100644 alembic/script.py.mako create mode 100644 alembic/versions/README.txt create mode 100644 hotcooler/__init__.py create mode 100644 hotcooler/admins.py create mode 100644 hotcooler/conf/base.ini create mode 100644 hotcooler/conf/development.ini create mode 100644 hotcooler/conf/production.ini create mode 100644 hotcooler/conf/staging.ini create mode 100644 hotcooler/conf/test.ini create mode 100644 hotcooler/models.py create mode 100644 hotcooler/static/logo.png create mode 100644 hotcooler/static/theme.css create mode 100644 hotcooler/templates/email/header.html create mode 100644 hotcooler/templates/hotcooler/home.html create mode 100644 hotcooler/templates/site/css.html create mode 100644 hotcooler/templates/site/logo.html create mode 100644 hotcooler/tests/__init__.py create mode 100644 hotcooler/tests/test_login.py create mode 100644 hotcooler/views.py create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..997db2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Never commit our secrets files +*secrets.ini + +# Default .gitignore with common Pythonic web ignores +venv +build/ +dist/ +*.pyc +__pycache__ +*.egg +*.egg-info + +# Logs +*.log + +# Database dumbs +*.sql +*.sqlite + +# If somebody does npm local installs +node_modules + +# Created by running a celery +celerybeat* + +# pytest-splinter creates screenshots from failed browsers tests. +# let's not pollute source tree with them by accient. +/*.tests.* + +# Static asset cache busting +cache-manifest.json +perma-asset + +# pytest and tox cache +.cache +.tox +.eggs diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..35a34f3 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..d2bad49 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include *.txt *.ini *.cfg *.rst +recursive-include hotcooler *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.ini *.yml *.yaml diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..fc13323 --- /dev/null +++ b/README.rst @@ -0,0 +1,57 @@ +This is a Websauna application package for hotcooler. + +To run this package you need Python 3.4+, PostgresSQL and Redis. + +Installation +============ + +This installation method assumes you the author of the hotcooler application and wish to develop it. Below are instructions to to install the package to a Python virtual environment using pip command in an editable mode. + +Example:: + + cd hotcooler # This is the folder with setup.py file + virtualenv venv + source venv/bin/activate + + # Make sure pip itself is up-to-date + pip install -U pip + + # Install the package and its dependencies to a currently + # activated virtualenv from the folder with setup.py file + pip install -e "." + +Running the website +=================== + +Local development machine +------------------------- + +Example (OSX / Homebrew):: + + # Create PostgreSQL database + psql create hotcooler_dev + + # Write table schemas for models + ws-sync-db hotcooler/conf/development.ini + + # Start web server + ws-pserve hotcooler/conf/development.ini --reload + +Running the test suite +====================== + +Example:: + + # Install testing dependencies + pip install ".[dev,test]" + + # Create database used for unit testing + psql create hotcooler_test + + # Run test suite using py.test running + py.test + +More information +================ + +Please see https://websauna.org/ \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000..f90ad94 --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,3 @@ +from websauna.system.devop import alembic + +alembic.run_alembic(package="hotcooler") \ No newline at end of file diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000..f54981d --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + +import datetime +import websauna.system.model.columns +from sqlalchemy.types import Text # Needed from proper creation of JSON fields as Alembic inserts astext_type=Text() row + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/alembic/versions/README.txt b/alembic/versions/README.txt new file mode 100644 index 0000000..78e2af7 --- /dev/null +++ b/alembic/versions/README.txt @@ -0,0 +1,3 @@ +This is a placeholder. + +ws-alembic command will place generated scripts here. \ No newline at end of file diff --git a/hotcooler/__init__.py b/hotcooler/__init__.py new file mode 100644 index 0000000..7737f58 --- /dev/null +++ b/hotcooler/__init__.py @@ -0,0 +1,57 @@ +"""App entry point and configuration.""" + +import websauna.system + + +class Initializer(websauna.system.Initializer): + """An initialization configuration used for starting hotcooler. + + Override parent class methods to customize application behavior. + """ + + def configure_static(self): + """Configure static asset serving and cache busting.""" + super(Initializer, self).configure_static() + + self.config.registry.static_asset_policy.add_static_view('hotcooler-static', 'hotcooler:static') + + def configure_templates(self): + """Include our package templates folder in Jinja 2 configuration.""" + super(Initializer, self).configure_templates() + + self.config.add_jinja2_search_path('hotcooler:templates', name='.html', prepend=True) # HTML templates for pages + self.config.add_jinja2_search_path('hotcooler:templates', name='.txt', prepend=True) # Plain text email templates (if any) + self.config.add_jinja2_search_path('hotcooler:templates', name='.xml', prepend=True) # Sitemap and misc XML files (if any) + + def configure_views(self): + """Configure views for your application. + + Let the config scanner to pick ``@simple_route`` definitions from scanned modules. Alternative you can call ``config.add_route()`` and ``config.add_view()`` here. + """ + # We override this method, so that we route home to our home screen, not Websauna default one + from . import views + self.config.scan(views) + + def configure_models(self): + """Register the models of this application.""" + from . import models + self.config.scan(models) + + def configure_model_admins(self): + """Register the models of this application.""" + + # Call parent which registers user and group admins + super(Initializer, self).configure_model_admins() + + # Scan our admins + from . import admins + self.config.scan(admins) + + def run(self): + super(Initializer, self).run() + + +def main(global_config, **settings): + init = Initializer(global_config) + init.run() + return init.make_wsgi_app() diff --git a/hotcooler/admins.py b/hotcooler/admins.py new file mode 100644 index 0000000..66a3656 --- /dev/null +++ b/hotcooler/admins.py @@ -0,0 +1 @@ +"""Place your admin resources in this file.""" \ No newline at end of file diff --git a/hotcooler/conf/base.ini b/hotcooler/conf/base.ini new file mode 100644 index 0000000..a5fcdc0 --- /dev/null +++ b/hotcooler/conf/base.ini @@ -0,0 +1,39 @@ +# Definition of hotcooler name and properties shared among development, testing and production instances + +# +# WSGI entry point and websauna INI settings +# +[app:main] +use = egg:hotcooler +websauna.init = hotcooler.Initializer + +# +# Websauna settings +# + +# Page/email title +websauna.site_name = hotcooler + +# HTML title tag for the browser address bar +websauna.site_title = hotcooler + +# Branding slogan +websauna.site_tag_line = Your site goes here + +websauna.site_url = http://localhost:6543 + +# Your name +websauna.site_author = hotcooler team + +# Used internally with databases/backups/etc. +websauna.site_id = hotcooler + +# pyramid_mailer settings +mail.default_sender = no-reply@example.com +mail.default_sender_name = hotcooler team + + + + + + diff --git a/hotcooler/conf/development.ini b/hotcooler/conf/development.ini new file mode 100644 index 0000000..bc3a884 --- /dev/null +++ b/hotcooler/conf/development.ini @@ -0,0 +1,15 @@ +# pserve and command line configuration for a local development machine + +[includes] +include_ini_files = + resource://websauna/conf/development.ini + resource://hotcooler/conf/base.ini + resource://websauna/conf/base.ini + +[app:main] +websauna.site_id = hotcooler_dev +websauna.site_email_prefix = [hotcooler DEV] +sqlalchemy.url = postgresql://localhost/hotcooler_dev +websauna.secrets_file = resource://hotcooler/conf/development-secrets.ini + + diff --git a/hotcooler/conf/production.ini b/hotcooler/conf/production.ini new file mode 100644 index 0000000..7a11869 --- /dev/null +++ b/hotcooler/conf/production.ini @@ -0,0 +1,16 @@ +# pserve and command line configuration for a production server + +[includes] +include_ini_files = + resource://websauna/conf/production.ini + resource://hotcooler/conf/base.ini + resource://websauna/conf/base.ini + +[app:main] +use = egg:hotcooler +websauna.init = hotcooler.Initializer +websauna.site_id = hotcooler_prod +websauna.site_email_prefix = [hotcooler] +sqlalchemy.url = postgresql://localhost/hotcooler_prod +websauna.secrets_file = resource://hotcooler/conf/production-secrets.ini + diff --git a/hotcooler/conf/staging.ini b/hotcooler/conf/staging.ini new file mode 100644 index 0000000..105b08e --- /dev/null +++ b/hotcooler/conf/staging.ini @@ -0,0 +1,13 @@ +# pserve and command line configuration for a staging server + +[includes] +include_ini_files = + resource://hotcooler/conf/production.ini + resource://websauna/conf/production.ini + resource://hotcooler/conf/base.ini + resource://websauna/conf/base.ini + +[app:main] +websauna.site_id = hotcooler_staging +sqlalchemy.url = postgresql://localhost/hotcooler_staging +websauna.secrets_file = resource://hotcooler/conf/staging-secrets.ini diff --git a/hotcooler/conf/test.ini b/hotcooler/conf/test.ini new file mode 100644 index 0000000..5638410 --- /dev/null +++ b/hotcooler/conf/test.ini @@ -0,0 +1,14 @@ +# py.test --ini configuration for running the hotcooler test suite + +[includes] +include_ini_files = + resource://websauna/conf/test.ini + resource://hotcooler/conf/base.ini + resource://websauna/conf/base.ini + +[app:main] +websauna.site_id = hotcooler_test +websauna.site_email_prefix = [hotcooler TEST] +sqlalchemy.url = postgresql://localhost/hotcooler_test +websauna.secrets_file = resource://hotcooler/conf/test-secrets.ini +websauna.test_web_server_port = 8533 diff --git a/hotcooler/models.py b/hotcooler/models.py new file mode 100644 index 0000000..b84d880 --- /dev/null +++ b/hotcooler/models.py @@ -0,0 +1 @@ +"""Place your SQLAlchemy models in this file.""" \ No newline at end of file diff --git a/hotcooler/static/logo.png b/hotcooler/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..90c1dc239c340809f3977ab6404f4e966105e9a8 GIT binary patch literal 11271 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 link translate the Google Drive sharing link. From:: + + https://drive.google.com/file/d/0B6nei6GpGxGsb1hnWHlBRFJhxxx/view?usp=sharing + +To:: + + https://drive.google.com/uc?id=0B6nei6GpGxGsb1hnWHlBRFJhxxx +#} + + +

+ + + +

+ + diff --git a/hotcooler/templates/hotcooler/home.html b/hotcooler/templates/hotcooler/home.html new file mode 100644 index 0000000..11a9a20 --- /dev/null +++ b/hotcooler/templates/hotcooler/home.html @@ -0,0 +1,24 @@ +{# Template for home view #} + +{% extends "site/base.html" %} + +{% block content %} + +
+

{{ site_name }}

+

+ {{ site_tag_line }} +

+
+ + {% if request.user %} +

+ Welcome {{ request.user.friendly_name }}! +

+ {% else %} +

+ Welcome visitor! +

+ {% endif %} + +{% endblock %} \ No newline at end of file diff --git a/hotcooler/templates/site/css.html b/hotcooler/templates/site/css.html new file mode 100644 index 0000000..35129dc --- /dev/null +++ b/hotcooler/templates/site/css.html @@ -0,0 +1,20 @@ +{# Specify CSS section for in the site #} + +{# Include Bootstrap CSS from Websauna core package - http://getbootstrap.com/ #} + + +{# Include Font-Awesome icons from CDN - http://fontawesome.io/ #} + + +{# Include some default Websauna styles #} + + +{# Include hotcooler package theme #} + + +{# Include CSS for widgets #} +{% if request.on_demand_resource_renderer %} + {% for css_url in request.on_demand_resource_renderer.get_resources("css") %} + + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/hotcooler/templates/site/logo.html b/hotcooler/templates/site/logo.html new file mode 100644 index 0000000..34875b6 --- /dev/null +++ b/hotcooler/templates/site/logo.html @@ -0,0 +1,6 @@ +{# Override the default navigation bar logo from Websauna templates #} + +{# Logo file taken from here https://openclipart.org/detail/1105/workman-ahead-roadsign #} + + hotcooler + \ No newline at end of file diff --git a/hotcooler/tests/__init__.py b/hotcooler/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hotcooler/tests/test_login.py b/hotcooler/tests/test_login.py new file mode 100644 index 0000000..5539583 --- /dev/null +++ b/hotcooler/tests/test_login.py @@ -0,0 +1,47 @@ +"""An example login test case.""" + +import transaction + +from sqlalchemy.orm.session import Session +from splinter.driver import DriverAPI + +from websauna.tests.utils import create_user +from websauna.tests.utils import EMAIL +from websauna.tests.utils import PASSWORD +from websauna.system import Initializer + + +def test_login(web_server:str, browser:DriverAPI, dbsession:Session, init:Initializer): + """Login as a user to the site. + + This is a functional test. Prepare the test by creating one user in the database. Then try to login as this user by using Splinter test browser. + + :param web_server: Functional web server py.test fixture - this string points to a started web server with test.ini configuration. + + :param browser: A Splinter web browser used to execute the tests. By default ``splinter.driver.webdriver.firefox.WebDriver``, but can be altered with py.test command line options for pytest-splinter. + + :param dbsession: Active SQLAlchemy database session for the test run. + + :param init: Websauna Initializer which ramps up the environment with the default ``test.ini`` and exposes the test config. + """ + + with transaction.manager: + # Create a dummy example@example.com user we test + create_user(dbsession, init.config.registry, email=EMAIL, password=PASSWORD) + + # Direct Splinter browser to the website + b = browser + b.visit(web_server) + + # This link should be in the top navigation + b.find_by_css("#nav-sign-in").click() + + # Link gives us the login form + assert b.is_element_present_by_css("#login-form") + + b.fill("username", EMAIL) + b.fill("password", PASSWORD) + b.find_by_name("login_email").click() + + # After login we see a profile link to our profile + assert b.is_element_present_by_css("#nav-logout") \ No newline at end of file diff --git a/hotcooler/views.py b/hotcooler/views.py new file mode 100644 index 0000000..292f8a8 --- /dev/null +++ b/hotcooler/views.py @@ -0,0 +1,9 @@ +from websauna.system.http import Request +from websauna.system.core.route import simple_route + + +# Configure view named home at path / using a template hotcooler/home.html +@simple_route("/", route_name="home", renderer='hotcooler/home.html') +def home(request: Request): + """Render site homepage.""" + return {"project": "hotcooler"} diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..7f2b074 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,26 @@ +[tool:pytest] +addopts = + --strict + -p websauna.tests.fixtures + --splinter-make-screenshot-on-failure=false + --ini=hotcooler/conf/test.ini + hotcooler/tests + +# E501: Line too long +# E128: continuation line under +# E731: http://stackoverflow.com/q/25010167/315168 +pep8ignore = E501 E128 E731 + +# Don't let py.test scan py.files in these folders +norecursedirs = alembic .tox .cache .eggs venv + +# Add some default py.test markers +# Slow marker is for tests taking > 15 seconds to complete. +# Fail marker signals the test is expected to fail. +markers = + slow + fail + +[flake8] +ignore = E128 E731_ +max-line-length = 999 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..f22d689 --- /dev/null +++ b/setup.py @@ -0,0 +1,63 @@ +import os +import sys +from setuptools import setup, find_packages + + +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, 'README.rst')) as f: + README = f.read() +with open(os.path.join(here, 'CHANGES.rst')) as f: + CHANGES = f.read() + + +# trying to run python setup.py install or python setup.py develop +if len(sys.argv) >= 2: + if sys.argv[0] == "setup.py" and sys.argv[1] in ("install", "develop"): + # Otherwise so much stuff would be broken later... + # Namely, namespaced packages clash as pip, setup.py and easy_install handle namespaces differently + raise RuntimeError("It is not possible to install this package with setup.py. Use pip to install this package as instructed in Websauna tutorial.") + + +setup(name='hotcooler', + version='0.0', + description='hotcooler', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + "Programming Language :: Python", + "Framework :: Pyramid", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], + author='', + author_email='', + url='', + keywords='web websauna pyramid', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + test_suite='hotcooler', + install_requires=['websauna'], + extras_require={ + # Dependencies for running test suite + 'test': [ + "pytest", + "pytest-runner", + "pytest-splinter", + "webtest", + + # Wait until Marionette matures + # http://stackoverflow.com/questions/37761668/cant-open-browser-with-selenium-after-firefox-update + "selenium==2.53.6", + ], + + + # Dependencies to make releases + 'dev': ['websauna[dev]'], + }, + + # Define where this application starts as referred by WSGI web servers + entry_points="""\ + [paste.app_factory] + main = hotcooler:main + """, + )