├── .gitignore ├── .readthedocs.txt ├── .travis.yml ├── MANIFEST.in ├── Makefile ├── Readme.rst ├── Todo.md ├── bin └── flask-diamond ├── docs ├── _extensions │ └── autoimage.py ├── _static │ ├── .gitignore │ ├── flask-diamond-logo.png │ ├── flask-diamond_libraries.graffle │ │ └── data.plist │ ├── flask-diamond_libraries.pdf │ ├── flask-diamond_libraries.png │ ├── flask-diamond_libraries.svg │ ├── flask-diamond_project.graffle │ │ └── data.plist │ ├── flask-diamond_project.pdf │ ├── flask-diamond_project.png │ ├── flask-diamond_project.svg │ ├── tavernier_blue-300.png │ └── tavernier_blue.png ├── _templates │ ├── .gitignore │ ├── sidebarintro.html │ └── sidebarlogo.html ├── _themes │ ├── .gitignore │ ├── LICENSE │ ├── README │ ├── flask │ │ ├── layout.html │ │ ├── relations.html │ │ ├── static │ │ │ └── flasky.css_t │ │ ├── theme.conf │ │ └── version.html │ ├── flask_small │ │ ├── layout.html │ │ ├── static │ │ │ └── flasky.css_t │ │ └── theme.conf │ └── flask_theme_support.py ├── accounts.rst ├── administration.rst ├── api.rst ├── blueprints.rst ├── changelog.rst ├── conf.py ├── configuration.rst ├── contributors.rst ├── crud.rst ├── debugging.rst ├── deploying.rst ├── designing-an-application.rst ├── documentation.rst ├── email.rst ├── facets.rst ├── helping.rst ├── index.rst ├── learn.rst ├── libraries-diagram.rst ├── libraries-included.rst ├── license.rst ├── makefile.rst ├── manage-py.rst ├── model-view-controller.rst ├── models.rst ├── philosophy.rst ├── quick-start.rst ├── requirements.rst ├── rest.rst ├── scaffold-diagram.rst ├── scaffolding.rst ├── schemas-and-migrations.rst ├── system-requirements.rst ├── testing.rst ├── tutorial-planets.rst ├── writing-an-application.rst └── wsgi.rst ├── flask_diamond ├── __init__.py ├── __meta__.py ├── facets │ ├── __init__.py │ ├── accounts.py │ ├── administration.py │ ├── blueprints.py │ ├── configuration.py │ ├── database.py │ ├── debugger.py │ ├── email.py │ ├── forms.py │ ├── handlers.py │ ├── logs.py │ ├── marshalling.py │ ├── rest.py │ ├── signals.py │ ├── task_queue.py │ └── webassets.py ├── mixins │ ├── __init__.py │ ├── crud.py │ └── marshmallow.py ├── models │ ├── __init__.py │ ├── role.py │ └── user.py ├── skels │ ├── app │ │ ├── +application.module+ │ │ │ ├── __init__.py.bob │ │ │ ├── __meta__.py.bob │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── alembic.ini.bob │ │ │ │ ├── env.py │ │ │ │ ├── script.py.mako │ │ │ │ └── versions │ │ │ │ │ ├── 20f04b9598da_flask-diamond-020.py │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── cf0f5b45967_user-and-role.py │ │ │ ├── models │ │ │ │ └── __init__.py.bob │ │ │ ├── tests │ │ │ │ ├── __init__.py │ │ │ │ ├── fixtures.py.bob │ │ │ │ ├── mixins.py.bob │ │ │ │ ├── test_basic.py.bob │ │ │ │ ├── test_facets.py.bob │ │ │ │ ├── test_models.py.bob │ │ │ │ ├── test_views.py.bob │ │ │ │ └── test_workflow.py.bob │ │ │ ├── views │ │ │ │ ├── __init__.py │ │ │ │ ├── administration │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── modelviews.py.bob │ │ │ │ │ ├── registration.py.bob │ │ │ │ │ └── templates │ │ │ │ │ │ ├── _wtf.html │ │ │ │ │ │ ├── admin │ │ │ │ │ │ ├── create_user.html │ │ │ │ │ │ └── login_base.html │ │ │ │ │ │ ├── base.html │ │ │ │ │ │ ├── fancy.html │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── plain.html │ │ │ │ │ │ └── security │ │ │ │ │ │ ├── base.html │ │ │ │ │ │ ├── change_password.html │ │ │ │ │ │ ├── forgot_password.html │ │ │ │ │ │ ├── login_user.html │ │ │ │ │ │ ├── register_user.html │ │ │ │ │ │ ├── reset_password.html │ │ │ │ │ │ └── send_confirmation.html │ │ │ │ └── diamond │ │ │ │ │ ├── __init__.py.bob │ │ │ │ │ ├── static │ │ │ │ │ ├── favicon.ico │ │ │ │ │ ├── index.html │ │ │ │ │ ├── js │ │ │ │ │ │ └── jquery.min.js │ │ │ │ │ ├── robots.txt │ │ │ │ │ └── style.css │ │ │ │ │ └── templates │ │ │ │ │ └── plain.html │ │ │ └── wsgi.py.bob │ │ ├── .gitignore │ │ ├── .mrbob.ini │ │ ├── .readthedocs.txt │ │ ├── .travis.yml │ │ ├── MANIFEST.in.bob │ │ ├── Makefile.bob │ │ ├── Readme.rst.bob │ │ ├── bin │ │ │ ├── manage.py.bob │ │ │ └── runserver.py.bob │ │ ├── docs │ │ │ ├── _static │ │ │ │ └── .gitignore │ │ │ ├── _templates │ │ │ │ └── .gitignore │ │ │ ├── conf.py.bob │ │ │ ├── index.rst.bob │ │ │ └── license.rst.bob │ │ ├── etc │ │ │ ├── conf │ │ │ │ ├── dev.conf.bob │ │ │ │ ├── production.conf.bob │ │ │ │ └── testing.conf.bob │ │ │ └── nose │ │ │ │ ├── test-all.cfg │ │ │ │ ├── test-single.cfg │ │ │ │ └── test.cfg │ │ ├── fabfile.py.bob │ │ ├── requirements.txt.bob │ │ ├── setup.py.bob │ │ └── var │ │ │ ├── db │ │ │ └── .gitignore │ │ │ └── log │ │ │ └── .gitignore │ └── tutorial-planets │ │ ├── +application.module+ │ │ ├── __init__.py.bob │ │ ├── api.py.bob │ │ ├── models │ │ │ ├── __init__.py.bob │ │ │ ├── planet.py.bob │ │ │ └── satellite.py.bob │ │ ├── tests │ │ │ ├── __init__.py.bob │ │ │ ├── fixtures.py.bob │ │ │ ├── test_crudmixin.py.bob │ │ │ ├── test_marshmallowmixin.py.bob │ │ │ └── test_models.py.bob │ │ └── views │ │ │ ├── base │ │ │ ├── __init__.py.bob │ │ │ └── templates │ │ │ │ └── index.html.bob │ │ │ └── frontend │ │ │ ├── __init__.py │ │ │ ├── forms.py.bob │ │ │ ├── simpleview.py.bob │ │ │ └── templates │ │ │ ├── _formhelpers.html │ │ │ └── hello.html.bob │ │ └── .mrbob.ini └── tests │ ├── __init__.py │ ├── mrbob.ini │ ├── test_app.py │ └── testing.conf ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /dist/ 3 | /*.egg-info/ 4 | -------------------------------------------------------------------------------- /.readthedocs.txt: -------------------------------------------------------------------------------- 1 | GitPython==1.0.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | # cache: pip 3 | python: 4 | - 2.7 5 | - 3.4 6 | - 3.5 7 | - 3.6 8 | # command to install dependencies 9 | install: make install 10 | # command to run tests 11 | script: make test 12 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft flask_diamond 2 | include *.cfg *.rst *.in *.txt *.ini *.mako *.html *.js *.css *.jpg *.gif 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Flask-Diamond (c) Ian Dennis Miller 2 | 3 | SHELL=/bin/bash 4 | MOD_NAME=flask_diamond 5 | WATCHMEDO_PATH=$$(which watchmedo) 6 | NOSETESTS_PATH=$$(which nosetests) 7 | TEST_CMD=SETTINGS=$$PWD/$(MOD_NAME)/tests/testing.conf $(NOSETESTS_PATH) $(MOD_NAME) 8 | 9 | install: 10 | python setup.py install 11 | 12 | requirements: 13 | pip install -r requirements.txt 14 | 15 | clean: 16 | rm -rf build dist *.egg-info 17 | find . -name '*.pyc' -delete 18 | find . -name __pycache__ -delete 19 | 20 | test: test-all test-scaffold 21 | @echo "OK" 22 | 23 | test-all: 24 | $(TEST_CMD) -c etc/nose/test-all.cfg 25 | 26 | single: 27 | $(TEST_CMD) -c etc/nose/test-single.cfg 2>&1 28 | 29 | test-scaffold: 30 | # create folder 31 | rm -rf build/test-app 32 | mkdir -p build/test-app 33 | 34 | # scaffold the app 35 | mrbob --config flask_diamond/tests/mrbob.ini -O build/test-app flask_diamond/skels/app 36 | cd build/test-app && make install test 37 | 38 | # planets tutorial 39 | mrbob --config flask_diamond/tests/mrbob.ini -O build/test-app flask_diamond/skels/tutorial-planets 40 | cd build/test-app && make install test 41 | 42 | docs: 43 | rm -rf build/sphinx 44 | SETTINGS=$$PWD/$(MOD_NAME)/tests/testing.conf sphinx-build -b html docs build/sphinx 45 | 46 | release: 47 | python setup.py sdist upload -r https://pypi.python.org/pypi 48 | 49 | .PHONY: install requirements clean test docs release 50 | -------------------------------------------------------------------------------- /Readme.rst: -------------------------------------------------------------------------------- 1 | Flask-Diamond 2 | ============= 3 | 4 | **Flask-Diamond** is a batteries-included Python Flask framework, sortof like Django but radically decomposable. 5 | **Flask-Diamond** offers some opinions about network information systems that process data. 6 | Using **Flask-Diamond**, you can scaffold a working application with sensible defaults, then easily override those defaults to meet your own goals. 7 | **Flask-Diamond** provides a shared vocabulary that helps teams coordinate as they scale up to develop multiple Flask applications while maintaining good code reuse and learning transfer. 8 | **Flask-Diamond** goes beyond a "project scaffold" by providing a complete architecture and team solution, including documentation, tutorials, and other learning support. 9 | 10 | Overview 11 | -------- 12 | 13 | A Flask-Diamond application consists of *facets*, which are common facilities that many applications eventually need to provide. 14 | The *facets* provided by Flask-Diamond include: 15 | 16 | - account management 17 | - administrative access 18 | - databases 19 | - Model object CRUD 20 | - email 21 | - testing 22 | - documentation 23 | - deployment 24 | - and more 25 | 26 | Usage 27 | ^^^^^ 28 | 29 | The following steps will create a new Flask-Diamond application. 30 | 31 | :: 32 | 33 | pip install Flask-Diamond 34 | mkdir my-application 35 | cd my-application 36 | flask-diamond scaffold app 37 | make install docs test db server 38 | 39 | Please read the `Introduction Documentation `_ to get started. 40 | 41 | Documentation 42 | ^^^^^^^^^^^^^ 43 | 44 | http://flask-diamond.org 45 | 46 | Quick Start Screencast 47 | ^^^^^^^^^^^^^^^^^^^^^^ 48 | 49 | .. image:: https://img.youtube.com/vi/dFp-YtV4898/0.jpg 50 | :alt: Flask-Diamond Quick Start 51 | :target: https://www.youtube.com/watch?v=dFp-YtV4898 52 | :align: center 53 | :height: 315px 54 | :width: 560px 55 | 56 | Length: 3:17 57 | -------------------------------------------------------------------------------- /Todo.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | - [ ] consider importing dodecahedron schema factory 4 | - [ ] tests for marshmallow 5 | - [ ] tests for facets 6 | - [ ] facets are inherited pythonically, not using the current wacky mechanism 7 | 8 | ## Done 9 | 10 | - [x] flask-diamond.sh is a python script (so it works on Windows too) 11 | -------------------------------------------------------------------------------- /bin/flask-diamond: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # flask-diamond (c) 2016 Ian Dennis Miller 4 | 5 | import click 6 | import pkg_resources 7 | import os 8 | import flask_diamond 9 | import string 10 | import random 11 | from flask_diamond.__meta__ import __version__ 12 | 13 | 14 | @click.group() 15 | @click.version_option(__version__) 16 | def cli(): 17 | "Flask-Diamond" 18 | 19 | 20 | @cli.command('scaffold', short_help='apply a scaffold') 21 | @click.argument('skel') 22 | def scaffold(skel): 23 | if skel in pkg_resources.resource_listdir('flask_diamond', 'skels'): 24 | filename = pkg_resources.resource_filename('flask_diamond', 'skels') 25 | pathname = os.path.dirname(os.path.abspath(flask_diamond.__file__)) 26 | print(os.path.join(pathname, filename)) 27 | 28 | if skel == "app": 29 | with open(".mrbob.ini", "w") as f: 30 | f.write("[defaults]\n") 31 | f.write("home_directory = {0}\n".format(os.environ["HOME"])) 32 | # f.write("flask_diamond_version = {0}\n".format(__version__)) 33 | chars = string.ascii_letters + string.digits + '^!$&=?+~#-_.:,;' 34 | secret_str = "".join([random.choice(chars) for _ in range(24)]) 35 | f.write("secret = \"{0}\"\n".format(secret_str)) 36 | f.write("hash_salt = {0}\n".format( 37 | "".join(random.choice(string.ascii_letters+string.digits) for _ in range(16)))) 38 | f.write("simple_password = {0}\n".format( 39 | "".join(random.choice(string.ascii_lowercase) for _ in range(3)))) 40 | 41 | cmd = "mrbob -w --config .mrbob.ini -O . {0}".format( 42 | os.path.join(pathname, filename, skel)) 43 | os.system(cmd) 44 | else: 45 | print("unrecognized skel: {0}".format(skel)) 46 | list_skels() 47 | 48 | 49 | @cli.command('list', short_help='list available skels.') 50 | def list_skels(): 51 | print("Available skels:\n") 52 | for skel in pkg_resources.resource_listdir('flask_diamond', 'skels'): 53 | print("- {0}".format(skel)) 54 | print("") 55 | 56 | 57 | if __name__ == '__main__': 58 | cli() 59 | -------------------------------------------------------------------------------- /docs/_extensions/autoimage.py: -------------------------------------------------------------------------------- 1 | # Pedro Kroger 2 | # https://gist.github.com/kroger/3856821 3 | # http://pedrokroger.net/using-sphinx-write-technical-books/ 4 | 5 | import os 6 | from docutils import nodes 7 | from docutils.parsers.rst import directives 8 | from docutils.parsers.rst.directives.images import Image 9 | 10 | 11 | def find_image(path, filename): 12 | fname = os.path.join(path, filename) 13 | if os.path.exists(fname + '.pdf'): 14 | return fname + '.pdf' 15 | elif os.path.exists(fname + '.png'): 16 | return fname + '.png' 17 | else: 18 | return False 19 | 20 | class Autoimage(Image): 21 | option_spec = {'scale-html': directives.percentage, 22 | 'scale-latex': directives.percentage, 23 | 'scale-epub2': directives.percentage, 24 | 'scale-mobi': directives.percentage, 25 | 'scale': directives.percentage, 26 | } 27 | 28 | def run(self): 29 | old_filename = self.arguments[0] 30 | env = self.state.document.settings.env 31 | builder_name = env.app.builder.name 32 | 33 | if builder_name == 'latex': 34 | self.arguments[0] = find_image(env.config.image_dir, old_filename) 35 | if env.config.black_and_white: 36 | bw_image = find_image(env.config.image_dir_black_white, old_filename) 37 | if bw_image: 38 | self.arguments[0] = bw_image 39 | else: 40 | self.arguments[0] = os.path.join(env.config.image_dir, old_filename + '.png') 41 | 42 | # this doesn't quite work because sphinx stores the previous 43 | # values and share among builds. If I do a make clean between 44 | # each run it works. Yuck. 45 | # I need to run sphinx-build with -E 46 | self.options['scale'] = self.options.get('scale-' + builder_name, 100) 47 | self.options['align'] = 'center' 48 | 49 | return super(Autoimage, self).run() 50 | 51 | 52 | def setup(app): 53 | app.add_directive('autoimage', Autoimage) 54 | app.add_config_value('image_dir', 'figs', False) 55 | app.add_config_value('black_and_white', False, True) 56 | app.add_config_value('image_dir_black_white', 'figs-bw', False) 57 | -------------------------------------------------------------------------------- /docs/_static/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/.gitignore -------------------------------------------------------------------------------- /docs/_static/flask-diamond-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/flask-diamond-logo.png -------------------------------------------------------------------------------- /docs/_static/flask-diamond_libraries.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/flask-diamond_libraries.pdf -------------------------------------------------------------------------------- /docs/_static/flask-diamond_libraries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/flask-diamond_libraries.png -------------------------------------------------------------------------------- /docs/_static/flask-diamond_project.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/flask-diamond_project.pdf -------------------------------------------------------------------------------- /docs/_static/flask-diamond_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/flask-diamond_project.png -------------------------------------------------------------------------------- /docs/_static/tavernier_blue-300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/tavernier_blue-300.png -------------------------------------------------------------------------------- /docs/_static/tavernier_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_static/tavernier_blue.png -------------------------------------------------------------------------------- /docs/_templates/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/docs/_templates/.gitignore -------------------------------------------------------------------------------- /docs/_templates/sidebarintro.html: -------------------------------------------------------------------------------- 1 |

Resources

2 | 8 | -------------------------------------------------------------------------------- /docs/_templates/sidebarlogo.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /docs/_themes/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /docs/_themes/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 by Armin Ronacher. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms of the theme, with or 6 | without modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | * The names of the contributors may not be used to endorse or 18 | promote products derived from this software without specific 19 | prior written permission. 20 | 21 | We kindly ask you to only use these themes in an unmodified manner just 22 | for Flask and Flask-related products, not for unrelated projects. If you 23 | like the visual style and want to use it for your own projects, please 24 | consider making some larger changes to the themes (such as changing 25 | font faces, sizes, colors or margins). 26 | 27 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE 37 | POSSIBILITY OF SUCH DAMAGE. 38 | -------------------------------------------------------------------------------- /docs/_themes/README: -------------------------------------------------------------------------------- 1 | Flask Sphinx Styles 2 | =================== 3 | 4 | This repository contains sphinx styles for Flask and Flask related 5 | projects. To use this style in your Sphinx documentation, follow 6 | this guide: 7 | 8 | 1. put this folder as _themes into your docs folder. Alternatively 9 | you can also use git submodules to check out the contents there. 10 | 2. add this to your conf.py: 11 | 12 | sys.path.append(os.path.abspath('_themes')) 13 | html_theme_path = ['_themes'] 14 | html_theme = 'flask' 15 | 16 | The following themes exist: 17 | 18 | - 'flask' - the standard flask documentation theme for large 19 | projects 20 | - 'flask_small' - small one-page theme. Intended to be used by 21 | very small addon libraries for flask. 22 | 23 | The following options exist for the flask_small theme: 24 | 25 | [options] 26 | index_logo = '' filename of a picture in _static 27 | to be used as replacement for the 28 | h1 in the index.rst file. 29 | index_logo_height = 120px height of the index logo 30 | github_fork = '' repository name on github for the 31 | "fork me" badge 32 | -------------------------------------------------------------------------------- /docs/_themes/flask/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | {%- block extrahead %} 3 | {{ super() }} 4 | {% if theme_touch_icon %} 5 | 6 | {% endif %} 7 | 8 | {% endblock %} 9 | {%- block relbar2 %}{% endblock %} 10 | {% block header %} 11 | {{ super() }} 12 | {% if pagename == 'index' %} 13 |
14 | {% endif %} 15 | {% endblock %} 16 | {%- block footer %} 17 | 22 | {% if pagename == 'index' %} 23 |
24 | {% endif %} 25 | {%- endblock %} 26 | -------------------------------------------------------------------------------- /docs/_themes/flask/relations.html: -------------------------------------------------------------------------------- 1 |

Topic Navigation

2 | 21 | -------------------------------------------------------------------------------- /docs/_themes/flask/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | pygments_style = flask_theme_support.FlaskyStyle 5 | 6 | [options] 7 | index_logo = 'flask-diamond-logo.png' 8 | index_logo_height = 120px 9 | touch_icon = 10 | -------------------------------------------------------------------------------- /docs/_themes/flask/version.html: -------------------------------------------------------------------------------- 1 |

Version

2 | 8 | -------------------------------------------------------------------------------- /docs/_themes/flask_small/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {% block header %} 3 | {{ super() }} 4 | {% if pagename == 'index' %} 5 |
6 | {% endif %} 7 | {% endblock %} 8 | {% block footer %} 9 | {% if pagename == 'index' %} 10 |
11 | {% endif %} 12 | {% endblock %} 13 | {# do not display relbars #} 14 | {% block relbar1 %}{% endblock %} 15 | {% block relbar2 %} 16 | {% if theme_github_fork %} 17 | Fork me on GitHub 19 | {% endif %} 20 | {% endblock %} 21 | {% block sidebar1 %}{% endblock %} 22 | {% block sidebar2 %}{% endblock %} 23 | -------------------------------------------------------------------------------- /docs/_themes/flask_small/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- flasky theme based on nature theme. 6 | * 7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'Georgia', serif; 18 | font-size: 17px; 19 | color: #000; 20 | background: white; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 40px auto 0 auto; 32 | width: 700px; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #ffffff; 41 | color: #3E4349; 42 | padding: 0 30px 30px 30px; 43 | } 44 | 45 | img.floatingflask { 46 | padding: 0 0 10px 10px; 47 | float: right; 48 | } 49 | 50 | div.footer { 51 | text-align: right; 52 | color: #888; 53 | padding: 10px; 54 | font-size: 14px; 55 | width: 650px; 56 | margin: 0 auto 40px auto; 57 | } 58 | 59 | div.footer a { 60 | color: #888; 61 | text-decoration: underline; 62 | } 63 | 64 | div.related { 65 | line-height: 32px; 66 | color: #888; 67 | } 68 | 69 | div.related ul { 70 | padding: 0 0 0 10px; 71 | } 72 | 73 | div.related a { 74 | color: #444; 75 | } 76 | 77 | /* -- body styles ----------------------------------------------------------- */ 78 | 79 | a { 80 | color: #004B6B; 81 | text-decoration: underline; 82 | } 83 | 84 | a:hover { 85 | color: #6D4100; 86 | text-decoration: underline; 87 | } 88 | 89 | div.body { 90 | padding-bottom: 40px; /* saved for footer */ 91 | } 92 | 93 | div.body h1, 94 | div.body h2, 95 | div.body h3, 96 | div.body h4, 97 | div.body h5, 98 | div.body h6 { 99 | font-family: 'Garamond', 'Georgia', serif; 100 | font-weight: normal; 101 | margin: 30px 0px 10px 0px; 102 | padding: 0; 103 | } 104 | 105 | {% if theme_index_logo %} 106 | div.indexwrapper h1 { 107 | text-indent: -999999px; 108 | background: url({{ theme_index_logo }}) no-repeat center center; 109 | height: {{ theme_index_logo_height }}; 110 | } 111 | {% endif %} 112 | 113 | div.body h2 { font-size: 180%; } 114 | div.body h3 { font-size: 150%; } 115 | div.body h4 { font-size: 130%; } 116 | div.body h5 { font-size: 100%; } 117 | div.body h6 { font-size: 100%; } 118 | 119 | a.headerlink { 120 | color: white; 121 | padding: 0 4px; 122 | text-decoration: none; 123 | } 124 | 125 | a.headerlink:hover { 126 | color: #444; 127 | background: #eaeaea; 128 | } 129 | 130 | div.body p, div.body dd, div.body li { 131 | line-height: 1.4em; 132 | } 133 | 134 | div.admonition { 135 | background: #fafafa; 136 | margin: 20px -30px; 137 | padding: 10px 30px; 138 | border-top: 1px solid #ccc; 139 | border-bottom: 1px solid #ccc; 140 | } 141 | 142 | div.admonition p.admonition-title { 143 | font-family: 'Garamond', 'Georgia', serif; 144 | font-weight: normal; 145 | font-size: 24px; 146 | margin: 0 0 10px 0; 147 | padding: 0; 148 | line-height: 1; 149 | } 150 | 151 | div.admonition p.last { 152 | margin-bottom: 0; 153 | } 154 | 155 | div.highlight{ 156 | background-color: white; 157 | } 158 | 159 | dt:target, .highlight { 160 | background: #FAF3E8; 161 | } 162 | 163 | div.note { 164 | background-color: #eee; 165 | border: 1px solid #ccc; 166 | } 167 | 168 | div.seealso { 169 | background-color: #ffc; 170 | border: 1px solid #ff6; 171 | } 172 | 173 | div.topic { 174 | background-color: #eee; 175 | } 176 | 177 | div.warning { 178 | background-color: #ffe4e4; 179 | border: 1px solid #f66; 180 | } 181 | 182 | p.admonition-title { 183 | display: inline; 184 | } 185 | 186 | p.admonition-title:after { 187 | content: ":"; 188 | } 189 | 190 | pre, tt { 191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 192 | font-size: 0.85em; 193 | } 194 | 195 | img.screenshot { 196 | } 197 | 198 | tt.descname, tt.descclassname { 199 | font-size: 0.95em; 200 | } 201 | 202 | tt.descname { 203 | padding-right: 0.08em; 204 | } 205 | 206 | img.screenshot { 207 | -moz-box-shadow: 2px 2px 4px #eee; 208 | -webkit-box-shadow: 2px 2px 4px #eee; 209 | box-shadow: 2px 2px 4px #eee; 210 | } 211 | 212 | table.docutils { 213 | border: 1px solid #888; 214 | -moz-box-shadow: 2px 2px 4px #eee; 215 | -webkit-box-shadow: 2px 2px 4px #eee; 216 | box-shadow: 2px 2px 4px #eee; 217 | } 218 | 219 | table.docutils td, table.docutils th { 220 | border: 1px solid #888; 221 | padding: 0.25em 0.7em; 222 | } 223 | 224 | table.field-list, table.footnote { 225 | border: none; 226 | -moz-box-shadow: none; 227 | -webkit-box-shadow: none; 228 | box-shadow: none; 229 | } 230 | 231 | table.footnote { 232 | margin: 15px 0; 233 | width: 100%; 234 | border: 1px solid #eee; 235 | } 236 | 237 | table.field-list th { 238 | padding: 0 0.8em 0 0; 239 | } 240 | 241 | table.field-list td { 242 | padding: 0; 243 | } 244 | 245 | table.footnote td { 246 | padding: 0.5em; 247 | } 248 | 249 | dl { 250 | margin: 0; 251 | padding: 0; 252 | } 253 | 254 | dl dd { 255 | margin-left: 30px; 256 | } 257 | 258 | pre { 259 | padding: 0; 260 | margin: 15px -30px; 261 | padding: 8px; 262 | line-height: 1.3em; 263 | padding: 7px 30px; 264 | background: #eee; 265 | border-radius: 2px; 266 | -moz-border-radius: 2px; 267 | -webkit-border-radius: 2px; 268 | } 269 | 270 | dl pre { 271 | margin-left: -60px; 272 | padding-left: 60px; 273 | } 274 | 275 | tt { 276 | background-color: #ecf0f3; 277 | color: #222; 278 | /* padding: 1px 2px; */ 279 | } 280 | 281 | tt.xref, a tt { 282 | background-color: #FBFBFB; 283 | } 284 | 285 | a:hover tt { 286 | background: #EEE; 287 | } 288 | -------------------------------------------------------------------------------- /docs/_themes/flask_small/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | nosidebar = true 5 | pygments_style = flask_theme_support.FlaskyStyle 6 | 7 | [options] 8 | index_logo = '' 9 | index_logo_height = 120px 10 | github_fork = '' 11 | -------------------------------------------------------------------------------- /docs/_themes/flask_theme_support.py: -------------------------------------------------------------------------------- 1 | # flasky extensions. flasky pygments style based on tango style 2 | from pygments.style import Style 3 | from pygments.token import Keyword, Name, Comment, String, Error, \ 4 | Number, Operator, Generic, Whitespace, Punctuation, Other, Literal 5 | 6 | 7 | class FlaskyStyle(Style): 8 | background_color = "#f8f8f8" 9 | default_style = "" 10 | 11 | styles = { 12 | # No corresponding class for the following: 13 | #Text: "", # class: '' 14 | Whitespace: "underline #f8f8f8", # class: 'w' 15 | Error: "#a40000 border:#ef2929", # class: 'err' 16 | Other: "#000000", # class 'x' 17 | 18 | Comment: "italic #8f5902", # class: 'c' 19 | Comment.Preproc: "noitalic", # class: 'cp' 20 | 21 | Keyword: "bold #004461", # class: 'k' 22 | Keyword.Constant: "bold #004461", # class: 'kc' 23 | Keyword.Declaration: "bold #004461", # class: 'kd' 24 | Keyword.Namespace: "bold #004461", # class: 'kn' 25 | Keyword.Pseudo: "bold #004461", # class: 'kp' 26 | Keyword.Reserved: "bold #004461", # class: 'kr' 27 | Keyword.Type: "bold #004461", # class: 'kt' 28 | 29 | Operator: "#582800", # class: 'o' 30 | Operator.Word: "bold #004461", # class: 'ow' - like keywords 31 | 32 | Punctuation: "bold #000000", # class: 'p' 33 | 34 | # because special names such as Name.Class, Name.Function, etc. 35 | # are not recognized as such later in the parsing, we choose them 36 | # to look the same as ordinary variables. 37 | Name: "#000000", # class: 'n' 38 | Name.Attribute: "#c4a000", # class: 'na' - to be revised 39 | Name.Builtin: "#004461", # class: 'nb' 40 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp' 41 | Name.Class: "#000000", # class: 'nc' - to be revised 42 | Name.Constant: "#000000", # class: 'no' - to be revised 43 | Name.Decorator: "#888", # class: 'nd' - to be revised 44 | Name.Entity: "#ce5c00", # class: 'ni' 45 | Name.Exception: "bold #cc0000", # class: 'ne' 46 | Name.Function: "#000000", # class: 'nf' 47 | Name.Property: "#000000", # class: 'py' 48 | Name.Label: "#f57900", # class: 'nl' 49 | Name.Namespace: "#000000", # class: 'nn' - to be revised 50 | Name.Other: "#000000", # class: 'nx' 51 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword 52 | Name.Variable: "#000000", # class: 'nv' - to be revised 53 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised 54 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised 55 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised 56 | 57 | Number: "#990000", # class: 'm' 58 | 59 | Literal: "#000000", # class: 'l' 60 | Literal.Date: "#000000", # class: 'ld' 61 | 62 | String: "#4e9a06", # class: 's' 63 | String.Backtick: "#4e9a06", # class: 'sb' 64 | String.Char: "#4e9a06", # class: 'sc' 65 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment 66 | String.Double: "#4e9a06", # class: 's2' 67 | String.Escape: "#4e9a06", # class: 'se' 68 | String.Heredoc: "#4e9a06", # class: 'sh' 69 | String.Interpol: "#4e9a06", # class: 'si' 70 | String.Other: "#4e9a06", # class: 'sx' 71 | String.Regex: "#4e9a06", # class: 'sr' 72 | String.Single: "#4e9a06", # class: 's1' 73 | String.Symbol: "#4e9a06", # class: 'ss' 74 | 75 | Generic: "#000000", # class: 'g' 76 | Generic.Deleted: "#a40000", # class: 'gd' 77 | Generic.Emph: "italic #000000", # class: 'ge' 78 | Generic.Error: "#ef2929", # class: 'gr' 79 | Generic.Heading: "bold #000080", # class: 'gh' 80 | Generic.Inserted: "#00A000", # class: 'gi' 81 | Generic.Output: "#888", # class: 'go' 82 | Generic.Prompt: "#745334", # class: 'gp' 83 | Generic.Strong: "bold #000000", # class: 'gs' 84 | Generic.Subheading: "bold #800080", # class: 'gu' 85 | Generic.Traceback: "bold #a40000", # class: 'gt' 86 | } 87 | -------------------------------------------------------------------------------- /docs/accounts.rst: -------------------------------------------------------------------------------- 1 | Facet: Accounts 2 | =============== 3 | 4 | *User* accounts are a common requirement for many applications. Another common requirement is *Roles*, which can be used to grant certain users access to specific functions. Both Users and Roles are provided in Flask-Diamond by `Flask-Security `_, which provides a nice interface for controlling these models. 5 | 6 | User and Role Models 7 | -------------------- 8 | 9 | Flask-Diamond is already set up with :class:`flask_diamond.models.user` and :class:`flask_diamond.models.role`, which provide everything needed for a simple setup. You will notice in your application that ``models/__init__.py`` then imports these classes from Flask-Diamond. In this manner, your application will incorporate these models into its own schema. 10 | 11 | Overloading the User Model 12 | -------------------------- 13 | 14 | With an application of moderate complexity, it may be necessary to store additional user data, and certain data may just be simple to put in the User model directly. The following code snippet describes an ``init_security()`` function that can be used to implement your own User model. 15 | 16 | :: 17 | 18 | def init_security(self): 19 | self.super(app_models=models) 20 | 21 | With that in place, ensure ``models__init__.py`` imports your own User model instead of importing from Flask-Diamond. 22 | 23 | Flask-Admin Integration 24 | ----------------------- 25 | 26 | You can use user accounts in lots of ways, but one common pattern for Users is to create a password-protected user interface. To make this easier, the Flask-Diamond scaffold includes templates in ``views/administration/templates/security`` that permit you to customize the look of login, password maintenance, account registration, and password reset. The templates already inherit from Flask-Admin, but by editing the templates in your project, you can customize them to other scenarios too. 27 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | API 2 | === 3 | 4 | This documentation is for Flask-Diamond |version|. 5 | 6 | .. _diamond-object: 7 | 8 | Diamond object 9 | -------------- 10 | 11 | .. automodule:: flask_diamond 12 | :members: 13 | :undoc-members: 14 | :show-inheritance: 15 | 16 | models 17 | ------ 18 | 19 | models.user 20 | ^^^^^^^^^^^ 21 | 22 | .. automodule:: flask_diamond.models.user 23 | :members: 24 | :undoc-members: 25 | :show-inheritance: 26 | 27 | models.role 28 | ^^^^^^^^^^^ 29 | 30 | .. automodule:: flask_diamond.models.role 31 | :members: 32 | :undoc-members: 33 | :show-inheritance: 34 | 35 | mixins 36 | ------ 37 | 38 | mixins.crud 39 | ^^^^^^^^^^^ 40 | 41 | .. automodule:: flask_diamond.mixins.crud 42 | :members: 43 | :undoc-members: 44 | :show-inheritance: 45 | 46 | mixins.marshmallow 47 | ^^^^^^^^^^^^^^^^^^ 48 | 49 | .. automodule:: flask_diamond.mixins.marshmallow 50 | :members: 51 | :undoc-members: 52 | :show-inheritance: 53 | 54 | facets 55 | ------ 56 | 57 | .. automodule:: flask_diamond.facets 58 | :members: 59 | :undoc-members: 60 | :show-inheritance: 61 | 62 | facets.accounts 63 | ^^^^^^^^^^^^^^^ 64 | 65 | .. automodule:: flask_diamond.facets.accounts 66 | :members: 67 | :undoc-members: 68 | :show-inheritance: 69 | 70 | facets.administration 71 | ^^^^^^^^^^^^^^^^^^^^^ 72 | 73 | .. automodule:: flask_diamond.facets.administration 74 | :members: init_administration 75 | :show-inheritance: 76 | 77 | facets.blueprints 78 | ^^^^^^^^^^^^^^^^^ 79 | 80 | .. automodule:: flask_diamond.facets.blueprints 81 | :members: 82 | :undoc-members: 83 | :show-inheritance: 84 | 85 | facets.configuration 86 | ^^^^^^^^^^^^^^^^^^^^ 87 | 88 | .. automodule:: flask_diamond.facets.configuration 89 | :members: 90 | :undoc-members: 91 | :show-inheritance: 92 | 93 | facets.database 94 | ^^^^^^^^^^^^^^^ 95 | 96 | .. automodule:: flask_diamond.facets.database 97 | :members: 98 | :undoc-members: 99 | :show-inheritance: 100 | 101 | facets.debugger 102 | ^^^^^^^^^^^^^^^ 103 | 104 | .. automodule:: flask_diamond.facets.debugger 105 | :members: 106 | :undoc-members: 107 | :show-inheritance: 108 | 109 | facets.email 110 | ^^^^^^^^^^^^ 111 | 112 | .. automodule:: flask_diamond.facets.email 113 | :members: 114 | :undoc-members: 115 | :show-inheritance: 116 | 117 | facets.forms 118 | ^^^^^^^^^^^^ 119 | 120 | .. automodule:: flask_diamond.facets.forms 121 | :members: 122 | :undoc-members: 123 | :show-inheritance: 124 | 125 | facets.handlers 126 | ^^^^^^^^^^^^^^^ 127 | 128 | .. automodule:: flask_diamond.facets.handlers 129 | :members: 130 | :undoc-members: 131 | :show-inheritance: 132 | 133 | facets.logs 134 | ^^^^^^^^^^^ 135 | 136 | .. automodule:: flask_diamond.facets.logs 137 | :members: 138 | :undoc-members: 139 | :show-inheritance: 140 | 141 | facets.marshalling 142 | ^^^^^^^^^^^^^^^^^^ 143 | 144 | .. automodule:: flask_diamond.facets.marshalling 145 | :members: 146 | :undoc-members: 147 | :show-inheritance: 148 | 149 | facets.rest 150 | ^^^^^^^^^^^ 151 | 152 | .. automodule:: flask_diamond.facets.rest 153 | :members: 154 | :undoc-members: 155 | :show-inheritance: 156 | 157 | facets.signals 158 | ^^^^^^^^^^^^^^ 159 | 160 | .. automodule:: flask_diamond.facets.signals 161 | :members: 162 | :undoc-members: 163 | :show-inheritance: 164 | 165 | facets.task_queue 166 | ^^^^^^^^^^^^^^^^^ 167 | 168 | .. automodule:: flask_diamond.facets.task_queue 169 | :members: 170 | :undoc-members: 171 | :show-inheritance: 172 | 173 | facets.webassets 174 | ^^^^^^^^^^^^^^^^ 175 | 176 | .. automodule:: flask_diamond.facets.webassets 177 | :members: 178 | :undoc-members: 179 | :show-inheritance: 180 | -------------------------------------------------------------------------------- /docs/contributors.rst: -------------------------------------------------------------------------------- 1 | Contributors 2 | ============ 3 | 4 | Flask-Diamond is a volunteer effort. This page discusses the Contributors Agreement and Contributors List. This page is also the official document for signing up as a contributor to the Flask-Diamond project. To learn more about how you can volunteer, please read :doc:`helping`. 5 | 6 | Contributors Agreement 7 | ---------------------- 8 | 9 | Contributors to the Flask-Diamond project affirm: 10 | 11 | - Because the :doc:`license` chosen for publishing Flask-Diamond complies with the principles of Open Source Software; and 12 | - Because works contributed to this project could become part of Flask-Diamond; and 13 | - Because all contributions to the project are either 1) the original work of the contributor; or 2) include proper attribution to the original author as permitted by applicable Open Source licenses; and 14 | - Because the Flask-Diamond copyright may be transferred to a qualified Open Source organization in the future; therefore 15 | 16 | For the benefit of the Flask-Diamond project: 17 | 18 | - Contributors will assign the rights to their contributed works to the the Flask-Diamond author, Ian Dennis Miller; and 19 | - Contributors will add their name or well-known pseudonym to the :ref:`contributors-list`. 20 | 21 | Contributors to the Flask-Diamond project signify acceptance of the Contributors Agreement by submitting a git pull request appending themselves to the :ref:`contributors-list`. 22 | 23 | .. _contributors-list: 24 | 25 | Contributors List 26 | ----------------- 27 | 28 | - `iandennismiller `_ 29 | -------------------------------------------------------------------------------- /docs/crud.rst: -------------------------------------------------------------------------------- 1 | CRUD: Create, Read, Update, Delete 2 | ================================== 3 | 4 | Create Read Update Delete 5 | ------------------------- 6 | 7 | CRUD stands for Create, Read, Update, and Delete. 8 | The CRUD pattern complements :doc:`Model-View-Controller ` by providing a standard set of methods that can be applied to most types of models. 9 | CRUD acts like a simple API for models in Flask-Diamond. 10 | The four CRUD actions describe the life-cycle of a model object: 11 | 12 | #. First, an object is **created** with certain attribute values. 13 | #. The object may be **read** to get the value of those object attributes. 14 | #. The object values may be **updated** to update the model based on changes in the world. 15 | #. Finally, the object is **deleted** to remove it from the database. 16 | 17 | CRUDMixin 18 | --------- 19 | 20 | :class:`flask_diamond.mixins.crud.CRUDMixin`, which has been adapted from `Flask-Kit `_, will extend your model with functions for ``create()``, ``read()``, ``update()``, and ``delete()``. 21 | The default Flask-Diamond scaffold provides an example of CRUDMixin usage: 22 | 23 | .. code-block:: python 24 | 25 | from flask.ext.diamond.utils.mixins import CRUDMixin 26 | from flask.ext.diamond import db 27 | 28 | class Planet(db.Model, CRUDMixin): 29 | id = db.Column(db.Integer, primary_key=True) 30 | name = db.Column(db.String(255)) 31 | mass = db.Column(db.Integer) 32 | 33 | Notice that ``class Planet()`` inherits both ``db.Model`` and ``CRUDMixin``. 34 | Now the following CRUD workflow is possible: 35 | 36 | .. code-block:: python 37 | 38 | earth = Planet.create(name="earth", mass=200) 39 | print "{name} has mass of {mass}".format(earth) 40 | earth.update({'name': "Earth"}) 41 | print "Its name is {name}".format(myself) 42 | earth.delete() 43 | 44 | Flask-Admin CRUD 45 | ---------------- 46 | 47 | `Flask-Admin `_ provides Model CRUD functionality with its `BaseModelView `_ class. 48 | ``BaseModelView`` is an extremely powerful tool for rapidly implementing a web-based CRUD Graphical User Interface that makes it easy to create, read, update, and delete model objects using a web browser. 49 | 50 | The following application instantiates a CRUD for the ``Planet`` model described above. 51 | 52 | .. code-block:: python 53 | 54 | from flask_diamond import Diamond 55 | from flask_diamond.administration import AdminModelView 56 | from .models import User, Role, Planet 57 | 58 | class my_diamond_app(Diamond): 59 | 60 | def init_administration(self): 61 | admin = self.super("administration", user=User, role=Role) 62 | admin.add_view(AdminModelView( 63 | Planet, 64 | db.session, 65 | name="Planet", 66 | category="Admin") 67 | ) 68 | 69 | Further Reading 70 | --------------- 71 | 72 | - See :doc:`models` for a more detailed examination of Models. 73 | - See :doc:`administration` for a more detailed examination of GUIs. 74 | -------------------------------------------------------------------------------- /docs/debugging.rst: -------------------------------------------------------------------------------- 1 | Facet: Debugger 2 | =============== 3 | 4 | To simplify debugging, Flask-Diamond provides a basic setup for logging so that your application can write trace data to a file for your inspection. Flask-Diamond also integrates `Flask-DebugToolbar `_ to provide a powerful debugger shell directly in the interface. 5 | 6 | Logs 7 | ---- 8 | 9 | Flask-Diamond creates a logging object during initialization. You can write log messages to this object with the following basic code: 10 | 11 | :: 12 | 13 | flask.current_app.logger.debug("this is a debug-level message") 14 | 15 | flask.current_app.logger.warn("important! this is a warning message") 16 | 17 | The destination for log files is controlled in the configuration file. During development, ``dev.conf`` will write log messages to the var/logs path of your project. 18 | 19 | Log Level 20 | ^^^^^^^^^ 21 | 22 | During debugging, it is sometimes useful to get extra information about your application while it is running. The *log level* is used to control how critical logging is. When the level is ``DEBUG``, then all messages are printed - this can be verbose. When the level is ``INFO``, then debugging messages are not printed at all, but general information messages are. When the level is ``WARN``, then only important messages are printed. This level may be suitable for production. 23 | 24 | DebugToolbar 25 | ------------ 26 | 27 | `Flask-DebugToolbar `_ is a useful web gadget that can help developers to diagnose problems within the browser. In practice, you may end up doing most of your development in a terminal, but the Debug Toolbar can nevertheless be handy for casual projects. 28 | 29 | In the application configuration file, whenever ``DEBUG=True`` the DebugToolbar will be active. By default, the development configuration enables debugtoolbar whereas the production configuration disables it. It is possible to completely remove the DebugToolbar by ensuring the ``debugger`` extension is not loaded at all during :func:`flask_diamond.Diamond.init_app()`. 30 | -------------------------------------------------------------------------------- /docs/deploying.rst: -------------------------------------------------------------------------------- 1 | IT Operations with Fabric 2 | ========================= 3 | 4 | There is frequently a split between the application development environment and the live deployment environment. When the application ends up running on a remote host while you are doing your work on a workstation, it can be helpful to simplify remote system operations. Flask-Diamond uses `Fabric `_ to make it pretty easy to invoke system functions from the command line. 5 | 6 | Using Fabric 7 | ------------ 8 | 9 | To use the Flask-Diamond Fabric functionality, navigate to the root directory of the project and issue the following command: 10 | 11 | :: 12 | 13 | fab help 14 | 15 | This will list all of the available commands. 16 | 17 | Flask-Diamond Fabric Commands 18 | ----------------------------- 19 | 20 | By default, Flask-Diamond provides a ``fabfile.py`` with the scaffold with the following functionality: 21 | 22 | - **rsync**: copy files directly from the working directory to the remote host 23 | - **pull**: on the remote host, execute ``git pull`` in the application directory 24 | - **setup**: run ``make install`` on the remote host 25 | - **ipython**: enter an ipython shell on the remote host 26 | - **shell**: open a bash shell on the remote host 27 | - **restart**: restart the remote application server 28 | - **nginx_restart**: restert the remote web server 29 | - **logs**: view the application logs 30 | 31 | Customizing Fabric 32 | ------------------ 33 | 34 | Because all systems are different, it is not too likely that all of the commands in ``fabfile.py`` will work. However, this at least provides a starting point. 35 | -------------------------------------------------------------------------------- /docs/designing-an-application.rst: -------------------------------------------------------------------------------- 1 | Designing a Flask-Diamond Application 2 | ===================================== 3 | 4 | Key Design Documents 5 | -------------------- 6 | 7 | When it is time to actually work on your application, it can save time to sketch designs before implementing code. 8 | 9 | Entity Relationship Diagram 10 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 11 | 12 | The Model can be described using an Entity Relationship Diagram (ERDs). An ERD is... 13 | 14 | Wireframes 15 | ^^^^^^^^^^ 16 | 17 | The Views can be described using Wireframes. 18 | 19 | Sitemap 20 | ^^^^^^^ 21 | 22 | Some of the Controller functionality can be captured using a Sitemap. -------------------------------------------------------------------------------- /docs/documentation.rst: -------------------------------------------------------------------------------- 1 | Documentation 2 | ============= 3 | 4 | As projects grow in size, the need to create documentation increases. The `Sphinx Project `_ has put forth a very robust Python documentation solution. As a result, Flask-Diamond provides a starter template for creating your own documentation using Sphinx. 5 | 6 | Where are the files 7 | ------------------- 8 | 9 | You will find a starter set of documentation in the ``/docs`` folder of your project. The first file you need to edit is called ``/docs/index.rst``, and from there build the table of contents to describe your project. Images and media may be placed in ``/docs/_static``. These source files will be transformed into a documentation website that can be browsed online. 10 | 11 | Sphinx 12 | ------ 13 | 14 | Sphinx is a structured document generator application. As a project author, you create documents that describe your project, and Sphinx will take care of the Table of Contents, links between documents, and all of the other parts that make it easy for somebody to navigate the documentation. Sphinx is particularly well suited to creating documentation websites, in which case Sphinx will automatically apply a theme to every page. The end result of using Sphinx is that your project will have an extremely functional online documentation website. 15 | 16 | Restructured Text 17 | ^^^^^^^^^^^^^^^^^ 18 | 19 | Documentation in Sphinx is written using a markup language called `ReStructured Text `_ (`quickref `_). While this language has some similarities to `Markdown `_, it has many extensions that are suited to document structuring. As a result, Restructured Text makes it easy enough to manage links between pages, make references to specific Python classes, and add footnotes. 20 | 21 | In my experience, the learning curve on Restructured Text is steeper than I expected. As a result, you need to persevere, but the payoff is totally worth it. The best place to start is with the `ReStructured Text `_ documentation from the Sphinx website. 22 | 23 | autodoc 24 | ^^^^^^^ 25 | 26 | Sphinx can use the `autodoc extension `_ to look at your project's file structure and determine all of the classes in your project. This can be extremely useful for generating API documentation. Sphinx can also extract method and object signatures, which makes it easy to describe all of the parameters for everything. 27 | 28 | docstrings 29 | ^^^^^^^^^^ 30 | 31 | Sphinx autodoc is also able to scan your source code for comments that have been formatted for Sphinx using Restructured Text. Here is an example taken from the :func:`flask_diamond.facets.accounts.init_accounts` Flask-Diamond source code: 32 | 33 | :: 34 | 35 | def init_accounts(self, app_models=None): 36 | """ 37 | Initialize Security for application. 38 | 39 | :param kwargs: parameters that will be passed through to Flask-Security 40 | :type kwargs: dict 41 | :returns: None 42 | 43 | >>> def ext_security(self): 44 | >>> super(MyApp, self).ext_security(confirm_register_form=CaptchaRegisterForm) 45 | """ 46 | 47 | The comment above will be used to create documentation by Sphinx. Special directives like ``:param:`` are used to specify details about the documentation that *autodoc* cannot determine on its own. 48 | 49 | Building the documentation 50 | -------------------------- 51 | 52 | In the project on the command line: 53 | 54 | :: 55 | 56 | make docs 57 | 58 | Output 59 | ^^^^^^ 60 | 61 | The results of ``make docs`` will end up in ``var/sphinx/build``. You can open that folder in your web browser to view the documentation. 62 | 63 | ReadTheDocs 64 | ^^^^^^^^^^^ 65 | 66 | It is easy to integrate Flask-Diamond applications with readthedocs.org due to the use of Sphinx. A special file called ``.readthedocs.txt`` is included with the project scaffold. This file contains a reduced set of Python requirements that may make it easier for RTD to build your project. 67 | -------------------------------------------------------------------------------- /docs/email.rst: -------------------------------------------------------------------------------- 1 | Facet: Email 2 | ============ 3 | 4 | Lots of applications need to send emails in the course of their operation. Email remains one of the best sources of *identity* on the Internet today. `Flask-Mail `_ is an easy way to integrate email-sending capabilities into your application. 5 | 6 | Configuration 7 | ------------- 8 | 9 | By default, :class:`flask_diamond.facets.email` is already enabled in :func:`flask_diamond.Diamond.init_app`, so there is nothing special to do. The project created by ``flask-diamond`` has email commented out by default, so you will have to enable this in your project's ``__init__.py`` file. 10 | 11 | The settings for the SMTP server are located in your configuration file. The relevant lines are these: 12 | 13 | :: 14 | 15 | MAIL_SERVER = '127.0.0.1' 16 | MAIL_PORT = 25 17 | MAIL_USE_TLS = False 18 | MAIL_USERNAME = None 19 | MAIL_PASSWORD = None 20 | 21 | You will need to set this configuration based on your particular email setup. 22 | 23 | SMTP Server 24 | ----------- 25 | 26 | In order for your application to be able to send emails, you will need access to an `SMTP server `_. It is possible to use an email provider such as Gmail, but be aware that you won't be able to route a very high volume of email through a service like that. Often times, you can use a web hosting provider for higher volumes. Since every situation is different, you will need to track down your mail server configuration on your own. 27 | -------------------------------------------------------------------------------- /docs/facets.rst: -------------------------------------------------------------------------------- 1 | Application Facets 2 | ================== 3 | 4 | A Flask-Diamond application consists of many components (called *facets*) that complement each other to form a coherent application. These facets are common to many applications, but they can be enabled and disabled individually depending on specific requirements. A Flask-Diamond application is customized by changing the way these facets behave. 5 | 6 | List of Facets 7 | -------------- 8 | 9 | Here are all of the facets currently shipping with Flask-Diamond: 10 | 11 | #. :class:`flask_diamond.facets.configuration`. the ``$SETTINGS`` environment variable is inspected and the file it points to is loaded. 12 | #. :class:`flask_diamond.facets.logs`. based on the configuration, write log messages to a file on the filesystem. 13 | #. :class:`flask_diamond.facets.database`. connect to a database and initialize the SQLAlchemy Object Relational Mapper (ORM) 14 | #. :class:`flask_diamond.facets.accounts`. manage users, roles, login, passwords, and other security things with Flask-Security. 15 | #. :class:`flask_diamond.facets.blueprints`. initialize your application's views (in the MVC sense), which are saved as "blueprints" in a Flask application. 16 | #. :class:`flask_diamond.facets.signals`. Flask provides a signals subsystem that your application can hook into to automate certain behaviors. 17 | #. :class:`flask_diamond.facets.forms`. initialize your application's form helpers, which may be global to the forms used in your application. 18 | #. :class:`flask_diamond.facets.handlers`. when something goes wrong, you may want to handle it (e.g. by displaying a 404 page). This is the place to create redirections or other custom request handlers that extend beyond views. 19 | #. :class:`flask_diamond.facets.administration`. a quick GUI using Flask-Admin with extensive Model support. 20 | #. :class:`flask_diamond.facets.rest`. provide a REST API using Flask-RESTful 21 | #. :class:`flask_diamond.facets.webassets`. it is possible to bundle assets like images, CSS, and javascript with your application. webassets simplifies some of this work. 22 | #. :class:`flask_diamond.facets.email`. send email with Flask-Mail 23 | #. :class:`flask_diamond.facets.debugger`. when the configuration specifies that ``DEBUG = True``, the web interface will display a widget with extra debugging tools. 24 | #. :class:`flask_diamond.facets.task_queue`. provide a task queue using Celery 25 | 26 | Next Steps 27 | ---------- 28 | 29 | Now that you know what facets are, learn how to use them by :doc:`writing-an-application`. 30 | -------------------------------------------------------------------------------- /docs/helping.rst: -------------------------------------------------------------------------------- 1 | How to Contribute to the Project 2 | ================================ 3 | 4 | Flask-Diamond welcomes contributions from everybody, and there are several ways you can help! The first step is always to clone the `project repository `_ using git. If you haven't done this yet, then do it now. Then, give the rest of this document a read for some specific ideas about contributing. 5 | 6 | Implement your favourite features 7 | --------------------------------- 8 | 9 | If you know a feature you'd like to code in order to help with Flask-Diamond, then the easiest way to help is by submitting a `pull request `_ to Flask-Diamond on GitHub. From your perspective, there is no barrier preventing you from contributing to Flask-Diamond today. 10 | 11 | Help with the documentation 12 | --------------------------- 13 | 14 | Project documentation is one of the most important aspects of an open source project. You can help immensely by editing the current documentation for clarity. You can find the documentation in the `Flask-Diamond repository `_, which you can modify by submitting a pull request. 15 | 16 | Help with an existing issue 17 | --------------------------- 18 | 19 | Another way you can contribute is by working directly upon issues that have been submitted by the community using the `Issue Tracker `_. This is more advanced than simply implementing a new feature, because the issue may specify acceptance criteria. It is recommended that you coordinate with project members before working on an issue. The easiest way to contact the team is through the `Issue Tracker `_ itself, because each issue has a comment thread associated with it. 20 | 21 | Submit your first pull request 22 | ------------------------------ 23 | 24 | The only requirement for your first pull request that you must add your name to the :ref:`contributors-list`, signifying your acceptance of the *Flask-Diamond Contributors Agreement*. 25 | 26 | Become a project developer 27 | -------------------------- 28 | 29 | If you have proven yourself to consistently deliver great work on Flask-Diamond, then you should join the development team! The team uses a `Project Kanban `_ to coordinate its development efforts. In order to join the team, you must be familiar with `Agile-Diamond `_, which is the project management framework used for working on Flask-Diamond. Other than that, you must simply open an issue in the `Issue Tracker `_ requesting to become a team member. 30 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../Readme.rst 2 | 3 | Get Started 4 | ----------- 5 | 6 | **Start here**. These introductory readings will teach you how to install Flask-Diamond and how to read the rest of the Flask-Diamond documentation. 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | quick-start 12 | learn 13 | system-requirements 14 | 15 | .. -basics: 16 | 17 | Basics 18 | ------ 19 | 20 | Learn how to create a new project. This section introduces the Flask-Diamond philosophy and the first steps for making an application. 21 | 22 | .. toctree:: 23 | :maxdepth: 1 24 | 25 | tutorial-planets 26 | scaffolding 27 | scaffold-diagram 28 | writing-an-application 29 | philosophy 30 | 31 | Developers Guide 32 | ---------------- 33 | 34 | Learn common development patterns. 35 | 36 | .. toctree:: 37 | :maxdepth: 1 38 | 39 | model-view-controller 40 | crud 41 | schemas-and-migrations 42 | documentation 43 | testing 44 | libraries-included 45 | libraries-diagram 46 | 47 | .. Tutorials 48 | .. --------- 49 | .. 50 | .. .. toctree:: 51 | .. 52 | .. tutorial/model-view-controller-architecture-with-flask-diamond 53 | .. developer/designing-a-flask-diamond-application 54 | 55 | .. -developer-guide: 56 | 57 | Facets Guide 58 | ------------ 59 | 60 | This section describes the facets of Flask-Diamond. 61 | 62 | .. toctree:: 63 | :maxdepth: 1 64 | 65 | facets 66 | accounts 67 | administration 68 | blueprints 69 | models 70 | debugging 71 | email 72 | rest 73 | 74 | .. -user-guide: 75 | 76 | Operation Guide 77 | --------------- 78 | 79 | The User Guide topics are for managing and installing a Flask-Diamond application. In particular, IT Ops and Deployment Engineers will benefit from this section. 80 | 81 | .. toctree:: 82 | :maxdepth: 1 83 | 84 | requirements 85 | configuration 86 | makefile 87 | manage-py 88 | wsgi 89 | deploying 90 | 91 | API Reference 92 | ------------- 93 | 94 | .. toctree:: 95 | :maxdepth: 3 96 | 97 | api 98 | 99 | Open Source Software 100 | -------------------- 101 | 102 | .. toctree:: 103 | :maxdepth: 1 104 | 105 | helping 106 | contributors 107 | license 108 | changelog 109 | 110 | Online Resources 111 | ---------------- 112 | 113 | - `GitHub Project Page `_ 114 | - `Issue Tracker `_ 115 | - `Python Project on PyPI `_ 116 | - `Diamond Methods `_ 117 | -------------------------------------------------------------------------------- /docs/learn.rst: -------------------------------------------------------------------------------- 1 | Learn Flask-Diamond 2 | =================== 3 | 4 | The best way to learn Flask-Diamond is to read the :ref:`developers-guide`; contained within is a growing list of documents that explain various aspects of the Flask-Diamond approach. To help new learners focus on the fundamentals, read the following documents first. 5 | 6 | - :doc:`quick-start` shows you how to scaffold a new application. You can be up and running in just a few minutes. 7 | - :doc:`scaffolding` offers many options for you to customize your application. Read this document to learn more about answering the questions during scaffolding. 8 | - :doc:`facets` describes the use of Flask-Diamond's *facets* for customizing your application's behavior. 9 | - :doc:`writing-an-application` provides examples and describes an approach for designing and programming an application that achieves your goals. 10 | - :doc:`model-view-controller` is a more advanced document that describes the Flask-Diamond architecture. Model-View-Controller (MVC) is widely used in software engineering to write applications that provide a user interface. Once you understand how to implement MVC using Flask-Diamond, you will be able to write applications for a wide range of domains. 11 | 12 | Learning Python 13 | --------------- 14 | 15 | If you are relatively new to Python, you may find that there are advanced computer science principles that Flask-Diamond requires some knowledge of. In particular, Flask-Diamond applications are heavily `Object Oriented `_ and make extensive use of `Inheritance `_. 16 | 17 | There are many tutorials online that can help you to learn about these fundamentals. In particular, I recommend the following: 18 | 19 | - `Dive Into Python `_ 20 | - `Learn Python the Hard Way `_ 21 | -------------------------------------------------------------------------------- /docs/libraries-diagram.rst: -------------------------------------------------------------------------------- 1 | Diagram of Libraries 2 | ==================== 3 | 4 | Flask-Diamond is composed of many other libraries. The following diagram is an effort to describe how these modules are organized. The :download:`SVG diagram of Flask-Diamond libraries ` is also available for download. 5 | 6 | .. image:: /_static/flask-diamond_libraries.png 7 | -------------------------------------------------------------------------------- /docs/libraries-included.rst: -------------------------------------------------------------------------------- 1 | Libraries Included in Flask-Diamond 2 | =================================== 3 | 4 | Flask-Diamond pulls from many different Flask extensions, which are all listed in this document. A significant factor for inclusion with Flask-Diamond is the existence of good documentation. Most of the following libraries therefore provide extensive documentation that can help you to understand everything in greater detail. 5 | 6 | Database/Model 7 | -------------- 8 | 9 | - `Flask-SQLAlchemy `_: "Flask-SQLAlchemy is an extension for Flask that adds support for SQLAlchemy to your application. It requires SQLAlchemy 0.6 or higher. It aims to simplify using SQLAlchemy with Flask by providing useful defaults and extra helpers that make it easier to accomplish common tasks." 10 | - `Flask-Migrate `_: "Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command line arguments for Flask-Script." 11 | - `Flask-Marshmallow `_: "Flask-Marshmallow is a thin integration layer for Flask (a Python web framework) and marshmallow (an object serialization/deserialization library) that adds additional features to marshmallow, including URL and Hyperlinks fields for HATEOAS-ready APIs. It also (optionally) integrates with Flask-SQLAlchemy." 12 | 13 | Development 14 | ----------- 15 | 16 | - `Flask-Testing `_: "The Flask-Testing extension provides unit testing utilities for Flask." 17 | - `Flask-DebugToolbar `_: "This extension adds a toolbar overlay to Flask applications containing useful information for debugging." 18 | - `Flask-DbShell `_: "The extension provides facilites for implementing Django-like ./manage.py dbshell command" 19 | - `Flask-Script `_: "The Flask-Script extension provides support for writing external scripts in Flask. This includes running a development server, a customised Python shell, scripts to set up your database, cronjobs, and other command-line tasks that belong outside the web application itself." 20 | 21 | Security 22 | -------- 23 | 24 | - `Flask-Security `_: "Flask-Security allows you to quickly add common security mechanisms to your Flask application." 25 | - `Flask-Login `_: "Flask-Login provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering your users’ sessions over extended periods of time." 26 | 27 | Doing Stuff 28 | ----------- 29 | 30 | - `Flask-Celery-Helper `_: "Celery support for Flask without breaking PyCharm inspections." 31 | - `Flask-Mail `_: "The Flask-Mail extension provides a simple interface to set up SMTP with your Flask application and to send messages from your views and scripts." 32 | 33 | Interfaces 34 | ---------- 35 | 36 | - `Flask-Admin `_: "In a world of micro-services and APIs, Flask-Admin solves the boring problem of building an admin interface on top of an existing data model. With little effort, it lets you manage your web service’s data through a user-friendly interface." 37 | - `Flask-RESTful `_: "Flask-RESTful is an extension for Flask that adds support for quickly building REST APIs. It is a lightweight abstraction that works with your existing ORM/libraries. Flask-RESTful encourages best practices with minimal setup. If you are familiar with Flask, Flask-RESTful should be easy to pick up." 38 | - `Flask-WTF `_: "Flask-WTF offers simple integration with `WTForms `_." 39 | 40 | Presentation 41 | ------------ 42 | 43 | - `Flask-Assets `_: "Flask-Assets helps you to integrate `webassets `_ into your Flask application." 44 | - `Flask-Markdown `_: "Flask-Markdown adds support for Markdown to your Flask application. There is little to no documentation for it, but it works just the same as markdown would normally." 45 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | The MIT License (MIT) 5 | 6 | Flask-Diamond 7 | 8 | Copyright (c) 2014-2017 Ian Dennis Miller 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | -------------------------------------------------------------------------------- /docs/makefile.rst: -------------------------------------------------------------------------------- 1 | ``Makefile`` Explanation 2 | ======================== 3 | 4 | The Makefile that ships with Flask-Diamond by default includes a number of targets that address several common tasks throughout the life cycle of a project. The way to use the Makefile is with the ``make`` command. Thus, to install the project with make, you'd invoke ``make install``. 5 | 6 | During development, the Makefile is one of the primary ways for you to interact with your project. You may find yourself running ``make db server`` or perhaps ``make single`` with some regularity. It is recommended to become familiar with the Flask-Diamond Makefile. 7 | 8 | Integration 9 | ----------- 10 | 11 | These targets control project builds. 12 | 13 | - ``install``: Use setup.py to install the project (typically inside a virtualenv). 14 | - ``clean``: Delete all of the temporary files created by ``install``. 15 | - ``docs``: Use `Sphinx `_ to render the documentation in ``etc/sphinx``. 16 | 17 | Development 18 | ----------- 19 | 20 | These targets are used to run the application with the ``dev.conf`` profile. 21 | 22 | - ``server``: Invoke the HTTP server in debug mode with ``dev.conf`` 23 | - ``shell``: Enter a python (or ipython) shell within the virtualenv. 24 | - ``notebook``: Use ipython notebook, if installed, to inspect the application. 25 | 26 | Testing 27 | ------- 28 | 29 | These targets are used to run automated tests. 30 | 31 | - ``test``: Run all of the tests using nosetests. 32 | - ``single``: Run only tests marked with the ``@attr("single")`` decorator. 33 | - ``watch``: Enter a loop that watches for any project source code to be changed, and then automatically run any tests marked with the ``@attr("single")`` decorator. 34 | 35 | Databases 36 | --------- 37 | 38 | These targets control the database. By default these use the ``dev.conf`` profile so as to avoid inadvertently changing the production database. 39 | 40 | - ``db``: drop the database, re-create the database, and populate the database with starter values. 41 | - ``newmigration``: when the data model schema has changed, use ``newmigration`` to create a new data migration. 42 | - ``migrate``: apply all data migrations, in order, until the database is up to date. 43 | -------------------------------------------------------------------------------- /docs/manage-py.rst: -------------------------------------------------------------------------------- 1 | ``manage.py`` Explanation 2 | ========================= 3 | 4 | Many applications will want to expose some functionality through a command line interface, and ``bin/manage.py`` provides an easy way to accomplish this. For example: 5 | 6 | - certain administrative tasks could be triggered from the command line 7 | - other tasks can be automated using a tool like ``cron`` 8 | 9 | Many of the targets within the Flask-Diamond Makefile are actually wrappers for ``manage.py``, but you can invoke it manually on the command line like so: 10 | 11 | :: 12 | 13 | SETTINGS=$PWD/etc/conf/dev.conf bin/manage.py runserver 14 | 15 | The following commands come with Flask-Diamond by default. 16 | 17 | Commands 18 | -------- 19 | 20 | - ``shell``: Launch the Python REPL (or iPython if installed) using `Flask-Script `_. By default, the following objects will be imported into the namespace: 21 | 22 | - ``app``: your app's Flask-Diamond object 23 | - ``db``: your app's database object 24 | - ``model``: your app's model 25 | 26 | - ``runserver``: Launch your application's HTTP server. When ``runserver`` is invoked, it will bind to ``localhost``. The ``PORT`` your application listens on is defined in the :doc:`configuration`. 27 | 28 | - ``publicserver``: Like ``runserver`` but public. This causes the server to bind to ``0.0.0.0`` so that remote hosts can connect to your application. This is intended for development purposes, and is not recommended for deployment. See :doc:`wsgi` for more information about running a public web service. 29 | 30 | - ``db``: This command acts as the entry point for `Flask-Migrate `_. The subcommands available, taken directly from the command output, are: 31 | 32 | - ``upgrade``: Upgrade to a later version 33 | - ``heads``: Show current available heads in the script directory 34 | - ``show``: Show the revision denoted by the given symbol. 35 | - ``migrate``: Alias for 'revision --autogenerate' 36 | - ``stamp``: 'stamp' the revision table with the given revision; don't run any migrations 37 | - ``current``: Display the current revision for each database. 38 | - ``merge``: Merge two revisions together. Creates a new migration file 39 | - ``init``: Generates a new migration 40 | - ``downgrade``: Revert to a previous version 41 | - ``branches``: Show current branch points 42 | - ``history``: List changeset scripts in chronological order. 43 | - ``revision``: Create a new revision file. 44 | 45 | - ``useradd``: Add a user to the users database via `Flask-Security `_. This accepts the following arguments: 46 | 47 | - ``email``: (required) 48 | - ``password``: (required) 49 | - ``admin``: *True/False* Should this user have the *Admin* role? 50 | 51 | - ``userdel``: Delete a user from the users database. 52 | 53 | - ``init_db``: Drop the existing database using `Flask-SQLAlchemy `_ and re-create it. This obviously destroys anything in the database, resetting it to its original state. 54 | -------------------------------------------------------------------------------- /docs/model-view-controller.rst: -------------------------------------------------------------------------------- 1 | MVC: Model, View, Controller 2 | ============================ 3 | 4 | Model-View-Controller (MVC) is a popular architecture for designing applications that have a user interface. 5 | At its heart, MVC is a collection of `software design patterns `_ that provide a vocabulary for designing your application. 6 | When you "speak MVC," other people who also know MVC will understand what you are saying. 7 | 8 | The MVC vocabulary consists of: 9 | 10 | - **Models**: a way for talking about data 11 | - **Views**: a way for talking about user interfaces 12 | - **Controllers**: a way for talking about program logic 13 | 14 | This document presents an overview of Model-View-Controller and links to more detailed documentation that discusses these ideas in greater detail. 15 | 16 | Model 17 | ----- 18 | 19 | A model is usually named after a noun. 20 | A model is a data representation of something that exists, and just about anything that exists can be modeled. 21 | 22 | Entities and Relationships 23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 24 | 25 | To model a solar system, you'd start with a model of Planets and Satellites, which are the *entities* we will be dealing with. 26 | A planet can have many satellites, so there is a relationship between our entities. 27 | Using nothing more than the idea of "Planets" and "Satellites", you can go a long way towards modeling a solar system. 28 | 29 | A complete data model consists of entities and the relationships between those entities. 30 | 31 | - **Entities**: An Entity is a type of object. Entities have attributes, which are characteristics of the Entity. 32 | In :doc:`tutorial-planets`, a Planet is a an Entity and so is a Satellite. 33 | An attribute of a Planet is its "mass"; the mass of a planet is stored in the data model alongside the name of the planet. 34 | Since a planet can have a name, *name* is therefore also an attribute of a Planet. 35 | - **Relationships**: Entities can affect one another through relationships. 36 | In :doc:`tutorial-planet`, a Planet can have many Satellites. 37 | Since a Planet can have many Satellites, we call this a "one-to-many" Relationship. 38 | There are also one-to-one and many-to-many relationships. 39 | 40 | A model can therefore be described using an Entity-Relationship Diagram, which shows all of the types of objects, their attributes, and the way entities relate to one another. 41 | Read more about :doc:`models` for a more detailed discussion and code examples. 42 | 43 | A Philosophy of Models 44 | ^^^^^^^^^^^^^^^^^^^^^^ 45 | 46 | A model might be a very simple representation of a real thing, or the model might be very detailed. 47 | A model of an entire country's economy might require lots of detail, whereas a model of a school district might be relatively simpler. 48 | 49 | A model is in some ways a platonic ideal of the actual domain being modeled. While things in the "real world" are irregular in an uncountable number of ways, our models are perfectly regular. Since models are stored in a database, all of the model attributes can be lined up nicely into rows and columns. Tidy! 50 | 51 | Paradoxically, a model is always an imperfect representation of the thing it is modeling. The irregularities of the real world are difficult to capture using a model. The goal for good model creation is to isolate the parts of the model that are regular so as to reduce the number of exceptions to your model. 52 | 53 | Sometimes, we talk about "domains" when we talk about models, because our models might be thematically related to one another. A domain might be something like *finance*, *gaming*, *email*, or any other broad category that people build applications for. To properly model a domain, we might talk to a "domain expert" to learn more about the kinds of models we are building. 54 | 55 | View 56 | ---- 57 | 58 | A view is a user interface that can present data that comes from a model. In classic MVC, the model pushes data to the view, and the view knows how to update itself to display the data that was received from the model. Views can also contain input elements like buttons, fields, and sliders. When these input elements are activated, the Controller must decide how to respond. Views are often written as templates that have placeholders for data. For web programming, a View template is frequently written using HTML [#f1]_. Read more about :doc:`blueprints` for a more detailed discussion and code examples. 59 | 60 | Controller 61 | ---------- 62 | 63 | A controller responds to input by changing a view or model. A common type of controller is driven with a Graphical User Interface, which uses things like menus, fields, and buttons so that a human can click stuff to get things done. Read more about :doc:`administration` for a more detailed discussion. 64 | 65 | A different type of controller is an API, which is typically used by other software (rather than a human) to make the application do something. Read more about :doc:`rest` for a more detailed discussion. 66 | 67 | .. rubric:: Footnotes 68 | 69 | .. [#f1] There's nothing inherently special about HTML templates for constructing Views. For example, Android Views can be constructed using Java. The point is that the idea of *views* can be generalized to any platform. -------------------------------------------------------------------------------- /docs/philosophy.rst: -------------------------------------------------------------------------------- 1 | Philosophy of Flask-Diamond 2 | =========================== 3 | 4 | *Flask-Diamond provides a path that can guide your thought and development. Flask-Diamond is the road that leads to other ideas.* 5 | 6 | The following principles serve to guide the development of Flask-Diamond. 7 | 8 | Inheritance 9 | ----------- 10 | 11 | Flask-Diamond is primarily configured via Pythonic Inheritance. Your main application object will inherit from Flask-Diamond, and any functions you wish to customize must be overridden in order to change their behavior. 12 | 13 | Economy 14 | ------- 15 | 16 | There are tons of libraries wrapped up in Flask-Diamond. If something has been done well by somebody else, then it is always preferable to leverage that work instead of re-building an unnecessary component. 17 | 18 | Stable 19 | ------ 20 | 21 | Flask-Diamond versions are tied closely to specific versions of third-party libraries. When you stick with a specific version of Flask-Diamond, you can lock the version of third-party libraries too. The goal is to prevent any requirements from changing accidentally so that your application will be more resistant to code rot and API breakage. 22 | 23 | Decomposable 24 | ------------ 25 | 26 | Because third-party libraries are constantly changing, it is sometimes desirable to upgrade just one library without upgrading anything else. Flask-Diamond is built atop the Flask ecosystem, which is architecturally decomposable. Thus, extensions may be upgraded individually. 27 | 28 | Data-centric 29 | ------------ 30 | 31 | Flask-Diamond was originally built to support the research objectives of `Ian Dennis Miller `_. Through his work on memes and social networks, Ian regularly needed to build lightweight API access to big data sets. Due to the precise nature of the SQL queries needed, a powerful standalone ORM like SQLAlchemy was preferred over the Django approach of bundling the ORM with the web platform. Thus, Flask-Diamond is suitable for projects that require raw access to SQL queries. 32 | 33 | Sensible 34 | -------- 35 | 36 | Flask-Diamond is pretty sensible about the defaults it presents. A lot of decisions have already been made in the service of delivering a functioning application out-of-the-box. The goal is to enable a focus upon the unique parts of your application, rather than the common tasks that most applications need anyway. 37 | -------------------------------------------------------------------------------- /docs/quick-start.rst: -------------------------------------------------------------------------------- 1 | Quick Start 2 | =========== 3 | 4 | Flask-Diamond installs in a Python environment with :doc:`virtualenv `. Please see the :doc:`system-requirements` for information about installation pre-requisites. 5 | 6 | Screencast 7 | ---------- 8 | 9 | .. raw:: html 10 | 11 |
12 | 13 |
14 | 15 | Length: 3:13 16 | 17 | Install Flask-Diamond 18 | --------------------- 19 | 20 | Create a :doc:`virtualenv ` for your application and install Flask-Diamond. 21 | 22 | :: 23 | 24 | mkdir my-application 25 | cd my-application 26 | mkvirtualenv -a . my-application 27 | pip install Flask-Diamond 28 | 29 | If any of these steps do not work, review the :doc:`system-requirements` and ensure everything is installed. 30 | 31 | Scaffold a new Flask-Diamond application 32 | ---------------------------------------- 33 | 34 | Enter the virtual environment and scaffold a new Flask-Diamond application. For this *Quick Start*, just use the default options. 35 | 36 | :: 37 | 38 | workon my-application 39 | flask-diamond scaffold app 40 | 41 | Use it! 42 | ------- 43 | 44 | Test the installation to ensure everything is installed correctly: 45 | 46 | :: 47 | 48 | make test 49 | 50 | Create the application database: 51 | 52 | :: 53 | 54 | make db 55 | 56 | Start the application server: 57 | 58 | :: 59 | 60 | make server 61 | 62 | You now have a server running at http://127.0.0.1:5000. Depending on your OS, the following command will open your application in a browser. 63 | 64 | :: 65 | 66 | open http://localhost:5000 67 | 68 | Login with the following account details: 69 | 70 | - username: **admin@example.com** 71 | - password: **the simple_password specified during scaffolding** 72 | 73 | Next Steps 74 | ---------- 75 | 76 | Now that you have scaffolded your first Flask-Diamond application, you can read about how to :doc:`learn` by using the online documentation and materials. 77 | If you prefer to learn by experimenting with a live example, see :doc:`tutorial-planets` for a hands-on introduction to an working Flask-Diamond application. 78 | -------------------------------------------------------------------------------- /docs/requirements.rst: -------------------------------------------------------------------------------- 1 | Requirements Management with ``virtualenv`` 2 | =========================================== 3 | 4 | The preferred way to manage your Python requirements is with virtualenv. The preferred way to manage virtualenvs is with virtualenvwrapper, which provides a regularized interface for creating and using virtualenvs. 5 | 6 | When you install your Flask-Diamond application inside a virtualenv, you are able to "freeze" the state of your installed Python libraries. This way, your application will never suffer from broken requirements due to a system-wide upgrade. This document describes the typical workflow for using virtualenv to manage a Flask-Diamond project. 7 | 8 | Pre-requisites 9 | -------------- 10 | 11 | You need to install the minimum :doc:`system-requirements` in order to have the necessary tools for this process. It is assumed that you now have ``workon`` and ``mkvirtualenv`` available. If you cannot call these commands in a terminal, then you should double-check your :doc:`system-requirements`. 12 | 13 | Making a New ``virtualenv`` 14 | --------------------------- 15 | 16 | The way to initialize a new virtualenv is with ``mkvirtualenv``, which is described on the `mkvirtualenv documentation `_. The following example demonstrates creating a virtualenv called *my-diamond-app*. 17 | 18 | :: 19 | 20 | $ mkvirtualenv my-diamond-app 21 | New python executable in my-diamond-app/bin/python2.7 22 | Also creating executable in my-diamond-app/bin/python 23 | Installing setuptools, pip...done. 24 | (my-diamond-app) $ 25 | 26 | Upon creating the virtualenv, it will be activated and your shell prompt changes to include your project name as a prefix. You can verify that you are inside the virtualenv by echoing an environment variable to the screen: 27 | 28 | :: 29 | 30 | (my-diamond-app) $ echo $VIRTUAL_ENV 31 | ~/.virtualenvs/my-diamond-app 32 | 33 | Using it 34 | -------- 35 | 36 | When you need to work on your project, you must activate your project's virtualenv using ``workon``. After calling ``workon``, your shell's search path will be updated so that scripts from your virtualenv will be called before any globally installed scripts. 37 | 38 | :: 39 | 40 | $ workon my-diamond-app 41 | (my-diamond-app) $ 42 | 43 | In order to leave the virtualenv and return to a normal shell, use ``deactivate``. 44 | 45 | :: 46 | 47 | (my-diamond-app) $ deactivate 48 | $ 49 | 50 | ``pip`` and Installation 51 | ------------------------ 52 | 53 | When you use ``pip`` inside your virtualenv, it will automatically install packages locally, instead of installing them at the system level. This way, it's easy to install anything without needing root access. In the following example, we are inside our virtualenv and we want to upgrade pip: 54 | 55 | :: 56 | 57 | (my-diamond-app) $ pip install --force -U pip 58 | Collecting pip 59 | Downloading pip-7.1.0-py2.py3-none-any.whl (1.1MB) 60 | 100% |████████████████████████████████| 1.1MB 172kB/s 61 | Installing collected packages: pip 62 | Successfully installed pip-7.1.0 63 | (my-diamond-app) $ 64 | 65 | Freezing Python Requirements 66 | ---------------------------- 67 | 68 | ``pip`` provides a handy mechanism for printing all of the installed libraries, along with their versions. When called within your virtualenv, it produces a snapshot of all your project requirements so you can easily re-install them later. 69 | 70 | :: 71 | 72 | (my-diamond-app) $ pip freeze 73 | Jinja2==2.7.3 74 | MarkupSafe==0.23 75 | mr.bob==0.1.1 76 | requests==2.7.0 77 | virtualenv==1.11.6 78 | virtualenvwrapper==4.2 79 | (my-diamond-app) $ 80 | 81 | You can also store your requirements in a requirements file. 82 | 83 | :: 84 | 85 | (my-diamond-app) $ pip freeze > requirements.txt 86 | 87 | 88 | ``Makefile`` support 89 | -------------------- 90 | 91 | By default, Flask-Diamond provides ``make install``, which will use requirements.txt to install your project's pre-requisites automatically. 92 | -------------------------------------------------------------------------------- /docs/rest.rst: -------------------------------------------------------------------------------- 1 | Facet: REST 2 | =========== 3 | 4 | One of the most common ways to build an API is using a paradigm called `REST `_. 5 | `Flask-RESTful `_ is a great library for Flask that simplifies the creation of a REST API. 6 | This document describes REST and how to implement it in a Flask-Diamond project. 7 | 8 | REST in a Nutshell 9 | ------------------ 10 | 11 | Representational State Transfer, usually abbreviated REST, can be simplified as follows: 12 | 13 | - The web consists of documents that are referred to by URL links. 14 | - Each URL is a "thing". As a part of speech, it is a noun. For example: a URL points to a picture, a document, or a person's timeline. 15 | - The language spoken by web browsers and web servers is called HTTP. This is the protocol for "hypertext". 16 | - HTTP provides actions (verbs) like GET, PUT, DELETE, and POST. 17 | - Using HTTP and URLs provides verbs and nouns that we can use to write statements, like "GET a Picture" or "DELETE a Document". 18 | 19 | REST is a strong statement about the grammar of the web. 20 | It tells us where the nouns are and where the verbs are. 21 | If we learn to make APIs that are *RESTful*, then we can count on others to be able to use our APIs in natural ways to create complex expressions. 22 | 23 | Examples of REST 24 | ^^^^^^^^^^^^^^^^ 25 | 26 | Let's say you are building an API for a solar system and you need to manage planets for observation. 27 | Our API could then expose one URL endpoint for retrieving all planets: ``/api/planet_list``. 28 | We could then add a planet to our solar system with an HTTP POST containing the following data: 29 | 30 | :: 31 | 32 | { 33 | 'name': 'Venus', 34 | 'mass': '90.0' 35 | } 36 | 37 | Such a POST could be issued using javascript, from the command line (e.g. with ``curl``), or using any HTTP client. 38 | That's the beauty of REST: if you work with the web, the web can work with you. 39 | 40 | Flask-RESTful 41 | ------------- 42 | 43 | It is easy to create RESTful APIs with `Flask-RESTful `_, which is already integrated into Flask-Diamond. 44 | 45 | The primary way to use Flask-RESTful is to create Resource objects. A Resource is a "thing" that your API will expose. You can easily add API Resources to your Flask-Diamond application by placing them into a function called *init_rest()*: 46 | 47 | :: 48 | 49 | from flask_diamond import db 50 | from .mixins import DiamondTestCase 51 | from ..models import Planet 52 | 53 | def init_rest(): 54 | class PlanetResource(Resource): 55 | def get(self, name): 56 | return models.planet.find(name=name).dumps() 57 | 58 | rest.add_resource(PlanetResource, '/api/planet/') 59 | 60 | This simple example creates a resource called PlanetResource. 61 | Then, it specifies a way to handle the GET verb, which will result in retrieving a planet. 62 | Finally, the resource is exposed using a URL: ``/api/planet/...``. 63 | 64 | MarshmallowMixin 65 | ---------------- 66 | 67 | :class:`flask_diamond.mixins.marshmallow.MarshmallowMixin` simplifies object marshalling, which is the process of mapping data to and from a serialization format like JSON. 68 | This functionality is provided by `Marshmallow `_ and `Flask-Marshmallow `_. 69 | 70 | Marshalling is useful because applications must frequently send model data across the Internet, and in order to do so, models are commonly translated into JSON or another format. 71 | For example, date and time objects are native Python objects that must be converted to a string in order for JSON to transmit them. 72 | Marshalling makes serialization and deserialization into a repeatable process. 73 | 74 | Let's look at the following line of code: 75 | 76 | :: 77 | 78 | return models.planet.get(id).dumps() 79 | 80 | The *dumps()* at the end of the line will cause an ORM model object to be converted to JSON using Marshmallow. 81 | For more information, please see the documentation for `Marshmallow `_ and `Flask-Marshmallow `_. 82 | 83 | How not to REST 84 | --------------- 85 | 86 | REST says we should not create URLs that imply actions; URLs must be things. This pattern is common in older websites. 87 | For example, you should not build an API with a URL called ``/api/rename_planet`` because that describes an action: changing a planet's name. 88 | REST says the only actions available are HTTP verbs, like GET and PUT. 89 | 90 | You can think about it this way too: when a web client visits a URL, it usually issues the GET verb. 91 | It doesn't really make sense to ``GET /api/rename_planet`` because that's actually something you want to push to the server, not get from the server. 92 | However, early web developers tried to make this work using arguments, which resulted in operations like ``GET /api/move_piece?from=A2&to=C3``. 93 | These became really long URLs that contained all the same information as the RESTful example, but which broke certain features of the Internet. 94 | For example, if a web spider visited that URL, the web server would incorrectly interpret that visit as a command to move a piece. 95 | This leads to chaos. 96 | Don't make URLs this way. 97 | Be RESTful, instead. 98 | -------------------------------------------------------------------------------- /docs/scaffold-diagram.rst: -------------------------------------------------------------------------------- 1 | Diagram of a Flask-Diamond Scaffold 2 | =================================== 3 | 4 | When a new Flask-Diamond project is scaffolded, it will generate files in roughly the following layout. The :download:`SVG scaffold diagram ` is also available for download. 5 | 6 | .. image:: /_static/flask-diamond_project.png 7 | -------------------------------------------------------------------------------- /docs/system-requirements.rst: -------------------------------------------------------------------------------- 1 | System Requirements 2 | =================== 3 | 4 | Flask-Diamond requires some software to be installed in order to function. Once you have installed these requirements, you can follow the :doc:`quick-start` to start your first project. The following packages should be installed globally, as the superuser, for all users on the system to access. 5 | 6 | - `Python 2.7.x `_ or `3.4 and above `_. 7 | - Python development libraries (i.e. header files for compiling C code) 8 | - `pip `_ 9 | - `virtualenv `_ 10 | - `virtualenvwrapper `_ 11 | 12 | The following sections describe the process for installing these requirements on various systems. In each of the following examples, it is assumed you will be using a root account (or some other privileged account). 13 | 14 | If you do not have root access, then refer to the section :ref:`unprivileged-installation` for information about creating a virtualenv in your user account. 15 | 16 | Debian/Ubuntu 17 | ------------- 18 | 19 | Flask-Diamond installs cleanly on Debian and Ubuntu systems released after 2011. 20 | 21 | :: 22 | 23 | apt-get install python python-dev python-pip build-essential 24 | apt-get install sqlite-dev 25 | pip install --upgrade pip 26 | pip install --upgrade virtualenv 27 | pip install virtualenvwrapper 28 | 29 | Redhat 30 | ------ 31 | 32 | Flask-Diamond can be installed on RedHat, but ensure your package manager is installing Python 2.7; as of August 2015, RHEL provides an older version. 33 | 34 | :: 35 | 36 | yum install python python-devel python-pip 37 | yum install sqlite-devel 38 | pip install --upgrade pip 39 | pip install --upgrade virtualenv 40 | pip install virtualenvwrapper 41 | 42 | OSX with Homebrew 43 | ----------------- 44 | 45 | Flask-Diamond installs pretty easily on OSX with Homebrew. Make sure you are using the *admin* user for this process, just like a normal Homebrew operation. 46 | 47 | :: 48 | 49 | brew install python --universal --framework 50 | brew install pyenv-virtualenv 51 | brew install pyenv-virtualenvwrapper 52 | brew install sqlite 53 | pip install --upgrade pip 54 | 55 | Windows with Cygwin 56 | ------------------- 57 | 58 | **Note**: Have you done this install successfully? Please share your process as a comment on `Issue 8 `_. 59 | 60 | Here are a few resources to get you started: 61 | 62 | - http://www.pdxpixel.com/blog/setting-up-python-and-virtualenv-windows-cygwin/ 63 | - http://atbrox.com/2009/09/21/how-to-get-pipvirtualenvfabric-working-on-cygwin/ 64 | - http://anythingsimple.blogspot.ca/2010/04/using-pip-virtualenv-and.html 65 | - http://stackoverflow.com/questions/2173963/how-do-i-get-virtualenvwrapper-and-cygwin-to-co-operate 66 | 67 | .. _unprivileged-installation: 68 | 69 | Unprivileged Installation 70 | ------------------------- 71 | 72 | Sometimes, you do not have root access to the system. It is still possible to use Flask-Diamond, but the installation process is slightly different because it does not use virtualenvwrapper. Instead, you will create your virtualenv directly and use the `activate` macro to work on it. 73 | 74 | :: 75 | 76 | curl -O https://raw.github.com/pypa/virtualenv/master/virtualenv.py 77 | python virtualenv.py my-diamond-app 78 | source my-diamond-app/bin/activate 79 | pip install Flask-Diamond 80 | -------------------------------------------------------------------------------- /docs/testing.rst: -------------------------------------------------------------------------------- 1 | Testing 2 | ======= 3 | 4 | Writing tests is an important part of building an application. Especially with dynamically typed languages such as Python, testing is one of the only ways to determine the correctness of your implementation. Python has built the concept of `Unit Tests `_ into the core library. *Unit Testing* is a technique for validating units of your program by making assertions about the behaviour of that code. If any assertions turn out to be false, then you know your code isn't correct. 5 | 6 | Nose 7 | ---- 8 | 9 | In the interest of making test as automatic as possible, Flask-Diamond uses `Nose `_ for *test discovery*, which will find and run all your tests without having to create a test harness. As an application becomes more developed, it may become necessary to build a test harness for the application. However, at the start of application development, it is usually easier to use test discovery in order to iterate faster. 10 | 11 | Nose will automatically search through the ``/tests`` folder for any files starting with the name "test" that have functions in them that also start with "test". 12 | 13 | Running Tests 14 | ------------- 15 | 16 | It is possible to invoke the testing subsystem from the command line. The following different testing methods are available in Flask-Diamond: 17 | 18 | Run every test 19 | ^^^^^^^^^^^^^^ 20 | 21 | Find every test and run it. 22 | 23 | :: 24 | 25 | make test 26 | 27 | Run individual tests 28 | ^^^^^^^^^^^^^^^^^^^^ 29 | 30 | Run tests identified with decorator ``@attr("single")``. These "single" tests can be used to make testing much faster by skipping all of the other tests. 31 | 32 | :: 33 | 34 | make single 35 | 36 | The following code snippet contains a simple test with the *single* attribute decorator applied to it. 37 | 38 | :: 39 | 40 | from nose.plugins.attrib import attr 41 | from flask.ext.testing import TestCase 42 | from flask.ext.diamond.mixins.testing import DiamondTestCaseMixin 43 | 44 | class BasicTestCase(DiamondTestCaseMixin, TestCase): 45 | @attr("single") 46 | def test_basic(self): 47 | assert True 48 | 49 | Automatically run individual tests 50 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 51 | 52 | This functionality will watch the project folder for files to change. When a file has changed, it will re-run tests identified with ``@attr("single")``. This feature is designed to make it very quick to get feedback on the performance of your code. 53 | 54 | :: 55 | 56 | make watch 57 | 58 | xUnit 59 | ----- 60 | 61 | It is possible to automatically interpret the results of testing using the `xUnit framework `_. `Nose xUnit `_ output can permit tools like `Jenkins CI `_, `Travis CI `_, or `Atlassian Bamboo `_ to capture the results of testing. 62 | -------------------------------------------------------------------------------- /docs/wsgi.rst: -------------------------------------------------------------------------------- 1 | Web Services with WSGI 2 | ====================== 3 | 4 | For deploying your application in a production environment, you will probably end up using a `WSGI `_ application server like `uwsgi `_ or `gunicorn `_. By default, Flask-Diamond will install gunicorn as a requirement. 5 | 6 | System Start-up 7 | --------------- 8 | 9 | Under most circumstances, you will want to automatically run the application server when the host boots. This is going to be different for every host, but an example demonstrates launching gunicorn via Ubuntu upstart: 10 | 11 | :: 12 | 13 | #!upstart 14 | description "flask-diamond daemon" 15 | 16 | env USER=flask-diamond 17 | env SETTINGS=/etc/flask-diamond.conf 18 | 19 | start on runlevel [2345] 20 | stop on runlevel [06] 21 | 22 | respawn 23 | 24 | exec start-stop-daemon --start \ 25 | --make-pidfile \ 26 | --pidfile /var/run/$USER-daemon.pid \ 27 | --chuid $USER \ 28 | --exec /var/lib/$USER/.virtualenvs/$USER/bin/gunicorn -- \ 29 | --workers 2 \ 30 | --bind 0.0.0.0:5000 \ 31 | --user $USER \ 32 | --chdir /var/lib/$USER \ 33 | --log-file /var/lib/$USER/gunicorn-error.log \ 34 | --access-logfile /var/lib/$USER/gunicorn-access.log \ 35 | --pid /var/run/$USER-daemon.pid \ 36 | --daemon \ 37 | flask_diamond.wsgi:app 38 | 39 | This demonstrates several important principles: 40 | 41 | - setting the configuration file that will control the application server 42 | - launching the application server from inside the Python virtual environment 43 | 44 | Reverse Proxy 45 | ------------- 46 | 47 | You probably want to keep your application server behind a firewall, so a common pattern for deploying Flask-Diamond applications relies upon a reverse proxy. There is `a good Digital Ocean tutorial `_ for setting up a reverse proxy with either nginx or apache. 48 | 49 | Puppet-Diamond 50 | -------------- 51 | 52 | For scaling up deployment, it is recommended to use an automation solution like Puppet or Chef. Flask-Diamond is particularly easy to deploy with `Puppet-Diamond `_, which will simplify the management of the host configurations as well as application deployment. 53 | 54 | Embedded Server 55 | --------------- 56 | 57 | Flask-Diamond provides an embedded web server with ``bin/manage.py`` and ``bin/runserver.py`` for simple deployments, like development and debugging. This is not recommended for production use. However, when paired with a reverse proxy, this is actually good enough to handle a surprising amount of traffic. 58 | -------------------------------------------------------------------------------- /flask_diamond/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | import flask 5 | from .facets import * 6 | 7 | try: 8 | from flask import _app_ctx_stack as stack 9 | except ImportError: 10 | from flask import _request_ctx_stack as stack 11 | 12 | application = None 13 | 14 | 15 | class Diamond: 16 | """ 17 | A Diamond application. 18 | 19 | :param app: a Flask app that you created on your own 20 | :type app: Flask 21 | :returns: None 22 | """ 23 | 24 | def __init__(self, name=None): 25 | """ 26 | Initialize a Diamond application. 27 | 28 | :param app: a Flask app that you created on your own 29 | :type app: Flask 30 | :returns: Flask -- the initialized application object 31 | 32 | This function is the backbone of every Diamond application. It will 33 | initialize every component of the application. To control the 34 | behaviour of the initialization process, override these functions 35 | within your own application. 36 | """ 37 | 38 | if not name: 39 | name = __name__ 40 | 41 | self.app = flask.Flask(name) 42 | 43 | if hasattr(self.app, 'teardown_appcontext'): 44 | self.app.teardown_appcontext(self.teardown) 45 | else: 46 | self.app.teardown_request(self.teardown) 47 | 48 | def facet(self, extension_name, *args, **kwargs): 49 | """ 50 | initialize an extension 51 | """ 52 | init_method = "init_{0}".format(extension_name) 53 | if not hasattr(self, init_method): 54 | method_to_call = globals()[init_method] 55 | else: 56 | method_to_call = getattr(self, init_method) 57 | setattr(self, init_method, method_to_call) 58 | 59 | try: 60 | # try to explicitly pass self as the first parameter 61 | result = method_to_call(self, *args, **kwargs) 62 | except TypeError: 63 | # just call it because it will be wrapped to inject self 64 | result = method_to_call(*args, **kwargs) 65 | 66 | self.app.logger.debug("facet {0}".format(extension_name)) 67 | return result 68 | 69 | def super(self, extension_name, *args, **kwargs): 70 | """ 71 | invoke the initialization method for the superclass 72 | 73 | ex: self.super("administration") 74 | """ 75 | 76 | init_method = "init_{0}".format(extension_name) 77 | # ensure the global version is called 78 | method_to_call = globals()[init_method] 79 | result = method_to_call(self, *args, **kwargs) 80 | return result 81 | 82 | def teardown(self, exception): 83 | """ 84 | Remove any persistent connections during application context teardown. 85 | 86 | :returns: None 87 | """ 88 | 89 | ctx = stack.top 90 | if hasattr(ctx, 'diamond'): 91 | pass 92 | # ctx.sqlite3_db.close() 93 | 94 | @property 95 | def _app(self): 96 | ctx = stack.top 97 | if ctx is not None: 98 | if not hasattr(ctx, 'app'): 99 | pass 100 | return ctx.app 101 | -------------------------------------------------------------------------------- /flask_diamond/__meta__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | __project__ = 'Flask-Diamond' 5 | __version__ = '0.5.1' 6 | __author__ = 'Ian Dennis Miller' 7 | __email__ = 'iandennismiller@gmail.com' 8 | __url__ = 'http://flask-diamond.readthedocs.org' 9 | __copyright__ = 'Ian Dennis Miller' 10 | -------------------------------------------------------------------------------- /flask_diamond/facets/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from .logs import * 5 | from .database import * 6 | from .marshalling import * 7 | from .accounts import * 8 | from .debugger import * 9 | from .webassets import * 10 | from .administration import * 11 | from .email import * 12 | from .task_queue import * 13 | from .forms import * 14 | from .rest import * 15 | from .handlers import * 16 | from .configuration import * 17 | from .blueprints import * 18 | from .signals import * 19 | -------------------------------------------------------------------------------- /flask_diamond/facets/accounts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_security import SQLAlchemyUserDatastore 5 | from flask_security import Security 6 | from flask import current_app 7 | 8 | security = Security() 9 | 10 | 11 | def init_accounts(self, user=None, role=None, *args, **kwargs): 12 | """ 13 | Initialize Security for application. 14 | 15 | :param kwargs: parameters that will be passed through to Flask-Security 16 | :type kwargs: dict 17 | :returns: None 18 | 19 | A number of common User account operations are provided by `Flask- 20 | Security `_. This function is 21 | responsible for associating User models in the database with the 22 | Security object. 23 | 24 | In case you need to override a Flask-Security form (as is the case 25 | with implementing CAPTCHA) then you must use super() from within your 26 | application and provide any arguments destined for Flask-Security. 27 | 28 | >>> result = self.super("accounts", user=User, role=Role, 29 | >>> confirm_register_form=ExtendedRegisterForm) 30 | """ 31 | 32 | # import database 33 | from .. import db 34 | 35 | if not user or not role: 36 | from ..models.user import User as user 37 | from ..models.role import Role as role 38 | 39 | # create datastore 40 | user_datastore = SQLAlchemyUserDatastore(db, user, role) 41 | setattr(Security, "user_datastore", user_datastore) 42 | 43 | security.init_app(self.app, datastore=user_datastore, *args, **kwargs) 44 | -------------------------------------------------------------------------------- /flask_diamond/facets/administration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_admin import Admin 5 | import flask_security as security 6 | from flask_admin import BaseView, expose, AdminIndexView 7 | from flask_admin.contrib.sqla import ModelView 8 | from flask_admin.base import MenuLink 9 | from flask_security.utils import encrypt_password 10 | import flask 11 | 12 | admin = Admin() 13 | 14 | 15 | def init_administration(self, index_view=None, user=None, role=None): 16 | """ 17 | Initialize the Administrative GUI. 18 | 19 | :param index_view: the View that will act as the index page of the admin GUI. 20 | :type index_view: AdminIndexView 21 | :returns: None 22 | 23 | The administration GUI is substantially derived from `Flask-Admin 24 | `_. When this function 25 | is called, it will instantiate blueprints so the application serves 26 | the admin GUI via the URL http://localhost/admin. 27 | 28 | Typically, you will want to call this function even if you override 29 | it. The following example illustrates using super() to invoke this 30 | administration() function from within your own application. 31 | 32 | >>> admin = super(MyApp, self).administration( 33 | >>> index_view=MyApp.modelviews.RedirectView(name="Home") 34 | >>> ) 35 | """ 36 | 37 | admin = Admin( 38 | name=self.app.config["PROJECT_NAME"], 39 | base_template='admin/login_base.html', 40 | index_view=index_view or ForceLoginView(name="Home") 41 | ) 42 | 43 | from .. import db 44 | 45 | if not user or not role: 46 | from ..models.user import User as user 47 | from ..models.role import Role as role 48 | 49 | admin.init_app(self.app) 50 | 51 | admin.add_view(UserView(user, db.session, category="Admin")) 52 | admin.add_view(AdminModelView(role, db.session, category="Admin")) 53 | 54 | return admin 55 | 56 | 57 | class AuthMixin: 58 | """ 59 | Require user authentication to be accessible 60 | """ 61 | 62 | def is_accessible(self): 63 | """ 64 | the View is accessible if the User is authenticated 65 | """ 66 | 67 | return security.current_user.is_authenticated 68 | 69 | 70 | class AdminMixin: 71 | """ 72 | Require admin Role to be accessible 73 | """ 74 | 75 | def is_accessible(self): 76 | """ 77 | the View is accessible if the User has the Admin Role 78 | """ 79 | 80 | return security.current_user.has_role("Admin") 81 | 82 | 83 | class AuthView(AuthMixin, BaseView): 84 | """ 85 | A View that requires authentication 86 | """ 87 | pass 88 | 89 | 90 | class AdminView(AdminMixin, BaseView): 91 | """ 92 | A View that requires the Admin Role 93 | """ 94 | pass 95 | 96 | 97 | class AuthModelView(AuthMixin, ModelView): 98 | """ 99 | A ModelView that requires authentication 100 | """ 101 | pass 102 | 103 | 104 | class AdminModelView(AdminMixin, ModelView): 105 | """ 106 | A ModelView that requires the Admin Role 107 | """ 108 | pass 109 | 110 | 111 | class AuthenticatedMenuLink(AuthMixin, MenuLink): 112 | """ 113 | A MenuLink that requires authentication 114 | """ 115 | pass 116 | 117 | 118 | class UserView(AdminModelView): 119 | """ 120 | Manage the User Model 121 | """ 122 | 123 | # column_filters = ['email'] 124 | column_exclude_list = ('password', 'active', 'confirmed_at') 125 | column_searchable_list = ('email', ) 126 | can_delete = False 127 | 128 | create_template = 'admin/create_user.html' 129 | 130 | def create_model(self, form): 131 | self.model.register( 132 | name=form.data["name"], 133 | email=form.data["email"], 134 | password=form.data["password"], 135 | confirmed=True, 136 | roles=["User"], 137 | ) 138 | return flask.redirect(flask.url_for("user.index_view")) 139 | 140 | def update_model(self, form, model): 141 | original_password = model.password 142 | model.update(**form.data) 143 | if form.data["password"] != original_password: 144 | model.password = encrypt_password(form.data["password"]) 145 | model.save() 146 | return flask.redirect(flask.url_for("user.index_view")) 147 | 148 | 149 | class ForceLoginView(AdminIndexView): 150 | """ 151 | Allocate the root URL and require authentication 152 | """ 153 | 154 | def is_accessible(self): 155 | """ 156 | the View is accessible if the User is authenticated 157 | """ 158 | 159 | return security.current_user.is_authenticated 160 | 161 | @expose('/') 162 | def index(self): 163 | return self.render("/admin/index.html") 164 | -------------------------------------------------------------------------------- /flask_diamond/facets/blueprints.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | 5 | def init_blueprints(self): 6 | """ 7 | Initialize blueprints. 8 | 9 | :returns: None 10 | 11 | By default, this function does nothing. Your application needs to 12 | overload this function in order to implement your View functionality. 13 | More information about blueprints can be found in the 14 | `Flask documentation `_. 15 | """ 16 | 17 | pass 18 | 19 | # from ..views.diamond import diamond_blueprint 20 | # self.app.register_blueprint(diamond_blueprint) 21 | -------------------------------------------------------------------------------- /flask_diamond/facets/configuration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | 5 | def init_configuration(self): 6 | """ 7 | Load the application configuration from the ``SETTINGS`` environment variable. 8 | 9 | :returns: None 10 | 11 | ``SETTINGS`` must contain a filename that points to the configuration file. 12 | """ 13 | 14 | self.app.config.from_envvar('SETTINGS') 15 | -------------------------------------------------------------------------------- /flask_diamond/facets/database.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_sqlalchemy import SQLAlchemy 5 | 6 | db = SQLAlchemy() 7 | 8 | 9 | def init_database(self): 10 | """ 11 | Initialize database 12 | 13 | :returns: None 14 | 15 | Flask-Diamond assumes you are modelling your solution using an Entity- 16 | Relationship framework, and that the application will use a relational 17 | database (e.g. MySQL, Postgres, or SQlite3) for model persistence. 18 | Thus, `SQLAlchemy 19 | `_ and `Flask- 20 | SQLalchemy `_ 21 | are used for database operations. 22 | 23 | Typically, this just works as long as ``SQLALCHEMY_DATABASE_URI`` is 24 | set correctly in the application configuration. 25 | """ 26 | 27 | db.app = self.app 28 | db.init_app(self.app) 29 | -------------------------------------------------------------------------------- /flask_diamond/facets/debugger.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_debugtoolbar import DebugToolbarExtension 5 | 6 | toolbar = DebugToolbarExtension() 7 | 8 | 9 | def init_debugger(self): 10 | """ 11 | Initialize the DebugToolbar 12 | 13 | :returns: None 14 | 15 | The `DebugToolbar `_ is a handy utility for 17 | debugging your application during development. 18 | 19 | This function obeys the ``DEBUG_TOOLBAR`` configuration setting. Only 20 | if this value is explicitly set to True will the Debug Toolbarr run. 21 | """ 22 | 23 | if 'DEBUG_TOOLBAR' in self.app.config and self.app.config['DEBUG_TOOLBAR']: 24 | toolbar.init_app(self.app) 25 | -------------------------------------------------------------------------------- /flask_diamond/facets/email.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_mail import Mail 5 | 6 | mail = Mail() 7 | 8 | 9 | def init_email(self): 10 | """ 11 | Initialize email facilities. 12 | 13 | :returns: None 14 | 15 | `Flask-Mail `_ is a 16 | useful tool for creating and sending emails from within a Flask application. 17 | There are a number of configuration settings beginning with ``MAIL_`` 18 | that permit control over the SMTP credentials used to send email. 19 | """ 20 | 21 | mail.init_app(self.app) 22 | -------------------------------------------------------------------------------- /flask_diamond/facets/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from wtforms.fields import HiddenField, BooleanField 5 | 6 | 7 | def init_forms(self): 8 | """ 9 | WTForms helpers 10 | 11 | :returns: None 12 | 13 | `WTForms `_ is a great library 14 | for using forms and `Flask-WTF `_ provides good integration with it. 16 | WTForms helpers enable you to add custom filters and other custom 17 | behaviours. 18 | """ 19 | 20 | add_helpers(self.app) 21 | 22 | 23 | def add_helpers(app): 24 | """ 25 | Create any Jinja2 helpers needed. 26 | """ 27 | 28 | def is_hidden_field_filter(field): 29 | return isinstance(field, HiddenField) 30 | 31 | def is_boolean_field_filter(field): 32 | return isinstance(field, BooleanField) 33 | 34 | app.jinja_env.filters['is_hidden_field'] = is_hidden_field_filter 35 | app.jinja_env.filters['is_boolean_field'] = is_boolean_field_filter 36 | -------------------------------------------------------------------------------- /flask_diamond/facets/handlers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | import flask 5 | 6 | 7 | def init_error_handlers(self): 8 | """ 9 | Initialize handlers for HTTP error events 10 | 11 | :returns: None 12 | 13 | Flask is able to respond to HTTP error codes with custom behaviours. 14 | By default, it will redirect error 403 (forbidden) to the login page. 15 | """ 16 | import flask_security as security 17 | 18 | @self.app.errorhandler(403) 19 | def page_forbidden(e): 20 | if security.current_user.is_authenticated: 21 | return flask.redirect(flask.url_for("admin.index")) 22 | else: 23 | return flask.redirect(security.url_for_security("login")) 24 | 25 | 26 | def init_request_handlers(self): 27 | """ 28 | request handlers 29 | 30 | :returns: None 31 | 32 | Flask handles requests for URLs by scanning the URL path. Typically, 33 | any serious functionality will be collected into Views. However, this 34 | function is a chance to define a few simple utility URLs. 35 | 36 | If in your application you want to disable the default handlers in 37 | Flask-Diamond, you can override them like this. 38 | 39 | >>> def request_handlers(self): 40 | >>> pass 41 | """ 42 | @self.app.route('/') 43 | def index(): 44 | "set up the default handler for requests to /" 45 | return flask.redirect(flask.url_for("admin.index")) 46 | -------------------------------------------------------------------------------- /flask_diamond/facets/logs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | import logging 5 | 6 | 7 | def init_logs(self): 8 | """ 9 | Initialize a log file to collect messages. 10 | 11 | :returns: None 12 | 13 | This file may be written to using 14 | 15 | >>> flask.current_app.logger.info("message") 16 | """ 17 | 18 | handler = logging.FileHandler(self.app.config['LOG']) 19 | handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) 20 | self.app.logger.addHandler(handler) 21 | if self.app.config.get("LOG_LEVEL") == "DEBUG": 22 | self.app.logger.setLevel(logging.DEBUG) 23 | elif self.app.config.get("LOG_LEVEL") == "WARN": 24 | self.app.logger.setLevel(logging.WARN) 25 | else: 26 | self.app.logger.setLevel(logging.INFO) 27 | self.app.logger.info('Startup with log: %s' % self.app.config['LOG']) 28 | -------------------------------------------------------------------------------- /flask_diamond/facets/marshalling.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_marshmallow import Marshmallow 5 | 6 | ma = Marshmallow() 7 | 8 | 9 | def init_marshalling(self): 10 | """ 11 | Initialize Marshmallow. 12 | 13 | :returns: None 14 | """ 15 | 16 | ma.app = self.app 17 | ma.init_app(self.app) 18 | -------------------------------------------------------------------------------- /flask_diamond/facets/rest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_restful import Api 5 | 6 | rest_api = Api() 7 | 8 | 9 | def init_rest(self, api_map=None): 10 | """ 11 | Initialize REST API. 12 | 13 | :returns: None 14 | 15 | By default, this function does nothing. Your application needs to 16 | overload this function in order to implement your REST API. 17 | More information about REST can be found in the 18 | `documentation `_. 19 | 20 | api_map is an optional function that can be responsible 21 | for setting up the API. This is usually accomplished with a series of 22 | add_resource() invocations. api_map must take one parameter, which is 23 | the Flask-Restful object managed by Flask-Diamond. 24 | 25 | You will end up writing something like this in your application: 26 | 27 | class PlanetResource(Resource): 28 | def get(self, name): 29 | planet = Planet.find(name=name) 30 | if planet: 31 | return(planet.dump()) 32 | 33 | def api_map(rest_extension): 34 | rest_extension.add_resource(PlanetResource, '/api/planet/') 35 | 36 | def create_app(): 37 | application.facet("rest", api_map=api_map) 38 | """ 39 | 40 | if api_map: 41 | api_map(rest_api) 42 | rest_api.init_app(self.app) 43 | return rest_api 44 | -------------------------------------------------------------------------------- /flask_diamond/facets/signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_security.signals import user_registered 5 | from .accounts import security 6 | from .database import db 7 | 8 | 9 | def init_signals(self): 10 | """ 11 | Initialize Flask signal handlers 12 | 13 | :returns: None 14 | 15 | Flask provides a number of signals corresponding to things that happen 16 | during the operation of the application, which can also be thought of 17 | as events. It is possible to create signal handlers that will respond 18 | to these events with some behaviour. 19 | """ 20 | @user_registered.connect_via(self.app) 21 | def user_registered_sighandler(sender, **extra): 22 | "add User role to all self-registration users" 23 | user_role = security.user_datastore.find_role("User") 24 | security.user_datastore.add_role_to_user(extra['user'], user_role) 25 | db.session.commit() 26 | self.app.logger.info("added role User to %s" % extra['user']) 27 | -------------------------------------------------------------------------------- /flask_diamond/facets/task_queue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_celery import Celery 5 | 6 | celery = Celery() 7 | 8 | 9 | def init_task_queue(self): 10 | """ 11 | Initialize celery. 12 | """ 13 | 14 | celery.init_app(self.app) 15 | -------------------------------------------------------------------------------- /flask_diamond/facets/webassets.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from flask_assets import Environment 5 | 6 | assets = Environment() 7 | 8 | 9 | def init_webassets(self, asset_map=None): 10 | """ 11 | Initialize web assets. 12 | 13 | :returns: None 14 | 15 | `webassets `_ make it simpler 16 | to process and bundle CSS and Javascript assets. This can be baked 17 | into a Flask application using 18 | `Flask-Assets `_ 19 | """ 20 | 21 | assets.init_app(self.app) 22 | if asset_map: 23 | asset_map(assets) 24 | -------------------------------------------------------------------------------- /flask_diamond/mixins/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | -------------------------------------------------------------------------------- /flask_diamond/mixins/crud.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | import flask 5 | from builtins import str 6 | from ..facets.database import db 7 | 8 | 9 | class CRUDMixin: 10 | """ 11 | Convenience functions for CRUD operations. 12 | 13 | Adapted from `flask-kit `_. 14 | """ 15 | 16 | __table_args__ = {'extend_existing': True} 17 | 18 | @classmethod 19 | def find(cls, **kwargs): 20 | """ 21 | Find an object in the database with certain properties. 22 | 23 | :param kwargs: the values of the object to find 24 | :type kwargs: dict 25 | :returns: the object that was found, or else None 26 | """ 27 | 28 | return cls.query.filter_by(**kwargs).first() 29 | 30 | @classmethod 31 | def find_or_create(cls, _commit=True, **kwargs): 32 | """ 33 | Find an object or, if it does not exist, create it. 34 | 35 | :param kwargs: the values of the object to find or create 36 | :type kwargs: dict 37 | :returns: the object that was created 38 | """ 39 | 40 | obj = cls.find(**kwargs) 41 | if not obj: 42 | obj = cls.create(_commit=_commit, **kwargs) 43 | return obj 44 | 45 | @classmethod 46 | def get_by_id(cls, id): 47 | """ 48 | Retrieve an object of this class from the database. 49 | 50 | :param id: the id of the object to be retrieved 51 | :type id: integer 52 | :returns: the object that was retrieved 53 | """ 54 | 55 | if any( 56 | (isinstance(id, str) and id.isdigit(), 57 | isinstance(id, (int, float))), 58 | ): 59 | return cls.query.get(int(id)) 60 | return None 61 | 62 | @classmethod 63 | def create(cls, _commit=True, **kwargs): 64 | """ 65 | Create a new object. 66 | 67 | :param commit: whether to commit the change immediately to the database 68 | :type commit: boolean 69 | :param kwargs: parameters corresponding to the new values 70 | :type kwargs: dict 71 | :returns: the object that was created 72 | """ 73 | 74 | instance = cls(**kwargs) 75 | obj = instance.save(_commit) 76 | flask.current_app.logger.debug("create %s" % str(obj)) 77 | return obj 78 | 79 | def update(self, _commit=True, **kwargs): 80 | """ 81 | Update this object with new values. 82 | 83 | :param commit: whether to commit the change immediately to the database 84 | :type commit: boolean 85 | :param kwargs: parameters corresponding to the new values 86 | :type kwargs: dict 87 | :returns: the object that was updated 88 | """ 89 | 90 | for attr, value in kwargs.items(): 91 | setattr(self, attr, value) 92 | return _commit and self.save() or self 93 | 94 | def save(self, _commit=True): 95 | """ 96 | Save this object to the database. 97 | 98 | :param commit: whether to commit the change immediately to the database 99 | :type commit: boolean 100 | :returns: the object that was saved 101 | """ 102 | 103 | db.session.add(self) 104 | if _commit: 105 | db.session.commit() 106 | return self 107 | 108 | def delete(self, _commit=True): 109 | """ 110 | Delete this object. 111 | 112 | :param commit: whether to commit the change immediately to the database 113 | :type commit: boolean 114 | :returns: whether the delete was successful 115 | """ 116 | 117 | db.session.delete(self) 118 | return _commit and db.session.commit() 119 | 120 | def __repr__(self): 121 | return "<{}(id={})>".format(self.__class__.__name__, self.id) 122 | 123 | def __str__(self): 124 | return self.__repr__() 125 | -------------------------------------------------------------------------------- /flask_diamond/mixins/marshmallow.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from ..facets.database import db 5 | 6 | 7 | class MarshmallowMixin: 8 | 9 | # dump 10 | 11 | def dump(self): 12 | "serialize the Model object as a python object" 13 | return self.__schema__().dump(self).data 14 | 15 | def dumps(self): 16 | "serialize the Model object as a JSON string" 17 | return self.__schema__().dumps(self).data 18 | 19 | def dumpf(self, file_handle): 20 | "write a Model object to file_handle as a JSON string" 21 | file_handle.write(self.dumps()) 22 | 23 | # load 24 | 25 | @classmethod 26 | def load(cls, python_obj): 27 | "create a Model object from a python object" 28 | obj = cls.__schema__().load(python_obj) 29 | return cls.create(**obj.data) 30 | 31 | @classmethod 32 | def loads(cls, buf): 33 | "create a Model object from a JSON-encoded string" 34 | obj = cls.__schema__().loads(buf) 35 | return cls.create(**obj.data) 36 | 37 | @classmethod 38 | def loadf(cls, file_handle): 39 | "create a Model object from a file_handle pointing to a JSON file" 40 | return cls.loads(file_handle.read()) 41 | 42 | # dump_all 43 | 44 | @classmethod 45 | def dump_all(cls): 46 | "write all objects of Model class to an array of python objects" 47 | return cls.__schema__().dump(cls.query.all(), many=True).data 48 | 49 | @classmethod 50 | def dumps_all(cls): 51 | "write all objects of Model class to a JSON-encoded array" 52 | return cls.__schema__().dumps(cls.query.all(), many=True).data 53 | 54 | @classmethod 55 | def dumpf_all(cls, file_handle): 56 | "write all objects of Model class to file_handle as JSON" 57 | file_handle.write(cls.dumps_all()) 58 | 59 | # load_all 60 | 61 | @classmethod 62 | def load_all(cls, python_objects): 63 | "create objects of Model class from an array of python objects" 64 | objs = cls.__schema__().load(python_objects, many=True) 65 | for obj in objs.data: 66 | cls.create(_commit=False, **obj) 67 | db.session.commit() 68 | db.session.flush() 69 | 70 | @classmethod 71 | def loads_all(cls, buf): 72 | "create objects of Model class from a string containing an array of JSON-encoded objects" 73 | objs = cls.__schema__().loads(buf, many=True) 74 | for obj in objs.data: 75 | cls.create(_commit=False, **obj) 76 | db.session.commit() 77 | db.session.flush() 78 | 79 | @classmethod 80 | def loadf_all(cls, file_handle): 81 | "create objects of Model class from a file containing an array of JSON-encoded objects" 82 | cls.loads_all(file_handle.read()) 83 | -------------------------------------------------------------------------------- /flask_diamond/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | -------------------------------------------------------------------------------- /flask_diamond/models/role.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from flask_security import RoleMixin 4 | from ..facets.database import db 5 | from ..facets.marshalling import ma 6 | from ..mixins.crud import CRUDMixin 7 | from ..mixins.marshmallow import MarshmallowMixin 8 | 9 | 10 | class RoleSchema(ma.Schema): 11 | class Meta: 12 | additional = ( 13 | "id", 14 | "name", 15 | "description", 16 | ) 17 | 18 | 19 | class Role(db.Model, RoleMixin, CRUDMixin, MarshmallowMixin): 20 | """ 21 | For the purpose of access controls, Roles can be used to create 22 | collections of users and give them permissions as a group. 23 | """ 24 | 25 | __schema__ = RoleSchema 26 | 27 | id = db.Column(db.Integer(), primary_key=True) 28 | "integer -- primary key" 29 | 30 | name = db.Column(db.String(80), unique=True) 31 | "string -- what the role is called" 32 | 33 | description = db.Column(db.String(255)) 34 | "string -- a sentence describing the role" 35 | 36 | def __str__(self): 37 | return self.name 38 | 39 | @classmethod 40 | def add_default_roles(cls): 41 | """ 42 | Create a basic set of users and roles 43 | 44 | :returns: None 45 | """ 46 | 47 | from .. import security 48 | 49 | # make roles 50 | security.user_datastore.find_or_create_role("Admin") 51 | security.user_datastore.find_or_create_role("User") 52 | db.session.commit() 53 | -------------------------------------------------------------------------------- /flask_diamond/models/user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | import flask 5 | import datetime 6 | from flask_security import UserMixin 7 | from flask_security.utils import encrypt_password 8 | from flask_marshmallow.fields import fields 9 | from ..facets.database import db 10 | from ..facets.marshalling import ma 11 | from ..mixins.crud import CRUDMixin 12 | from ..mixins.marshmallow import MarshmallowMixin 13 | 14 | 15 | "A secondary table is used for the one-to-many relationship: User has many Roles" 16 | roles_users = db.Table('roles_users', 17 | db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), 18 | db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))) 19 | 20 | 21 | class UserSchema(ma.Schema): 22 | confirmed_at = fields.DateTime(required=False) 23 | last_login_at = fields.DateTime(required=False) 24 | current_login_at = fields.DateTime(required=False) 25 | 26 | # RoleSchema must be specified in role.py 27 | roles = fields.Nested('RoleSchema', allow_none=True, many=True) 28 | 29 | class Meta: 30 | dateformat = ("%F %T %z") 31 | additional = ( 32 | "id", 33 | "email", 34 | "password", 35 | "active", 36 | "last_login_ip", 37 | "current_login_ip", 38 | "login_count", 39 | ) 40 | 41 | 42 | class User(db.Model, UserMixin, CRUDMixin, MarshmallowMixin): 43 | __schema__ = UserSchema 44 | 45 | id = db.Column(db.Integer, primary_key=True) 46 | "integer -- primary key" 47 | 48 | email = db.Column(db.String(255), unique=True) 49 | "string -- email address" 50 | 51 | password = db.Column('password', db.String(255), nullable=False) 52 | "password -- the users's password" 53 | 54 | active = db.Column(db.Boolean()) 55 | "boolean -- whether the user account is active" 56 | 57 | confirmed_at = db.Column(db.DateTime()) 58 | "datetime -- when the user account was confirmed" 59 | 60 | last_login_at = db.Column(db.DateTime()) 61 | "datetime -- the time of the most recent login" 62 | 63 | current_login_at = db.Column(db.DateTime()) 64 | "datetime -- the time of the current login, if any" 65 | 66 | last_login_ip = db.Column(db.String(255)) 67 | "string -- the IP address of the previous login" 68 | 69 | current_login_ip = db.Column(db.String(255)) 70 | "string -- the IP address of the current login" 71 | 72 | login_count = db.Column(db.Integer(), default=0) 73 | "integer -- the number of times this account been accessed" 74 | 75 | roles = db.relationship('Role', 76 | enable_typechecks=False, 77 | secondary=roles_users, 78 | # backref=db.backref('users', lazy='dynamic'), 79 | ) 80 | 81 | def __str__(self): 82 | return self.email 83 | 84 | def confirm(self): 85 | """ 86 | update a User account so that login is permitted 87 | 88 | :returns: None 89 | """ 90 | 91 | self.confirmed_at = datetime.datetime.now() 92 | self.active = True 93 | self.save() 94 | 95 | def add_role(self, role_name): 96 | """ 97 | update a User account so that it includes a new Role 98 | 99 | :param role_name: the name of the Role to add 100 | :type role_name: string 101 | """ 102 | 103 | from .. import security 104 | 105 | new_role = security.user_datastore.find_or_create_role(role_name) 106 | security.user_datastore.add_role_to_user(self, new_role) 107 | db.session.commit() 108 | 109 | @classmethod 110 | def register(cls, email, password, confirmed=False, roles=None): 111 | """ 112 | Create a new user account. 113 | 114 | :param email: the email address used to identify the account 115 | :type email: string 116 | :param password: the plaintext password for the account 117 | :type password: string 118 | :param confirmed: whether to confirm the account immediately 119 | :type confirmed: boolean 120 | :param roles: a list containing the names of the Roles for this User 121 | :type roles: list(string) 122 | """ 123 | 124 | from .. import security 125 | 126 | new_user = security.user_datastore.create_user( 127 | email=email, 128 | password=encrypt_password(password) 129 | ) 130 | db.session.commit() 131 | if confirmed: 132 | new_user.confirm() 133 | if roles: 134 | for role_name in roles: 135 | new_user.add_role(role_name) 136 | flask.current_app.logger.debug("Created user {0}".format(email)) 137 | return new_user 138 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/__init__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_diamond import Diamond 5 | from flask_diamond.facets.administration import AdminModelView 6 | from flask_diamond.facets.database import db 7 | from .models import User, Role 8 | 9 | # declare these globalish objects before initializing models 10 | application = None 11 | 12 | 13 | class {{{ application.module }}}(Diamond): 14 | 15 | def init_accounts(self): 16 | "initialize accounts with the User and Role classes imported from .models" 17 | return self.super("accounts", user=User, role=Role) 18 | 19 | def init_administration(self): 20 | "Initialize admin interface" 21 | 22 | admin = self.super("administration", user=User, role=Role) 23 | 24 | model_list = [ 25 | ] 26 | 27 | for model in model_list: 28 | admin.add_view(AdminModelView( 29 | model, 30 | db.session, 31 | name=model.__name__, 32 | category="Models") 33 | ) 34 | 35 | return admin 36 | 37 | def init_blueprints(self): 38 | "Application blueprints" 39 | 40 | self.super("blueprints") 41 | 42 | # administration blueprint is custom to this application 43 | from .views.administration.modelviews import adminbaseview 44 | self.app.register_blueprint(adminbaseview) 45 | 46 | from .views.diamond import diamond_blueprint 47 | self.app.register_blueprint(diamond_blueprint) 48 | 49 | 50 | def create_app(): 51 | global application 52 | if not application: 53 | application = {{{ application.module }}}() 54 | application.facet("configuration") 55 | application.facet("logs") 56 | application.facet("database") 57 | application.facet("marshalling") 58 | application.facet("blueprints") 59 | application.facet("accounts") 60 | application.facet("signals") 61 | application.facet("forms") 62 | application.facet("error_handlers") 63 | application.facet("request_handlers") 64 | application.facet("administration") 65 | # application.facet("rest", api_map=api_map) 66 | # application.facet("webassets") 67 | # application.facet("email") 68 | # application.facet("debugger") 69 | # application.facet("task_queue") 70 | 71 | # print application.app.url_map 72 | return application.app 73 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/__meta__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | __project__ = '{{{ application.name }}}' 5 | __version__ = '{{{ project.version }}}' 6 | __author__ = '{{{ author.name }}}' 7 | __email__ = '{{{ author.email }}}' 8 | __url__ = '{{{ project.url }}}' 9 | __copyright__ = '{{{ author.name }}}' 10 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/+application.module+/migrations/__init__.py -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/migrations/alembic.ini.bob: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | script_location = {{{ application.module }}}/migrations 12 | 13 | # Logging configuration 14 | [loggers] 15 | keys = root,sqlalchemy,alembic 16 | 17 | [handlers] 18 | keys = console 19 | 20 | [formatters] 21 | keys = generic 22 | 23 | [logger_root] 24 | level = WARN 25 | handlers = console 26 | qualname = 27 | 28 | [logger_sqlalchemy] 29 | level = WARN 30 | handlers = 31 | qualname = sqlalchemy.engine 32 | 33 | [logger_alembic] 34 | level = INFO 35 | handlers = 36 | qualname = alembic 37 | 38 | [handler_console] 39 | class = StreamHandler 40 | args = (sys.stderr,) 41 | level = NOTSET 42 | formatter = generic 43 | 44 | [formatter_generic] 45 | format = %(levelname)-5.5s [%(name)s] %(message)s 46 | datefmt = %H:%M:%S 47 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/migrations/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | from alembic import context 3 | from sqlalchemy import engine_from_config, pool 4 | from logging.config import fileConfig 5 | 6 | # this is the Alembic Config object, which provides 7 | # access to the values within the .ini file in use. 8 | config = context.config 9 | 10 | # Interpret the config file for Python logging. 11 | # This line sets up loggers basically. 12 | fileConfig(config.config_file_name) 13 | 14 | # add your model's MetaData object here 15 | # for 'autogenerate' support 16 | # from myapp import mymodel 17 | # target_metadata = mymodel.Base.metadata 18 | from flask import current_app 19 | config.set_main_option('sqlalchemy.url', current_app.config.get('SQLALCHEMY_DATABASE_URI')) 20 | target_metadata = current_app.extensions['migrate'].metadata 21 | 22 | # other values from the config, defined by the needs of env.py, 23 | # can be acquired: 24 | # my_important_option = config.get_main_option("my_important_option") 25 | # ... etc. 26 | 27 | def run_migrations_offline(): 28 | """Run migrations in 'offline' mode. 29 | 30 | This configures the context with just a URL 31 | and not an Engine, though an Engine is acceptable 32 | here as well. By skipping the Engine creation 33 | we don't even need a DBAPI to be available. 34 | 35 | Calls to context.execute() here emit the given string to the 36 | script output. 37 | 38 | """ 39 | url = config.get_main_option("sqlalchemy.url") 40 | context.configure(url=url) 41 | 42 | with context.begin_transaction(): 43 | context.run_migrations() 44 | 45 | def run_migrations_online(): 46 | """Run migrations in 'online' mode. 47 | 48 | In this scenario we need to create an Engine 49 | and associate a connection with the context. 50 | 51 | """ 52 | engine = engine_from_config( 53 | config.get_section(config.config_ini_section), 54 | prefix='sqlalchemy.', 55 | poolclass=pool.NullPool) 56 | 57 | connection = engine.connect() 58 | context.configure( 59 | connection=connection, 60 | target_metadata=target_metadata 61 | ) 62 | 63 | try: 64 | with context.begin_transaction(): 65 | context.run_migrations() 66 | finally: 67 | connection.close() 68 | 69 | if context.is_offline_mode(): 70 | run_migrations_offline() 71 | else: 72 | run_migrations_online() 73 | 74 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = ${repr(up_revision)} 11 | down_revision = ${repr(down_revision)} 12 | 13 | from alembic import op 14 | import sqlalchemy as sa 15 | ${imports if imports else ""} 16 | 17 | def upgrade(): 18 | ${upgrades if upgrades else "pass"} 19 | 20 | 21 | def downgrade(): 22 | ${downgrades if downgrades else "pass"} 23 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/migrations/versions/20f04b9598da_flask-diamond-020.py: -------------------------------------------------------------------------------- 1 | """update to flask-diamond 0.2.0 2 | 3 | Revision ID: 20f04b9598da 4 | Revises: cf0f5b45967 5 | Create Date: 2015-02-07 22:54:24.608403 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '20f04b9598da' 11 | down_revision = 'cf0f5b45967' 12 | 13 | from alembic import op 14 | import sqlalchemy as sa 15 | 16 | 17 | def upgrade(): 18 | ### commands auto generated by Alembic - please adjust! ### 19 | op.add_column('user', sa.Column('current_login_at', sa.DateTime(), nullable=True)) 20 | op.add_column('user', sa.Column('current_login_ip', sa.String(length=255), nullable=True)) 21 | op.add_column('user', sa.Column('last_login_at', sa.DateTime(), nullable=True)) 22 | op.add_column('user', sa.Column('last_login_ip', sa.String(length=255), nullable=True)) 23 | op.add_column('user', sa.Column('login_count', sa.Integer(), nullable=True)) 24 | ### end Alembic commands ### 25 | 26 | 27 | def downgrade(): 28 | ### commands auto generated by Alembic - please adjust! ### 29 | op.drop_column('user', 'login_count') 30 | op.drop_column('user', 'last_login_ip') 31 | op.drop_column('user', 'last_login_at') 32 | op.drop_column('user', 'current_login_ip') 33 | op.drop_column('user', 'current_login_at') 34 | ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/migrations/versions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/+application.module+/migrations/versions/__init__.py -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/migrations/versions/cf0f5b45967_user-and-role.py: -------------------------------------------------------------------------------- 1 | """Initialize User and Role 2 | 3 | Revision ID: cf0f5b45967 4 | Revises: None 5 | Create Date: 2014-03-06 12:55:50.423498 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = 'cf0f5b45967' 11 | down_revision = None 12 | 13 | from alembic import op 14 | import sqlalchemy as sa 15 | 16 | 17 | def upgrade(): 18 | ### commands auto generated by Alembic - please adjust! ### 19 | op.create_table('role', 20 | sa.Column('id', sa.Integer(), nullable=False), 21 | sa.Column('name', sa.String(length=80), nullable=True), 22 | sa.Column('description', sa.String(length=255), nullable=True), 23 | sa.PrimaryKeyConstraint('id'), 24 | sa.UniqueConstraint('name') 25 | ) 26 | op.create_table('user', 27 | sa.Column('id', sa.Integer(), nullable=False), 28 | sa.Column('email', sa.String(length=255), nullable=True), 29 | sa.Column('password', sa.String(length=255), nullable=False), 30 | sa.Column('active', sa.Boolean(), nullable=True), 31 | sa.Column('confirmed_at', sa.DateTime(), nullable=True), 32 | sa.PrimaryKeyConstraint('id'), 33 | sa.UniqueConstraint('email') 34 | ) 35 | op.create_table('roles_users', 36 | sa.Column('user_id', sa.Integer(), nullable=True), 37 | sa.Column('role_id', sa.Integer(), nullable=True), 38 | sa.ForeignKeyConstraint(['role_id'], ['role.id'], ), 39 | sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), 40 | sa.PrimaryKeyConstraint() 41 | ) 42 | ### end Alembic commands ### 43 | 44 | 45 | def downgrade(): 46 | ### commands auto generated by Alembic - please adjust! ### 47 | op.drop_table('roles_users') 48 | op.drop_table('user') 49 | op.drop_table('role') 50 | ### end Alembic commands ### 51 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/models/__init__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_diamond.models.user import User 5 | from flask_diamond.models.role import Role 6 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/+application.module+/tests/__init__.py -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/fixtures.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from ..models import Role, User 5 | 6 | 7 | def typical_workflow(): 8 | "create some example objects" 9 | 10 | Role.add_default_roles() 11 | 12 | User.register( 13 | email="guest@example.com", 14 | password="guest", 15 | roles=["User"], 16 | ) 17 | 18 | User.register( 19 | email="admin@example.com", 20 | password="{{{ simple_password }}}", 21 | roles=["Admin"], 22 | ) 23 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/mixins.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask import current_app 5 | from .. import create_app, db 6 | from flask_testing import TestCase 7 | 8 | 9 | class DiamondTestCase(TestCase): 10 | def create_app(self): 11 | """ 12 | Create a Flask-Diamond app for testing. 13 | """ 14 | 15 | app = create_app() 16 | app.config['TESTING'] = True 17 | app.config['WTF_CSRF_ENABLED'] = False 18 | return app 19 | 20 | def setUp(self): 21 | """ 22 | Prepare for a test case. 23 | """ 24 | 25 | db.create_all() 26 | current_app.logger.debug("setup complete") 27 | 28 | def tearDown(self): 29 | """ 30 | Clean up after a test case. 31 | """ 32 | 33 | db.session.remove() 34 | db.drop_all() 35 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/test_basic.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from .mixins import DiamondTestCase 6 | 7 | 8 | class BasicTestCase(DiamondTestCase): 9 | def test_basic(self): 10 | "ensure the minimum test works" 11 | assert True 12 | 13 | @attr("skip") 14 | def test_skip(self): 15 | assert False 16 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/test_facets.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from ..models import User 6 | from .mixins import DiamondTestCase 7 | 8 | 9 | class UserTestCase(DiamondTestCase): 10 | "Coverage for User Model" 11 | 12 | def test_create(self): 13 | "ensure an account can be created" 14 | User.create(email='guest@example.com', password='a_password') 15 | an_account = User.find(email='guest@example.com') 16 | assert an_account 17 | assert an_account.email == 'guest@example.com' 18 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/test_models.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from ..models import User 6 | from .mixins import DiamondTestCase 7 | 8 | 9 | class UserTestCase(DiamondTestCase): 10 | "Coverage for User Model" 11 | 12 | def test_create(self): 13 | "ensure an account can be created" 14 | User.create(email='guest@example.com', password='a_password') 15 | an_account = User.find(email='guest@example.com') 16 | assert an_account 17 | assert an_account.email == 'guest@example.com' 18 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/test_views.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from .mixins import DiamondTestCase 6 | 7 | 8 | class ViewTestCase(DiamondTestCase): 9 | def test_login(self): 10 | "ensure the login screen loads" 11 | rv = self.client.get('/user/login') 12 | assert b"Login" in rv.data 13 | 14 | def test_index(self): 15 | "ensure index is redirecting" 16 | rv = self.client.get('/') 17 | assert rv.status_code == 302 18 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/tests/test_workflow.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from ..models import User 6 | from .mixins import DiamondTestCase 7 | from .fixtures import typical_workflow 8 | 9 | 10 | class WorkflowTestCase(DiamondTestCase): 11 | def setUp(self): 12 | super(WorkflowTestCase, self).setUp() 13 | typical_workflow() 14 | 15 | @attr("single") 16 | def test_user(self): 17 | "user created in workflow" 18 | u = User.find(email='guest@example.com') 19 | assert u 20 | assert u.email == 'guest@example.com' 21 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/+application.module+/views/__init__.py -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/+application.module+/views/administration/__init__.py -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/modelviews.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | import flask 5 | import flask_security as security 6 | from flask_admin import expose 7 | from flask_diamond import db 8 | from flask_diamond.facets.administration import AuthModelView, AdminIndexView 9 | 10 | 11 | adminbaseview = flask.Blueprint('adminbaseview', __name__, 12 | template_folder='templates', static_folder='static') 13 | 14 | 15 | class RedirectView(AdminIndexView): 16 | def is_visible(self): 17 | return False 18 | 19 | def is_accessible(self): 20 | return security.current_user.is_authenticated() 21 | 22 | @expose('/') 23 | def index(self): 24 | return flask.redirect(flask.url_for('user.list_view')) 25 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/registration.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_security import ConfirmRegisterForm 5 | from flask_wtf.recaptcha import RecaptchaField 6 | 7 | 8 | class ExtendedRegisterForm(ConfirmRegisterForm): 9 | recaptcha = RecaptchaField() 10 | 11 | def validate(self): 12 | rv = ConfirmRegisterForm.validate(self) 13 | if not rv: 14 | return False 15 | 16 | return True 17 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/_wtf.html: -------------------------------------------------------------------------------- 1 | {%- macro inline_field(field) %} 2 | {%- with required = "required" if kwargs['required'] or field.flags.required else "" -%} 3 | {{field(placeholder=field.label.text, required=required, **kwargs)}} 4 | {%- endwith %} 5 | {%- endmacro %} 6 | 7 | {%- macro horizontal_field(field, label_width=2, field_width=3) %} 8 |
9 | {{field.label(class="control-label col-lg-" ~ label_width)}} 10 |
11 | {%- if field|is_boolean_field %} 12 |
13 | 16 |
17 | {% else %} 18 | {{field(class="form-control", **kwargs)|safe}} 19 | {% endif %} 20 |
21 | 22 | {%- if field.errors %} 23 | {%- for error in field.errors %} 24 | {{error}} 25 | {%- endfor %} 26 | {%- elif field.description -%} 27 | {{field.description|safe}} 28 | {%- endif %} 29 |
30 | {% endmacro %} 31 | 32 | 33 | {% macro form_errors(form, hiddens=True) %} 34 | {%- if form.errors %} 35 | {%- for fieldname, errors in form.errors.iteritems() %} 36 | {%- if (form[fieldname]|is_hidden_field and hiddens) or 37 | (form[fieldname]|is_hidden_field and hiddens != 'only') %} 38 | {%- for error in errors %} 39 |

{{error}}

40 | {%- endfor %} 41 | {%- endif %} 42 | {%- endfor %} 43 | {%- endif %} 44 | {%- endmacro %} 45 | 46 | {% macro quick_form(form, action=".", method="post", class="form form-horizontal", buttons = [('submit', 'primary', 'Save')]) %} 47 |
48 | {{ form.hidden_tag() }} 49 | {{ form_errors(form, 'only') }} 50 | {%- for field in form %} 51 | {% if not field|is_hidden_field %} 52 | {{ horizontal_field(field) }} 53 | {%- endif %} 54 | {%- endfor %} 55 |
56 | {% for name, type, text in buttons %} 57 | 58 | {%- endfor %} 59 |
60 |
61 | {%- endmacro %} 62 | 63 | {%- macro checkbox_field(field) %} 64 |
65 |
66 | {{field(**kwargs)|safe}} 67 | {{field.label(class="control-label")}} 68 | 69 | {%- if field.errors %} 70 | {%- for error in field.errors %} 71 |

{{error}}

72 | {%- endfor %} 73 | {%- elif field.description -%} 74 |

{{field.description|safe}}

75 | {%- endif %} 76 |
77 |
78 | {% endmacro %} -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/admin/create_user.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/model/create.html' %} 2 | 3 | {% block head %} 4 | {{ super() }} 5 | 14 | {% endblock %} 15 | 16 | {% block body %} 17 | 25 | 26 |

Create New User

27 | 28 | {% call lib.form_tag(form) %} 29 | 30 | {% if form.hidden_tag is defined %} 31 | {{ form.hidden_tag() }} 32 | {% else %} 33 | {% if csrf_token %} 34 | 35 | {% endif %} 36 | {% for f in form if f.type == 'HiddenField' %} 37 | {{ f }} 38 | {% endfor %} 39 | {% endif %} 40 | 41 | {{ lib.render_field(form, form.name) }} 42 | {{ lib.render_field(form, form.email) }} 43 | {{ lib.render_field(form, form.password) }} 44 | 45 | {{ lib.render_form_buttons(return_url) }} 46 | 47 | {% endcall %} 48 | 49 | {% endblock %} 50 | 51 | {% block tail_js %} 52 | {{ super() }} 53 | 56 | {% endblock %} 57 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/admin/login_base.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/base.html' %} 2 | 3 | {% block access_control %} 4 | {% if current_user.is_authenticated %} 5 |
6 | 7 | {{ current_user.login }} 8 | 9 | 15 |
16 | {% endif %} 17 | {% endblock %} 18 | 19 | {% block head_css %} 20 | {{ super() }} 21 | 22 | {% endblock %} 23 | 24 | {% block tail %} 25 | 26 | 31 | 32 | {% endblock %} -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block htmlhead %}{% endblock %} 5 | 6 | 7 | {% block title %}{% endblock %} - {{ config['PROJECT_NAME'] }} 8 | 9 | 10 | {% block navigation %}{% endblock %} 11 | {% block heading %}{% endblock %} 12 | 13 |
14 | {% block content %}{% endblock %} 15 |
16 | 17 | 22 | 23 | {% block scripts %}{% endblock %} 24 | 25 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/fancy.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block heading %} 4 |
5 |

