changeset 601:effbc78b5528

admin: Better error reporting, general clean-up.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 24 Jan 2016 10:42:33 -0800
parents c37307a72f79
children c6bc0ef03f82
files foodtruck/config.py foodtruck/configuration.py foodtruck/sites.py foodtruck/templates/error.html foodtruck/templates/install.html foodtruck/views/dashboard.py foodtruck/views/main.py foodtruck/web.py piecrust/commands/builtin/admin.py
diffstat 9 files changed, 258 insertions(+), 228 deletions(-) [+]
line wrap: on
line diff
--- a/foodtruck/config.py	Sun Jan 24 10:41:36 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-import os.path
-import copy
-import logging
-import yaml
-from piecrust.configuration import (
-        Configuration, ConfigurationError, ConfigurationLoader,
-        merge_dicts)
-
-
-logger = logging.getLogger(__name__)
-
-
-def get_foodtruck_config(dirname=None):
-    dirname = dirname or os.getcwd()
-    cfg_path = os.path.join(dirname, 'foodtruck.yml')
-    return FoodTruckConfiguration(cfg_path)
-
-
-class FoodTruckConfigNotFoundError(Exception):
-    pass
-
-
-class FoodTruckConfiguration(Configuration):
-    def __init__(self, cfg_path):
-        super(FoodTruckConfiguration, self).__init__()
-        self.cfg_path = cfg_path
-
-    def _load(self):
-        try:
-            with open(self.cfg_path, 'r', encoding='utf-8') as fp:
-                values = yaml.load(
-                        fp.read(),
-                        Loader=ConfigurationLoader)
-
-            self._values = self._validateAll(values)
-        except OSError:
-            raise FoodTruckConfigNotFoundError()
-        except Exception as ex:
-            raise ConfigurationError(
-                    "Error loading configuration from: %s" %
-                    self.cfg_path) from ex
-
-    def _validateAll(self, values):
-        if values is None:
-            values = {}
-
-        values = merge_dicts(copy.deepcopy(default_configuration), values)
-
-        return values
-
-    def save(self):
-        with open(self.cfg_path, 'w', encoding='utf8') as fp:
-            self.cfg.write(fp)
-
-
-default_configuration = {
-        'triggers': {
-            'bake': 'chef bake'
-            },
-        'scm': {
-            'type': 'hg'
-            },
-        'security': {
-            'username': '',
-            'password': ''
-            }
-        }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foodtruck/configuration.py	Sun Jan 24 10:42:33 2016 -0800
@@ -0,0 +1,68 @@
+import os.path
+import copy
+import logging
+import yaml
+from piecrust.configuration import (
+        Configuration, ConfigurationError, ConfigurationLoader,
+        merge_dicts)
+
+
+logger = logging.getLogger(__name__)
+
+
+def get_foodtruck_config(dirname=None):
+    dirname = dirname or os.getcwd()
+    cfg_path = os.path.join(dirname, 'foodtruck.yml')
+    return FoodTruckConfiguration(cfg_path)
+
+
+class FoodTruckConfigNotFoundError(Exception):
+    pass
+
+
+class FoodTruckConfiguration(Configuration):
+    def __init__(self, cfg_path):
+        super(FoodTruckConfiguration, self).__init__()
+        self.cfg_path = cfg_path
+
+    def _load(self):
+        try:
+            with open(self.cfg_path, 'r', encoding='utf-8') as fp:
+                values = yaml.load(
+                        fp.read(),
+                        Loader=ConfigurationLoader)
+
+            self._values = self._validateAll(values)
+        except OSError:
+            raise FoodTruckConfigNotFoundError()
+        except Exception as ex:
+            raise ConfigurationError(
+                    "Error loading configuration from: %s" %
+                    self.cfg_path) from ex
+
+    def _validateAll(self, values):
+        if values is None:
+            values = {}
+
+        values = merge_dicts(copy.deepcopy(default_configuration), values)
+
+        return values
+
+    def save(self):
+        with open(self.cfg_path, 'w', encoding='utf8') as fp:
+            self.cfg.write(fp)
+
+
+default_configuration = {
+        'triggers': {
+            'bake': 'chef bake'
+            },
+        'scm': {
+            'type': 'hg'
+            },
+        'security': {
+            'username': '',
+            'password': ''
+            }
+        }
+
--- a/foodtruck/sites.py	Sun Jan 24 10:41:36 2016 -0800
+++ b/foodtruck/sites.py	Sun Jan 24 10:42:33 2016 -0800
@@ -6,6 +6,7 @@
 import threading
 import subprocess
 from piecrust.app import PieCrust
+from piecrust.configuration import merge_dicts
 
 
 logger = logging.getLogger(__name__)
