Skip to content

Commit

Permalink
Merge pull request #76 from DallasMorningNews/subprocess
Browse files Browse the repository at this point in the history
JS compiler subprocess. Example app.
  • Loading branch information
hobbes7878 committed Dec 6, 2017
2 parents cbf19c7 + 209e1b1 commit 6cf3381
Show file tree
Hide file tree
Showing 17 changed files with 6,202 additions and 5 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [0.5.17] - 2017-12-02
### Added
- JS compiler subprocess
- Example app in repo

## [0.5.15] - 2017-09-15
### Changed
- Updated chartwerk-editor to 0.2.4
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ship:
python setup.py sdist bdist_wheel
twine upload dist/* --skip-existing
3 changes: 3 additions & 0 deletions chartwerk/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,7 @@ class Settings:
Settings.SLACK_TOKEN = getattr(
project_settings, 'CHARTWERK_SLACK_TOKEN', None)

Settings.JS_SUBPROCESS = getattr(
project_settings, 'CHARTWERK_JS_SUBPROCESS', None)

settings = Settings
34 changes: 33 additions & 1 deletion chartwerk/tasks/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import logging
import os
import subprocess
from datetime import datetime

import boto3
Expand All @@ -12,6 +13,7 @@
from chartwerk.models import Chart
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.template.loader import render_to_string
from django.utils.encoding import smart_bytes, smart_text
from django.utils.six.moves.urllib.request import urlopen

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -114,6 +116,36 @@ def cleaner(werk):
return werk


def compile_js(scripts):
"""Optionally compile JavaScript.
Users can specify args to subprocess and pipe JS through any
available CLI compiler.
"""
def subprocess_js(script):
process = subprocess.Popen(
app_settings.JS_SUBPROCESS,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
output, output_err = process.communicate(
smart_bytes(script, encoding='utf-8')
)

if process.returncode != 0:
logging.error("Error compiling JavaScript: %s", output_err)
return script

return smart_text(output)

if app_settings.JS_SUBPROCESS is None:
return scripts

scripts['helper'] = subprocess_js(scripts['helper'])
scripts['draw'] = subprocess_js(scripts['draw'])
return scripts


@shared_task
def write_to_aws(pk):
"""Write to AWS S3 bucket.
Expand All @@ -137,7 +169,7 @@ def write_to_aws(pk):
scripts=werk.data['scripts']['dependencies']['scripts'],
styles=werk.data['scripts']['dependencies']['styles']
)
werk.scripts = werk.data['scripts']
werk.scripts = compile_js(werk.data['scripts'])
werk = cleaner(werk)
# DOUBLE-WIDE
werk.data['ui']['size'] = 'double'
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@
# built documents.
#
# The short X.Y version.
version = u'0.5.15'
version = u'0.5.17'
# The full version, including alpha/beta/rc tags.
release = u'0.5.15'
release = u'0.5.17'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
22 changes: 22 additions & 0 deletions docs/configuring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,28 @@ Cache header to add to chart files when published to S3.
If you're using Amazon CloudFront in front of your S3 bucket and would like to create an invalidation whenever charts are updated, add your distribution ID to this setting.


Compiling JavaScript
--------------------

:code:`CHARTWERK_JS_SUBPROCESS`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you'd like to write your template scripts using modern JavaScript syntax, you can pipe them through a compiler before publishing by specifying arguments to pass to Python's `subprocess <https://docs.python.org/3/library/subprocess.html>`_ module.

For example, if you'd like to compile ES2015 syntax using `Babel <https://babeljs.io/docs/usage/cli/>`_, you could specify subprocess args like this:

.. code-block:: python
CHARTWERK_JS_SUBPROCESS = ['npx', 'babel', '--presets=es2015']
This option assumes you have already installed the dependencies referenced in your subprocess on your server. Obviously, you can only use CLI compilers with this method.


.. note::

This method **does not** compile scripts in the Editor. You should use a browser that supports the syntax features you're targeting when you and your users develop charts. JavaScript will be compiled before baking your charts to S3.

GitHub
------

Expand Down
217 changes: 217 additions & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@

# Created by https://www.gitignore.io/api/python,django,osx,linux,node

### Django ###
*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
media

# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
# in your Git repository. Update and uncomment the following line accordingly.
# <django-project-name>/staticfiles/

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### Node ###
# Logs
logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env


### OSX ###
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Python ###
# Byte-compiled / optimized / DLL files
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo

# Django stuff:

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# End of https://www.gitignore.io/api/python,django,osx,linux,node
3 changes: 3 additions & 0 deletions example/chartwerkapp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .celery import app as celery_app

__all__ = ['celery_app']
16 changes: 16 additions & 0 deletions example/chartwerkapp/celery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os

from celery import Celery
from django.conf import settings

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'chartwerkapp.settings')

app = Celery('chartwerk')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.conf.update(
task_serializer='json'
)
# Use synchronous tasks in local dev
if settings.DEBUG:
app.conf.update(task_always_eager=True)
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS, related_name='celery')

0 comments on commit 6cf3381

Please sign in to comment.