{{ config["PROJECT_NAME"] }}

6 |
7 | {% endblock %} 8 | 9 | {% block footer %} 10 | {{ config["PROJECT_NAME"] }} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block htmlhead %} 4 | {% endblock %} 5 | 6 | {% block content %} 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/plain.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block footer %}{% endblock %} -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/security/base.html: -------------------------------------------------------------------------------- 1 | {% import "_wtf.html" as wtf %} 2 | {% import "admin/layout.html" as layout %} 3 | {% extends "admin/base.html" %} 4 | 5 | {% block title %}{% endblock %} 6 | 7 | {% block head_css %} 8 | {{ super() }} 9 | 10 | {% endblock %} 11 | 12 | {% block page_body %} 13 |
14 | 29 | 30 | {% block messages %} 31 | {{ layout.messages() }} 32 | {% endblock %} 33 | 34 | {% block body %} 35 | {% block security_content %} 36 | {% endblock %} 37 | {% endblock %} 38 |
39 | {% endblock %} 40 | 41 | {% block tail_js %} 42 | {{ super() }} 43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/security/change_password.html: -------------------------------------------------------------------------------- 1 | {% extends "security/base.html" %} 2 | 3 | {% block security_content %} 4 | 7 | 8 |
9 | {{ change_password_form.csrf_token }} 10 | 11 | {{ wtf.horizontal_field(change_password_form.password) }} 12 | {{ wtf.horizontal_field(change_password_form.new_password) }} 13 | {{ wtf.horizontal_field(change_password_form.new_password_confirm) }} 14 | 15 |
16 | 17 |
18 |
19 | 20 | {% endblock %} 21 | 22 | {% block tail_js %} 23 | {{ super() }} 24 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/security/forgot_password.html: -------------------------------------------------------------------------------- 1 | {% extends "security/base.html" %} 2 | 3 | {% block security_content %} 4 |
5 | 6 |
    7 | {% if security.registerable %} 8 |
  • 9 | Don't have an account yet? Create a new account 10 |
  • 11 | {% endif %} 12 |
  • 13 | Remembered your password? Try to login 14 |
  • 15 |