@@ -15,6 +16,10 @@
     pass
 
 
+class InvalidSiteError(Exception):
+    pass
+
+
 class Site(object):
     def __init__(self, name, root_dir, config):
         self.name = name
@@ -48,7 +53,7 @@
             elif global_scm_cfg:
                 cfg = copy.deepcopy(global_scm_cfg)
 
-            if not cfg or not 'type' in cfg:
+            if not cfg or 'type' not in cfg:
                 raise Exception("No SCM available for site: %s" % self.name)
 
             if cfg['type'] == 'hg':
@@ -134,12 +139,12 @@
 
         scfg = self.config.get('sites/%s' % name)
         if scfg is None:
-            raise Exception("No such site: %s" % name)
+            raise InvalidSiteError("No such site: %s" % name)
         root_dir = scfg.get('path')
         if root_dir is None:
-            raise Exception("Site '%s' has no path defined." % name)
+            raise InvalidSiteError("Site '%s' has no path defined." % name)
         if not os.path.isdir(root_dir):
-            raise Exception("Site '%s' has an invalid path." % name)
+            raise InvalidSiteError("Site '%s' has an invalid path." % name)
         self._site_dirs[name] = root_dir
         return root_dir
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foodtruck/templates/error.html	Sun Jan 24 10:42:33 2016 -0800
@@ -0,0 +1,13 @@
+{% set title = 'An Error Occured' %}
+
+{% extends 'layouts/master.html' %}
+
+{% block content %}
+<div class="container">
+    {% if error == 'invalid_site' %}
+        <p>There was an error with your configuration file: {{exception}}</p>
+    {% endif %}
+</div>
+{% endblock %}
+
+
--- a/foodtruck/templates/install.html	Sun Jan 24 10:41:36 2016 -0800
+++ b/foodtruck/templates/install.html	Sun Jan 24 10:42:33 2016 -0800
@@ -1,8 +1,10 @@
-{% extends 'layouts/default.html' %}
+{% set title = 'Configuration File Missing' %}
+
+{% extends 'layouts/master.html' %}
 
 {% block content %}
 <div class="container">
-Please install this shit.
+    No FoodTruck configuration file was found. Did you run <code>chef admin init</code>?
 </div>
 {% endblock %}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foodtruck/views/dashboard.py	Sun Jan 24 10:42:33 2016 -0800
