Initial commit
as generated from: pcreate -t websauna_app hotcooler https://websauna.org/docs/tutorials/gettingstarted/tutorial_03.html
This commit is contained in:
commit
e036abd313
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
|
@ -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
|
4
CHANGES.rst
Normal file
4
CHANGES.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
0.0
|
||||
---
|
||||
|
||||
- Initial version
|
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
|
@ -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
|
57
README.rst
Normal file
57
README.rst
Normal file
|
@ -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/
|
3
alembic/env.py
Normal file
3
alembic/env.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from websauna.system.devop import alembic
|
||||
|
||||
alembic.run_alembic(package="hotcooler")
|
28
alembic/script.py.mako
Normal file
28
alembic/script.py.mako
Normal file
|
@ -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"}
|
3
alembic/versions/README.txt
Normal file
3
alembic/versions/README.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
This is a placeholder.
|
||||
|
||||
ws-alembic command will place generated scripts here.
|
57
hotcooler/__init__.py
Normal file
57
hotcooler/__init__.py
Normal file
|
@ -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()
|
1
hotcooler/admins.py
Normal file
1
hotcooler/admins.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Place your admin resources in this file."""
|
39
hotcooler/conf/base.ini
Normal file
39
hotcooler/conf/base.ini
Normal file
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
15
hotcooler/conf/development.ini
Normal file
15
hotcooler/conf/development.ini
Normal file
|
@ -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
|
||||
|
||||
|
16
hotcooler/conf/production.ini
Normal file
16
hotcooler/conf/production.ini
Normal file
|
@ -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
|
||||
|
13
hotcooler/conf/staging.ini
Normal file
13
hotcooler/conf/staging.ini
Normal file
|
@ -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
|
14
hotcooler/conf/test.ini
Normal file
14
hotcooler/conf/test.ini
Normal file
|
@ -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
|
1
hotcooler/models.py
Normal file
1
hotcooler/models.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Place your SQLAlchemy models in this file."""
|
BIN
hotcooler/static/logo.png
Normal file
BIN
hotcooler/static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
4
hotcooler/static/theme.css
Normal file
4
hotcooler/static/theme.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
/* Don't let the logo image to grow too big */
|
||||
.navbar-brand img {
|
||||
max-height: 26px;
|
||||
}
|
23
hotcooler/templates/email/header.html
Normal file
23
hotcooler/templates/email/header.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{# HTML email header
|
||||
|
||||
.. note ::
|
||||
|
||||
If you wish to use the company logo here, so that it actually shows up in the opened email, don't serve it from your own server. Instead upload the logo to Google Drive and hot link it there. GMail and other email applications are little bit picky where they allow images come from.
|
||||
|
||||
For raw ``<img src>`` 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
|
||||
#}
|
||||
<tr>
|
||||
<td align="right">
|
||||
<h2>
|
||||
<a href="home">
|
||||
<img class="logo" src="{{ 'hotcooler:static/logo.png'|static_url }}" alt="{{ site_name }}">
|
||||
</a>
|
||||
</h2>
|
||||
</td>
|
||||
</tr>
|
24
hotcooler/templates/hotcooler/home.html
Normal file
24
hotcooler/templates/hotcooler/home.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
{# Template for home view #}
|
||||
|
||||
{% extends "site/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="jumbotron text-center">
|
||||
<h1>{{ site_name }}</h1>
|
||||
<p class="lead text-center">
|
||||
{{ site_tag_line }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if request.user %}
|
||||
<p id="demo-text">
|
||||
Welcome {{ request.user.friendly_name }}!
|
||||
</p>
|
||||
{% else %}
|
||||
<p id="demo-text">
|
||||
Welcome visitor!
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
20
hotcooler/templates/site/css.html
Normal file
20
hotcooler/templates/site/css.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{# Specify CSS section for in the site <head> #}
|
||||
|
||||
{# Include Bootstrap CSS from Websauna core package - http://getbootstrap.com/ #}
|
||||
<link rel="stylesheet" href="{{ 'websauna.system:static/bootstrap.min.css'|static_url }}">
|
||||
|
||||
{# Include Font-Awesome icons from CDN - http://fontawesome.io/ #}
|
||||
<link href="//netdna.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
|
||||
{# Include some default Websauna styles #}
|
||||
<link rel="stylesheet" href="{{ 'websauna.system:static/theme.css'|static_url }}">
|
||||
|
||||
{# Include hotcooler package theme #}
|
||||
<link rel="stylesheet" href="{{ 'hotcooler:static/theme.css'|static_url }}">
|
||||
|
||||
{# Include CSS for widgets #}
|
||||
{% if request.on_demand_resource_renderer %}
|
||||
{% for css_url in request.on_demand_resource_renderer.get_resources("css") %}
|
||||
<link rel="stylesheet" href="{{ css_url }}"></link>
|
||||
{% endfor %}
|
||||
{% endif %}
|
6
hotcooler/templates/site/logo.html
Normal file
6
hotcooler/templates/site/logo.html
Normal file
|
@ -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 #}
|
||||
<a class="navbar-brand" href="{{'home'|route_url}}">
|
||||
<img src="{{ 'hotcooler:static/logo.png'|static_url }}" alt="hotcooler">
|
||||
</a>
|
0
hotcooler/tests/__init__.py
Normal file
0
hotcooler/tests/__init__.py
Normal file
47
hotcooler/tests/test_login.py
Normal file
47
hotcooler/tests/test_login.py
Normal file
|
@ -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")
|
9
hotcooler/views.py
Normal file
9
hotcooler/views.py
Normal file
|
@ -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"}
|
26
setup.cfg
Normal file
26
setup.cfg
Normal file
|
@ -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
|
63
setup.py
Normal file
63
setup.py
Normal file
|
@ -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
|
||||
""",
|
||||
)
|
Loading…
Reference in a new issue