16 |
17 | 18 | 21 | 22 |
23 | {{ forgot_password_form.csrf_token }} 24 | 25 | {{ wtf.horizontal_field(forgot_password_form.email) }} 26 | 27 |
28 | 29 |
30 |
31 | 32 |
33 | 34 | {% endblock %} 35 | 36 | {% block tail_js %} 37 | {{ super() }} 38 | 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/security/login_user.html: -------------------------------------------------------------------------------- 1 | {% extends "security/base.html" %} 2 | 3 | {% block security_content %} 4 | 5 |
6 | 7 |
    8 | {% if security.registerable %} 9 |
  • 10 | Don't have an account yet? Create a new account 11 |
  • 12 | {% endif %} 13 | {% if security.recoverable %} 14 |
  • 15 | Forgot your password? Reset it via email 16 |
  • 17 | {% endif %} 18 | {% if security.confirmable %} 19 |
  • 20 | Didn't receive your confirmation email? Resend confirmation email 21 |
  • 22 | {% endif %} 23 |
24 |
25 | 26 | 29 | 30 |
31 | {{ login_user_form.hidden_tag() }} 32 | 33 | {{ wtf.horizontal_field(login_user_form.email) }} 34 | {{ wtf.horizontal_field(login_user_form.password) }} 35 | {{ wtf.horizontal_field(login_user_form.remember) }} 36 | 37 |
38 | 39 |
40 |
41 | 42 |
43 | 44 | {% endblock %} 45 | 46 | {% block tail_js %} 47 | {{ super() }} 48 | 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/security/register_user.html: -------------------------------------------------------------------------------- 1 | {% extends "security/base.html" %} 2 | 3 | {% block security_content %} 4 | 5 |
6 | 7 |
    8 |
  • 9 | Already have an account? Login here 10 |
  • 11 |
  • 12 | During beta testing, you must have a utoronto.ca email address to create a turkr account. 13 |
  • 14 |