@@ -0,0 +1,146 @@
+import os
+import os.path
+import logging
+from flask import (
+        g, request,
+        render_template, url_for, redirect)
+from flask.ext.login import login_user, logout_user, login_required
+from piecrust.configuration import parse_config_header
+from piecrust.rendering import QualifiedPage
+from piecrust.uriutil import split_uri
+from ..textutil import text_preview
+from ..views import with_menu_context
+from ..web import app, load_user, after_this_request
+
+
+logger = logging.getLogger(__name__)
+
+
+@app.route('/')
+@login_required
+def index():
+    data = {}
+    site_name = request.cookies.get('foodtruck_site_name')
+    site = g.sites.get(site_name)
+    assert site is not None
+
+    fs_endpoints = {}
+    data['sources'] = []
+    for source in site.piecrust_app.sources:
+        if source.is_theme_source:
+            continue
+        facs = source.getPageFactories()
+        src_data = {
+                'name': source.name,
+                'list_url': url_for('list_source', source_name=source.name),
+                'page_count': len(facs)}
+        data['sources'].append(src_data)
+
+        fe = getattr(source, 'fs_endpoint', None)
+        if fe:
+            fs_endpoints[fe] = source
+
+    st = site.scm.getStatus()
+    data['new_pages'] = []
+    for p in st.new_files:
+        pd = _getWipData(p, site, fs_endpoints)
+        if pd:
+            data['new_pages'].append(pd)
+    data['edited_pages'] = []
+    for p in st.edited_files:
+        pd = _getWipData(p, site, fs_endpoints)
+        if pd:
+            data['edited_pages'].append(pd)
+
+    data['site_name'] = site.name
+    data['site_title'] = site.piecrust_app.config.get('site/title', site.name)
+    data['url_bake'] = url_for('bake_site')
+    data['url_preview'] = url_for('preview_site_root', sitename=site.name)
+
+    data['sites'] = []
+    for k, v in g.config.get('sites').items():
+        data['sites'].append({
+            'name': k,
+            'display_name': v.get('name', k),
+            'url': url_for('index', site_name=site_name)
+            })
+    data['needs_switch'] = len(g.config.get('sites')) > 1
+    data['url_switch'] = url_for('switch_site')
+
+    with_menu_context(data)
+    return render_template('dashboard.html', **data)
+
+
+def _getWipData(path, site, fs_endpoints):
+    source = None
+    for endpoint, s in fs_endpoints.items():
+        if path.startswith(endpoint):
+            source = s
+            break
+    if source is None:
+        return None
+
+    fac = source.buildPageFactory(os.path.join(site.root_dir, path))
+    route = site.piecrust_app.getRoute(
+            source.name, fac.metadata, skip_taxonomies=True)
+    if not route:
+        return None
+
+    qp = QualifiedPage(fac.buildPage(), route, fac.metadata)
+    uri = qp.getUri()
+    _, slug = split_uri(site.piecrust_app, uri)
+
+    with open(fac.path, 'r', encoding='utf8') as fp:
+        raw_text = fp.read()
+
+    header, offset = parse_config_header(raw_text)
+    extract = text_preview(raw_text, offset=offset)
+    return {
+            'title': qp.config.get('title'),
+            'slug': slug,
+            'url': url_for('edit_page', slug=slug),
+            'text': extract
+            }
+
+
+@login_required
+@app.route('/switch_site', methods=['POST'])
+def switch_site():
+    site_name = request.form.get('site_name')
+    if not site_name:
+        return redirect(url_for('index'))
+
+    @after_this_request
+    def _save_site(resp):
+        resp.set_cookie('foodtruck_site_name', site_name)
+
+    return redirect(url_for('index'))
+
+
+@app.route('/login', methods=['GET', 'POST'])
+def login():
+    data = {}
+
+    if request.method == 'POST':
+        username = request.form.get('username')
+        password = request.form.get('password')
+        remember = request.form.get('remember')
+
+        user = load_user(username)
+        if user is not None and app.bcrypt:
+            if app.bcrypt.check_password_hash(user.password, password):
+                login_user(user, remember=bool(remember))
+                return redirect(url_for('index'))
+        data['message'] = (
+                "User '%s' doesn't exist or password is incorrect." %
+                username)
+
+    return render_template('login.html', **data)
+
+
+@app.route('/logout')
+@login_required
+def logout():
+    logout_user()
+    return redirect(url_for('index'))
+
--- a/foodtruck/views/main.py	Sun Jan 24 10:41:36 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-import os
-import os.path
-import logging
-from flask import (
-        g, request,
-        render_template, url_for, redirect)
-from flask.ext.login import login_user, logout_user, login_required
-from piecrust.configuration import parse_config_header
-from piecrust.rendering import QualifiedPage
-from piecrust.uriutil import split_uri
-from ..textutil import text_preview
-from ..views import with_menu_context
-from ..web import app, load_user, after_this_request
-
-
-logger = logging.getLogger(__name__)
-
-
-@app.route('/')
-@login_required
-def index():
-    data = {}
-    site_name = request.cookies.get('foodtruck_site_name')
-    site = g.sites.get(site_name)
-    assert site is not None
-
-    fs_endpoints = {}
-    data['sources'] = []
-    for source in site.piecrust_app.sources:
-        if source.is_theme_source:
-            continue
-        facs = source.getPageFactories()
-        src_data = {
-                'name': source.name,
-                'list_url': url_for('list_source', source_name=source.name),
-                'page_count': len(facs)}
-        data['sources'].append(src_data)
-
-        fe = getattr(source, 'fs_endpoint', None)
-        if fe:
-            fs_endpoints[fe] = source
-
-    st = site.scm.getStatus()
-    data['new_pages'] = []
-    for p in st.new_files:
-        pd = _getWipData(p, site, fs_endpoints)
-        if pd:
-            data['new_pages'].append(pd)
-    data['edited_pages'] = []
-    for p in st.edited_files:
-        pd = _getWipData(p, site, fs_endpoints)
-        if pd:
-            data['edited_pages'].append(pd)
-
-    data['site_name'] = site.name
-    data['site_title'] = site.piecrust_app.config.get('site/title', site.name)
-    data['url_bake'] = url_for('bake_site')
-    data['url_preview'] = url_for('preview_site_root', sitename=site.name)
-
-    data['sites'] = []
-    for k, v in g.config.get('sites').items():
-        data['sites'].append({
-            'name': k,
-            'display_name': v.get('name', k),
-            'url': url_for('index', site_name=site_name)
-            })
-    data['needs_switch'] = len(g.config.get('sites')) > 1
-    data['url_switch'] = url_for('switch_site')
-
-    with_menu_context(data)
-    return render_template('dashboard.html', **data)
-
-
-def _getWipData(path, site, fs_endpoints):
-    source = None
-    for endpoint, s in fs_endpoints.items():
-        if path.startswith(endpoint):
-            source = s
-            break
-    if source is None:
-        return None
-
-    fac = source.buildPageFactory(os.path.join(site.root_dir, path))
-    route = site.piecrust_app.getRoute(
-            source.name, fac.metadata, skip_taxonomies=True)
-    if not route:
-        return None
-
-    qp = QualifiedPage(fac.buildPage(), route, fac.metadata)
-    uri = qp.getUri()
-    _, slug = split_uri(site.piecrust_app, uri)
-
-    with open(fac.path, 'r', encoding='utf8') as fp:
-        raw_text = fp.read()
-
-    header, offset = parse_config_header(raw_text)
-    extract = text_preview(raw_text, offset=offset)
-    return {
-            'title': qp.config.get('title'),
-            'slug': slug,
-            'url': url_for('edit_page', slug=slug),
-            'text': extract
-            }
-
-
-@login_required
-@app.route('/switch_site', methods=['POST'])
-def switch_site():
-    site_name = request.form.get('site_name')
-    if not site_name:
-        return redirect(url_for('index'))
-
-    @after_this_request
-    def _save_site(resp):
-        resp.set_cookie('foodtruck_site_name', site_name)
-
-    return redirect(url_for('index'))
-
-
-@app.route('/login', methods=['GET', 'POST'])
-def login():
-    data = {}
-
-    if request.method == 'POST':
-        username = request.form.get('username')
-        password = request.form.get('password')
-        remember = request.form.get('remember')
-
-        user = load_user(username)
-        if user is not None and app.bcrypt:
-            if app.bcrypt.check_password_hash(user.password, password):
-                login_user(user, remember=bool(remember))
-                return redirect(url_for('index'))
-        data['message'] = (
-                "User '%s' doesn't exist or password is incorrect." %
-                username)
-
-    return render_template('login.html', **data)
-
-
-@app.route('/logout')
-@login_required
-def logout():
-    logout_user()
-    return redirect(url_for('index'))
-
--- a/foodtruck/web.py	Sun Jan 24 10:41:36 2016 -0800
+++ b/foodtruck/web.py	Sun Jan 24 10:42:33 2016 -0800
@@ -2,9 +2,9 @@
 import os.path
 import logging
 from flask import Flask, g, request, render_template
