Skip to content

Commit

Permalink
Merge pull request #221 from SEL-Columbia/staging
Browse files Browse the repository at this point in the history
0.2.2 release
  • Loading branch information
vr2262 committed Dec 16, 2015
2 parents 0227cf5 + e8dee9d commit 4718b34
Show file tree
Hide file tree
Showing 67 changed files with 2,496 additions and 711 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015", "react"]
}
6 changes: 5 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@
"browser": true,
"node": true,
"commonjs": true,
"jquery": true
"jquery": true,
"jest": true
},
"extends": "eslint:recommended",
"ecmaFeatures": {
"jsx": true,
"modules": true,
"arrowFunctions": true,
"blockBindings": true,
"experimentalObjectRestSpread": true
},
"plugins": [
Expand Down
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ addons:
firefox: "42.0"

before_install:
- pip install coveralls flake8 coverage beautifulsoup4 py-dateutil selenium
- pip install coveralls flake8 coverage beautifulsoup4 py-dateutil pytz selenium

before_script:
- python3 -m flake8 .
Expand All @@ -20,17 +20,17 @@ before_script:
- nvm install stable
- npm install npm -g
- npm install
- node_modules/gulp/bin/gulp.js dev-build
- ./tests/python/selenium_webapp.py &>/dev/null &
- sleep 2
- node_modules/gulp/bin/gulp.js dev-build

script:
# - npm test
- npm test
- xvfb-run --server-args="-screen 0, 1280x1280x16" tests/python/coverage_run.sh

after_success:
- coveralls
# - npm coveralls
- npm coveralls

notifications:
email:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM python:3.4
WORKDIR /dokomo
RUN apt-get update && apt-get install npm nodejs -y
RUN apt-get update && apt-get install npm nodejs postgresql-client -y
ADD package.json /tmp/package.json
RUN cd /tmp && npm install && npm install lodash --save-dev
RUN cp -a /tmp/node_modules /dokomo/
Expand Down
95 changes: 35 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,90 +1,65 @@
# About
# Dokomo Forms

Dokomo Forms is a self-hosted data collection and analysis platform, and is the successor to [Formhub](https://formhub.org/).
Dokomo Forms is a free and open source data collection and analysis platform.

[![Build Status](https://travis-ci.org/SEL-Columbia/dokomoforms.svg?branch=master)](https://travis-ci.org/SEL-Columbia/dokomoforms)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SEL-Columbia/dokomoforms?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

[![Coverage Status](https://coveralls.io/repos/SEL-Columbia/dokomoforms/badge.svg?branch=master)](https://coveralls.io/r/SEL-Columbia/dokomoforms?branch=master)

[![Sauce Test Status](https://saucelabs.com/browser-matrix/dokomo_sauce_matrix.svg)](https://saucelabs.com/u/dokomo_sauce_matrix)

[![Documentation Status](https://readthedocs.org/projects/dokomoforms/badge/?version=master)](https://readthedocs.org/projects/dokomoforms/?badge=latest)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SEL-Columbia/dokomoforms?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Dependency Status](https://gemnasium.com/SEL-Columbia/dokomoforms.svg)](https://gemnasium.com/SEL-Columbia/dokomoforms)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/dokomo_sauce_matrix.svg)](https://saucelabs.com/u/dokomo_sauce_matrix)

[![Documentation Status](https://readthedocs.org/projects/dokomoforms/badge/?version=latest)](https://readthedocs.org/projects/dokomoforms/?badge=latest)

# Staging

1. Organization owns instance, and all users belong to the organization. (TODO)

2. Filesystem-level encryption. (TODO)

3. i18n

4. Focus on questions rather than surveys. (TODO)

5. You can specify configuration options in `local_config.py` or as command line flags to [webapp.py](webapp.py). The available options are defined in [dokomoforms/options.py](dokomoforms/options.py)

6. [webapp.py](webapp.py) now sets up the tables for you (no more `manage_db.py`). If you want `$ manage_db.py -d`, run

`$ ./webapp.py --kill=True`

You can also specify the schema you want like `$ ./webappy.py --schema=whatever`

7. New way to run tests (after `$ pip install tox`):

`$ tox`

Or, if you want the coverage report as well,

`$ tox -e cover`

The tests only touch the `doko_test` schema (which they create/destroy for you).
## About the Project

# Using Docker for Local Dev Environment and Deployment
Several solutions exist to handle offline mobile data collection. While this type of technology is increasingly valuable to organizations working in the developing world, the available solutions are cumbersome to use, often requiring a confusing mélange of individual software components and advanced technical skills to setup and manage.

[Docker](https://en.wikipedia.org/wiki/Docker_(software)) is a container management software that aims at component separation and deployment automation. Please refer to [the Docker API](https://docs.docker.com/) for a fuller introduction.
**Dokomo strives to simplify the process by integrating the elements of a data collection effort into a unified system, from creation of mobile-ready surveys to quick analysis and visualization of the collected data.**

## Using Docker Manually (Docker knowledge required)
## Features

There is a [Dockerfile](Dockerfile) in the root directory to build the Docker image of the Dokomo Forms webapp component building on top a Python 3 image. To build the webapp image, run
#### Mobile-Web Technology

> $ docker build -t selcolumbia/dokomoforms .
Instead of relying on platform-specific apps, Dokomo's surveys are conducted using an offline-capable mobile web app. This makes for an easier workflow for administrators and enumerators — surveys can be distributed and accessed via a normal web link, and can be conducted on (almost) any device that has a web browser.

However, Dokomo Forms as a service needs other components such as the database in order to work. We have referenced `mdillon/postgis` as the image, since we are using PostgreSQL with the PostGIS extension. You may also substitute `mdillon/postgis` with any image includes PostGIS. A manual way to run Dokomo Forms as a service would involve starting the `postgis` container and linking it to the Dokomo Forms image we have just built, such as:
![alt Dokomo Forms Admin - Manage](https://i.imgur.com/saW5zcB.jpg)

> $ docker run -d -p 8888:8888 --link postgis:db selcolumbia/dokomoforms
#### Survey Monitoring

## Using Docker for Local Development
As an adminstrator of a surveying effort, it's important to know where, when, and by whom data is being submitted. Dokomo Forms lets administrators quickly see the current progress of an effort, providing a quick list and map of the latest submissions and a graph showing submissions/day for the recent past.

`docker-compose` is the program that automates Docker container building, running, and linking as described above. It uses the [docker-compose.yml](docker-compose.yml) file which is provided in the root directory.
![alt Dokomo Forms Admin - Manage](https://i.imgur.com/6z7UJt2.jpg)

To start the service locally, run:
#### Submission Data Quick Views

> $ docker-compose up
Administrators can quickly view data from individual submissions and get some basic statistics and aggregations from each question on a survey.

Docker will download the necessary images, then build and link them. This step takes 3-5 minutes for the first build. Once the command has finished, you can visit [http://localhost:8888](http://localhost:8888) and start using Dokomo Forms.
![alt Dokomo Forms Admin - Data](https://i.imgur.com/hwYRf8e.jpg)

## Using Docker for Automated Deployment
#### Revisit Integration

`docker-machine` is the program that automates the deployment process. It can hook into many VPS providers such as [AWS](http://aws.amazon.com/), [Rackspace](http://www.rackspace.com/) and [DigitalOcean](https://www.digitalocean.com/).
Dokomo Forms integrates with [Revisit](http://revisit.global), a global facility registry API built here at the Sustainable Engineering Lab. Leveraging Revisit, multiple surveys conducted at the same facility can be easily linked, allowing changes in data points at survey locations to be tracked over time.

Here is an example using DigitalOcean:
## Under Development

1. Obtain a token from DigitalOcean. Click on "Generate New Token" from the API page as indicated below.
Dokomo Forms is under active development, with some pretty nifty features on the horizon.

![doapi](http://i.imgur.com/0SrmqX7.jpg)
#### Survey Creation GUI

2. Create a droplet with the token you have just acquired
Soon survey administrators will be able to quickly create surveys though a web-based creation tool, built directly into Dokomo Forms.

> $ docker-machine create -d digitalocean --digitalocean-access-token YOUR_ACCESS_TOKEN dokomoforms
#### Better Survey Administration

3. Make your local Docker environment aware of this new machine
- Publish surveys directly from the administration panel to enumerators' mobile devices.
- Send updates and communications to enumerators

> $ eval $(docker-machine env dokomoforms)
#### Data Visualization

4. Run `docker-compose` with the new environment
- View collected data on map
- See quick statistics and aggregations on a per-question basis

> $ docker-compose up -d
## Guides and Documentation

Now you have an instance of Dokomo Forms running on your DigitalOcean droplet!
- [User Guide](https://github.com/SEL-Columbia/dokomoforms/wiki/User-Guide)
- [Local Development Environment Setup](https://github.com/SEL-Columbia/dokomoforms/wiki/Local-Development-Environment)
- [Deployment](https://github.com/SEL-Columbia/dokomoforms/wiki/Deployment)
- [REST API Documentation](https://github.com/SEL-Columbia/dokomoforms/wiki/REST-API-v0.2.0)
5 changes: 3 additions & 2 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@

db_database = 'doko'
db_user = 'postgres'
organization = 'unconfigured organization'

https = True
organization = 'unconfigured organization'
admin_email = '[email protected]'
admin_name = 'admin'

try:
from local_config import * # NOQA
Expand Down
3 changes: 1 addition & 2 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
webapp-dev:
build: .
command: bash -c "head -c 24 /dev/urandom > cookie_secret && python webapp.py"
command: bash -c "./docker-wait-for-postgres.sh db-dev && head -c 24 /dev/urandom > cookie_secret && python webapp.py"
volumes:
- ./:/dokomo
links:
Expand All @@ -10,5 +10,4 @@ webapp-dev:
db-dev:
image: "mdillon/postgis:9.4"
environment:
POSTGRES_PASSWORD: 'password'
POSTGRES_DB: 'doko'
12 changes: 6 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
nginx:
image: "nginx:1.9.5"
image: "nginx:1.9.8"
links:
- "webapp:webapp"
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- /etc/letsencrypt:/etc/letsencrypt
- /tmp:/tmp
webapp:
image: "selcolumbia/dokomoforms"
command: bash -c "head -c 24 /dev/urandom > cookie_secret && python webapp.py"
image: "selcolumbia/dokomoforms:0.2.2"
command: bash -c "./docker-wait-for-postgres.sh db && head -c 24 /dev/urandom > cookie_secret && python webapp.py"
links:
- "db:db"
ports:
- "8888:8888"
volumes:
- ./local_config.py:/dokomo/local_config.py
db:
image: "mdillon/postgis:9.4"
environment:
POSTGRES_PASSWORD: 'password'
POSTGRES_DB: 'doko'
6 changes: 6 additions & 0 deletions docker-wait-for-postgres.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env sh
until psql --host=$1 --username=postgres -w &>/dev/null
do
echo "Waiting for PostgreSQL..."
sleep 1
done
1 change: 1 addition & 0 deletions dokomoforms/handlers/api/v0/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ def list(self, where=None):
Given a model class, build up the ORM query based on query params
and return the query result.
"""
self.session.flush()
model_cls = self.resource_type
query = self.session.query(model_cls, count().over())

Expand Down
25 changes: 16 additions & 9 deletions dokomoforms/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from dokomoforms.options import options
from dokomoforms.handlers.util import BaseHandler, authenticated_admin
from dokomoforms.models import User, Email
from dokomoforms.models import User, Administrator, Email


class Login(BaseHandler):
Expand Down Expand Up @@ -79,14 +79,21 @@ def post(self):
.one()
)
except NoResultFound:
_ = self.locale.translate
raise tornado.web.HTTPError(
422,
reason=_(
'There is no account associated with the e-mail'
' address {}'.format(data['email'])
),
)
if data['email'] != options.admin_email:
_ = self.locale.translate
raise tornado.web.HTTPError(
422,
reason=_(
'There is no account associated with the e-mail'
' address {}'.format(data['email'])
),
)
with self.session.begin():
user = Administrator(
name=options.admin_name,
emails=[Email(address=options.admin_email)]
)
self.session.add(user)
cookie_options = {
'httponly': True,
}
Expand Down
2 changes: 1 addition & 1 deletion dokomoforms/handlers/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def get(self):
if not revisit_online:
raise tornado.web.HTTPError(502)
if slow_mode: # pragma: no cover
sleep(2)
sleep(2.5)
self.write(compressed_facilities)
self.set_header('Content-Type', 'application/json')

Expand Down
4 changes: 3 additions & 1 deletion dokomoforms/handlers/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from sqlalchemy.orm.exc import NoResultFound

from dokomoforms.options import options
import dokomoforms.models as models
from dokomoforms.models import Administrator, Email
from dokomoforms.handlers.util import BaseHandler
Expand Down Expand Up @@ -193,9 +194,10 @@ def get(self):
except NoResultFound:
user = _create_demo_user(self.session)
cookie_options = {
'expires_days': None,
'httponly': True,
}
if options.https:
cookie_options['secure'] = True
self.set_secure_cookie('user', user.id, **cookie_options)
self.redirect('/')

Expand Down
3 changes: 2 additions & 1 deletion dokomoforms/handlers/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ def set_default_headers(self):
"img-src 'self' *.tile.openstreetmap.org data: blob:;"
"object-src 'self' blob:;"
"media-src 'self' blob: mediastream:;"
"connect-src 'self' blob: *.revisit.global localhost:3000;"
"connect-src 'self' blob: revisit.global *.revisit.global"
" localhost:3000;"
"default-src 'self';"
)

Expand Down
6 changes: 4 additions & 2 deletions dokomoforms/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,19 @@
define('debug', default=False, help='whether to enable debug mode', type=bool)

https_help = 'whether the application accepts https traffic'
define('https', help=https_help, type=bool)
define('https', default=True, help=https_help, type=bool)

define('organization', help='the name of your organization')
define('admin_email', help='the e-mail address of the main administrator')
define('admin_name', help='the user name of the main administrator')

persona_help = (
'the URL for login verification. Do not change this without a good reason.'
)
persona_url = 'https://verifier.login.persona.org/verify'
define('persona_verification_url', default=persona_url, help=persona_help)

revisit_url = 'http://revisit.global/api/v0/facilities.json'
revisit_url = 'https://revisit.global/api/v0/facilities.json'
revisit_help = (
'the URL for facility data. Do not change this without a good reason.'
)
Expand Down
5 changes: 3 additions & 2 deletions dokomoforms/static/src/admin/js/account-overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var AccountOverview = (function() {
var submissions = data.submissions,
$table = $('#recent-list table tbody');
submissions.forEach(function(sub) {
sub.submission_time = moment(sub.submission_time).format('MMM d, YYYY [at] HH:mm');
sub.submission_time = moment(sub.submission_time).format('MMM D, YYYY [at] HH:mm');
sub.survey = {
id: sub.survey_id,
default_language: sub.survey_default_language
Expand All @@ -70,7 +70,8 @@ var AccountOverview = (function() {

function loadRecentSubmissions() {
var limit = 5;
return $.getJSON('/api/v0/submissions?order_by=save_time:DESC&limit=' + limit +
return $.getJSON('/api/v0/submissions?user_id=' + window.CURRENT_USER_ID +
'&order_by=save_time:DESC&limit=' + limit +
'&fields=id,submission_time,submitter_name,survey_title,survey_id,survey_default_language,answers');
}

Expand Down
2 changes: 2 additions & 0 deletions dokomoforms/static/src/admin/less/variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -863,5 +863,7 @@
@page-header-border-color: @gray-lighter;
//** Width of horizontal description list titles
@dl-horizontal-offset: @component-offset-horizontal;
//** Point at which .dl-horizontal becomes horizontal
@dl-horizontal-breakpoint: @grid-float-breakpoint;
//** Horizontal line color.
@hr-border: @gray-lighter;
Loading

0 comments on commit 4718b34

Please sign in to comment.