15 |
16 | 17 | 20 | 21 |
22 | {{ register_user_form.csrf_token }} 23 | 24 | {{ wtf.horizontal_field(register_user_form.email) }} 25 | {{ wtf.horizontal_field(register_user_form.password) }} 26 | {% if register_user_form.password_confirm %} 27 | {{ wtf.horizontal_field(register_user_form.password_confirm) }} 28 | {% endif %} 29 | 30 |

31 | {% for error in register_user_form.recaptcha.errors %} 32 | {{ error }} 33 | {% endfor %} 34 | {{ register_user_form.recaptcha }} 35 |

36 | 37 |
38 | 39 |
40 |
41 | 42 | {% endblock %} 43 | 44 | {% block tail_js %} 45 | {{ super() }} 46 | 49 | {% endblock %} 50 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/security/reset_password.html: -------------------------------------------------------------------------------- 1 | {% extends "security/base.html" %} 2 | 3 | {% block security_content %} 4 |
5 | Remembered your password? Try and login 6 |
7 | 8 | 11 | 12 |
13 | {{ reset_password_form.csrf_token }} 14 | 15 | {{ wtf.horizontal_field(reset_password_form.password) }} 16 | {{ wtf.horizontal_field(reset_password_form.password_confirm) }} 17 | 18 |
19 | 20 |
21 |
22 | 23 |
24 | 25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/administration/templates/security/send_confirmation.html: -------------------------------------------------------------------------------- 1 | {% extends "security/base.html" %} 2 | 3 | {% block security_content %} 4 |
5 | 6 |
    7 | {% if security.registerable %} 8 |
  • 9 | Don't have an account yet? Create a new account 10 |
  • 11 | {% endif %} 12 | {% if security.recoverable %} 13 |
  • 14 | Forgot your password? Reset it via email 15 |
  • 16 | {% endif %} 17 |