-from .config import (
+from .configuration import (
         FoodTruckConfigNotFoundError, get_foodtruck_config)
-from .sites import FoodTruckSites
+from .sites import FoodTruckSites, InvalidSiteError
 
 
 logger = logging.getLogger(__name__)
@@ -55,7 +55,8 @@
         if current is None:
             names = g.config.get('sites')
             if not names or not isinstance(names, dict):
-                raise FoodTruckConfigNotFoundError()
+                raise InvalidSiteError(
+                        "No sites are defined in the configuration file.")
             current = next(iter(names.keys()))
         s = FoodTruckSites(g.config, current)
         return s
@@ -71,9 +72,17 @@
     return response
 
 
-@app.errorhandler(FoodTruckConfigNotFoundError)
-def _on_config_missing(ex):
-    return render_template('install.html')
+if not app.config['DEBUG']:
+    app.logger.debug("Registering exception handlers.")
+
+    @app.errorhandler(FoodTruckConfigNotFoundError)
+    def _on_config_missing(ex):
+        return render_template('install.html')
+
+    @app.errorhandler(InvalidSiteError)
+    def _on_invalid_site(ex):
+        data = {'error': 'invalid_site', 'exception': str(ex)}
+        return render_template('error.html', **data)
 
 
 @app.errorhandler
@@ -132,8 +141,8 @@
 
 import foodtruck.views.baking  # NOQA
 import foodtruck.views.create  # NOQA
+import foodtruck.views.dashboard  # NOQA
 import foodtruck.views.edit  # NOQA
-import foodtruck.views.main  # NOQA
 import foodtruck.views.menu  # NOQA
 import foodtruck.views.preview  # NOQA
 import foodtruck.views.settings  # NOQA
--- a/piecrust/commands/builtin/admin.py	Sun Jan 24 10:41:36 2016 -0800
+++ b/piecrust/commands/builtin/admin.py	Sun Jan 24 10:42:33 2016 -0800
@@ -41,6 +41,7 @@
     def _runFoodTruck(self, ctx):
         from foodtruck import settings
         settings.FOODTRUCK_CMDLINE_MODE = True
+        settings.FOODTRUCK_ROOT = os.getcwd()
         from foodtruck.main import run_foodtruck
         run_foodtruck(debug=ctx.args.debug)
 
@@ -76,7 +77,7 @@
             fp.write(ft_config)
 
         flask_config = """
-secret_key = '%(secret_key)s'
+SECRET_KEY = %(secret_key)s
 """
         flask_config = flask_config % {'secret_key': secret_key}
         with open('app.cfg', 'w', encoding='utf8') as fp: