diff --git a/fabfile/__init__.py b/fabfile/__init__.py index 13047162..48d90c73 100644 --- a/fabfile/__init__.py +++ b/fabfile/__init__.py @@ -81,13 +81,13 @@ def deploy(*slugs): for slug in slugs: deploy_single(slug) -def deploy_single(slug): +def deploy_single(path): """ Deploy a single project to S3 and, if configured, to our servers. """ require('settings', provided_by=[production, staging]) - - graphic_root = '%s/%s' % (app_config.GRAPHICS_PATH, slug) + slug, abspath = utils.parse_path(path) + graphic_root = '%s/%s' % (abspath, slug) s3_root = '%s/graphics/%s' % (app_config.PROJECT_SLUG, slug) graphic_assets = '%s/assets' % graphic_root s3_assets = '%s/assets' % s3_root @@ -98,14 +98,11 @@ def deploy_single(slug): use_assets = getattr(graphic_config, 'USE_ASSETS', True) default_max_age = getattr(graphic_config, 'DEFAULT_MAX_AGE', None) or app_config.DEFAULT_MAX_AGE assets_max_age = getattr(graphic_config, 'ASSETS_MAX_AGE', None) or app_config.ASSETS_MAX_AGE - - update_copy(slug) - + update_copy(path) if use_assets: - assets.sync(slug) - - render.render(slug) + assets.sync(path) + render.render(path) flat.deploy_folder( graphic_root, s3_root, @@ -114,7 +111,7 @@ def deploy_single(slug): }, ignore=['%s/*' % graphic_assets, '%s/*' % graphic_node_modules, # Ignore files unused on static S3 server - '*.xls', '*.xlsx', '*.pyc', '*.py', '*.less', + '*.xls', '*.xlsx', '*.pyc', '*.py', '*.less', '*.bak', '%s/base_template.html' % graphic_root, '%s/child_template.html' % graphic_root] ) @@ -136,11 +133,12 @@ def deploy_single(slug): print '' print '%s URL: %s/graphics/%s/%s' % (env.settings.capitalize(), app_config.S3_BASE_URL, slug, file_suffix) -def download_copy(slug): +def download_copy(path): """ Downloads a Google Doc as an .xlsx file. """ - graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) + slug, abspath = utils.parse_path(path) + graphic_path = '%s/%s' % (abspath, slug) try: graphic_config = load_graphic_config(graphic_path) @@ -156,12 +154,12 @@ def download_copy(slug): get_document(graphic_config.COPY_GOOGLE_DOC_KEY, copy_path) @task -def update_copy(slug=None): +def update_copy(path=None): """ Fetches the latest Google Doc and updates local JSON. """ - if slug: - download_copy(slug) + if path: + download_copy(path) return slugs = os.listdir(app_config.GRAPHICS_PATH) diff --git a/fabfile/assets.py b/fabfile/assets.py index 0b9c736d..bfbe84c0 100644 --- a/fabfile/assets.py +++ b/fabfile/assets.py @@ -14,17 +14,17 @@ import utils @task -def sync(slug): +def sync(path): """ Intelligently synchronize assets between S3 and local folder. """ ignore_globs = [] - - if not os.path.exists('%s/%s' % (app_config.GRAPHICS_PATH, slug)): + slug, abspath = utils.parse_path(path) + if not os.path.exists('%s/%s' % (abspath, slug)): print 'Slug "%s" does not exist!' % slug return - assets_root = '%s/%s/assets' % (app_config.GRAPHICS_PATH, slug) + assets_root = '%s/%s/assets' % (abspath, slug) s3_root = '%s/%s' % (app_config.ASSETS_SLUG, slug) try: diff --git a/fabfile/render.py b/fabfile/render.py index c40e8d95..d1812b71 100644 --- a/fabfile/render.py +++ b/fabfile/render.py @@ -4,22 +4,29 @@ from fabric.api import task from fabric.state import env -from fabric.tasks import execute +from glob import glob import app import app_config +import utils + @task(default=True) -def render(slug=''): +def render(path=''): """ Render HTML templates and compile assets. """ - if slug: - _render_graphics(['%s/%s' % (app_config.GRAPHICS_PATH, slug)]) + archive = False + if path: + slug, abspath = utils.parse_path(path) + if abspath != app_config.GRAPHICS_PATH: + archive = True + _render_graphics(['%s/%s' % (abspath, slug)], archive) else: _render_graphics(glob('%s/*' % app_config.GRAPHICS_PATH)) -def _render_graphics(paths): + +def _render_graphics(paths, archive=False): """ Render a set of graphics """ @@ -29,12 +36,12 @@ def _render_graphics(paths): app_config.configure_targets(env.get('settings', None)) for path in paths: - slug = path.split('%s/' % app_config.GRAPHICS_PATH)[1].split('/')[0] - + slug = path.split('/')[-1] with app.app.test_request_context(path='graphics/%s/' % slug): g.compile_includes = True g.compiled_includes = {} - + if archive: + g.alt_path = path view = app.graphic.__dict__['_graphics_detail'] content = view(slug).data @@ -45,10 +52,12 @@ def _render_graphics(paths): if not os.path.exists('%s/child_template.html' % path): continue - with app.app.test_request_context(path='graphics/%s/child.html' % slug): + with app.app.test_request_context(path='graphics/%s/child.html' % ( + slug)): g.compile_includes = True g.compiled_includes = {} - + if archive: + g.alt_path = path view = app.graphic.__dict__['_graphics_child'] content = view(slug).data diff --git a/fabfile/utils.py b/fabfile/utils.py index 8535d116..0bf00768 100644 --- a/fabfile/utils.py +++ b/fabfile/utils.py @@ -1,8 +1,11 @@ #!/usr/bin/env python +import os import boto from boto.s3.connection import OrdinaryCallingFormat from fabric.api import prompt +import app_config + def confirm(message): """ @@ -32,3 +35,18 @@ def get_bucket(bucket_name): s3 = boto.connect_s3() return s3.get_bucket(bucket_name) + + +def parse_path(path): + """ + Parse the path into abspath and slug + """ + bits = path.split('/') + if len(bits) > 1: + slug = bits[-1] + path = '/'.join(bits[:-1]) + abspath = os.path.abspath(path) + else: + slug = path + abspath = app_config.GRAPHICS_PATH + return slug, abspath diff --git a/graphic.py b/graphic.py index b64f89cf..c0f85201 100644 --- a/graphic.py +++ b/graphic.py @@ -21,9 +21,13 @@ def _graphics_detail(slug): """ Renders a parent.html index with child.html embedded as iframe. """ - from flask import request + from flask import request, g - graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) + alt_path = getattr(g, 'alt_path', None) + if alt_path: + graphic_path = alt_path + else: + graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) # NOTE: Parent must load pym.js from same source as child to prevent version conflicts! context = make_context(asset_depth=2, root_path=graphic_path) @@ -59,7 +63,12 @@ def _graphics_child(slug): """ Renders a child.html for embedding. """ - graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) + from flask import g + alt_path = getattr(g, 'alt_path', None) + if alt_path: + graphic_path = alt_path + else: + graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) print graphic_path # Fallback for legacy projects w/o child templates @@ -102,7 +111,12 @@ def _graphic_less(slug, filename): """ Compiles LESS for a graphic. """ - graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) + from flask import g + alt_path = getattr(g, 'alt_path', None) + if alt_path: + graphic_path = alt_path + else: + graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) less_path = '%s/css/%s.less' % (graphic_path, filename) if not os.path.exists(less_path): @@ -115,7 +129,13 @@ def _graphic_less(slug, filename): # Serve arbitrary static files on-demand @graphic.route('//') def _static(slug, path): - real_path = '%s/%s/%s' % (app_config.GRAPHICS_PATH, slug, path) + from flask import g + alt_path = getattr(g, 'alt_path', None) + if alt_path: + graphic_path = alt_path + else: + graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) + real_path = '%s/%s' % (graphic_path, path) try: with open(real_path) as f: diff --git a/oauth.py b/oauth.py index c95edd4d..9b3b577f 100644 --- a/oauth.py +++ b/oauth.py @@ -63,11 +63,14 @@ def oauth_required(f): """ @wraps(f) def decorated_function(*args, **kwargs): - from flask import request - + from flask import request, g + alt_path = getattr(g, 'alt_path', None) if request.path.startswith('/graphics/'): slug = request.path.split('/')[-2] - graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) + if alt_path: + graphic_path = alt_path + else: + graphic_path = '%s/%s' % (app_config.GRAPHICS_PATH, slug) try: graphic_config = load_graphic_config(graphic_path) diff --git a/render_utils.py b/render_utils.py index 9f8c2aee..aba9867d 100644 --- a/render_utils.py +++ b/render_utils.py @@ -163,9 +163,14 @@ def load_graphic_config(graphic_path, base_paths=[]): paths = [graphic_path] + base_paths - f, path, desc = imp.find_module('graphic_config', paths) - graphic_config = imp.load_module('graphic_config', f, path, desc) - f.close() + try: + f, path, desc = imp.find_module('graphic_config', paths) + graphic_config = imp.load_module('graphic_config', f, path, desc) + f.close() + except ImportError: + class EmptyConfig: + pass + graphic_config = EmptyConfig() sys.path.pop(0)