18 |
19 | 20 | 23 | 24 |
25 | {{ send_confirmation_form.csrf_token }} 26 | 27 | {{ wtf.horizontal_field(send_confirmation_form.email) }} 28 | 29 |
30 | 31 |
32 |
33 | 34 |
35 | 36 | {% endblock %} 37 | 38 | {% block tail_js %} 39 | {{ super() }} 40 | 43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/diamond/__init__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | import flask 5 | 6 | diamond_blueprint = flask.Blueprint( 7 | 'diamond_blueprint', 8 | __name__, 9 | static_folder='static', 10 | template_folder='templates', 11 | static_url_path='/static/diamond', 12 | ) 13 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/diamond/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/+application.module+/views/diamond/static/favicon.ico -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/diamond/static/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/+application.module+/views/diamond/static/index.html -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/diamond/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Allow: / -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/diamond/static/style.css: -------------------------------------------------------------------------------- 1 | #footer { 2 | text-align: center; 3 | font-family: arial, helvetica, sans; 4 | font-size: 0.7em; 5 | } 6 | 7 | #content { 8 | width: 800px; 9 | margin: 0px auto; 10 | } 11 | 12 | label.control-label { 13 | margin-right: 1em ; 14 | } 15 | 16 | .checkbox input[type="checkbox"] { 17 | margin-left: 0px; 18 | } 19 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/views/diamond/templates/plain.html: -------------------------------------------------------------------------------- 1 | {% block header %}{% endblock %} 2 | {% block content %}Plain{% endblock %} 3 | {% block footer %}{% endblock %} 4 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/+application.module+/wsgi.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from {{{ application.module }}} import create_app 5 | app = create_app() 6 | 7 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /dist/ 3 | /*.egg-info/ 4 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/.mrbob.ini: -------------------------------------------------------------------------------- 1 | [mr.bob] 2 | ignored_files = .git 3 | 4 | [questions] 5 | application.module.question = What is the name of the module? (use Pythonic naming) 6 | application.module.required = True 7 | application.module.default = my_app 8 | 9 | application.name.question = What is the name of the application? (i.e. username, daemon name, etc) 10 | application.name.required = True 11 | application.name.default = my-app 12 | 13 | project.version.question = What is the current version of this project? (for new projects use 0.1.0) 14 | project.version.required = True 15 | project.version.default = 0.1.0 16 | 17 | description.short.question = What is a short description for this project? 18 | description.short.required = True 19 | description.short.default = my-app does something new. 20 | 21 | author.name.question = Who is the author of this software? 22 | author.name.required = True 23 | author.name.default = Ian Dennis Miller 24 | 25 | author.email.question = What is the author's contact email? 26 | author.email.required = True 27 | author.email.default = author@example.com 28 | 29 | project.url.question = What is the project URL? 30 | project.url.required = True 31 | project.url.default = http://my-app.example.com 32 | 33 | application.port.question = Which port will the daemon listen on? 34 | application.port.required = True 35 | application.port.default = 5000 36 | 37 | secret.question = What is the secret key? 38 | secret.required = True 39 | 40 | hash_salt.question = What is the hash_salt? 41 | hash_salt.required = True 42 | 43 | simple_password.question = What is the simple_password? 44 | simple_password.required = True 45 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/.readthedocs.txt: -------------------------------------------------------------------------------- 1 | GitPython==1.0.1 2 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | cache: pip 3 | python: 4 | - 2.7 5 | - 3.5 6 | # command to install dependencies 7 | install: make install 8 | # command to run tests 9 | script: make test 10 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/MANIFEST.in.bob: -------------------------------------------------------------------------------- 1 | graft {{{ application.module }}} 2 | include *.cfg *.rst *.in *.txt *.ini *.mako *.html *.js *.css *.jpg *.gif 3 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/Makefile.bob: -------------------------------------------------------------------------------- 1 | # {{{ application.name }}} (c) {{{ author.name }}} 2 | 3 | SHELL=/bin/bash 4 | PROJECT_NAME={{{ application.name }}} 5 | MOD_NAME={{{ application.module }}} 6 | WATCHMEDO_PATH=$$(which watchmedo) 7 | NOSETESTS_PATH=$$(which nosetests) 8 | TEST_CMD=SETTINGS=$$PWD/etc/conf/testing.conf $(NOSETESTS_PATH) $(MOD_NAME) 9 | 10 | install: 11 | python setup.py install 12 | 13 | requirements: 14 | pip install -r requirements.txt 15 | 16 | clean: 17 | rm -rf build dist *.egg-info 18 | find . -name '*.pyc' -delete 19 | find . -name __pycache__ -delete 20 | 21 | server: 22 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py runserver 23 | 24 | shell: 25 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py shell 26 | 27 | watch: 28 | watchmedo shell-command -R -p "*.py" -c 'echo \\n\\n\\n\\nSTART; date; $(TEST_CMD) -c etc/nose/test-single.cfg; date' . 29 | 30 | test: 31 | $(TEST_CMD) -c etc/nose/test.cfg 32 | 33 | single: 34 | $(TEST_CMD) -c etc/nose/test-single.cfg 35 | 36 | db: 37 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py init_db 38 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py user_add --email "guest@example.com" --password "guest" 39 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py user_add --email "admin@example.com" --password "{{{ simple_password }}}" --admin 40 | 41 | newmigration: 42 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py drop_db 43 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py db upgrade 44 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py db migrate 45 | 46 | migrate: 47 | SETTINGS=$$PWD/etc/conf/dev.conf bin/manage.py db upgrade 48 | 49 | apidocs: 50 | rm -rf var/sphinx/auto-api 51 | mkdir -p var/sphinx/auto-api/$(MOD_NAME) 52 | sphinx-apidoc --separate -o var/sphinx/auto-api/$(MOD_NAME) $(MOD_NAME) $(MOD_NAME)/tests 53 | -rm docs/auto-api 54 | ln -s $$PWD/var/sphinx/auto-api docs/auto-api 55 | 56 | docs: 57 | rm -rf build/sphinx 58 | SETTINGS=$$PWD/etc/conf/testing.conf sphinx-build -b html docs build/sphinx 59 | 60 | notebook: 61 | SETTINGS=$$PWD/etc/conf/dev.conf cd var/ipython && ipython notebook 62 | 63 | release: 64 | python setup.py sdist upload -r https://pypi.python.org/pypi 65 | 66 | .PHONY: clean install test server watch notebook db single docs shell upgradedb migratedb release requirements apidocs gh-pages 67 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/Readme.rst.bob: -------------------------------------------------------------------------------- 1 | {{{ application.name }}} 2 | ============= 3 | 4 | {{{ description.short }}} 5 | 6 | Overview 7 | -------- 8 | 9 | Flask-Diamond imports many other Flask extensions and glues them all together. The end result is a model administration view, accounts and high-level account operations (e.g. password reset), testing, documentation, deployment, and more. 10 | 11 | Installation 12 | ^^^^^^^^^^^^ 13 | 14 | The following will install {{{ application.name }}}. 15 | 16 | :: 17 | 18 | mkdir {{{ application.name }}} 19 | cd {{{ application.name }}} 20 | mkvirtualenv -a . {{{ application.name }}} 21 | make install docs test db server 22 | 23 | Documentation 24 | ^^^^^^^^^^^^^ 25 | 26 | {{{ project.url }}} 27 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/bin/manage.py.bob: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # {{{ application.name }}} (c) {{{ author.name }}} 4 | 5 | import sys 6 | import traceback 7 | sys.path.insert(0, '.') 8 | 9 | from flask_script import Manager, Shell, Server 10 | from flask_migrate import Migrate, MigrateCommand, upgrade 11 | import alembic 12 | import alembic.config 13 | from {{{ application.module }}} import create_app, db 14 | from {{{ application.module }}}.models import User, Role 15 | 16 | app = create_app() 17 | migrate = Migrate(app, db, directory="{{{ application.module }}}/migrations") 18 | 19 | 20 | def _make_context(): 21 | return { 22 | "app": app, 23 | "db": db, 24 | } 25 | 26 | manager = Manager(app) 27 | manager.add_command("shell", Shell(make_context=_make_context)) 28 | manager.add_command("runserver", Server(port=app.config['PORT'])) 29 | manager.add_command("publicserver", Server(port=app.config['PORT'], host="0.0.0.0")) 30 | manager.add_command('db', MigrateCommand) 31 | 32 | 33 | @manager.option('-e', '--email', help='email address', required=True) 34 | @manager.option('-p', '--password', help='password', required=True) 35 | @manager.option('-a', '--admin', help='make user an admin user', action='store_true', default=None) 36 | def user_add(email, password, admin=False): 37 | "add a user to the database" 38 | if admin: 39 | roles = ["Admin"] 40 | else: 41 | roles = ["User"] 42 | User.register( 43 | email=email, 44 | password=password, 45 | confirmed=True, 46 | roles=roles 47 | ) 48 | 49 | 50 | @manager.option('-e', '--email', help='email address', required=True) 51 | def user_del(email): 52 | "delete a user from the database" 53 | obj = User.find(email=email) 54 | if obj: 55 | obj.delete() 56 | print("Deleted") 57 | else: 58 | print("User not found") 59 | 60 | 61 | @manager.command 62 | def drop_db(): 63 | "drop all databases, instantiate schemas" 64 | db.reflect() 65 | db.drop_all() 66 | 67 | 68 | @manager.option('-m', '--migration', 69 | help='create database from migrations', 70 | action='store_true', default=None) 71 | def init_db(migration): 72 | "drop all databases, instantiate schemas" 73 | db.drop_all() 74 | 75 | if migration: 76 | # create database using migrations 77 | print("applying migration") 78 | upgrade() 79 | else: 80 | # create database from model schema directly 81 | db.create_all() 82 | db.session.commit() 83 | cfg = alembic.config.Config("{{{ application.module }}}/migrations/alembic.ini") 84 | alembic.command.stamp(cfg, "head") 85 | Role.add_default_roles() 86 | 87 | 88 | if __name__ == "__main__": 89 | manager.run() 90 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/bin/runserver.py.bob: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # {{{ application.name }}} (c) {{{ author.name }}} 4 | 5 | from {{{ application.module }}}.wsgi import app 6 | app.run(port=app.config['PORT']) 7 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/docs/_static/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/docs/_static/.gitignore -------------------------------------------------------------------------------- /flask_diamond/skels/app/docs/_templates/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/app/docs/_templates/.gitignore -------------------------------------------------------------------------------- /flask_diamond/skels/app/docs/index.rst.bob: -------------------------------------------------------------------------------- 1 | .. include:: ../Readme.rst 2 | 3 | Introduction 4 | ------------ 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | 9 | license 10 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/docs/license.rst.bob: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | The MIT License (MIT) 5 | 6 | {{{ application.name }}} 7 | Copyright (c) 2016 {{{ author.name }}} 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/etc/conf/dev.conf.bob: -------------------------------------------------------------------------------- 1 | # {{{ application.name }}} (c) {{{ author.name }}} 2 | 3 | PROJECT_NAME = "{{{ application.name }}}" 4 | PORT = {{{ application.port }}} 5 | LOG = "/tmp/{{{ application.name }}}-dev.log" 6 | LOG_LEVEL = "DEBUG" 7 | SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/{{{ application.name }}}-dev.db" 8 | SECRET_KEY = {{{ secret }}} 9 | BASE_URL = "http://{{{ application.name }}}.com" 10 | 11 | SECURITY_PASSWORD_SALT = "{{{ hash_salt }}}" 12 | SECURITY_POST_LOGIN_VIEW = "/admin" 13 | SECURITY_PASSWORD_HASH = 'sha256_crypt' 14 | SECURITY_URL_PREFIX = '/user' 15 | SECURITY_CHANGEABLE = True 16 | SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False 17 | SECURITY_CONFIRMABLE = False 18 | SECURITY_REGISTERABLE = False 19 | SECURITY_RECOVERABLE = False 20 | SECURITY_TRACKABLE = False 21 | SECURITY_EMAIL_SENDER = "accounts@{{{ application.name }}}.com" 22 | 23 | RECAPTCHA_PUBLIC_KEY = '0000_00000000000000000000000000000000000' 24 | RECAPTCHA_PRIVATE_KEY = '0000_00000000000000000000000000000000000' 25 | 26 | DEBUG = True 27 | DEBUG_TOOLBAR = True 28 | DEBUG_TB_INTERCEPT_REDIRECTS = False 29 | 30 | MAIL_SERVER = '127.0.0.1' 31 | MAIL_PORT = 25 32 | MAIL_USE_TLS = False 33 | MAIL_USERNAME = None 34 | MAIL_PASSWORD = None 35 | 36 | CELERY_BROKER_URL = 'sqla+sqlite:///var/db/celerydb.sqlite' 37 | CELERY_RESULT_BACKEND = 'db+sqlite:///var/db/results.sqlite' 38 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/etc/conf/production.conf.bob: -------------------------------------------------------------------------------- 1 | # {{{ application.name }}} (c) {{{ author.name }}} 2 | 3 | PROJECT_NAME = "{{{ application.name }}}" 4 | PORT = {{{ application.port }}} 5 | LOG = "/var/log/{{{ application.name }}}/daemon.log" 6 | LOG_LEVEL = "DEBUG" 7 | SQLALCHEMY_DATABASE_URI = "sqlite:////var/lib/{{{ application.name }}}/main.db" 8 | SECRET_KEY = {{{ secret }}} 9 | BASE_URL = "http://{{{ application.name }}}.com" 10 | 11 | SECURITY_PASSWORD_SALT = "{{{ hash_salt }}}" 12 | SECURITY_POST_LOGIN_VIEW = "/admin" 13 | SECURITY_PASSWORD_HASH = 'sha256_crypt' 14 | SECURITY_URL_PREFIX = '/user' 15 | SECURITY_CHANGEABLE = True 16 | SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False 17 | SECURITY_CONFIRMABLE = False 18 | SECURITY_REGISTERABLE = False 19 | SECURITY_RECOVERABLE = False 20 | SECURITY_TRACKABLE = False 21 | SECURITY_EMAIL_SENDER = "accounts@{{{ application.name }}}.com" 22 | 23 | RECAPTCHA_PUBLIC_KEY = '0000_00000000000000000000000000000000000' 24 | RECAPTCHA_PRIVATE_KEY = '0000_00000000000000000000000000000000000' 25 | 26 | DEBUG = False 27 | DEBUG_TOOLBAR = False 28 | 29 | MAIL_SERVER = '127.0.0.1' 30 | MAIL_PORT = 25 31 | MAIL_USE_TLS = False 32 | MAIL_USERNAME = None 33 | MAIL_PASSWORD = None 34 | 35 | CELERY_BROKER_URL = 'sqla+sqlite:///var/db/celerydb.sqlite' 36 | CELERY_RESULT_BACKEND = 'db+sqlite:///var/db/results.sqlite' 37 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/etc/conf/testing.conf.bob: -------------------------------------------------------------------------------- 1 | # {{{ application.name }}} (c) {{{ author.name }}} 2 | 3 | PROJECT_NAME = "{{{ application.name }}}" 4 | PORT = {{{ application.port }}} 5 | LOG = "/tmp/{{{ application.name }}}-testing.log" 6 | LOG_LEVEL = "DEBUG" 7 | SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/{{{ application.name }}}-testing.db" 8 | SECRET_KEY = {{{ secret }}} 9 | BASE_URL = "http://{{{ application.name }}}.com" 10 | 11 | SECURITY_PASSWORD_SALT = "{{{ hash_salt }}}" 12 | SECURITY_POST_LOGIN_VIEW = "/admin" 13 | SECURITY_PASSWORD_HASH = 'sha256_crypt' 14 | SECURITY_URL_PREFIX = '/user' 15 | SECURITY_CHANGEABLE = True 16 | SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False 17 | SECURITY_CONFIRMABLE = False 18 | SECURITY_REGISTERABLE = False 19 | SECURITY_RECOVERABLE = False 20 | SECURITY_TRACKABLE = False 21 | SECURITY_EMAIL_SENDER = "accounts@{{{ application.name }}}.com" 22 | 23 | RECAPTCHA_PUBLIC_KEY = '0000_00000000000000000000000000000000000' 24 | RECAPTCHA_PRIVATE_KEY = '0000_00000000000000000000000000000000000' 25 | 26 | DEBUG = False 27 | DEBUG_TOOLBAR = False 28 | 29 | MAIL_SERVER = '127.0.0.1' 30 | MAIL_PORT = 25 31 | MAIL_USE_TLS = False 32 | MAIL_USERNAME = None 33 | MAIL_PASSWORD = None 34 | 35 | CELERY_BROKER_URL = 'sqla+sqlite:///var/db/celerydb.sqlite' 36 | CELERY_RESULT_BACKEND = 'db+sqlite:///var/db/results.sqlite' 37 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/etc/nose/test-all.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | verbosity=2 3 | 4 | attr=!skip 5 | 6 | debug=True 7 | logging-level=DEBUG 8 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/etc/nose/test-single.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | verbosity=2 3 | 4 | attr=single,!skip 5 | 6 | debug=True 7 | logging-level=DEBUG 8 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/etc/nose/test.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | verbosity=2 3 | 4 | attr=!slow,!skip,!online 5 | #attr=!slow,!skip 6 | #attr=single 7 | 8 | debug=True 9 | logging-level=DEBUG 10 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/fabfile.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | import sys 5 | import os 6 | from fabric.api import env, run, open_shell 7 | from fabric.contrib.project import rsync_project 8 | 9 | env.user = '{{{ application.name }}}' 10 | env.key_filename = [os.path.expanduser('~/.ssh/id_rsa')] 11 | 12 | if not env.hosts: 13 | print("need to call with -H [host.example.com]") 14 | sys.exit(1) 15 | 16 | 17 | def rsync(): 18 | "rsync local changes (ignoring git)" 19 | if not os.path.exists("fabfile.py"): 20 | print("need to run in root of project") 21 | sys.exit(1) 22 | 23 | excluded = [ 24 | "*.egg-info", 25 | ".build", 26 | ".git*", 27 | "*.pyc", 28 | "dist", 29 | "build", 30 | ] 31 | rsync_project(remote_dir=env.user, local_dir="./", exclude=excluded, delete=True) 32 | 33 | 34 | def pull(): 35 | "pull on the remote system" 36 | run('cd ~/{app} && git pull'.format(app=env.user)) 37 | 38 | 39 | def setup(): 40 | "run python setup.py install, which installs module" 41 | cmd = 'source ~/.virtualenvs/{app}/bin/activate && cd {app} && make install' 42 | run(cmd.format(app=env.user)) 43 | 44 | 45 | def ipython(): 46 | "open ipython environment on remote host" 47 | open_shell("~/.virtualenvs/{app}/bin/shell.py && exit".format(app=env.user)) 48 | 49 | 50 | def shell(): 51 | "open a shell on the remote host" 52 | open_shell("source ~/.virtualenvs/{app}/bin/activate".format(app=env.user)) 53 | 54 | 55 | def restart(): 56 | "restart app server" 57 | run("pkill -HUP runserver.py") 58 | 59 | 60 | def nginx_restart(sudoer="root"): 61 | "restart nginx" 62 | env.user = sudoer 63 | run("sudo killall nginx && sudo /etc/init.d/nginx start") 64 | 65 | 66 | def logs(): 67 | "watch logs on remote server" 68 | open_shell("tail -f /var/log/{app}/*log /var/log/nginx/{app}*log && exit".format(app=env.user)) 69 | 70 | 71 | def help(): 72 | "get help on testing and deploying" 73 | print(helpfile) 74 | 75 | helpfile = """ 76 | # quickly copy some changes over 77 | fab rsync setup 78 | """ 79 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/requirements.txt.bob: -------------------------------------------------------------------------------- 1 | flask-diamond>=0.5.0 2 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/setup.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | import re 5 | import os 6 | import codecs 7 | from setuptools import setup, find_packages 8 | from distutils.dir_util import copy_tree 9 | 10 | 11 | def read(*rnames): 12 | return codecs.open(os.path.join(os.path.dirname(__file__), *rnames), 'r', 'utf-8').read() 13 | 14 | 15 | def grep(attrname): 16 | pattern = r"{0}\W*=\W*'([^']+)'".format(attrname) 17 | strval, = re.findall(pattern, read('{{{ application.module }}}/__meta__.py')) 18 | return strval 19 | 20 | 21 | setup( 22 | version=grep('__version__'), 23 | name='{{{ application.name }}}', 24 | description="{{{ description.short }}}", 25 | packages=find_packages(), 26 | scripts=[ 27 | "bin/runserver.py", 28 | "bin/manage.py", 29 | ], 30 | long_description=read('Readme.rst'), 31 | classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers 32 | include_package_data=True, 33 | keywords='', 34 | author=grep('__author__'), 35 | author_email=grep('__email__'), 36 | url=grep('__url__'), 37 | install_requires=read('requirements.txt'), 38 | license='MIT', 39 | zip_safe=False, 40 | ) 41 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/var/db/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | 6 | -------------------------------------------------------------------------------- /flask_diamond/skels/app/var/log/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | 6 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/__init__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_diamond import Diamond 5 | from flask_diamond.facets.administration import AdminModelView 6 | from flask_diamond.facets.database import db 7 | from .models import User, Role, Planet, Satellite 8 | 9 | # declare these globalish objects before initializing models 10 | application = None 11 | 12 | 13 | class {{{ application.module }}}(Diamond): 14 | 15 | def init_accounts(self): 16 | "initialize accounts with the User and Role classes imported from .models" 17 | return self.super("accounts", user=User, role=Role) 18 | 19 | def init_administration(self): 20 | "Initialize admin interface" 21 | 22 | admin = self.super("administration", user=User, role=Role) 23 | 24 | model_list = [ 25 | Planet, 26 | Satellite 27 | ] 28 | 29 | for model in model_list: 30 | admin.add_view(AdminModelView( 31 | model, 32 | db.session, 33 | name=model.__name__, 34 | category="Admin") 35 | ) 36 | 37 | return admin 38 | 39 | def init_blueprints(self): 40 | "Application blueprints" 41 | 42 | self.super("blueprints") 43 | 44 | # administration blueprint is custom to this application 45 | from .views.administration.modelviews import adminbaseview 46 | self.app.register_blueprint(adminbaseview) 47 | 48 | from .views.diamond import diamond_blueprint 49 | self.app.register_blueprint(diamond_blueprint) 50 | 51 | 52 | def api_map(rest_extension): 53 | from .api import Planet 54 | rest_extension.add_resource(Planet, '/planet/') 55 | 56 | 57 | def create_app(): 58 | global application 59 | if not application: 60 | application = {{{ application.module }}}() 61 | application.facet("configuration") 62 | application.facet("logs") 63 | application.facet("database") 64 | application.facet("marshalling") 65 | application.facet("blueprints") 66 | application.facet("accounts") 67 | application.facet("signals") 68 | application.facet("forms") 69 | application.facet("error_handlers") 70 | application.facet("request_handlers") 71 | application.facet("administration") 72 | application.facet("rest", api_map=api_map) 73 | # application.facet("webassets") 74 | # application.facet("email") 75 | # application.facet("debugger") 76 | # application.facet("task_queue") 77 | 78 | # print application.app.url_map 79 | return application.app 80 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/api.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_restful import Resource 5 | from .models import Planet as PlanetModel 6 | 7 | 8 | class Planet(Resource): 9 | 10 | def get(self, name): 11 | planet = PlanetModel.find(name=name) 12 | if planet: 13 | return(planet.dumps()) 14 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/models/__init__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_diamond.models.user import User 5 | from flask_diamond.models.role import Role 6 | from .planet import Planet 7 | from .satellite import Satellite 8 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/models/planet.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_marshmallow.fields import fields 5 | from flask_diamond import db, ma 6 | from flask_diamond.mixins.crud import CRUDMixin 7 | from flask_diamond.mixins.marshmallow import MarshmallowMixin 8 | 9 | 10 | class PlanetSchema(ma.Schema): 11 | satellites = fields.Nested('SatelliteSchema', allow_none=True, many=True) 12 | 13 | class Meta: 14 | additional = ("id", "name", "mass") 15 | 16 | 17 | class Planet(db.Model, CRUDMixin, MarshmallowMixin): 18 | "A Planet is a celestial body" 19 | __schema__ = PlanetSchema 20 | id = db.Column(db.Integer(), primary_key=True) 21 | name = db.Column(db.String(80), unique=True) 22 | mass = db.Column(db.Float()) 23 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/models/satellite.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_diamond import db, ma 5 | from flask_diamond.mixins.crud import CRUDMixin 6 | from flask_diamond.mixins.marshmallow import MarshmallowMixin 7 | 8 | 9 | class SatelliteSchema(ma.Schema): 10 | class Meta: 11 | additional = ("id", "name") 12 | 13 | 14 | class Satellite(db.Model, CRUDMixin, MarshmallowMixin): 15 | "A Satellite orbits a Planet" 16 | __schema__ = SatelliteSchema 17 | id = db.Column(db.Integer(), primary_key=True) 18 | name = db.Column(db.String(80), unique=True) 19 | mass = db.Column(db.Float()) 20 | planet = db.relationship('Planet', backref=db.backref('satellites', lazy='dynamic')) 21 | planet_id = db.Column(db.Integer(), db.ForeignKey("planet.id")) 22 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/tests/__init__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/tests/fixtures.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from ..models import Role, User, Planet, Satellite 5 | 6 | 7 | def typical_workflow(): 8 | "instantiate the minimal viable product" 9 | 10 | # create user accounts 11 | Role.add_default_roles() 12 | 13 | User.register( 14 | email="guest@example.com", 15 | password="guest", 16 | roles=["User"], 17 | ) 18 | 19 | User.register( 20 | email="admin@example.com", 21 | password="admin", 22 | roles=["Admin"], 23 | ) 24 | 25 | # create data for users to manage 26 | earth = Planet.create(name="Earth") 27 | Satellite.create(name="Moon", planet=earth) 28 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/tests/test_crudmixin.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from flask_diamond import db 6 | from .mixins import DiamondTestCase 7 | from ..models import User 8 | 9 | 10 | class CRUDMixinTestCase(DiamondTestCase): 11 | "Coverage for CRUD Mixin" 12 | 13 | def test_create(self): 14 | # "test CRUDMixin Create and Read" 15 | user = User.create(email="user@example.com", password="password") 16 | self.assertIsNotNone(user) 17 | 18 | def test_find(self): 19 | # "test CRUDMixin find" 20 | User.create(email="user@example.com", password="password") 21 | user = User.find(email="user@example.com") 22 | self.assertIsNotNone(user) 23 | self.assertEqual(user.email, "user@example.com") 24 | 25 | def test_get_by_id(self): 26 | # "test CRUDMixin get_by_id" 27 | User.create(email="user@example.com", password="password") 28 | user = User.get_by_id(1) 29 | self.assertIsNotNone(user) 30 | self.assertEqual(user.email, "user@example.com") 31 | 32 | def test_update(self): 33 | # "test CRUDMixin Update" 34 | user = User.create(email="user@example.com", password="password") 35 | 36 | # set the mass of Earth 37 | user.update(confirmed=True) 38 | 39 | # retrieve it from the db again 40 | user = User.find(email="user@example.com") 41 | self.assertEqual(user.confirmed, True) 42 | 43 | def test_delete(self): 44 | # "test CRUDMixin Delete" 45 | # create and immediately delete the object 46 | user = User.create(email="user@example.com", password="password") 47 | user.delete() 48 | 49 | # attempt to retrieve it and verify that it fails 50 | user = User.find(email="user@example.com") 51 | self.assertIsNone(user) 52 | 53 | @attr("skip") 54 | def test_create_commit_false(self): 55 | # "ensure database commits can be deferred" 56 | User.create(email="user@example.com", password="password", _commit=False) 57 | 58 | user = User.get_by_id(1) 59 | print(user) 60 | print(user.email) 61 | # assert user is None 62 | 63 | db.session.commit() 64 | 65 | user = User.find(email="user@example.com") 66 | self.assertIsNotNone(user) 67 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/tests/test_marshmallowmixin.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from .mixins import DiamondTestCase 6 | from .fixtures import typical_workflow 7 | from ..models import Planet, Satellite, User 8 | 9 | 10 | class MarshmallowMixinTestCase(DiamondTestCase): 11 | "Coverage for CRUD Mixin" 12 | 13 | def setUp(self): 14 | super(MarshmallowMixinTestCase, self).setUp() 15 | typical_workflow() 16 | 17 | def test_dump(self): 18 | user = User.find(email="guest@example.com") 19 | result = user.dump() 20 | self.assertEqual(result['email'], "guest@example.com") 21 | 22 | def test_dumps(self): 23 | user = User.find(email="guest@example.com") 24 | result = user.dumps() 25 | self.assertRegexpMatches(result, r"guest@example.com") 26 | 27 | @attr("skip") 28 | def test_dumpf(self): 29 | pass 30 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/tests/test_models.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from nose.plugins.attrib import attr 5 | from ..models import User, Planet, Satellite 6 | from .mixins import DiamondTestCase 7 | from .fixtures import typical_workflow 8 | 9 | 10 | class ModelTestCase(DiamondTestCase): 11 | "Coverage for Models" 12 | 13 | def setUp(self): 14 | super(ModelTestCase, self).setUp() 15 | typical_workflow() 16 | 17 | @attr("single") 18 | def test_relationship(self): 19 | "test relationship: child belongs to parent" 20 | moon = Satellite.find(name="Moon") 21 | self.assertEqual(moon.planet.name, "Earth") 22 | 23 | def test_relationship_security(self): 24 | "test relationship: flask-security model" 25 | guest_user = User.find(email="guest@example.com") 26 | self.assertEqual(guest_user.email, "guest@example.com") 27 | 28 | def test_backref(self): 29 | "test relationship: parent has one or more children" 30 | earth = Planet.find(name="Earth") 31 | self.assertEqual(earth.satellites[0].name, "Moon") 32 | 33 | def test_create_user(self): 34 | "ensure User model object can be created" 35 | User.create(email='an_account@example.com', password='a_password') 36 | an_account = User.find(email='an_account@example.com') 37 | self.assertIsNotNone(an_account) 38 | self.assertEqual(an_account.email, 'an_account@example.com') 39 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/views/base/__init__.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | import flask 5 | baseview = flask.Blueprint( 6 | 'baseview', 7 | __name__, 8 | template_folder='templates', 9 | static_folder='static' 10 | ) 11 | 12 | 13 | @baseview.route('/') 14 | def index(): 15 | "set up the default handler for requests to /" 16 | return flask.redirect(flask.url_for("admin.index")) 17 | # return flask.redirect(flask.url_for("simpleview.hello")) 18 | # return flask.render_template("index.html") 19 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/views/base/templates/index.html.bob: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block htmlhead %} 4 | {% endblock %} 5 | 6 | {% block content %} 7 | {{{ application.name }}} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/views/frontend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/skels/tutorial-planets/+application.module+/views/frontend/__init__.py -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/views/frontend/forms.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | from flask_wtf import Form 5 | import wtforms.fields as fields 6 | from wtforms.validators import Optional 7 | 8 | 9 | class SubForm(Form): 10 | "This is an individual" 11 | friend = fields.SelectField( 12 | label="Friend Name", 13 | validators=[Optional()]) 14 | 15 | 16 | class ExampleForm(Form): 17 | sex = fields.RadioField(label="Sex", choices=[("M", "Male"), ("F", "Female")]) 18 | subfield = fields.FieldList(fields.FormField(SubForm)) 19 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/views/frontend/simpleview.py.bob: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # {{{ application.name }}} (c) {{{ author.name }}} 3 | 4 | import flask 5 | 6 | simpleview = flask.Blueprint( 7 | 'simpleview', 8 | __name__, 9 | template_folder='templates', 10 | static_folder='static' 11 | ) 12 | 13 | 14 | @simpleview.route('/') 15 | def index(): 16 | return flask.redirect(flask.url_for(".hello")) 17 | 18 | 19 | @simpleview.route('/hello') 20 | def hello(): 21 | return flask.render_template('hello.html') 22 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/views/frontend/templates/_formhelpers.html: -------------------------------------------------------------------------------- 1 | {% macro render_field(field) %} 2 |
3 | {{ field.label }} 4 | {{ field(**kwargs)|safe }} 5 | {% if field.errors %} 6 |
    7 | {% for error in field.errors %} 8 |
  • {{ error }}
  • 9 | {% endfor %} 10 |
11 | {% endif %} 12 |
13 | {% endmacro %} 14 | 15 | {% macro render_field_plain(field) %} 16 |
17 | {{ field(**kwargs)|safe }} 18 | {% if field.errors %} 19 |
    20 | {% for error in field.errors %} 21 |
  • {{ error }}
  • 22 | {% endfor %} 23 |
24 | {% endif %} 25 |
26 | {% endmacro %} 27 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/+application.module+/views/frontend/templates/hello.html.bob: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block htmlhead %} 4 | {% endblock %} 5 | 6 | {% block content %} 7 | hello world! 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /flask_diamond/skels/tutorial-planets/.mrbob.ini: -------------------------------------------------------------------------------- 1 | [mr.bob] 2 | ignored_files = .git 3 | 4 | [questions] 5 | application.module.question = What is the name of the module? (can be different from the application name) 6 | application.module.required = True 7 | application.module.default = flask_diamond 8 | 9 | application.name.question = What is the name of the application? (i.e. username, daemon name, etc) 10 | application.name.required = True 11 | application.name.default = Flask-Diamond 12 | 13 | author.name.question = Who is the author of this software? 14 | author.name.required = True 15 | author.name.default = Ian Dennis Miller 16 | -------------------------------------------------------------------------------- /flask_diamond/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diamond-org/flask-diamond/bb1c1f64f18a0c9af6ff1d245b0bf22aa206d0c3/flask_diamond/tests/__init__.py -------------------------------------------------------------------------------- /flask_diamond/tests/mrbob.ini: -------------------------------------------------------------------------------- 1 | [variables] 2 | author.name = Example Author 3 | application.port = 5000 4 | project.url = http://example.com 5 | application.name = test-app 6 | secret = "av^\x81\x03\xd7\xd1\xbd\x92~b\x00\xe8\xf7n9\x0e\xf8i\xdb\xba'\xa9\xea" 7 | application.module = test_app 8 | author.email = author@example.com 9 | hash_salt = aIf8ObrvtSTkIIGd 10 | description.short = Test App is a short little test. 11 | project.version = 0.1.0 12 | simple_password = zzz 13 | -------------------------------------------------------------------------------- /flask_diamond/tests/test_app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | from .. import Diamond 5 | from nose.plugins.attrib import attr 6 | from unittest import TestCase 7 | 8 | 9 | class MyDiamondApp(Diamond): 10 | def init_blueprints(self): 11 | self.super("blueprints") 12 | 13 | 14 | def create_app(): 15 | diamond = Diamond() 16 | diamond.facet("configuration") 17 | diamond.facet("logs") 18 | diamond.facet("database") 19 | diamond.facet("marshalling") 20 | diamond.facet("accounts") 21 | diamond.facet("blueprints") 22 | diamond.facet("signals") 23 | diamond.facet("forms") 24 | diamond.facet("error_handlers") 25 | diamond.facet("request_handlers") 26 | diamond.facet("administration") 27 | diamond.facet("rest") 28 | diamond.facet("webassets") 29 | diamond.facet("email") 30 | diamond.facet("debugger") 31 | diamond.facet("task_queue") 32 | return diamond.app 33 | 34 | 35 | class BasicTestCase(TestCase): 36 | 37 | @attr("single") 38 | def test_create_app(self): 39 | from ..facets.database import db 40 | from ..models.role import Role 41 | from ..models.user import User 42 | 43 | app = create_app() 44 | with app.app_context(): 45 | db.drop_all() 46 | try: 47 | db.create_all() 48 | 49 | Role.add_default_roles() 50 | 51 | User.register( 52 | email="guest@example.com", 53 | password="guest", 54 | roles=["User"], 55 | ) 56 | 57 | User.register( 58 | email="admin@example.com", 59 | password="abc", 60 | roles=["Admin"], 61 | ) 62 | 63 | self.assertIsNotNone(app, msg="create app") 64 | finally: 65 | db.drop_all() 66 | -------------------------------------------------------------------------------- /flask_diamond/tests/testing.conf: -------------------------------------------------------------------------------- 1 | # Flask-Diamond (c) Ian Dennis Miller 2 | 3 | PROJECT_NAME = "Flask-Diamond" 4 | PORT = 5000 5 | LOG = "/tmp/testing.log" 6 | LOG_LEVEL = "DEBUG" 7 | SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/flask_diamond-testing.db" 8 | SECRET_KEY = "av^\x81\x03\xd7\xd1\xbd\x92~b\x00\xe8\xf7n9\x0e\xf8i\xdb\xba'\xa9\xea" 9 | BASE_URL = "http://flask_diamond.com" 10 | 11 | SECURITY_PASSWORD_SALT = "aIf8ObrvtSTkIIGd" 12 | SECURITY_POST_LOGIN_VIEW = "/admin" 13 | SECURITY_PASSWORD_HASH = 'sha256_crypt' 14 | SECURITY_URL_PREFIX = '/user' 15 | SECURITY_CHANGEABLE = True 16 | SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False 17 | SECURITY_CONFIRMABLE = False 18 | SECURITY_REGISTERABLE = False 19 | SECURITY_RECOVERABLE = False 20 | SECURITY_TRACKABLE = False 21 | SECURITY_EMAIL_SENDER = "accounts@Flask-Diamond.com" 22 | 23 | RECAPTCHA_PUBLIC_KEY = '0000_00000000000000000000000000000000000' 24 | RECAPTCHA_PRIVATE_KEY = '0000_00000000000000000000000000000000000' 25 | 26 | DEBUG = False 27 | DEBUG_TOOLBAR = False 28 | 29 | MAIL_SERVER = '127.0.0.1' 30 | MAIL_PORT = 25 31 | MAIL_USE_TLS = False 32 | MAIL_USERNAME = None 33 | MAIL_PASSWORD = None 34 | 35 | CELERY_BROKER_URL = 'sqla+sqlite:///var/db/celerydb.sqlite' 36 | CELERY_RESULT_BACKEND = 'db+sqlite:///var/db/results.sqlite' 37 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.11.1 2 | Flask-Migrate==1.3.0 3 | Flask-Admin==1.1.0 4 | Flask-WTF==0.13.1 5 | Flask-Login==0.3.2 6 | Flask-Security==1.7.5 7 | Flask-Script==2.0.5 8 | Flask-Mail==0.9.1 9 | Flask-Testing==0.4.2 10 | Flask-DebugToolbar==0.9.2 11 | Flask-Assets==0.10 12 | Flask-DbShell==1.0 13 | Flask-RESTful==0.3.2 14 | Flask-Markdown==0.3 15 | Flask-SQLAlchemy==2.0 16 | Flask-Marshmallow==0.5.0 17 | Flask-Celery-Helper==1.1.0 18 | Werkzeug==0.11.11 19 | Jinja2==2.8 20 | Sphinx==1.2.3 21 | Fabric3>=1.10.2 22 | nose==1.3.4 23 | watchdog==0.8.3 24 | wheel==0.24.0 25 | pylint==1.4.1 26 | mr.bob==0.1.1 27 | marshmallow==1.2.6 28 | alembic==0.7.4 29 | gunicorn==19.3.0 30 | GitPython==1.0.1 31 | WTForms==1.0.5 32 | alabaster==0.7.7 33 | future==0.15.2 34 | click==6.6 35 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Flask-Diamond (c) Ian Dennis Miller 3 | 4 | import re 5 | import os 6 | import codecs 7 | from setuptools import setup, find_packages 8 | 9 | 10 | def read(*rnames): 11 | return codecs.open(os.path.join(os.path.dirname(__file__), *rnames), 'r', 'utf-8').read() 12 | 13 | 14 | def grep(attrname): 15 | pattern = r"{0}\W*=\W*'([^']+)'".format(attrname) 16 | strval, = re.findall(pattern, read('flask_diamond/__meta__.py')) 17 | return strval 18 | 19 | 20 | setup( 21 | version=grep('__version__'), 22 | name='Flask-Diamond', 23 | description=( 24 | "Flask-Diamond is a batteries-included Flask framework, " 25 | "sortof like Django but radically decomposable. " 26 | "Flask-Diamond offers some opinions about " 27 | "data-centric Internet applications and systems." 28 | ), 29 | packages=find_packages(), 30 | scripts=[ 31 | "bin/flask-diamond", 32 | ], 33 | long_description=read('Readme.rst'), 34 | classifiers=[ 35 | "Development Status :: 4 - Beta", 36 | "Framework :: Flask", 37 | "Intended Audience :: Developers", 38 | "Intended Audience :: Science/Research", 39 | "License :: OSI Approved :: MIT License", 40 | "Operating System :: POSIX", 41 | "Operating System :: MacOS :: MacOS X", 42 | "Programming Language :: Python :: 2.7", 43 | "Programming Language :: Python :: 3.4", 44 | "Programming Language :: Python :: 3.5", 45 | "Topic :: Internet :: WWW/HTTP", 46 | ], 47 | include_package_data=True, 48 | keywords='', 49 | author=grep('__author__'), 50 | author_email=grep('__email__'), 51 | url=grep('__url__'), 52 | install_requires=read('requirements.txt'), 53 | license='MIT', 54 | zip_safe=False, 55 | ) 56 | --------------------------------------------------------------------------------